diff --git a/.build/common.ts b/.build/common.ts
index e9d0ebfa69..274977fa26 100644
--- a/.build/common.ts
+++ b/.build/common.ts
@@ -22,4 +22,9 @@ export const packageOptions = {
packageName: 'mermaid-zenuml',
file: 'detector.ts',
},
+ 'mermaid-flowchart-elk': {
+ name: 'mermaid-flowchart-elk',
+ packageName: 'mermaid-flowchart-elk',
+ file: 'detector.ts',
+ },
} as const;
diff --git a/.build/types.ts b/.build/types.ts
new file mode 100644
index 0000000000..4192407824
--- /dev/null
+++ b/.build/types.ts
@@ -0,0 +1,18 @@
+import { packageOptions } from './common.js';
+import { execSync } from 'child_process';
+
+const buildType = (packageName: string) => {
+ console.log(`Building types for ${packageName}`);
+ try {
+ const out = execSync(`tsc -p ./packages/${packageName}/tsconfig.json --emitDeclarationOnly`);
+ out.length > 0 && console.log(out.toString());
+ } catch (e) {
+ console.error(e);
+ e.stdout.length > 0 && console.error(e.stdout.toString());
+ e.stderr.length > 0 && console.error(e.stderr.toString());
+ }
+};
+
+for (const { packageName } of Object.values(packageOptions)) {
+ buildType(packageName);
+}
diff --git a/.esbuild/server.ts b/.esbuild/server.ts
index ae23212472..9102c7de83 100644
--- a/.esbuild/server.ts
+++ b/.esbuild/server.ts
@@ -5,39 +5,25 @@ import { getBuildConfig, defaultOptions } from './util.js';
import { context } from 'esbuild';
import chokidar from 'chokidar';
import { generateLangium } from '../.build/generateLangium.js';
+import { packageOptions } from '../.build/common.js';
-const parserCtx = await context(
- getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'parser' })
+const configs = Object.values(packageOptions).map(({ packageName }) =>
+ getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: packageName })
);
-const mermaidCtx = await context({
- ...getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid' }),
- sourcemap: 'linked',
+const mermaidIIFEConfig = getBuildConfig({
+ ...defaultOptions,
+ minify: false,
+ core: false,
+ entryName: 'mermaid',
+ format: 'iife',
});
-const mermaidIIFECtx = await context(
- getBuildConfig({
- ...defaultOptions,
- minify: false,
- core: false,
- entryName: 'mermaid',
- format: 'iife',
- })
-);
-const externalCtx = await context(
- getBuildConfig({
- ...defaultOptions,
- minify: false,
- core: false,
- entryName: 'mermaid-example-diagram',
- })
-);
-const zenumlCtx = await context(
- getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid-zenuml' })
-);
-const contexts = [parserCtx, mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx];
+configs.push(mermaidIIFEConfig);
+
+const contexts = await Promise.all(configs.map((config) => context(config)));
const rebuildAll = async () => {
console.time('Rebuild time');
- await Promise.all(contexts.map((ctx) => ctx.rebuild()));
+ await Promise.all(contexts.map((ctx) => ctx.rebuild())).catch((e) => console.error(e));
console.timeEnd('Rebuild time');
};
@@ -102,10 +88,9 @@ async function createServer() {
app.use(cors());
app.get('/events', eventsHandler);
- app.use(express.static('./packages/parser/dist'));
- app.use(express.static('./packages/mermaid/dist'));
- app.use(express.static('./packages/mermaid-zenuml/dist'));
- app.use(express.static('./packages/mermaid-example-diagram/dist'));
+ for (const { packageName } of Object.values(packageOptions)) {
+ app.use(express.static(`./packages/${packageName}/dist`));
+ }
app.use(express.static('demos'));
app.use(express.static('cypress/platform'));
diff --git a/.esbuild/util.ts b/.esbuild/util.ts
index 1efe3bc901..5c21cbf452 100644
--- a/.esbuild/util.ts
+++ b/.esbuild/util.ts
@@ -65,6 +65,9 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
minify,
logLevel: 'info',
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
+ define: {
+ 'import.meta.vitest': 'undefined',
+ },
});
if (core) {
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 7e0c78ff1e..fa15f39e1c 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -3,9 +3,9 @@ contact_links:
- name: GitHub Discussions
url: https://github.com/mermaid-js/mermaid/discussions
about: Ask the Community questions or share your own graphs in our discussions.
- - name: Slack
- url: https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE
- about: Join our Community on Slack for Help and a casual chat.
+ - name: Discord
+ url: https://discord.gg/wwtabKgp8y
+ about: Join our Community on Discord for Help and a casual chat.
- name: Documentation
url: https://mermaid.js.org
about: Read our documentation for all that Mermaid.js can offer.
diff --git a/.github/codecov.yaml b/.github/codecov.yaml
index 950edb6a9a..9450430859 100644
--- a/.github/codecov.yaml
+++ b/.github/codecov.yaml
@@ -15,3 +15,4 @@ coverage:
# Turing off for now as code coverage isn't stable and causes unnecessary build failures.
# default:
# threshold: 2%
+ patch: off
diff --git a/.github/lychee.toml b/.github/lychee.toml
index b13e536161..4af304a990 100644
--- a/.github/lychee.toml
+++ b/.github/lychee.toml
@@ -34,8 +34,8 @@ exclude = [
# Don't check files that are generated during the build via `pnpm docs:code`
'packages/mermaid/src/docs/config/setup/*',
-# Ignore slack invite
-"https://join.slack.com/"
+# Ignore Discord invite
+"https://discord.gg"
]
# Exclude all private IPs from checking.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 9847c25232..f20204a714 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -14,5 +14,5 @@ Make sure you
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
- [ ] :computer: have added necessary unit/e2e tests.
-- [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/docs/community/code.md#3-update-documentation) is used for all new features.
+- [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/docs/community/contributing.md#update-documentation) is used for all new features.
- [ ] :bookmark: targeted `develop` branch
diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml
index 543fb5dbb4..fd32e59adf 100644
--- a/.github/workflows/e2e-applitools.yml
+++ b/.github/workflows/e2e-applitools.yml
@@ -28,7 +28,7 @@ jobs:
- if: ${{ ! env.USE_APPLI }}
name: Warn if not using Applitools
run: |
- echo "::error,title=Not using Applitols::APPLITOOLS_API_KEY is empty, disabling Applitools for this run."
+ echo "::error,title=Not using Applitools::APPLITOOLS_API_KEY is empty, disabling Applitools for this run."
- uses: actions/checkout@v4
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 71806a9c46..b8232b8c0e 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -1,16 +1,62 @@
+# We use github cache to save snapshots between runs.
+# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
+# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
+# These are then downloaded before running the E2E, providing the reference snapshots.
+# If there are any errors, the diff image is uploaded to artifacts, and the user is notified.
+
name: E2E
on:
push:
+ branches-ignore:
+ - 'gh-readonly-queue/**'
pull_request:
merge_group:
permissions:
contents: read
+env:
+ # For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
+ targetHash: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || (github.event.before == '0000000000000000000000000000000000000000' && 'develop' || github.event.before) }}
+
jobs:
+ cache:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: pnpm/action-setup@v2
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 18.x
+ - name: Cache snapshots
+ id: cache-snapshot
+ uses: actions/cache@v4
+ with:
+ save-always: true
+ path: ./cypress/snapshots
+ key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
+
+ # If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
+ - name: Switch to base branch
+ if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ env.targetHash }}
+
+ - name: Cypress run
+ uses: cypress-io/github-action@v4
+ id: cypress-snapshot-gen
+ if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
+ with:
+ start: pnpm run dev
+ wait-on: 'http://localhost:9000'
+ browser: chrome
+
e2e:
runs-on: ubuntu-latest
+ needs: cache
strategy:
fail-fast: false
matrix:
@@ -27,6 +73,14 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
+ # These cached snapshots are downloaded, providing the reference snapshots.
+ - name: Cache snapshots
+ id: cache-snapshot
+ uses: actions/cache/restore@v3
+ with:
+ path: ./cypress/snapshots
+ key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
+
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
@@ -38,6 +92,7 @@ jobs:
with:
start: pnpm run dev:coverage
wait-on: 'http://localhost:9000'
+ browser: chrome
# Disable recording if we don't have an API key
# e.g. if this action was run from a fork
record: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
@@ -46,6 +101,7 @@ jobs:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
VITEST_COVERAGE: true
CYPRESS_COMMIT: ${{ github.sha }}
+
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v3
# Run step only pushes to develop and pull_requests
@@ -57,9 +113,55 @@ jobs:
fail_ci_if_error: false
verbose: true
token: 6845cc80-77ee-4e17-85a1-026cd95e0766
+
+ # We upload the artifacts into numbered archives to prevent overwriting
- name: Upload Artifacts
- uses: actions/upload-artifact@v3
- if: ${{ failure() && steps.cypress.conclusion == 'failure' }}
+ uses: actions/upload-artifact@v4
+ if: ${{ always() }}
+ with:
+ name: snapshots-${{ matrix.containers }}
+ retention-days: 1
+ path: ./cypress/snapshots
+
+ combineArtifacts:
+ needs: e2e
+ runs-on: ubuntu-latest
+ if: ${{ always() }}
+ steps:
+ # Download all snapshot artifacts and merge them into a single folder
+ - name: Download All Artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: snapshots
+ pattern: snapshots-*
+ merge-multiple: true
+
+ # For successful push events, we save the snapshots cache
+ - name: Save snapshots cache
+ id: cache-upload
+ if: ${{ github.event_name == 'push' && needs.e2e.result != 'failure' }}
+ uses: actions/cache/save@v3
+ with:
+ path: ./snapshots
+ key: ${{ runner.os }}-snapshots-${{ github.event.after }}
+
+ - name: Flatten images to a folder
+ if: ${{ needs.e2e.result == 'failure' }}
+ run: |
+ mkdir errors
+ cd snapshots
+ find . -mindepth 2 -type d -name "*__diff_output__*" -exec sh -c 'mv "$0"/*.png ../errors/' {} \;
+
+ - name: Upload Error snapshots
+ if: ${{ needs.e2e.result == 'failure' }}
+ uses: actions/upload-artifact@v4
+ id: upload-artifacts
with:
name: error-snapshots
- path: cypress/snapshots/**/__diff_output__/*
+ retention-days: 10
+ path: errors/
+
+ - name: Notify Users
+ if: ${{ needs.e2e.result == 'failure' }}
+ run: |
+ echo "::error title=Visual tests failed::You can view images that failed by downloading the error-snapshots artifact: ${{ steps.upload-artifacts.outputs.artifact-url }}"
diff --git a/.github/workflows/link-checker.yml b/.github/workflows/link-checker.yml
index c3e2ee44fe..3d4956945e 100644
--- a/.github/workflows/link-checker.yml
+++ b/.github/workflows/link-checker.yml
@@ -36,7 +36,7 @@ jobs:
restore-keys: cache-lychee-
- name: Link Checker
- uses: lycheeverse/lychee-action@v1.8.0
+ uses: lycheeverse/lychee-action@v1.9.1
with:
args: >-
--config .github/lychee.toml
diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml
index 8ad1b13ecd..db1dd1f485 100644
--- a/.github/workflows/release-draft.yml
+++ b/.github/workflows/release-draft.yml
@@ -3,7 +3,7 @@ name: Draft Release
on:
push:
branches:
- - develop
+ - master
permissions:
contents: read
diff --git a/.github/workflows/update-browserlist.yml b/.github/workflows/update-browserlist.yml
index 0a83df795d..f4fa2a982f 100644
--- a/.github/workflows/update-browserlist.yml
+++ b/.github/workflows/update-browserlist.yml
@@ -9,10 +9,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - run: npx browserslist@latest --update-db
+ - uses: pnpm/action-setup@v2
+ - run: npx update-browserslist-db@latest
- name: Commit changes
uses: EndBug/add-and-commit@v9
with:
author_name: ${{ github.actor }}
author_email: ${{ github.actor }}@users.noreply.github.com
message: 'chore: update browsers list'
+ push: false
+ - name: Create Pull Request
+ uses: peter-evans/create-pull-request@v5
+ with:
+ branch: update-browserslist
+ title: Update Browserslist
diff --git a/.gitignore b/.gitignore
index 5e612aabe3..a0fd1c50b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,6 +47,7 @@ stats/
demos/dev/**
!/demos/dev/example.html
!/demos/dev/reload.js
+tsx-0/**
# autogenereated by langium-cli
-generated/
+generated/
\ No newline at end of file
diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs
index 3ae66bba2e..231c91f8f4 100644
--- a/.lintstagedrc.mjs
+++ b/.lintstagedrc.mjs
@@ -6,6 +6,6 @@ export default {
// https://prettier.io/docs/en/cli.html#--cache
'prettier --write',
],
- 'cSpell.json': ['ts-node-esm scripts/fixCSpell.ts'],
+ 'cSpell.json': ['tsx scripts/fixCSpell.ts'],
'**/*.jison': ['pnpm -w run lint:jison'],
};
diff --git a/.npmrc b/.npmrc
index 4c2f52b3be..e72930ead1 100644
--- a/.npmrc
+++ b/.npmrc
@@ -1,2 +1,3 @@
+registry=https://registry.npmjs.org
auto-install-peers=true
strict-peer-dependencies=false
diff --git a/.prettierignore b/.prettierignore
index af5c555393..a0cd771e33 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -10,6 +10,8 @@ stats
.nyc_output
# Autogenerated by `pnpm run --filter mermaid types:build-config`
packages/mermaid/src/config.type.ts
-
# autogenereated by langium-cli
generated/
+# Ignore the files creates in /demos/dev except for example.html
+demos/dev/**
+!/demos/dev/example.html
diff --git a/.vite/build.ts b/.vite/build.ts
index 9cb128eb18..7ce93a497f 100644
--- a/.vite/build.ts
+++ b/.vite/build.ts
@@ -74,6 +74,9 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
output,
},
},
+ define: {
+ 'import.meta.vitest': 'undefined',
+ },
resolve: {
extensions: [],
},
diff --git a/.vite/server.ts b/.vite/server.ts
index 838d711910..99d16f6f24 100644
--- a/.vite/server.ts
+++ b/.vite/server.ts
@@ -1,6 +1,7 @@
import express from 'express';
import cors from 'cors';
import { createServer as createViteServer } from 'vite';
+import { packageOptions } from '../.build/common.js';
async function createServer() {
const app = express();
@@ -14,10 +15,9 @@ async function createServer() {
});
app.use(cors());
- app.use(express.static('./packages/parser/dist'));
- app.use(express.static('./packages/mermaid/dist'));
- app.use(express.static('./packages/mermaid-zenuml/dist'));
- app.use(express.static('./packages/mermaid-example-diagram/dist'));
+ for (const { packageName } of Object.values(packageOptions)) {
+ app.use(express.static(`./packages/${packageName}/dist`));
+ }
app.use(vite.middlewares);
app.use(express.static('demos'));
app.use(express.static('cypress/platform'));
diff --git a/.vscode/launch.json b/.vscode/launch.json
index dc5ec94a10..7d17e55d2d 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -18,7 +18,8 @@
"type": "node",
"request": "launch",
"args": ["scripts/docs.cli.mts"],
- "runtimeArgs": ["--loader", "ts-node/esm"],
+ // we'll need to change this to --import in Node.JS v20.6.0 and up
+ "runtimeArgs": ["--loader", "tsx/esm"],
"cwd": "${workspaceRoot}/packages/mermaid",
"skipFiles": ["
- 📖 Documentation | 🚀 Getting Started | 🌐 CDN | 🙌 Join Us + 📖 Documentation | 🚀 Getting Started | 🌐 CDN | 🙌 Join Us
简体中文
@@ -33,7 +33,7 @@ Try Live Editor previews of future releases:
@@ -74,12 +74,12 @@ Mermaid addresses this problem by enabling users to create easily modifiable dia
Mermaid allows even non-programmers to easily create detailed diagrams through the [Mermaid Live Editor](https://mermaid.live/).
-For video tutorials, visit our [Tutorials](./docs/config/Tutorials.md) page.
+For video tutorials, visit our [Tutorials](./docs/ecosystem/tutorials.md) page.
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations-community.md).
You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations-community.md).
-For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/intro/getting-started.md), [Usage](./docs/config/usage.md) and [Tutorials](./docs/config/Tutorials.md).
+For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/intro/getting-started.md), [Usage](./docs/config/usage.md) and [Tutorials](./docs/ecosystem/tutorials.md).
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 98975ea331..c468b2d9fa 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -15,7 +15,7 @@ Mermaid
实时编辑器!
- 📖 文档 | 🚀 入门 | 🌐 CDN | 🙌 加入我们 + 📖 文档 | 🚀 入门 | 🌐 CDN | 🙌 加入我们
English
@@ -34,7 +34,7 @@ Mermaid
[![Coverage Status](https://codecov.io/github/mermaid-js/mermaid/branch/develop/graph/badge.svg)](https://app.codecov.io/github/mermaid-js/mermaid/tree/develop)
[![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid)
[![NPM Downloads](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid)
-[![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
+[![Join our Discord!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=discord&label=discord)](https://discord.gg/wwtabKgp8y)
[![Twitter Follow](https://img.shields.io/badge/Social-mermaidjs__-blue?style=social&logo=X)](https://twitter.com/mermaidjs_)
@@ -57,9 +57,9 @@ Mermaid 是一个基于 Javascript 的图表绘制工具,通过解析类 Markd
Mermaid 通过允许用户创建便于修改的图表来解决这一难题,它也可以作为生产脚本(或其他代码)的一部分。
Mermaid 甚至能让非程序员也能通过 [Mermaid Live Editor](https://mermaid.live/) 轻松创建详细的图表。
-你可以访问 [教程](./docs/config/Tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/ecosystem/integrations-community.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
+你可以访问 [教程](./docs/ecosystem/tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/ecosystem/integrations-community.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
-如果想要查看关于 Mermaid 更详细的介绍及基础使用方式,可以查看 [入门指引](./docs/intro/getting-started.md), [用法](./docs/config/usage.md) 和 [教程](./docs/config/Tutorials.md).
+如果想要查看关于 Mermaid 更详细的介绍及基础使用方式,可以查看 [入门指引](./docs/intro/getting-started.md), [用法](./docs/config/usage.md) 和 [教程](./docs/ecosystem/tutorials.md).
diff --git a/applitools.config.js b/applitools.config.js
deleted file mode 100644
index 4cf02220ac..0000000000
--- a/applitools.config.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-var-requires
-const { defineConfig } = require('cypress');
-
-module.exports = defineConfig({
- testConcurrency: 1,
- browser: [
- // Add browsers with different viewports
- // { width: 800, height: 600, name: 'chrome' },
- // { width: 700, height: 500, name: 'firefox' },
- // { width: 1600, height: 1200, name: 'ie11' },
- // { width: 1024, height: 768, name: 'edgechromium' },
- // { width: 800, height: 600, name: 'safari' },
- // // Add mobile emulation devices in Portrait mode
- // { deviceName: 'iPhone X', screenOrientation: 'portrait' },
- // { deviceName: 'Pixel 2', screenOrientation: 'portrait' },
- ],
- // set batch name to the configuration
- // batchName: `Mermaid ${process.env.APPLI_BRANCH ?? "'no APPLI_BRANCH set'"}`,
-});
diff --git a/cSpell.json b/cSpell.json
index f60e7b3785..5566b673fe 100644
--- a/cSpell.json
+++ b/cSpell.json
@@ -26,6 +26,7 @@
"città",
"classdef",
"codedoc",
+ "codemia",
"colour",
"commitlint",
"cpettitt",
@@ -105,6 +106,7 @@
"pathe",
"pbrolin",
"phpbb",
+ "pixelmatch",
"plantuml",
"playfair",
"pnpm",
@@ -127,6 +129,7 @@
"sidharth",
"sidharthv",
"sphinxcontrib",
+ "ssim",
"startx",
"starty",
"statediagram",
diff --git a/cypress.config.cjs b/cypress.config.cjs
deleted file mode 100644
index 30076c56ef..0000000000
--- a/cypress.config.cjs
+++ /dev/null
@@ -1,24 +0,0 @@
-/* eslint-disable @typescript-eslint/no-var-requires */
-
-const { defineConfig } = require('cypress');
-const { addMatchImageSnapshotPlugin } = require('cypress-image-snapshot/plugin');
-const coverage = require('@cypress/code-coverage/task');
-
-module.exports = defineConfig({
- projectId: 'n2sma2',
- e2e: {
- specPattern: 'cypress/integration/**/*.{js,jsx,ts,tsx}',
- setupNodeEvents(on, config) {
- coverage(on, config);
- addMatchImageSnapshotPlugin(on, config);
- // copy any needed variables from process.env to config.env
- config.env.useAppli = process.env.USE_APPLI ? true : false;
-
- // do not forget to return the changed config object!
- return config;
- },
- },
- video: false,
-});
-
-require('@applitools/eyes-cypress')(module);
diff --git a/cypress.config.ts b/cypress.config.ts
new file mode 100644
index 0000000000..4182d92a87
--- /dev/null
+++ b/cypress.config.ts
@@ -0,0 +1,30 @@
+import { defineConfig } from 'cypress';
+import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin';
+import coverage from '@cypress/code-coverage/task';
+import eyesPlugin from '@applitools/eyes-cypress';
+export default eyesPlugin(
+ defineConfig({
+ projectId: 'n2sma2',
+ viewportWidth: 1440,
+ viewportHeight: 1024,
+ e2e: {
+ specPattern: 'cypress/integration/**/*.{js,ts}',
+ setupNodeEvents(on, config) {
+ coverage(on, config);
+ on('before:browser:launch', (browser, launchOptions) => {
+ if (browser.name === 'chrome' && browser.isHeadless) {
+ launchOptions.args.push('--window-size=1440,1024', '--force-device-scale-factor=1');
+ }
+ return launchOptions;
+ });
+ addMatchImageSnapshotPlugin(on, config);
+ // copy any needed variables from process.env to config.env
+ config.env.useAppli = process.env.USE_APPLI ? true : false;
+
+ // do not forget to return the changed config object!
+ return config;
+ },
+ },
+ video: false,
+ })
+);
diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts
index c656f638da..aed5d7973c 100644
--- a/cypress/helpers/util.ts
+++ b/cypress/helpers/util.ts
@@ -10,7 +10,7 @@ interface CypressConfig {
type CypressMermaidConfig = MermaidConfig & CypressConfig;
interface CodeObject {
- code: string;
+ code: string | string[];
mermaid: CypressMermaidConfig;
}
@@ -25,7 +25,7 @@ const batchId: string =
: Cypress.env('CYPRESS_COMMIT') || Date.now().toString());
export const mermaidUrl = (
- graphStr: string,
+ graphStr: string | string[],
options: CypressMermaidConfig,
api: boolean
): string => {
@@ -82,7 +82,7 @@ export const urlSnapshotTest = (
};
export const renderGraph = (
- graphStr: string,
+ graphStr: string | string[],
options: CypressMermaidConfig = {},
api = false
): void => {
diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index 7cbc5d1059..23338271f5 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -117,7 +117,6 @@ describe('Configuration', () => {
});
it('should not taint the initial configuration when using multiple directives', () => {
const url = 'http://localhost:9000/regression/issue-1874.html';
- cy.viewport(1440, 1024);
cy.visit(url);
cy.get('svg');
diff --git a/cypress/integration/other/flowchart-elk.spec.js b/cypress/integration/other/flowchart-elk.spec.js
new file mode 100644
index 0000000000..22a6efc0f5
--- /dev/null
+++ b/cypress/integration/other/flowchart-elk.spec.js
@@ -0,0 +1,14 @@
+import { urlSnapshotTest, openURLAndVerifyRendering } from '../../helpers/util.ts';
+
+describe('Flowchart elk', () => {
+ it('should use dagre as fallback', () => {
+ urlSnapshotTest('http://localhost:9000/flow-elk.html', {
+ name: 'flow-elk fallback to dagre',
+ });
+ });
+ it('should allow overriding with external package', () => {
+ urlSnapshotTest('http://localhost:9000/flow-elk.html?elk=true', {
+ name: 'flow-elk overriding dagre with elk',
+ });
+ });
+});
diff --git a/cypress/integration/other/rerender.spec.js b/cypress/integration/other/rerender.spec.js
index f160a2e273..d14c6257e0 100644
--- a/cypress/integration/other/rerender.spec.js
+++ b/cypress/integration/other/rerender.spec.js
@@ -1,14 +1,12 @@
describe('Rerendering', () => {
it('should be able to render after an error has occurred', () => {
const url = 'http://localhost:9000/render-after-error.html';
- cy.viewport(1440, 1024);
cy.visit(url);
cy.get('#graphDiv').should('exist');
});
it('should be able to render and rerender a graph via API', () => {
const url = 'http://localhost:9000/rerender.html';
- cy.viewport(1440, 1024);
cy.visit(url);
cy.get('#graph [id^=flowchart-A]').should('have.text', 'XMas');
diff --git a/cypress/integration/rendering/classDiagram-v2.spec.js b/cypress/integration/rendering/classDiagram-v2.spec.js
index 37e9cada02..20a1aea0ab 100644
--- a/cypress/integration/rendering/classDiagram-v2.spec.js
+++ b/cypress/integration/rendering/classDiagram-v2.spec.js
@@ -571,4 +571,14 @@ class C13["With Città foreign language"]
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
});
+ it('should render a simple class diagram with style definition', () => {
+ imgSnapshotTest(
+ `
+ classDiagram-v2
+ class Class10
+ style Class10 fill:#f9f,stroke:#333,stroke-width:4px
+ `,
+ { logLevel: 1, flowchart: { htmlLabels: false } }
+ );
+ });
});
diff --git a/cypress/integration/rendering/debug.spec.js b/cypress/integration/rendering/debug.spec.js
deleted file mode 100644
index 56ad0f15f8..0000000000
--- a/cypress/integration/rendering/debug.spec.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { imgSnapshotTest } from '../../helpers/util.ts';
-
-describe('Flowchart', () => {
- it('34: testing the label width in percy', () => {
- imgSnapshotTest(
- `graph TD
- A[Christmas]
- `,
- { theme: 'forest', fontFamily: '"Noto Sans SC", sans-serif' }
- );
- });
-});
diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js
index aac4a31b17..857d395be7 100644
--- a/cypress/integration/rendering/flowchart-v2.spec.js
+++ b/cypress/integration/rendering/flowchart-v2.spec.js
@@ -729,6 +729,37 @@ A ~~~ B
{}
);
});
+
+ it('5064: Should render when subgraph child has links to outside node and subgraph', () => {
+ imgSnapshotTest(
+ `flowchart TB
+ Out --> In
+ subgraph Sub
+ In
+ end
+ Sub --> In`
+ );
+ });
+
+ it('5059: Should render when subgraph contains only subgraphs, has link to outside and itself is part of a link', () => {
+ imgSnapshotTest(
+ `flowchart
+
+ subgraph Main
+ subgraph Child1
+ Node1
+ Node2
+ end
+ subgraph Child2
+ Node3
+ Node4
+ end
+ end
+ Main --> Out1
+ Child2 --> Out2`
+ );
+ });
+
describe('Markdown strings flowchart (#4220)', () => {
describe('html labels', () => {
it('With styling and classes', () => {
@@ -874,4 +905,93 @@ end
});
});
});
+ describe('Subgraph title margins', () => {
+ it('Should render subgraphs with title margins set (LR)', () => {
+ imgSnapshotTest(
+ `flowchart LR
+
+ subgraph TOP
+ direction TB
+ subgraph B1
+ direction RL
+ i1 -->f1
+ end
+ subgraph B2
+ direction BT
+ i2 -->f2
+ end
+ end
+ A --> TOP --> B
+ B1 --> B2
+ `,
+ { flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } }
+ );
+ });
+ it('Should render subgraphs with title margins set (TD)', () => {
+ imgSnapshotTest(
+ `flowchart TD
+
+ subgraph TOP
+ direction LR
+ subgraph B1
+ direction RL
+ i1 -->f1
+ end
+ subgraph B2
+ direction BT
+ i2 -->f2
+ end
+ end
+ A --> TOP --> B
+ B1 --> B2
+ `,
+ { flowchart: { subGraphTitleMargin: { top: 8, bottom: 16 } } }
+ );
+ });
+ it('Should render subgraphs with title margins set (LR) and htmlLabels set to false', () => {
+ imgSnapshotTest(
+ `flowchart LR
+
+ subgraph TOP
+ direction TB
+ subgraph B1
+ direction RL
+ i1 -->f1
+ end
+ subgraph B2
+ direction BT
+ i2 -->f2
+ end
+ end
+ A --> TOP --> B
+ B1 --> B2
+ `,
+ {
+ htmlLabels: false,
+ flowchart: { htmlLabels: false, subGraphTitleMargin: { top: 10, bottom: 5 } },
+ }
+ );
+ });
+ it('Should render subgraphs with title margins and edge labels', () => {
+ imgSnapshotTest(
+ `flowchart LR
+
+ subgraph TOP
+ direction TB
+ subgraph B1
+ direction RL
+ i1 --lb1-->f1
+ end
+ subgraph B2
+ direction BT
+ i2 --lb2-->f2
+ end
+ end
+ A --lb3--> TOP --lb4--> B
+ B1 --lb5--> B2
+ `,
+ { flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } }
+ );
+ });
+ });
});
diff --git a/cypress/integration/rendering/gantt.spec.js b/cypress/integration/rendering/gantt.spec.js
index 998a092c24..73ff4ee005 100644
--- a/cypress/integration/rendering/gantt.spec.js
+++ b/cypress/integration/rendering/gantt.spec.js
@@ -245,7 +245,10 @@ describe('Gantt diagram', () => {
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
- expect(maxWidthValue).to.be.within(984 * 0.95, 984 * 1.05);
+ expect(maxWidthValue).to.be.within(
+ Cypress.config().viewportWidth * 0.95,
+ Cypress.config().viewportWidth * 1.05
+ );
});
});
@@ -285,11 +288,11 @@ describe('Gantt diagram', () => {
{ gantt: { useMaxWidth: false } }
);
cy.get('svg').should((svg) => {
- // const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
- // use within because the absolute value can be slightly different depending on the environment ±5%
- // expect(height).to.be.within(484 * 0.95, 484 * 1.05);
- expect(width).to.be.within(984 * 0.95, 984 * 1.05);
+ expect(width).to.be.within(
+ Cypress.config().viewportWidth * 0.95,
+ Cypress.config().viewportWidth * 1.05
+ );
expect(svg).to.not.have.attr('style');
});
});
diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js
index 9f040a36f0..19ddde31d4 100644
--- a/cypress/integration/rendering/gitGraph.spec.js
+++ b/cypress/integration/rendering/gitGraph.spec.js
@@ -701,4 +701,246 @@ gitGraph TB:
{}
);
});
+ it('34: should render a simple gitgraph with two branches from same commit', () => {
+ imgSnapshotTest(
+ `gitGraph
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ branch feature-001
+ commit id:"3-abcdefg"
+ commit id:"4-abcdefg"
+ checkout main
+ branch feature-002
+ commit id:"5-abcdefg"
+ checkout feature-001
+ merge feature-002
+ `,
+ {}
+ );
+ });
+ it('35: should render a simple gitgraph with two branches from same commit | Vertical Branch', () => {
+ imgSnapshotTest(
+ `gitGraph TB:
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ branch feature-001
+ commit id:"3-abcdefg"
+ commit id:"4-abcdefg"
+ checkout main
+ branch feature-002
+ commit id:"5-abcdefg"
+ checkout feature-001
+ merge feature-002
+ `,
+ {}
+ );
+ });
+ it('36: should render GitGraph with branch that is not used immediately', () => {
+ imgSnapshotTest(
+ `gitGraph LR:
+ commit id:"1-abcdefg"
+ branch x
+ checkout main
+ commit id:"2-abcdefg"
+ checkout x
+ commit id:"3-abcdefg"
+ checkout main
+ merge x
+ `,
+ {}
+ );
+ });
+ it('37: should render GitGraph with branch that is not used immediately | Vertical Branch', () => {
+ imgSnapshotTest(
+ `gitGraph TB:
+ commit id:"1-abcdefg"
+ branch x
+ checkout main
+ commit id:"2-abcdefg"
+ checkout x
+ commit id:"3-abcdefg"
+ checkout main
+ merge x
+ `,
+ {}
+ );
+ });
+ it('38: should render GitGraph with branch and sub-branch neither of which used immediately', () => {
+ imgSnapshotTest(
+ `gitGraph LR:
+ commit id:"1-abcdefg"
+ branch x
+ checkout main
+ commit id:"2-abcdefg"
+ checkout x
+ commit id:"3-abcdefg"
+ checkout main
+ merge x
+ checkout x
+ branch y
+ checkout x
+ commit id:"4-abcdefg"
+ checkout y
+ commit id:"5-abcdefg"
+ checkout x
+ merge y
+ `,
+ {}
+ );
+ });
+ it('39: should render GitGraph with branch and sub-branch neither of which used immediately | Vertical Branch', () => {
+ imgSnapshotTest(
+ `gitGraph TB:
+ commit id:"1-abcdefg"
+ branch x
+ checkout main
+ commit id:"2-abcdefg"
+ checkout x
+ commit id:"3-abcdefg"
+ checkout main
+ merge x
+ checkout x
+ branch y
+ checkout x
+ commit id:"4-abcdefg"
+ checkout y
+ commit id:"5-abcdefg"
+ checkout x
+ merge y
+ `,
+ {}
+ );
+ });
+ it('40: should render a simple gitgraph with cherry pick merge commit', () => {
+ imgSnapshotTest(
+ `gitGraph
+ commit id: "ZERO"
+ branch feature
+ branch release
+ checkout feature
+ commit id: "A"
+ commit id: "B"
+ checkout main
+ merge feature id: "M"
+ checkout release
+ cherry-pick id: "M" parent:"B"`
+ );
+ });
+ it('41: should render default GitGraph with parallelCommits set to false', () => {
+ imgSnapshotTest(
+ `gitGraph
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ branch develop
+ commit id:"3-abcdefg"
+ commit id:"4-abcdefg"
+ checkout main
+ branch feature
+ commit id:"5-abcdefg"
+ commit id:"6-abcdefg"
+ checkout main
+ commit id:"7-abcdefg"
+ commit id:"8-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: false } }
+ );
+ });
+ it('42: should render GitGraph with parallel commits', () => {
+ imgSnapshotTest(
+ `gitGraph
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ branch develop
+ commit id:"3-abcdefg"
+ commit id:"4-abcdefg"
+ checkout main
+ branch feature
+ commit id:"5-abcdefg"
+ commit id:"6-abcdefg"
+ checkout main
+ commit id:"7-abcdefg"
+ commit id:"8-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: true } }
+ );
+ });
+ it('43: should render GitGraph with parallel commits | Vertical Branch', () => {
+ imgSnapshotTest(
+ `gitGraph TB:
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ branch develop
+ commit id:"3-abcdefg"
+ commit id:"4-abcdefg"
+ checkout main
+ branch feature
+ commit id:"5-abcdefg"
+ commit id:"6-abcdefg"
+ checkout main
+ commit id:"7-abcdefg"
+ commit id:"8-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: true } }
+ );
+ });
+ it('44: should render GitGraph with unconnected branches and no parallel commits', () => {
+ imgSnapshotTest(
+ `gitGraph
+ branch dev
+ branch v2
+ branch feat
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ checkout main
+ commit id:"3-abcdefg"
+ checkout dev
+ commit id:"4-abcdefg"
+ checkout v2
+ commit id:"5-abcdefg"
+ checkout main
+ commit id:"6-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: false } }
+ );
+ });
+ it('45: should render GitGraph with unconnected branches and parallel commits', () => {
+ imgSnapshotTest(
+ `gitGraph
+ branch dev
+ branch v2
+ branch feat
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ checkout main
+ commit id:"3-abcdefg"
+ checkout dev
+ commit id:"4-abcdefg"
+ checkout v2
+ commit id:"5-abcdefg"
+ checkout main
+ commit id:"6-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: true } }
+ );
+ });
+ it('46: should render GitGraph with unconnected branches and parallel commits | Vertical Branch', () => {
+ imgSnapshotTest(
+ `gitGraph TB:
+ branch dev
+ branch v2
+ branch feat
+ commit id:"1-abcdefg"
+ commit id:"2-abcdefg"
+ checkout main
+ commit id:"3-abcdefg"
+ checkout dev
+ commit id:"4-abcdefg"
+ checkout v2
+ commit id:"5-abcdefg"
+ checkout main
+ commit id:"6-abcdefg"
+ `,
+ { gitGraph: { parallelCommits: true } }
+ );
+ });
});
diff --git a/cypress/integration/rendering/pie.spec.ts b/cypress/integration/rendering/pie.spec.ts
index 269efafb26..4a1d774c0a 100644
--- a/cypress/integration/rendering/pie.spec.ts
+++ b/cypress/integration/rendering/pie.spec.ts
@@ -44,7 +44,7 @@ describe('pie chart', () => {
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
- expect(maxWidthValue).to.eq(984);
+ expect(maxWidthValue).to.be.within(590, 600); // depends on installed fonts: 596.2 on my PC, 597.5 on CI
});
});
@@ -59,7 +59,7 @@ describe('pie chart', () => {
);
cy.get('svg').should((svg) => {
const width = parseFloat(svg.attr('width'));
- expect(width).to.eq(984);
+ expect(width).to.be.within(590, 600); // depends on installed fonts: 596.2 on my PC, 597.5 on CI
expect(svg).to.not.have.attr('style');
});
});
diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js
index 7659138241..27e03da9c0 100644
--- a/cypress/integration/rendering/sequencediagram.spec.js
+++ b/cypress/integration/rendering/sequencediagram.spec.js
@@ -930,4 +930,36 @@ context('Sequence diagram', () => {
});
});
});
+ context('render after error', () => {
+ it('should render diagram after fixing destroy participant error', () => {
+ cy.on('uncaught:exception', (err) => {
+ return false;
+ });
+
+ renderGraph([
+ `sequenceDiagram
+ Alice->>Bob: Hello Bob, how are you ?
+ Bob->>Alice: Fine, thank you. And you?
+ create participant Carl
+ Alice->>Carl: Hi Carl!
+ create actor D as Donald
+ Carl->>D: Hi!
+ destroy Carl
+ Alice-xCarl: We are too many
+ destroy Bo
+ Bob->>Alice: I agree`,
+ `sequenceDiagram
+ Alice->>Bob: Hello Bob, how are you ?
+ Bob->>Alice: Fine, thank you. And you?
+ create participant Carl
+ Alice->>Carl: Hi Carl!
+ create actor D as Donald
+ Carl->>D: Hi!
+ destroy Carl
+ Alice-xCarl: We are too many
+ destroy Bob
+ Bob->>Alice: I agree`,
+ ]);
+ });
+ });
});
diff --git a/cypress/platform/flow-elk.html b/cypress/platform/flow-elk.html
new file mode 100644
index 0000000000..f319f62e28
--- /dev/null
+++ b/cypress/platform/flow-elk.html
@@ -0,0 +1,28 @@
+
+
+ flowchart-elk + a[hello] --> b[world] + b --> c{test} + c --> one + c --> two + c --> three ++ + + + diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 3589640d98..6fc1fe17db 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -24,8 +24,18 @@ // -- This is will overwrite an existing command -- // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) -// import '@percy/cypress'; - import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command'; +// The SSIM comparison method can be used if the pixelmatch is throwing lots of false positives. +// SSIM actually does not catch minute changes in the image, so it is not as accurate as pixelmatch. +// addMatchImageSnapshotCommand({ +// comparisonMethod: 'ssim', +// failureThreshold: 0.01, +// failureThresholdType: 'percent', +// customDiffConfig: { +// ssim: 'fast', +// }, +// blur: 1, +// }); + addMatchImageSnapshotCommand(); diff --git a/demos/flowchart-elk.html b/demos/flowchart-elk.html new file mode 100644 index 0000000000..69ac2d2bc6 --- /dev/null +++ b/demos/flowchart-elk.html @@ -0,0 +1,35 @@ + + + + + +
+ flowchart-elk TD + A([Start]) ==> B[Step 1] + B ==> C{Flow 1} + C -- Choice 1.1 --> D[Step 2.1] + C -- Choice 1.3 --> I[Step 2.3] + C == Choice 1.2 ==> E[Step 2.2] + D --> F{Flow 2} + E ==> F{Flow 2} + F{Flow 2} == Choice 2.1 ==> H[Feedback node] + H[Feedback node] ==> B[Step 1] + F{Flow 2} == Choice 2.2 ==> G((Finish)) + ++ + + + diff --git a/demos/git.html b/demos/git.html index f24217711e..92e0e68635 100644 --- a/demos/git.html +++ b/demos/git.html @@ -14,30 +14,364 @@ -
--- - title: Simple Git diagram + title: Simple "branch and merge" (left-to-right) --- - gitGraph: - options - { - "nodeSpacing": 50, - "nodeRadius": 5 - } - end - branch master + gitGraph LR: commit branch newbranch checkout newbranch commit + checkout main + merge newbranch ++
+ --- + title: Simple "branch and merge" (top-to-bottom) + --- + gitGraph TB: + commit + branch newbranch + checkout newbranch + commit + checkout main + merge newbranch ++
+ --- + title: Continuous development (left-to-right) + --- + gitGraph LR: commit - checkout master + branch develop + checkout develop commit + checkout main + merge develop + checkout develop + commit + checkout main + merge develop ++
+ --- + title: Continuous development (top-to-bottom) + --- + gitGraph TB: + commit + branch develop + checkout develop + commit + checkout main + merge develop + checkout develop + commit + checkout main + merge develop ++
+ --- + title: Merge feature to advanced main (left-to-right) + --- + gitGraph LR: + commit + branch newbranch + checkout newbranch + commit + checkout main commit merge newbranch- +
+ --- + title: Merge feature to advanced main (top-to-bottom) + --- + gitGraph TB: + commit + branch newbranch + checkout newbranch + commit + checkout main + commit + merge newbranch ++
+ --- + title: Two-way merges (left-to-right) + --- + gitGraph LR: + commit + branch develop + checkout develop + commit + checkout main + merge develop + commit + checkout develop + merge main + commit + checkout main + merge develop ++
+ --- + title: Two-way merges (top-to-bottom) + --- + gitGraph TB: + commit + branch develop + checkout develop + commit + checkout main + merge develop + commit + checkout develop + merge main + commit + checkout main + merge develop ++
+ --- + title: Cherry-pick from branch (left-to-right) + --- + gitGraph LR: + commit + branch newbranch + checkout newbranch + commit id: "Pick me" + checkout main + commit + checkout newbranch + commit + checkout main + cherry-pick id: "Pick me" ++
+ --- + title: Cherry-pick from branch (top-to-bottom) + --- + gitGraph TB: + commit + branch newbranch + checkout newbranch + commit id: "Pick me" + checkout main + commit + checkout newbranch + commit + checkout main + cherry-pick id: "Pick me" ++
+ --- + title: Cherry-pick from main (left-to-right) + --- + gitGraph LR: + commit + branch develop + commit + checkout main + commit id:"A" + checkout develop + commit + cherry-pick id: "A" ++
+ --- + title: Cherry-pick from main (top-to-bottom) + --- + gitGraph TB: + commit + branch develop + commit + checkout main + commit id:"A" + checkout develop + commit + cherry-pick id: "A" ++
+ --- + title: Cherry-pick then merge (left-to-right) + --- + gitGraph LR: + commit + branch newbranch + checkout newbranch + commit id: "Pick me" + checkout main + commit + checkout newbranch + commit + checkout main + cherry-pick id: "Pick me" + merge newbranch ++
+ --- + title: Cherry-pick then merge (top-to-bottom) + --- + gitGraph TB: + commit + branch newbranch + checkout newbranch + commit id: "Pick me" + checkout main + commit + checkout newbranch + commit + checkout main + cherry-pick id: "Pick me" + merge newbranch ++
+ --- + title: Merge from main onto undeveloped branch (left-to-right) + --- + gitGraph LR: + commit + branch develop + commit + checkout main + commit + checkout develop + merge main ++
+ --- + title: Merge from main onto undeveloped branch (top-to-bottom) + --- + gitGraph TB: + commit + branch develop + commit + checkout main + commit + checkout develop + merge main ++
+ --- + title: Merge from main onto developed branch (left-to-right) + --- + gitGraph LR: + commit + branch develop + commit + checkout main + commit + checkout develop + commit + merge main ++
+ --- + title: Merge from main onto developed branch (top-to-bottom) + --- + gitGraph TB: + commit + branch develop + commit + checkout main + commit + checkout develop + commit + merge main ++
+ --- + title: Two branches from same commit (left-to-right) + --- + gitGraph LR: + commit + commit + branch feature-001 + commit + commit + checkout main + branch feature-002 + commit + checkout feature-001 + merge feature-002 ++
+ --- + title: Two branches from same commit (top-to-bottom) + --- + gitGraph TB: + commit + commit + branch feature-001 + commit + commit + checkout main + branch feature-002 + commit + checkout feature-001 + merge feature-002 ++
+ --- + title: Three branches and a cherry-pick from each (left-to-right) + --- + gitGraph LR: + commit id: "ZERO" + branch develop + commit id:"A" + checkout main + commit id:"ONE" + checkout develop + commit id:"B" + branch featureA + commit id:"FIX" + commit id: "FIX-2" + checkout main + commit id:"TWO" + cherry-pick id:"A" + commit id:"THREE" + cherry-pick id:"FIX" + checkout develop + commit id:"C" + merge featureA ++
+ --- + title: Three branches and a cherry-pick from each (top-to-bottom) + --- + gitGraph TB: + commit id: "ZERO" + branch develop + commit id:"A" + checkout main + commit id:"ONE" + checkout develop + commit id:"B" + branch featureA + commit id:"FIX" + commit id: "FIX-2" + checkout main + commit id:"TWO" + cherry-pick id:"A" + commit id:"THREE" + cherry-pick id:"FIX" + checkout develop + commit id:"C" + merge featureA +