diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3574c3599..ff34d24fd 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -13,6 +13,6 @@ Describe the way your implementation works or what design decisions you made if Make sure you - [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md) -- [ ] :computer: have added unit/e2e tests (if appropriate) -- [ ] :notebook: have added documentation (if appropriate) +- [ ] :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/development.md#3-update-documentation) is used for all new features. - [ ] :bookmark: targeted `develop` branch diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index b25ff89cc..152b177ae 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -1,6 +1,10 @@ name: Build Vitepress docs on: + push: + branches: + - master + - release/* pull_request: merge_group: @@ -25,5 +29,9 @@ jobs: - name: Install Packages run: pnpm install --frozen-lockfile + - name: Verify release verion + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release')) }} + run: pnpm --filter mermaid run docs:verify-version + - name: Run Build run: pnpm --filter mermaid run docs:build:vitepress diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 6777a1e4f..3e6966677 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -45,6 +45,7 @@ jobs: env: 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 diff --git a/.vscode/launch.json b/.vscode/launch.json index 83b80fa40..dc5ec94a1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,7 +17,7 @@ "name": "Docs generation", "type": "node", "request": "launch", - "args": ["src/docs.mts"], + "args": ["scripts/docs.cli.mts"], "runtimeArgs": ["--loader", "ts-node/esm"], "cwd": "${workspaceRoot}/packages/mermaid", "skipFiles": ["/**", "**/node_modules/**"], diff --git a/cypress/helpers/util.js b/cypress/helpers/util.js deleted file mode 100644 index 4d13b3590..000000000 --- a/cypress/helpers/util.js +++ /dev/null @@ -1,93 +0,0 @@ -const utf8ToB64 = (str) => { - return window.btoa(unescape(encodeURIComponent(str))); -}; - -const batchId = 'mermaid-batch' + new Date().getTime(); - -export const mermaidUrl = (graphStr, options, api) => { - const obj = { - code: graphStr, - mermaid: options, - }; - const objStr = JSON.stringify(obj); - let url = 'http://localhost:9000/e2e.html?graph=' + utf8ToB64(objStr); - if (api) { - url = 'http://localhost:9000/xss.html?graph=' + graphStr; - } - - if (options.listUrl) { - cy.log(options.listId, ' ', url); - } - - return url; -}; - -export const imgSnapshotTest = (graphStr, _options = {}, api = false, validation = undefined) => { - cy.log(_options); - const options = Object.assign(_options); - if (!options.fontFamily) { - options.fontFamily = 'courier'; - } - if (!options.sequence) { - options.sequence = {}; - } - if (!options.sequence || (options.sequence && !options.sequence.actorFontFamily)) { - options.sequence.actorFontFamily = 'courier'; - } - if (options.sequence && !options.sequence.noteFontFamily) { - options.sequence.noteFontFamily = 'courier'; - } - options.sequence.actorFontFamily = 'courier'; - options.sequence.noteFontFamily = 'courier'; - options.sequence.messageFontFamily = 'courier'; - if (options.sequence && !options.sequence.actorFontFamily) { - options.sequence.actorFontFamily = 'courier'; - } - if (!options.fontSize) { - options.fontSize = '16px'; - } - const url = mermaidUrl(graphStr, options, api); - openURLAndVerifyRendering(url, options, validation); -}; - -export const urlSnapshotTest = (url, _options, api = false, validation) => { - const options = Object.assign(_options); - openURLAndVerifyRendering(url, options, validation); -}; - -export const renderGraph = (graphStr, options, api) => { - const url = mermaidUrl(graphStr, options, api); - openURLAndVerifyRendering(url, options); -}; - -export const openURLAndVerifyRendering = (url, options, validation = undefined) => { - const useAppli = Cypress.env('useAppli'); - const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); - - if (useAppli) { - cy.log('Opening eyes ' + Cypress.spec.name + ' --- ' + name); - cy.eyesOpen({ - appName: 'Mermaid', - testName: name, - batchName: Cypress.spec.name, - batchId: batchId + Cypress.spec.name, - }); - } - - cy.visit(url); - cy.window().should('have.property', 'rendered', true); - cy.get('svg').should('be.visible'); - - if (validation) { - cy.get('svg').should(validation); - } - - if (useAppli) { - cy.log('Check eyes' + Cypress.spec.name); - cy.eyesCheckWindow('Click!'); - cy.log('Closing eyes' + Cypress.spec.name); - cy.eyesClose(); - } else { - cy.matchImageSnapshot(name); - } -}; diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts new file mode 100644 index 000000000..ea217b4fc --- /dev/null +++ b/cypress/helpers/util.ts @@ -0,0 +1,132 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Buffer } from 'buffer'; +import type { MermaidConfig } from '../../packages/mermaid/src/config.type.js'; + +interface CypressConfig { + listUrl?: boolean; + listId?: string; + name?: string; +} +type CypressMermaidConfig = MermaidConfig & CypressConfig; + +interface CodeObject { + code: string; + mermaid: CypressMermaidConfig; +} + +const utf8ToB64 = (str: string): string => { + return Buffer.from(decodeURIComponent(encodeURIComponent(str))).toString('base64'); +}; + +const batchId: string = 'mermaid-batch-' + Cypress.env('CYPRESS_COMMIT') || Date.now().toString(); + +export const mermaidUrl = ( + graphStr: string, + options: CypressMermaidConfig, + api: boolean +): string => { + const codeObject: CodeObject = { + code: graphStr, + mermaid: options, + }; + const objStr: string = JSON.stringify(codeObject); + let url = `http://localhost:9000/e2e.html?graph=${utf8ToB64(objStr)}`; + if (api) { + url = `http://localhost:9000/xss.html?graph=${graphStr}`; + } + + if (options.listUrl) { + cy.log(options.listId, ' ', url); + } + + return url; +}; + +export const imgSnapshotTest = ( + graphStr: string, + _options: CypressMermaidConfig = {}, + api = false, + validation?: any +): void => { + cy.log(JSON.stringify(_options)); + const options: CypressMermaidConfig = Object.assign(_options); + if (!options.fontFamily) { + options.fontFamily = 'courier'; + } + if (!options.sequence) { + options.sequence = {}; + } + if (!options.sequence || (options.sequence && !options.sequence.actorFontFamily)) { + options.sequence.actorFontFamily = 'courier'; + } + if (options.sequence && !options.sequence.noteFontFamily) { + options.sequence.noteFontFamily = 'courier'; + } + options.sequence.actorFontFamily = 'courier'; + options.sequence.noteFontFamily = 'courier'; + options.sequence.messageFontFamily = 'courier'; + if (options.sequence && !options.sequence.actorFontFamily) { + options.sequence.actorFontFamily = 'courier'; + } + if (!options.fontSize) { + options.fontSize = 16; + } + + const url: string = mermaidUrl(graphStr, options, api); + openURLAndVerifyRendering(url, options, validation); +}; + +export const urlSnapshotTest = ( + url: string, + _options: CypressMermaidConfig, + _api = false, + validation?: any +): void => { + const options: CypressMermaidConfig = Object.assign(_options); + openURLAndVerifyRendering(url, options, validation); +}; + +export const renderGraph = ( + graphStr: string, + options: CypressMermaidConfig = {}, + api = false +): void => { + const url: string = mermaidUrl(graphStr, options, api); + openURLAndVerifyRendering(url, options); +}; + +export const openURLAndVerifyRendering = ( + url: string, + options: CypressMermaidConfig, + validation?: any +): void => { + const useAppli: boolean = Cypress.env('useAppli'); + const name: string = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); + + if (useAppli) { + cy.log(`Opening eyes ${Cypress.spec.name} --- ${name}`); + cy.eyesOpen({ + appName: 'Mermaid', + testName: name, + batchName: Cypress.spec.name, + batchId: batchId + Cypress.spec.name, + }); + } + + cy.visit(url); + cy.window().should('have.property', 'rendered', true); + cy.get('svg').should('be.visible'); + + if (validation) { + cy.get('svg').should(validation); + } + + if (useAppli) { + cy.log(`Check eyes ${Cypress.spec.name}`); + cy.eyesCheckWindow('Click!'); + cy.log(`Closing eyes ${Cypress.spec.name}`); + cy.eyesClose(); + } else { + cy.matchImageSnapshot(name); + } +}; diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js index 6df7edd84..7cbc5d105 100644 --- a/cypress/integration/other/configuration.spec.js +++ b/cypress/integration/other/configuration.spec.js @@ -1,4 +1,4 @@ -import { renderGraph } from '../../helpers/util.js'; +import { renderGraph } from '../../helpers/util.ts'; describe('Configuration', () => { describe('arrowMarkerAbsolute', () => { it('should handle default value false of arrowMarkerAbsolute', () => { diff --git a/cypress/integration/other/external-diagrams.spec.js b/cypress/integration/other/external-diagrams.spec.js index 4ade11e81..704222f2f 100644 --- a/cypress/integration/other/external-diagrams.spec.js +++ b/cypress/integration/other/external-diagrams.spec.js @@ -1,4 +1,4 @@ -import { urlSnapshotTest } from '../../helpers/util.js'; +import { urlSnapshotTest } from '../../helpers/util.ts'; describe('mermaid', () => { describe('registerDiagram', () => { diff --git a/cypress/integration/other/ghsa.spec.js b/cypress/integration/other/ghsa.spec.js index 912f35728..48eb57a91 100644 --- a/cypress/integration/other/ghsa.spec.js +++ b/cypress/integration/other/ghsa.spec.js @@ -1,4 +1,4 @@ -import { urlSnapshotTest, openURLAndVerifyRendering } from '../../helpers/util.js'; +import { urlSnapshotTest, openURLAndVerifyRendering } from '../../helpers/util.ts'; describe('CSS injections', () => { it('should not allow CSS injections outside of the diagram', () => { diff --git a/cypress/integration/other/xss.spec.js b/cypress/integration/other/xss.spec.js index 76b2c47f2..fa4ca4fc8 100644 --- a/cypress/integration/other/xss.spec.js +++ b/cypress/integration/other/xss.spec.js @@ -1,4 +1,4 @@ -import { mermaidUrl } from '../../helpers/util.js'; +import { mermaidUrl } from '../../helpers/util.ts'; describe('XSS', () => { it('should handle xss in tags', () => { const str = diff --git a/cypress/integration/rendering/appli.spec.js b/cypress/integration/rendering/appli.spec.js index 462fe869c..5def96815 100644 --- a/cypress/integration/rendering/appli.spec.js +++ b/cypress/integration/rendering/appli.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('Git Graph diagram', () => { it('1: should render a simple gitgraph with commit on main branch', () => { diff --git a/cypress/integration/rendering/c4.spec.js b/cypress/integration/rendering/c4.spec.js index 0cf128ff6..59af6504b 100644 --- a/cypress/integration/rendering/c4.spec.js +++ b/cypress/integration/rendering/c4.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('C4 diagram', () => { it('should render a simple C4Context diagram', () => { diff --git a/cypress/integration/rendering/classDiagram-v2.spec.js b/cypress/integration/rendering/classDiagram-v2.spec.js index 2e7a1cbd7..be6aa643b 100644 --- a/cypress/integration/rendering/classDiagram-v2.spec.js +++ b/cypress/integration/rendering/classDiagram-v2.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('Class diagram V2', () => { it('0: should render a simple class diagram', () => { imgSnapshotTest( diff --git a/cypress/integration/rendering/classDiagram.spec.js b/cypress/integration/rendering/classDiagram.spec.js index dd79303b8..a23430b08 100644 --- a/cypress/integration/rendering/classDiagram.spec.js +++ b/cypress/integration/rendering/classDiagram.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('Class diagram', () => { it('1: should render a simple class diagram', () => { diff --git a/cypress/integration/rendering/conf-and-directives.spec.js b/cypress/integration/rendering/conf-and-directives.spec.js index 3fc0f7f02..bc17f6236 100644 --- a/cypress/integration/rendering/conf-and-directives.spec.js +++ b/cypress/integration/rendering/conf-and-directives.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('Configuration and directives - nodes should be light blue', () => { it('No config - use default', () => { diff --git a/cypress/integration/rendering/current.spec.js b/cypress/integration/rendering/current.spec.js index e0b36d53a..d7bc08105 100644 --- a/cypress/integration/rendering/current.spec.js +++ b/cypress/integration/rendering/current.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('Current diagram', () => { it('should render a state with states in it', () => { diff --git a/cypress/integration/rendering/debug.spec.js b/cypress/integration/rendering/debug.spec.js index afde4af3e..56ad0f15f 100644 --- a/cypress/integration/rendering/debug.spec.js +++ b/cypress/integration/rendering/debug.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('Flowchart', () => { it('34: testing the label width in percy', () => { diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js index c125d6f74..28c6191c8 100644 --- a/cypress/integration/rendering/erDiagram.spec.js +++ b/cypress/integration/rendering/erDiagram.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('Entity Relationship Diagram', () => { it('should render a simple ER diagram', () => { diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js index 4f90646a2..221806b07 100644 --- a/cypress/integration/rendering/flowchart-elk.spec.js +++ b/cypress/integration/rendering/flowchart-elk.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe.skip('Flowchart ELK', () => { it('1-elk: should render a simple flowchart', () => { @@ -681,7 +681,7 @@ title: Simple flowchart flowchart-elk TD A --> B `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('elk: should include classes on the edges', () => { @@ -710,7 +710,7 @@ flowchart-elk LR style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5 classDef someclass fill:#f96 `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('With formatting in a node', () => { @@ -726,7 +726,7 @@ flowchart-elk LR b --> d(The dog in the hog) c --> d `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('New line in node and formatted edge label', () => { @@ -736,7 +736,7 @@ flowchart-elk LR b("\`The dog in **the** hog.(1) NL\`") --"\`1o **bold**\`"--> c `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('Wrapping long text with a new line', () => { @@ -749,7 +749,7 @@ Word! Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`) --> c `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('Sub graphs and markdown strings', () => { @@ -766,7 +766,7 @@ subgraph "\`**Two**\`" end `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); }); @@ -782,7 +782,7 @@ flowchart-elk LR style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5 classDef someclass fill:#f96 `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('With formatting in a node', () => { @@ -798,7 +798,7 @@ flowchart-elk LR b --> d(The dog in the hog) c --> d `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('New line in node and formatted edge label', () => { @@ -808,7 +808,7 @@ flowchart-elk LR b("\`The dog in **the** hog.(1) NL\`") --"\`1o **bold**\`"--> c `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('Wrapping long text with a new line', () => { @@ -821,7 +821,7 @@ Word! Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`") --> c `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('Sub graphs and markdown strings', () => { @@ -838,7 +838,7 @@ subgraph "\`**Two**\`" end `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); }); diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js index 391f6c90e..27e728405 100644 --- a/cypress/integration/rendering/flowchart-v2.spec.js +++ b/cypress/integration/rendering/flowchart-v2.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('Flowchart v2', () => { it('1: should render a simple flowchart', () => { @@ -671,7 +671,7 @@ title: Simple flowchart flowchart TD A --> B `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('3192: It should be possieble to render flowcharts with invisible edges', () => { @@ -682,7 +682,7 @@ title: Simple flowchart with invisible edges flowchart TD A ~~~ B `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('4023: Should render html labels with images and-or text correctly', () => { @@ -716,7 +716,7 @@ flowchart LR style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5 classDef someclass fill:#f96 `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('With formatting in a node', () => { @@ -732,7 +732,7 @@ flowchart LR b --> d(The dog in the hog) c --> d `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('New line in node and formatted edge label', () => { @@ -742,7 +742,7 @@ flowchart LR b("\`The dog in **the** hog.(1) NL\`") --"\`1o **bold**\`"--> c `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('Wrapping long text with a new line', () => { @@ -755,7 +755,7 @@ Word! Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`") --> c `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('Sub graphs and markdown strings', () => { @@ -772,7 +772,7 @@ subgraph "\`**Two**\`" end `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); }); @@ -788,7 +788,7 @@ flowchart LR style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5 classDef someclass fill:#f96 `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('With formatting in a node', () => { @@ -804,7 +804,7 @@ flowchart LR b --> d(The dog in the hog) c --> d `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('New line in node and formatted edge label', () => { @@ -814,7 +814,7 @@ flowchart LR b("\`The dog in **the** hog.(1) NL\`") --"\`1o **bold**\`"--> c `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('Wrapping long text with a new line', () => { @@ -827,7 +827,7 @@ Word! Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`") --> c `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); it('Sub graphs and markdown strings', () => { @@ -844,7 +844,7 @@ subgraph "\`**Two**\`" end `, - { titleTopMargin: 0 } + { flowchart: { titleTopMargin: 0 } } ); }); }); diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index d25043d28..4f6d6478e 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('Graph', () => { it('1: should render a simple flowchart no htmlLabels', () => { diff --git a/cypress/integration/rendering/gantt.spec.js b/cypress/integration/rendering/gantt.spec.js index 0005e3e76..33d3ac9e1 100644 --- a/cypress/integration/rendering/gantt.spec.js +++ b/cypress/integration/rendering/gantt.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('Gantt diagram', () => { beforeEach(() => { diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js index 43f91a983..c01a55796 100644 --- a/cypress/integration/rendering/gitGraph.spec.js +++ b/cypress/integration/rendering/gitGraph.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('Git Graph diagram', () => { it('1: should render a simple gitgraph with commit on main branch', () => { @@ -333,4 +333,372 @@ gitGraph {} ); }); + it('15: should render a simple gitgraph with commit on main branch | Vertical Branch', () => { + imgSnapshotTest( + `gitGraph TB: + commit id: "1" + commit id: "2" + commit id: "3" + `, + {} + ); + }); + it('16: should render a simple gitgraph with commit on main branch with Id | Vertical Branch', () => { + imgSnapshotTest( + `gitGraph TB: + commit id: "One" + commit id: "Two" + commit id: "Three" + `, + {} + ); + }); + it('17: should render a simple gitgraph with different commitTypes on main branch | Vertical Branch', () => { + imgSnapshotTest( + `gitGraph TB: + commit id: "Normal Commit" + commit id: "Reverse Commit" type: REVERSE + commit id: "Hightlight Commit" type: HIGHLIGHT + `, + {} + ); + }); + it('18: should render a simple gitgraph with tags commitTypes on main branch | Vertical Branch', () => { + imgSnapshotTest( + `gitGraph TB: + commit id: "Normal Commit with tag" tag: "v1.0.0" + commit id: "Reverse Commit with tag" type: REVERSE tag: "RC_1" + commit id: "Hightlight Commit" type: HIGHLIGHT tag: "8.8.4" + `, + {} + ); + }); + it('19: should render a simple gitgraph with two branches | Vertical Branch', () => { + imgSnapshotTest( + `gitGraph TB: + commit id: "1" + commit id: "2" + branch develop + checkout develop + commit id: "3" + commit id: "4" + checkout main + commit id: "5" + commit id: "6" + `, + {} + ); + }); + it('20: should render a simple gitgraph with two branches and merge commit | Vertical Branch', () => { + imgSnapshotTest( + `gitGraph TB: + commit id: "1" + commit id: "2" + branch develop + checkout develop + commit id: "3" + commit id: "4" + checkout main + merge develop + commit id: "5" + commit id: "6" + `, + {} + ); + }); + it('21: should render a simple gitgraph with three branches and tagged merge commit | Vertical Branch', () => { + imgSnapshotTest( + `gitGraph TB: + commit id: "1" + commit id: "2" + branch nice_feature + checkout nice_feature + commit id: "3" + checkout main + commit id: "4" + checkout nice_feature + branch very_nice_feature + checkout very_nice_feature + commit id: "5" + checkout main + commit id: "6" + checkout nice_feature + commit id: "7" + checkout main + merge nice_feature id: "12345" tag: "my merge commit" + checkout very_nice_feature + commit id: "8" + checkout main + commit id: "9" + `, + {} + ); + }); + it('22: should render a simple gitgraph with more than 8 branchs & overriding variables | Vertical Branch', () => { + imgSnapshotTest( + `%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': { + 'gitBranchLabel0': '#ffffff', + 'gitBranchLabel1': '#ffffff', + 'gitBranchLabel2': '#ffffff', + 'gitBranchLabel3': '#ffffff', + 'gitBranchLabel4': '#ffffff', + 'gitBranchLabel5': '#ffffff', + 'gitBranchLabel6': '#ffffff', + 'gitBranchLabel7': '#ffffff', + } } }%% + gitGraph TB: + checkout main + branch branch1 + branch branch2 + branch branch3 + branch branch4 + branch branch5 + branch branch6 + branch branch7 + branch branch8 + branch branch9 + checkout branch1 + commit id: "1" + `, + {} + ); + }); + it('23: should render a simple gitgraph with rotated labels | Vertical Branch', () => { + imgSnapshotTest( + `%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'gitGraph': { + 'rotateCommitLabel': true + } } }%% + gitGraph TB: + commit id: "75f7219e83b321cd3fdde7dcf83bc7c1000a6828" + commit id: "0db4784daf82736dec4569e0dc92980d328c1f2e" + commit id: "7067e9973f9eaa6cd4a4b723c506d1eab598e83e" + commit id: "66972321ad6c199013b5b31f03b3a86fa3f9817d" + `, + {} + ); + }); + it('24: should render a simple gitgraph with horizontal labels | Vertical Branch', () => { + imgSnapshotTest( + `%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'gitGraph': { + 'rotateCommitLabel': false + } } }%% + gitGraph TB: + commit id: "Alpha" + commit id: "Beta" + commit id: "Gamma" + commit id: "Delta" + `, + {} + ); + }); + it('25: should render a simple gitgraph with cherry pick commit | Vertical Branch', () => { + imgSnapshotTest( + ` + gitGraph TB: + commit id: "ZERO" + branch develop + commit id:"A" + checkout main + commit id:"ONE" + checkout develop + commit id:"B" + checkout main + commit id:"TWO" + cherry-pick id:"A" + commit id:"THREE" + checkout develop + commit id:"C" + `, + {} + ); + }); + it('26: should render a gitgraph with cherry pick commit with custom tag | Vertical Branch', () => { + imgSnapshotTest( + ` + gitGraph TB: + commit id: "ZERO" + branch develop + commit id:"A" + checkout main + commit id:"ONE" + checkout develop + commit id:"B" + checkout main + commit id:"TWO" + cherry-pick id:"A" tag: "snapshot" + commit id:"THREE" + checkout develop + commit id:"C" + `, + {} + ); + }); + it('27: should render a gitgraph with cherry pick commit with no tag | Vertical Branch', () => { + imgSnapshotTest( + ` + gitGraph TB: + commit id: "ZERO" + branch develop + commit id:"A" + checkout main + commit id:"ONE" + checkout develop + commit id:"B" + checkout main + commit id:"TWO" + cherry-pick id:"A" tag: "" + commit id:"THREE" + checkout develop + commit id:"C" + `, + {} + ); + }); + it('28: should render a simple gitgraph with two cherry pick commit | Vertical Branch', () => { + imgSnapshotTest( + ` + 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 + `, + {} + ); + }); + it('29: should render commits for more than 8 branches | Vertical Branch', () => { + imgSnapshotTest( + ` + gitGraph TB: + checkout main + %% Make sure to manually set the ID of all commits, for consistent visual tests + commit id: "1-abcdefg" + checkout main + branch branch1 + commit id: "2-abcdefg" + checkout main + merge branch1 + branch branch2 + commit id: "3-abcdefg" + checkout main + merge branch2 + branch branch3 + commit id: "4-abcdefg" + checkout main + merge branch3 + branch branch4 + commit id: "5-abcdefg" + checkout main + merge branch4 + branch branch5 + commit id: "6-abcdefg" + checkout main + merge branch5 + branch branch6 + commit id: "7-abcdefg" + checkout main + merge branch6 + branch branch7 + commit id: "8-abcdefg" + checkout main + merge branch7 + branch branch8 + commit id: "9-abcdefg" + checkout main + merge branch8 + branch branch9 + commit id: "10-abcdefg" + `, + {} + ); + }); + it('30: should render a simple gitgraph with three branches,custom merge commit id,tag,type | Vertical Branch', () => { + imgSnapshotTest( + `gitGraph TB: + commit id: "1" + commit id: "2" + branch nice_feature + checkout nice_feature + commit id: "3" + checkout main + commit id: "4" + checkout nice_feature + branch very_nice_feature + checkout very_nice_feature + commit id: "5" + checkout main + commit id: "6" + checkout nice_feature + commit id: "7" + checkout main + merge nice_feature id: "customID" tag: "customTag" type: REVERSE + checkout very_nice_feature + commit id: "8" + checkout main + commit id: "9" + `, + {} + ); + }); + it('31: should render a simple gitgraph with a title | Vertical Branch', () => { + imgSnapshotTest( + `--- +title: simple gitGraph +--- +gitGraph TB: + commit id: "1-abcdefg" +`, + {} + ); + }); + it('32: should render a simple gitgraph overlapping commits | Vertical Branch', () => { + imgSnapshotTest( + `gitGraph TB: + commit id:"s1" + commit id:"s2" + branch branch1 + commit id:"s3" + commit id:"s4" + checkout main + commit id:"s5" + checkout branch1 + commit id:"s6" + commit id:"s7" + merge main + `, + {} + ); + }); + it('33: should render a simple gitgraph overlapping commits', () => { + imgSnapshotTest( + `gitGraph + commit id:"s1" + commit id:"s2" + branch branch1 + commit id:"s3" + commit id:"s4" + checkout main + commit id:"s5" + checkout branch1 + commit id:"s6" + commit id:"s7" + merge main + `, + {} + ); + }); }); diff --git a/cypress/integration/rendering/info.spec.ts b/cypress/integration/rendering/info.spec.ts index 3db74c980..97b384eb5 100644 --- a/cypress/integration/rendering/info.spec.ts +++ b/cypress/integration/rendering/info.spec.ts @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('info diagram', () => { it('should handle an info definition', () => { diff --git a/cypress/integration/rendering/journey.spec.js b/cypress/integration/rendering/journey.spec.js index 6f9d9bb60..d8bef6d1b 100644 --- a/cypress/integration/rendering/journey.spec.js +++ b/cypress/integration/rendering/journey.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('User journey diagram', () => { it('Simple test', () => { diff --git a/cypress/integration/rendering/mindmap.spec.ts b/cypress/integration/rendering/mindmap.spec.ts index e390beaee..a77459f58 100644 --- a/cypress/integration/rendering/mindmap.spec.ts +++ b/cypress/integration/rendering/mindmap.spec.ts @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; /** * Check whether the SVG Element has a Mindmap root @@ -242,8 +242,7 @@ mindmap a second line 😎\`] id2[\`The dog in **the** hog... a *very long text* about it Word!\`] -`, - { titleTopMargin: 0 } +` ); }); }); diff --git a/cypress/integration/rendering/pie.spec.js b/cypress/integration/rendering/pie.spec.js index 8a89a0cde..01b248486 100644 --- a/cypress/integration/rendering/pie.spec.js +++ b/cypress/integration/rendering/pie.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('Pie Chart', () => { it('should render a simple pie diagram', () => { diff --git a/cypress/integration/rendering/quadrantChart.spec.js b/cypress/integration/rendering/quadrantChart.spec.js index 4bcf58b60..50520eb1a 100644 --- a/cypress/integration/rendering/quadrantChart.spec.js +++ b/cypress/integration/rendering/quadrantChart.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('Quadrant Chart', () => { it('should render if only chart type is provided', () => { diff --git a/cypress/integration/rendering/requirement.spec.js b/cypress/integration/rendering/requirement.spec.js index 0bf9014bf..f33ae7a0c 100644 --- a/cypress/integration/rendering/requirement.spec.js +++ b/cypress/integration/rendering/requirement.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('Requirement diagram', () => { it('sample', () => { diff --git a/cypress/integration/rendering/sankey.spec.ts b/cypress/integration/rendering/sankey.spec.ts index e334b898b..ad0d75f18 100644 --- a/cypress/integration/rendering/sankey.spec.ts +++ b/cypress/integration/rendering/sankey.spec.ts @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('Sankey Diagram', () => { it('should render a simple example', () => { diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js index 7d36c1ff1..765913824 100644 --- a/cypress/integration/rendering/sequencediagram.spec.js +++ b/cypress/integration/rendering/sequencediagram.spec.js @@ -1,6 +1,6 @@ /// -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; context('Sequence diagram', () => { it('should render a sequence diagram with boxes', () => { diff --git a/cypress/integration/rendering/stateDiagram-v2.spec.js b/cypress/integration/rendering/stateDiagram-v2.spec.js index 700791621..9a1a27abe 100644 --- a/cypress/integration/rendering/stateDiagram-v2.spec.js +++ b/cypress/integration/rendering/stateDiagram-v2.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('State diagram', () => { it('v2 should render a simple info', () => { diff --git a/cypress/integration/rendering/stateDiagram.spec.js b/cypress/integration/rendering/stateDiagram.spec.js index 28c24d398..01e7a2b44 100644 --- a/cypress/integration/rendering/stateDiagram.spec.js +++ b/cypress/integration/rendering/stateDiagram.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; +import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('State diagram', () => { it('should render a simple state diagrams', () => { diff --git a/cypress/integration/rendering/theme.spec.js b/cypress/integration/rendering/theme.spec.js index ef3bd9a4b..c84ad0c4b 100644 --- a/cypress/integration/rendering/theme.spec.js +++ b/cypress/integration/rendering/theme.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('themeCSS balancing, it', () => { it('should not allow unbalanced CSS definitions', () => { diff --git a/cypress/integration/rendering/timeline.spec.ts b/cypress/integration/rendering/timeline.spec.ts index 6fae82fb4..68da01d50 100644 --- a/cypress/integration/rendering/timeline.spec.ts +++ b/cypress/integration/rendering/timeline.spec.ts @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('Timeline diagram', () => { it('1: should render a simple timeline with no specific sections', () => { diff --git a/cypress/integration/rendering/zenuml.spec.js b/cypress/integration/rendering/zenuml.spec.js index f317fbe82..53d8ae346 100644 --- a/cypress/integration/rendering/zenuml.spec.js +++ b/cypress/integration/rendering/zenuml.spec.js @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.js'; +import { imgSnapshotTest } from '../../helpers/util.ts'; describe('Zen UML', () => { it('Basic Zen UML diagram', () => { diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json index e3351cebe..baa9a7217 100644 --- a/cypress/tsconfig.json +++ b/cypress/tsconfig.json @@ -2,7 +2,9 @@ "compilerOptions": { "target": "es2020", "lib": ["es2020", "dom"], - "types": ["cypress", "node"] + "types": ["cypress", "node"], + "allowImportingTsExtensions": true, + "noEmit": true }, "include": ["**/*.ts"] } diff --git a/docs/community/development.md b/docs/community/development.md index 90f728e15..0634759f5 100644 --- a/docs/community/development.md +++ b/docs/community/development.md @@ -223,6 +223,9 @@ If the users have no way to know that things have changed, then you haven't real Likewise, if users don't know that there is a new feature that you've implemented, it will forever remain unknown and unused. The documentation has to be updated to users know that things have changed and added! +If you are adding a new feature, add `(v+)` in the title or description. It will be replaced automatically with the current version number when the release happens. + +eg: `# Feature Name (v+)` We know it can sometimes be hard to code _and_ write user documentation. diff --git a/docs/syntax/gitgraph.md b/docs/syntax/gitgraph.md index 964fe3886..e93071851 100644 --- a/docs/syntax/gitgraph.md +++ b/docs/syntax/gitgraph.md @@ -825,6 +825,82 @@ NOTE: Because we have overridden the `mainBranchOrder` to `2`, the `main` branch Here, we have changed the default main branch name to `MetroLine1`. +## Orientation + +In Mermaid, the default orientation is Left to Right. The branches are lined vertically. + +Usage example: + +```mermaid-example + gitGraph + commit + commit + branch develop + commit + commit + commit + checkout main + commit + commit + merge develop + commit + commit +``` + +```mermaid + gitGraph + commit + commit + branch develop + commit + commit + commit + checkout main + commit + commit + merge develop + commit + commit +``` + +Sometimes we may want to change the orientation. Currently, Mermaid supports two orientations: **Left to Right**(default) and **Top to Bottom**. + +In order to change the orientation from top to bottom i.e. branches lined horizontally, you need to add `TB` along with `gitGraph`. + +Usage example: + +```mermaid-example + gitGraph TB: + commit + commit + branch develop + commit + commit + commit + checkout main + commit + commit + merge develop + commit + commit +``` + +```mermaid + gitGraph TB: + commit + commit + branch develop + commit + commit + commit + checkout main + commit + commit + merge develop + commit + commit +``` + ## Themes Mermaid supports a bunch of pre-defined themes which you can use to find the right one for you. PS: you can actually override an existing theme's variable to get your own custom theme going. Learn more about theming your diagram [here](../config/theming.md). diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index d04083baa..9a1f34676 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -25,14 +25,16 @@ "scripts": { "clean": "rimraf dist", "docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaidAPI.ts && prettier --write ./src/docs/config/setup", - "docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts", - "docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts --verify", - "docs:pre:vitepress": "pnpm --filter ./src/docs prefetch && rimraf src/vitepress && pnpm docs:code && ts-node-esm src/docs.mts --vitepress && pnpm --filter ./src/vitepress install --no-frozen-lockfile --ignore-scripts", + "docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm scripts/docs.cli.mts", + "docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm scripts/docs.cli.mts --verify", + "docs:pre:vitepress": "pnpm --filter ./src/docs prefetch && rimraf src/vitepress && pnpm docs:code && ts-node-esm scripts/docs.cli.mts --vitepress && pnpm --filter ./src/vitepress install --no-frozen-lockfile --ignore-scripts", "docs:build:vitepress": "pnpm docs:pre:vitepress && (cd src/vitepress && pnpm run build) && cpy --flat src/docs/landing/ ./src/vitepress/.vitepress/dist/landing", - "docs:dev": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev\" \"ts-node-esm src/docs.mts --watch --vitepress\"", - "docs:dev:docker": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev:docker\" \"ts-node-esm src/docs.mts --watch --vitepress\"", + "docs:dev": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev\" \"ts-node-esm scripts/docs.cli.mts --watch --vitepress\"", + "docs:dev:docker": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev:docker\" \"ts-node-esm scripts/docs.cli.mts --watch --vitepress\"", "docs:serve": "pnpm docs:build:vitepress && vitepress serve src/vitepress", "docs:spellcheck": "cspell --config ../../cSpell.json \"src/docs/**/*.md\"", + "docs:release-version": "ts-node-esm scripts/update-release-version.mts", + "docs:verify-version": "ts-node-esm scripts/update-release-version.mts --verify", "types:build-config": "ts-node-esm --transpileOnly scripts/create-types-from-json-schema.mts", "types:verify-config": "ts-node-esm scripts/create-types-from-json-schema.mts --verify", "release": "pnpm build", diff --git a/packages/mermaid/scripts/docs.cli.mts b/packages/mermaid/scripts/docs.cli.mts new file mode 100644 index 000000000..067eec1c5 --- /dev/null +++ b/packages/mermaid/scripts/docs.cli.mts @@ -0,0 +1,3 @@ +import { processDocs } from './docs.mjs'; + +void processDocs(); diff --git a/packages/mermaid/src/docs.mts b/packages/mermaid/scripts/docs.mts similarity index 92% rename from packages/mermaid/src/docs.mts rename to packages/mermaid/scripts/docs.mts index 356cd3cd1..3689cb68b 100644 --- a/packages/mermaid/src/docs.mts +++ b/packages/mermaid/scripts/docs.mts @@ -27,14 +27,21 @@ * get their absolute paths. Ensures that the location of those 2 directories is not dependent on * where this file resides. * - * @todo Write a test file for this. (Will need to be able to deal .mts file. Jest has trouble with - * it.) */ +// @ts-ignore: we're importing internal jsonschema2md functions +import { default as schemaLoader } from '@adobe/jsonschema2md/lib/schemaProxy.js'; +// @ts-ignore: we're importing internal jsonschema2md functions +import { default as traverseSchemas } from '@adobe/jsonschema2md/lib/traverseSchema.js'; +// @ts-ignore: we're importing internal jsonschema2md functions +import { default as buildMarkdownFromSchema } from '@adobe/jsonschema2md/lib/markdownBuilder.js'; +// @ts-ignore: we're importing internal jsonschema2md functions +import { default as jsonSchemaReadmeBuilder } from '@adobe/jsonschema2md/lib/readmeBuilder.js'; import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync, rmdirSync } from 'fs'; import { exec } from 'child_process'; import { globby } from 'globby'; import { JSDOM } from 'jsdom'; -import type { Code, ListItem, Root, Text } from 'mdast'; +import { dump, load, JSON_SCHEMA } from 'js-yaml'; +import type { Code, ListItem, Root, Text, YAML } from 'mdast'; import { posix, dirname, relative, join } from 'path'; import prettier from 'prettier'; import { remark } from 'remark'; @@ -46,9 +53,9 @@ import mm from 'micromatch'; import flatmap from 'unist-util-flatmap'; import { visit } from 'unist-util-visit'; -const MERMAID_MAJOR_VERSION = ( - JSON.parse(readFileSync('../mermaid/package.json', 'utf8')).version as string -).split('.')[0]; +export const MERMAID_RELEASE_VERSION = JSON.parse(readFileSync('../mermaid/package.json', 'utf8')) + .version as string; +const MERMAID_MAJOR_VERSION = MERMAID_RELEASE_VERSION.split('.')[0]; const CDN_URL = 'https://cdn.jsdelivr.net/npm'; // 'https://unpkg.com'; const MERMAID_KEYWORD = 'mermaid'; @@ -71,7 +78,7 @@ const vitepress: boolean = process.argv.includes('--vitepress'); const noHeader: boolean = process.argv.includes('--noHeader') || vitepress; // These paths are from the root of the mono-repo, not from the mermaid subdirectory -const SOURCE_DOCS_DIR = 'src/docs'; +export const SOURCE_DOCS_DIR = 'src/docs'; const FINAL_DOCS_DIR = vitepress ? 'src/vitepress' : '../../docs'; const LOGMSG_TRANSFORMED = 'transformed'; @@ -158,7 +165,7 @@ const copyTransformedContents = (filename: string, doCopy = false, transformedCo logWasOrShouldBeTransformed(fileInFinalDocDir, doCopy); }; -const readSyncedUTF8file = (filename: string): string => { +export const readSyncedUTF8file = (filename: string): string => { return readFileSync(filename, 'utf8'); }; @@ -209,6 +216,8 @@ interface TransformMarkdownAstOptions { originalFilename: string; /** If `true`, add a warning that the file is autogenerated */ addAutogeneratedWarning?: boolean; + /** If `true`, adds an `editLink: "https://..."` YAML frontmatter field */ + addEditLink?: boolean; /** * If `true`, remove the YAML metadata from the Markdown input. * Generally, YAML metadata is only used for Vitepress. @@ -231,6 +240,7 @@ interface TransformMarkdownAstOptions { export function transformMarkdownAst({ originalFilename, addAutogeneratedWarning, + addEditLink, removeYAML, }: TransformMarkdownAstOptions) { return (tree: Root, _file?: any): Root => { @@ -270,6 +280,27 @@ export function transformMarkdownAst({ } } + if (addEditLink) { + // add originalFilename as custom editLink in YAML frontmatter + let yamlFrontMatter: YAML; + if (astWithTransformedBlocks.children[0].type === 'yaml') { + yamlFrontMatter = astWithTransformedBlocks.children[0]; + } else { + yamlFrontMatter = { + type: 'yaml', + value: '', + }; + astWithTransformedBlocks.children.unshift(yamlFrontMatter); + } + const filePathFromRoot = posix.join('packages/mermaid', originalFilename); + yamlFrontMatter.value = dump({ + ...(load(yamlFrontMatter.value, { schema: JSON_SCHEMA }) as + | Record + | undefined), + editLink: `https://github.com/mermaid-js/mermaid/edit/develop/${filePathFromRoot}`, + }); + } + if (removeYAML) { const firstNode = astWithTransformedBlocks.children[0]; if (firstNode.type == 'yaml') { @@ -306,6 +337,7 @@ const transformMarkdown = (file: string) => { // mermaid project specific plugin originalFilename: file, addAutogeneratedWarning: !noHeader, + addEditLink: noHeader, removeYAML: !noHeader, }) .processSync(doc) @@ -323,20 +355,10 @@ const transformMarkdown = (file: string) => { copyTransformedContents(file, !verifyOnly, formatted); }; -import { load, JSON_SCHEMA } from 'js-yaml'; -// @ts-ignore: we're importing internal jsonschema2md functions -import { default as schemaLoader } from '@adobe/jsonschema2md/lib/schemaProxy.js'; -// @ts-ignore: we're importing internal jsonschema2md functions -import { default as traverseSchemas } from '@adobe/jsonschema2md/lib/traverseSchema.js'; -// @ts-ignore: we're importing internal jsonschema2md functions -import { default as buildMarkdownFromSchema } from '@adobe/jsonschema2md/lib/markdownBuilder.js'; -// @ts-ignore: we're importing internal jsonschema2md functions -import { default as jsonSchemaReadmeBuilder } from '@adobe/jsonschema2md/lib/readmeBuilder.js'; - /** * Transforms the given JSON Schema into Markdown documentation */ -async function transormJsonSchema(file: string) { +async function transformJsonSchema(file: string) { const yamlContents = readSyncedUTF8file(file); const jsonSchema = load(yamlContents, { filename: file, @@ -420,16 +442,18 @@ async function transormJsonSchema(file: string) { } }); - const transformed = remark() + const transformer = remark() .use(remarkGfm) .use(remarkFrontmatter, ['yaml']) // support YAML front-matter in Markdown .use(transformMarkdownAst, { // mermaid project specific plugin originalFilename: file, addAutogeneratedWarning: !noHeader, + addEditLink: noHeader, removeYAML: !noHeader, - }) - .stringify(markdownAst as Root); + }); + + const transformed = transformer.stringify(await transformer.run(markdownAst as Root)); const formatted = prettier.format(transformed, { parser: 'markdown', @@ -480,7 +504,7 @@ const transformHtml = (filename: string) => { copyTransformedContents(filename, !verifyOnly, formattedHTML); }; -const getGlobs = (globs: string[]): string[] => { +export const getGlobs = (globs: string[]): string[] => { globs.push('!**/dist/**', '!**/redirect.spec.ts', '!**/landing/**', '!**/node_modules/**'); if (!vitepress) { globs.push( @@ -494,12 +518,12 @@ const getGlobs = (globs: string[]): string[] => { return globs; }; -const getFilesFromGlobs = async (globs: string[]): Promise => { +export const getFilesFromGlobs = async (globs: string[]): Promise => { return await globby(globs, { dot: true }); }; /** Main method (entry point) */ -const main = async () => { +export const processDocs = async () => { if (verifyOnly) { console.log('Verifying that all files are in sync with the source files'); } @@ -509,7 +533,7 @@ const main = async () => { if (vitepress) { console.log(`${action} 1 .schema.yaml file`); - await transormJsonSchema('src/schemas/config.schema.yaml'); + await transformJsonSchema('src/schemas/config.schema.yaml'); } else { // skip because this creates so many Markdown files that it lags git console.log('Skipping 1 .schema.yaml file'); @@ -577,5 +601,3 @@ const main = async () => { }); } }; - -void main(); diff --git a/packages/mermaid/src/docs.spec.ts b/packages/mermaid/scripts/docs.spec.ts similarity index 90% rename from packages/mermaid/src/docs.spec.ts rename to packages/mermaid/scripts/docs.spec.ts index 50feaee6a..c84bc1bac 100644 --- a/packages/mermaid/src/docs.spec.ts +++ b/packages/mermaid/scripts/docs.spec.ts @@ -105,6 +105,29 @@ This Markdown should be kept. }); }); + it('should add an editLink in the YAML frontmatter if `addEditLink: true`', async () => { + const contents = `--- +title: Flowcharts Syntax +--- + +This Markdown should be kept. +`; + const withYaml = ( + await remarkBuilder() + .use(transformMarkdownAst, { originalFilename, addEditLink: true }) + .process(contents) + ).toString(); + expect(withYaml).toEqual(`--- +title: Flowcharts Syntax +editLink: >- + https://github.com/mermaid-js/mermaid/edit/develop/packages/mermaid/example-input-filename.md + +--- + +This Markdown should be kept. +`); + }); + describe('transformToBlockQuote', () => { // TODO Is there a way to test this with --vitepress given as a process argument? diff --git a/packages/mermaid/scripts/update-release-version.mts b/packages/mermaid/scripts/update-release-version.mts new file mode 100644 index 000000000..7f292f21b --- /dev/null +++ b/packages/mermaid/scripts/update-release-version.mts @@ -0,0 +1,53 @@ +/* eslint-disable no-console */ + +/** + * @file Update the MERMAID_RELEASE_VERSION placeholder in the documentation source files with the current version of mermaid. + * So contributors adding new features will only have to add the placeholder and not worry about updating the version number. + * + */ +import { posix } from 'path'; +import { + getGlobs, + getFilesFromGlobs, + SOURCE_DOCS_DIR, + readSyncedUTF8file, + MERMAID_RELEASE_VERSION, +} from './docs.mjs'; +import { writeFile } from 'fs/promises'; + +const verifyOnly: boolean = process.argv.includes('--verify'); +const versionPlaceholder = ''; + +const main = async () => { + const sourceDirGlob = posix.join('.', SOURCE_DOCS_DIR, '**'); + const mdFileGlobs = getGlobs([posix.join(sourceDirGlob, '*.md')]); + mdFileGlobs.push('!**/community/development.md'); + const mdFiles = await getFilesFromGlobs(mdFileGlobs); + mdFiles.sort(); + const mdFilesWithPlaceholder: string[] = []; + for (const mdFile of mdFiles) { + const content = readSyncedUTF8file(mdFile); + if (content.includes(versionPlaceholder)) { + mdFilesWithPlaceholder.push(mdFile); + } + } + + if (mdFilesWithPlaceholder.length === 0) { + return; + } + + if (verifyOnly) { + console.log( + `${mdFilesWithPlaceholder.length} file(s) were found with the placeholder ${versionPlaceholder}. Run \`pnpm --filter mermaid docs:release-version\` to update them.` + ); + process.exit(1); + } + + for (const mdFile of mdFilesWithPlaceholder) { + const content = readSyncedUTF8file(mdFile); + const newContent = content.replace(versionPlaceholder, MERMAID_RELEASE_VERSION); + await writeFile(mdFile, newContent); + } +}; + +void main(); diff --git a/packages/mermaid/src/diagrams/git/gitGraphRenderer.js b/packages/mermaid/src/diagrams/git/gitGraphRenderer.js index 8d88c601d..2b88cfe7e 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphRenderer.js +++ b/packages/mermaid/src/diagrams/git/gitGraphRenderer.js @@ -19,12 +19,14 @@ let branchPos = {}; let commitPos = {}; let lanes = []; let maxPos = 0; +let dir = 'LR'; const clear = () => { branchPos = {}; commitPos = {}; allCommitsDict = {}; maxPos = 0; lanes = []; + dir = 'LR'; }; /** @@ -77,6 +79,10 @@ const drawCommits = (svg, commits, modifyGraph) => { const gLabels = svg.append('g').attr('class', 'commit-labels'); let pos = 0; + if (dir === 'TB') { + pos = 30; + } + const keys = Object.keys(commits); const sortedKeys = keys.sort((a, b) => { return commits[a].seq - commits[b].seq; @@ -84,8 +90,9 @@ const drawCommits = (svg, commits, modifyGraph) => { sortedKeys.forEach((key) => { const commit = commits[key]; - const y = branchPos[commit.branch].pos; - const x = pos + 10; + const y = dir === 'TB' ? pos + 10 : branchPos[commit.branch].pos; + const x = dir === 'TB' ? branchPos[commit.branch].pos : pos + 10; + // Don't draw the commits now but calculate the positioning which is used by the branch lines etc. if (modifyGraph) { let typeClass; @@ -208,7 +215,11 @@ const drawCommits = (svg, commits, modifyGraph) => { } } } - commitPos[commit.id] = { x: pos + 10, y: y }; + if (dir === 'TB') { + commitPos[commit.id] = { x: x, y: pos + 10 }; + } else { + commitPos[commit.id] = { x: pos + 10, y: y }; + } // The first iteration over the commits are for positioning purposes, this // is required for drawing the lines. The circles and labels is drawn after the labels @@ -240,14 +251,27 @@ const drawCommits = (svg, commits, modifyGraph) => { .attr('y', y + 13.5) .attr('width', bbox.width + 2 * py) .attr('height', bbox.height + 2 * py); - text.attr('x', pos + 10 - bbox.width / 2); + + if (dir === 'TB') { + labelBkg.attr('x', x - (bbox.width + 4 * px + 5)).attr('y', y - 12); + text.attr('x', x - (bbox.width + 4 * px)).attr('y', y + bbox.height - 12); + } + + if (dir !== 'TB') { + text.attr('x', pos + 10 - bbox.width / 2); + } if (gitGraphConfig.rotateCommitLabel) { - let r_x = -7.5 - ((bbox.width + 10) / 25) * 9.5; - let r_y = 10 + (bbox.width / 25) * 8.5; - wrapper.attr( - 'transform', - 'translate(' + r_x + ', ' + r_y + ') rotate(' + -45 + ', ' + pos + ', ' + y + ')' - ); + if (dir === 'TB') { + text.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')'); + labelBkg.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')'); + } else { + let r_x = -7.5 - ((bbox.width + 10) / 25) * 9.5; + let r_y = 10 + (bbox.width / 25) * 8.5; + wrapper.attr( + 'transform', + 'translate(' + r_x + ', ' + r_y + ') rotate(' + -45 + ', ' + pos + ', ' + y + ')' + ); + } } } if (commit.tag) { @@ -280,6 +304,30 @@ const drawCommits = (svg, commits, modifyGraph) => { .attr('cy', ly) .attr('r', 1.5) .attr('class', 'tag-hole'); + + if (dir === 'TB') { + rect + .attr('class', 'tag-label-bkg') + .attr( + 'points', + ` + ${x},${pos + py} + ${x},${pos - py} + ${x + 10},${pos - h2 - py} + ${x + 10 + tagBbox.width + px},${pos - h2 - py} + ${x + 10 + tagBbox.width + px},${pos + h2 + py} + ${x + 10},${pos + h2 + py}` + ) + .attr('transform', 'translate(12,12) rotate(45, ' + x + ',' + pos + ')'); + hole + .attr('cx', x + px / 2) + .attr('cy', pos) + .attr('transform', 'translate(12,12) rotate(45, ' + x + ',' + pos + ')'); + tag + .attr('x', x + 5) + .attr('y', pos + 3) + .attr('transform', 'translate(14,14) rotate(45, ' + x + ',' + pos + ')'); + } } } pos += 50; @@ -365,46 +413,94 @@ const drawArrow = (svg, commit1, commit2, allCommits) => { colorClassNum = branchPos[commit2.branch].index; const lineY = p1.y < p2.y ? findLane(p1.y, p2.y) : findLane(p2.y, p1.y); + const lineX = p1.x < p2.x ? findLane(p1.x, p2.x) : findLane(p2.x, p1.x); - if (p1.y < p2.y) { - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY - radius} ${arc} ${p1.x + offset} ${lineY} L ${ - p2.x - radius - } ${lineY} ${arc2} ${p2.x} ${lineY + offset} L ${p2.x} ${p2.y}`; + if (dir === 'TB') { + if (p1.x < p2.x) { + lineDef = `M ${p1.x} ${p1.y} L ${lineX - radius} ${p1.y} ${arc2} ${lineX} ${ + p1.y + offset + } L ${lineX} ${p2.y - radius} ${arc} ${lineX + offset} ${p2.y} L ${p2.x} ${p2.y}`; + } else { + lineDef = `M ${p1.x} ${p1.y} L ${lineX + radius} ${p1.y} ${arc} ${lineX} ${ + p1.y + offset + } L ${lineX} ${p2.y - radius} ${arc2} ${lineX - offset} ${p2.y} L ${p2.x} ${p2.y}`; + } } else { - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY + radius} ${arc2} ${ - p1.x + offset - } ${lineY} L ${p2.x - radius} ${lineY} ${arc} ${p2.x} ${lineY - offset} L ${p2.x} ${p2.y}`; + if (p1.y < p2.y) { + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY - radius} ${arc} ${ + p1.x + offset + } ${lineY} L ${p2.x - radius} ${lineY} ${arc2} ${p2.x} ${lineY + offset} L ${p2.x} ${p2.y}`; + } else { + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY + radius} ${arc2} ${ + p1.x + offset + } ${lineY} L ${p2.x - radius} ${lineY} ${arc} ${p2.x} ${lineY - offset} L ${p2.x} ${p2.y}`; + } } } else { - if (p1.y < p2.y) { - arc = 'A 20 20, 0, 0, 0,'; - radius = 20; - offset = 20; + if (dir === 'TB') { + if (p1.x < p2.x) { + arc = 'A 20 20, 0, 0, 0,'; + arc2 = 'A 20 20, 0, 0, 1,'; + radius = 20; + offset = 20; - // Figure out the color of the arrow,arrows going down take the color from the destination branch - colorClassNum = branchPos[commit2.branch].index; + // Figure out the color of the arrow,arrows going down take the color from the destination branch + colorClassNum = branchPos[commit2.branch].index; - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${p2.y} L ${ - p2.x - } ${p2.y}`; - } - if (p1.y > p2.y) { - arc = 'A 20 20, 0, 0, 0,'; - radius = 20; - offset = 20; + lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc2} ${p2.x} ${ + p1.y + offset + } L ${p2.x} ${p2.y}`; + } + if (p1.x > p2.x) { + arc = 'A 20 20, 0, 0, 0,'; + arc2 = 'A 20 20, 0, 0, 1,'; + radius = 20; + offset = 20; - // Arrows going up take the color from the source branch - colorClassNum = branchPos[commit1.branch].index; - lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${p1.y - offset} L ${ - p2.x - } ${p2.y}`; - } + // Arrows going up take the color from the source branch + colorClassNum = branchPos[commit1.branch].index; + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc2} ${p1.x - offset} ${ + p2.y + } L ${p2.x} ${p2.y}`; + } - if (p1.y === p2.y) { - colorClassNum = branchPos[commit1.branch].index; - lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${p2.y} L ${ - p2.x - } ${p2.y}`; + if (p1.x === p2.x) { + colorClassNum = branchPos[commit1.branch].index; + lineDef = `M ${p1.x} ${p1.y} L ${p1.x + radius} ${p1.y} ${arc} ${p1.x + offset} ${ + p2.y + radius + } L ${p2.x} ${p2.y}`; + } + } else { + if (p1.y < p2.y) { + arc = 'A 20 20, 0, 0, 0,'; + radius = 20; + offset = 20; + + // Figure out the color of the arrow,arrows going down take the color from the destination branch + colorClassNum = branchPos[commit2.branch].index; + + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${p2.y} L ${ + p2.x + } ${p2.y}`; + } + if (p1.y > p2.y) { + arc = 'A 20 20, 0, 0, 0,'; + radius = 20; + offset = 20; + + // Arrows going up take the color from the source branch + colorClassNum = branchPos[commit1.branch].index; + lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${p1.y - offset} L ${ + p2.x + } ${p2.y}`; + } + + if (p1.y === p2.y) { + colorClassNum = branchPos[commit1.branch].index; + lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${p2.y} L ${ + p2.x + } ${p2.y}`; + } } } svg @@ -445,6 +541,13 @@ const drawBranches = (svg, branches) => { line.attr('y2', pos); line.attr('class', 'branch branch' + adjustIndexForTheme); + if (dir === 'TB') { + line.attr('y1', 30); + line.attr('x1', pos); + line.attr('y2', maxPos); + line.attr('x2', pos); + } + lanes.push(pos); let name = branch.name; @@ -467,7 +570,6 @@ const drawBranches = (svg, branches) => { .attr('y', -bbox.height / 2 + 8) .attr('width', bbox.width + 18) .attr('height', bbox.height + 4); - label.attr( 'transform', 'translate(' + @@ -476,7 +578,13 @@ const drawBranches = (svg, branches) => { (pos - bbox.height / 2 - 1) + ')' ); - bkg.attr('transform', 'translate(' + -19 + ', ' + (pos - bbox.height / 2) + ')'); + if (dir === 'TB') { + bkg.attr('x', pos - bbox.width / 2 - 10).attr('y', 0); + label.attr('transform', 'translate(' + (pos - bbox.width / 2 - 5) + ', ' + 0 + ')'); + } + if (dir !== 'TB') { + bkg.attr('transform', 'translate(' + -19 + ', ' + (pos - bbox.height / 2) + ')'); + } }); }; @@ -495,15 +603,24 @@ export const draw = function (txt, id, ver, diagObj) { allCommitsDict = diagObj.db.getCommits(); const branches = diagObj.db.getBranchesAsObjArray(); - - // Position branches vertically + dir = diagObj.db.getDirection(); + const diagram = select(`[id="${id}"]`); + // Position branches let pos = 0; branches.forEach((branch, index) => { - branchPos[branch.name] = { pos, index }; - pos += 50 + (gitGraphConfig.rotateCommitLabel ? 40 : 0); - }); + const labelElement = drawText(branch.name); + const g = diagram.append('g'); + const branchLabel = g.insert('g').attr('class', 'branchLabel'); + const label = branchLabel.insert('g').attr('class', 'label branch-label'); + label.node().appendChild(labelElement); + let bbox = labelElement.getBBox(); - const diagram = select(`[id="${id}"]`); + branchPos[branch.name] = { pos, index }; + pos += 50 + (gitGraphConfig.rotateCommitLabel ? 40 : 0) + (dir === 'TB' ? bbox.width / 2 : 0); + label.remove(); + branchLabel.remove(); + g.remove(); + }); drawCommits(diagram, allCommitsDict, false); if (gitGraphConfig.showBranches) { diff --git a/packages/mermaid/src/diagrams/git/parser/gitGraph.jison b/packages/mermaid/src/diagrams/git/parser/gitGraph.jison index 1c87f3bf3..9ff5623f8 100644 --- a/packages/mermaid/src/diagrams/git/parser/gitGraph.jison +++ b/packages/mermaid/src/diagrams/git/parser/gitGraph.jison @@ -51,7 +51,7 @@ cherry\-pick(?=\s|$) return 'CHERRY_PICK'; // "reset" return 'RESET'; checkout(?=\s|$) return 'CHECKOUT'; "LR" return 'DIR'; -"BT" return 'DIR'; +"TB" return 'DIR'; ":" return ':'; "^" return 'CARET' "options"\r?\n this.begin("options"); // diff --git a/packages/mermaid/src/docs/.vitepress/config.ts b/packages/mermaid/src/docs/.vitepress/config.ts index 330b088d9..2644fa088 100644 --- a/packages/mermaid/src/docs/.vitepress/config.ts +++ b/packages/mermaid/src/docs/.vitepress/config.ts @@ -35,7 +35,12 @@ export default defineConfig({ themeConfig: { nav: nav(), editLink: { - pattern: 'https://github.com/mermaid-js/mermaid/edit/develop/packages/mermaid/src/docs/:path', + pattern: ({ filePath, frontmatter }) => { + if (typeof frontmatter.editLink === 'string') { + return frontmatter.editLink; + } + return `https://github.com/mermaid-js/mermaid/edit/develop/packages/mermaid/src/docs/${filePath}`; + }, text: 'Edit this page on GitHub', }, sidebar: { diff --git a/packages/mermaid/src/docs/community/development.md b/packages/mermaid/src/docs/community/development.md index a5aa39539..93146f0c3 100644 --- a/packages/mermaid/src/docs/community/development.md +++ b/packages/mermaid/src/docs/community/development.md @@ -212,6 +212,9 @@ If the users have no way to know that things have changed, then you haven't real Likewise, if users don't know that there is a new feature that you've implemented, it will forever remain unknown and unused. The documentation has to be updated to users know that things have changed and added! +If you are adding a new feature, add `(v+)` in the title or description. It will be replaced automatically with the current version number when the release happens. + +eg: `# Feature Name (v+)` We know it can sometimes be hard to code _and_ write user documentation. diff --git a/packages/mermaid/src/docs/syntax/gitgraph.md b/packages/mermaid/src/docs/syntax/gitgraph.md index f1930bb27..a9b99e684 100644 --- a/packages/mermaid/src/docs/syntax/gitgraph.md +++ b/packages/mermaid/src/docs/syntax/gitgraph.md @@ -511,6 +511,50 @@ NOTE: Because we have overridden the `mainBranchOrder` to `2`, the `main` branch Here, we have changed the default main branch name to `MetroLine1`. +## Orientation + +In Mermaid, the default orientation is Left to Right. The branches are lined vertically. + +Usage example: + +```mermaid-example + gitGraph + commit + commit + branch develop + commit + commit + commit + checkout main + commit + commit + merge develop + commit + commit +``` + +Sometimes we may want to change the orientation. Currently, Mermaid supports two orientations: **Left to Right**(default) and **Top to Bottom**. + +In order to change the orientation from top to bottom i.e. branches lined horizontally, you need to add `TB` along with `gitGraph`. + +Usage example: + +```mermaid-example + gitGraph TB: + commit + commit + branch develop + commit + commit + commit + checkout main + commit + commit + merge develop + commit + commit +``` + ## Themes Mermaid supports a bunch of pre-defined themes which you can use to find the right one for you. PS: you can actually override an existing theme's variable to get your own custom theme going. Learn more about theming your diagram [here](../config/theming.md). diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f258400a6..db8f628cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12469,7 +12469,7 @@ packages: levn: 0.3.0 prelude-ls: 1.1.2 type-check: 0.3.2 - word-wrap: 1.2.3 + word-wrap: 1.2.4 dev: true /optionator@0.9.1: @@ -12481,7 +12481,7 @@ packages: levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 - word-wrap: 1.2.3 + word-wrap: 1.2.4 dev: true /ospath@1.2.2: @@ -16073,8 +16073,8 @@ packages: resolution: {integrity: sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==} dev: true - /word-wrap@1.2.3: - resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + /word-wrap@1.2.4: + resolution: {integrity: sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==} engines: {node: '>=0.10.0'} dev: true