diff --git a/.esbuild/esbuild.cjs b/.esbuild/esbuild.cjs index 5cdee62c7..e38951bdb 100644 --- a/.esbuild/esbuild.cjs +++ b/.esbuild/esbuild.cjs @@ -1,4 +1,4 @@ -const { esmBuild, umdBuild } = require('./util.cjs'); +const { esmBuild, esmCoreBuild, iifeBuild } = require('./util.cjs'); const { build } = require('esbuild'); const handler = (e) => { @@ -7,8 +7,14 @@ const handler = (e) => { }; const watch = process.argv.includes('--watch'); -build(umdBuild({ minify: false, watch })).catch(handler); +// mermaid.js +build(iifeBuild({ minify: false, watch })).catch(handler); +// mermaid.esm.mjs build(esmBuild({ minify: false, watch })).catch(handler); +// mermaid.min.js build(esmBuild()).catch(handler); -build(umdBuild()).catch(handler); +// mermaid.esm.min.mjs +build(iifeBuild()).catch(handler); +// mermaid.core.mjs (node_modules unbundled) +build(esmCoreBuild()).catch(handler); diff --git a/.esbuild/serve.cjs b/.esbuild/serve.cjs index 13eba401d..c54ff1e9f 100644 --- a/.esbuild/serve.cjs +++ b/.esbuild/serve.cjs @@ -1,7 +1,7 @@ const esbuild = require('esbuild'); const http = require('http'); const path = require('path'); -const { umdBuild } = require('./util.cjs'); +const { iifeBuild } = require('./util.cjs'); // Start esbuild's server on a random local port esbuild @@ -9,7 +9,7 @@ esbuild { servedir: path.join(__dirname, '..'), }, - umdBuild({ minify: false }) + iifeBuild({ minify: false }) ) .then((result) => { // The result tells us where esbuild's local server is diff --git a/.esbuild/util.cjs b/.esbuild/util.cjs index 7fc77d0c5..0cddb7e45 100644 --- a/.esbuild/util.cjs +++ b/.esbuild/util.cjs @@ -1,5 +1,6 @@ const { Generator } = require('jison'); const fs = require('fs'); +const { dependencies } = require('../package.json'); /** @typedef {import('esbuild').BuildOptions} Options */ @@ -18,33 +19,68 @@ const buildOptions = (override = {}) => { tsconfig: 'tsconfig.json', resolveExtensions: ['.ts', '.js', '.json', '.jison'], external: ['require', 'fs', 'path'], - entryPoints: ['src/mermaid.ts'], - outfile: 'dist/mermaid.min.js', + outdir: 'dist', plugins: [jisonPlugin], sourcemap: 'external', ...override, }; }; +const getOutFiles = (extension) => { + return { + [`mermaid${extension}`]: 'src/mermaid.ts', + [`diagramAPI${extension}`]: 'src/diagram-api/diagramAPI.ts', + }; +}; /** - * @param {Options} override - * @returns {Options} + * Build options for mermaid.esm.* build. + * + * For ESM browser use. + * + * @param {Options} override - Override options. + * @returns {Options} ESBuild build options. */ exports.esmBuild = (override = { minify: true }) => { return buildOptions({ format: 'esm', - outfile: `dist/mermaid.esm${override.minify ? '.min' : ''}.mjs`, + entryPoints: getOutFiles(`.esm${override.minify ? '.min' : ''}`), + outExtension: { '.js': '.mjs' }, ...override, }); }; /** - * @param {Options} override - * @returns {Options} + * Build options for mermaid.core.* build. + * + * This build does not bundle `./node_modules/`, as it is designed to be used with + * Webpack/ESBuild/Vite to use mermaid inside an app/website. + * + * @param {Options} override - Override options. + * @returns {Options} ESBuild build options. */ -exports.umdBuild = (override = { minify: true }) => { +exports.esmCoreBuild = (override) => { return buildOptions({ - outfile: `dist/mermaid${override.minify ? '.min' : ''}.js`, + format: 'esm', + entryPoints: getOutFiles(`.core`), + outExtension: { '.js': '.mjs' }, + external: ['require', 'fs', 'path', ...Object.keys(dependencies)], + platform: 'neutral', + ...override, + }); +}; + +/** + * Build options for mermaid.js build. + * + * For IIFE browser use (where ESM is not yet supported). + * + * @param {Options} override - Override options. + * @returns {Options} ESBuild build options. + */ +exports.iifeBuild = (override = { minify: true }) => { + return buildOptions({ + entryPoints: getOutFiles(override.minify ? '.min' : ''), + format: 'iife', ...override, }); }; @@ -55,7 +91,9 @@ const jisonPlugin = { build.onLoad({ filter: /\.jison$/ }, async (args) => { // Load the file from the file system const source = await fs.promises.readFile(args.path, 'utf8'); - const contents = new Generator(source, { 'token-stack': true }).generate(); + const contents = new Generator(source, { 'token-stack': true }).generate({ + moduleMain: '() => {}', // disable moduleMain (default one requires Node.JS modules) + }); return { contents, warnings: [] }; }); }, diff --git a/.eslintignore b/.eslintignore index 9d8891de0..60c278861 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,5 @@ dist/** .github/** docs/Setup.md +cypress.config.js +cypress/plugins/index.js diff --git a/.eslintrc.json b/.eslintrc.json index 4c702fcbc..02753280c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -15,6 +15,7 @@ }, "extends": [ "eslint:recommended", + "plugin:@typescript-eslint/recommended", "plugin:jsdoc/recommended", "plugin:json/recommended", "plugin:markdown/recommended", @@ -22,6 +23,7 @@ ], "plugins": ["@typescript-eslint", "html", "jest", "jsdoc", "json"], "rules": { + "no-console": "error", "no-prototype-builtins": "off", "no-unused-vars": "off", "jsdoc/check-indentation": "off", @@ -35,6 +37,16 @@ "jsdoc/require-returns": "off", "jsdoc/require-returns-description": "off", "cypress/no-async-tests": "off", + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "ts-expect-error": "allow-with-description", + "ts-ignore": "allow-with-description", + "ts-nocheck": "allow-with-description", + "ts-check": "allow-with-description", + "minimumDescriptionLength": 10 + } + ], "json/*": ["error", "allowComments"], "no-empty": ["error", { "allowEmptyCatch": true }] }, @@ -45,6 +57,19 @@ "no-undef": "off", "jsdoc/require-jsdoc": "off" } + }, + { + "files": ["./cypress/**", "./demos/**"], + "rules": { + "no-console": "off" + } + }, + { + "files": ["./**/*.spec.{ts,js}", "./cypress/**", "./demos/**", "./**/docs/**"], + "rules": { + "jsdoc/require-jsdoc": "off", + "@typescript-eslint/no-unused-vars": "off" + } } ] } diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index c7015dbe7..396ff4e6e 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -14,7 +14,7 @@ jobs: name: check tests if: github.repository_owner == 'mermaid-js' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - uses: testomatio/check-tests@stable diff --git a/.github/workflows/e2e b/.github/workflows/e2e index 5b716e429..338869490 100644 --- a/.github/workflows/e2e +++ b/.github/workflows/e2e @@ -36,3 +36,9 @@ jobs: run: yarn e2e env: CYPRESS_CACHE_FOLDER: .cache/Cypress + + - name: Upload Coverage to Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + flag-name: e2e diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 44e2f4cb1..e567aba89 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -40,3 +40,18 @@ jobs: - name: Verify Docs run: yarn docs:verify + + - name: Check no `console.log()` in .jison files + # ESLint can't parse .jison files directly + # In the future, it might be worth making a `eslint-plugin-jison`, so + # that this will be built into the `yarn lint` command. + run: | + shopt -s globstar + mkdir -p tmp/ + for jison_file in src/**/*.jison; do + outfile="tmp/$(basename -- "$jison_file" .jison)-jison.js" + echo "Converting $jison_file to $outfile" + # default module-type (CJS) always adds a console.log() + yarn jison "$jison_file" --outfile "$outfile" --module-type "amd" + done + yarn eslint --no-eslintrc --rule no-console:error --parser "@babel/eslint-parser" "./tmp/*-jison.js" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97aa0a377..08c35befa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,3 +32,12 @@ jobs: - name: Run Unit Tests run: | yarn ci --coverage + + - name: Upload Coverage to Coveralls + # it feels a bit weird to use @master, but that's what the docs use + # (coveralls also doesn't publish a @v1 we can use) + # https://github.com/marketplace/actions/coveralls-github-action + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + flag-name: unit diff --git a/.lintstagedrc.json b/.lintstagedrc.json index db16ea99a..b88abda4b 100644 --- a/.lintstagedrc.json +++ b/.lintstagedrc.json @@ -1,4 +1,5 @@ { "src/docs/**": ["yarn docs:build --git"], - "*.{ts,js,json,html,md}": ["eslint --fix", "prettier --write"] + "src/docs.mts": ["yarn docs:build --git"], + "*.{ts,js,json,html,md,mts}": ["eslint --fix", "prettier --write"] } diff --git a/.webpack/webpack.config.babel.js b/.webpack/webpack.config.babel.js index 9dfd834b7..15760b19b 100644 --- a/.webpack/webpack.config.babel.js +++ b/.webpack/webpack.config.babel.js @@ -3,43 +3,44 @@ import nodeExternals from 'webpack-node-externals'; import baseConfig from './webpack.config.base'; export default (_env, args) => { - switch (args.mode) { - case 'development': - return [ - baseConfig, - merge(baseConfig, { - externals: [nodeExternals()], - output: { - filename: '[name].core.js', - }, - }), - ]; - case 'production': - return [ - // umd - merge(baseConfig, { - output: { - filename: '[name].min.js', - }, - }), - // esm - mergeWithCustomize({ - customizeObject: customizeObject({ - 'output.library': 'replace', - }), - })(baseConfig, { - experiments: { - outputModule: true, - }, - output: { - library: { - type: 'module', - }, - filename: '[name].esm.min.mjs', - }, - }), - ]; - default: - throw new Error('No matching configuration was found!'); - } + return [ + // non-minified + merge(baseConfig, { + optimization: { + minimize: false, + }, + }), + // core [To be used by webpack/esbuild/vite etc to bundle mermaid] + merge(baseConfig, { + externals: [nodeExternals()], + output: { + filename: '[name].core.js', + }, + optimization: { + minimize: false, + }, + }), + // umd + merge(baseConfig, { + output: { + filename: '[name].min.js', + }, + }), + // esm + mergeWithCustomize({ + customizeObject: customizeObject({ + 'output.library': 'replace', + }), + })(baseConfig, { + experiments: { + outputModule: true, + }, + output: { + library: { + type: 'module', + }, + filename: '[name].esm.min.mjs', + }, + }), + ]; }; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb7f3bf5a..8171aeca9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,8 +20,8 @@ yarn test We make all changes via pull requests. As we have many pull requests from developers new to mermaid, the current approach is to have _knsv, Knut Sveidqvist_ as a main reviewer of changes and merging pull requests. More precisely like this: - Large changes reviewed by knsv or other developer asked to review by knsv -- Smaller low-risk changes like dependencies, documentation etc can be merged by active collaborators -- documentation (updates to the docs folder is also allowed via direct commits) +- Smaller low-risk changes like dependencies, documentation, etc. can be merged by active collaborators +- Documentation (updates to the `src/docs` folder is also allowed via direct commits) To commit code, create a branch, let it start with the type like feature or bug followed by the issue number for reference and some describing text. @@ -37,12 +37,28 @@ Another: Less strict here, it is OK to commit directly in the `develop` branch if you are a collaborator. -The documentation is located in the `docs` directory and published using GitHub Pages. -The documentation site is powered by [Docsify](https://docsify.js.org), a simple documentation site generator. +The documentation is written in **Markdown**. For more information about Markdown [see the GitHub Markdown help page](https://help.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax). -The documentation is written in Markdown, for more information about Markdown [see the GitHub Markdown help page](https://help.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax). +### Documentation source files are in /src/docs -If you want to preview the documentation site on your machine, you need to install `docsify-cli`: +The source files for the project documentation are located in the `/src/docs` directory. This is where you should make changes. +The files under `/src/docs` are processed to generate the published documentation, and the resulting files are put into the `/docs` directory. + +```mermaid +flowchart LR + classDef default fill:#fff,color:black,stroke:black + + source["files in /src/docs\n(changes should be done here)"] -- automatic processing\nto generate the final documentation--> published["files in /docs\ndisplayed on the official documentation site"] + +``` + +**_DO NOT CHANGE FILES IN `/docs`_** + +### The official documentation site + +**[The mermaid documentation site](https://mermaid-js.github.io/mermaid/) is powered by [Docsify](https://docsify.js.org), a simple documentation site generator.** + +If you want to preview the whole documentation site on your machine, you need to install `docsify-cli`: ```sh $ npm i docsify-cli -g @@ -121,9 +137,13 @@ it('should render forks and joins', () => { Finally, if it is not in the documentation, no one will know about it and then **no one will use it**. Wouldn't that be sad? With all the effort that was put into the feature? -The docs are located in the docs folder and are ofc written in markdown. Just pick the right section and start typing. If you want to add to the structure as in adding a new section and new file you do that via the \_navbar.md. +The source files for documentation are in `/src/docs` and are written in markdown. Just pick the right section and start typing. See the [Committing Documentation](#committing-documentation) section for more about how the documentation is generated. -The changes in master is reflected in https://mermaid-js.github.io/mermaid/ once released the updates are committed to https://mermaid-js.github.io/#/ +#### Adding to or changing the documentation organization + +If you want to add a new section or change the organization (structure), then you need to make sure to **change the side navigation** in `src/docs/_sidebar.md`. + +When changes are committed and then released, they become part of the `master` branch and become part of the published documentation on https://mermaid-js.github.io/mermaid/ ## Last words diff --git a/cypress.config.js b/cypress.config.js index 044c5d523..d7c9831d4 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ + const { defineConfig } = require('cypress'); const { addMatchImageSnapshotPlugin } = require('cypress-image-snapshot/plugin'); require('@applitools/eyes-cypress')(module); diff --git a/cypress/platform/bundle-test.js b/cypress/platform/bundle-test.js index 22f4fe93c..373f8741a 100644 --- a/cypress/platform/bundle-test.js +++ b/cypress/platform/bundle-test.js @@ -1,4 +1,4 @@ -import mermaid from '../../dist/mermaid'; +import mermaid from '../../dist/mermaid.core'; let code = `flowchart LR Power_Supply --> Transmitter_A diff --git a/cypress/platform/knsv.html b/cypress/platform/knsv.html index c98ce70c8..a06667c1f 100644 --- a/cypress/platform/knsv.html +++ b/cypress/platform/knsv.html @@ -39,6 +39,14 @@
+flowchart LR + a --- ++
+flowchart LR + a2 --- ++
flowchart LR classDef aPID stroke:#4e4403,fill:#fdde29,color:#4e4403,rx:5px,ry:5px; classDef crm stroke:#333333,fill:#DCDCDC,color:#333333,rx:5px,ry:5px; @@ -73,7 +81,31 @@ flowchart TD
flowchart TD -id + + release-branch[Create Release Branch]:::relClass + develop-branch[Update Develop Branch]:::relClass + github-release-draft[GitHub Release Draft]:::relClass + trigger-pipeline[Trigger Jenkins pipeline]:::fixClass + github-release[GitHub Release]:::postClass + + build-ready --> release-branch + build-ready --> develop-branch + release-branch --> jenkins-release-build + jenkins-release-build --> github-release-draft + jenkins-release-build --> install-release + install-release --> verify-release + jenkins-release-build --> announce + github-release-draft --> github-release + verify-release --> verify-check + verify-check -- Yes --> github-release + verify-check -- No --> release-fix + release-fix --> release-branch-pr + verify-check -- No --> delete-artifacts + release-branch-pr --> trigger-pipeline + delete-artifacts --> trigger-pipeline + trigger-pipeline --> jenkins-release-build + +
flowchart LR @@ -99,7 +131,7 @@ flowchart TD class A someclass; class C someclass;-
+sequenceDiagram title: My Sequence Diagram Title accTitle: My Acc Sequence Diagram @@ -109,14 +141,14 @@ flowchart TD John-->>Alice: Great! Alice-)John: See you later!-+graph TD A -->|000| B B -->|111| C linkStyle 1 stroke:#ff3,stroke-width:4px,color:red;-+journey accTitle: My User Journey Diagram accDescr: My User Journey Diagram Description @@ -130,10 +162,10 @@ graph TD Go downstairs: 5: Me Sit down: 5: Me-+info-+requirementDiagram accTitle: My req Diagram accDescr: My req Diagram Description @@ -174,7 +206,7 @@ requirementDiagram test_req - contains -> test_req3 test_req <- copies - test_entity2-+gantt dateFormat YYYY-MM-DD title Adding GANTT diagram functionality to mermaid @@ -206,7 +238,7 @@ gantt Add gantt diagram to demo page :20h Add another diagram to demo page :48h-+stateDiagram state Active { Idle @@ -234,7 +266,7 @@ stateDiagram end B ->> A: Return-+classDiagram accTitle: My class diagram accDescr: My class diagram Description @@ -259,7 +291,7 @@ class Class10 { A->>Bob: Hola Bob-->A: Pasten !-+gitGraph commit id: "ZERO" branch develop @@ -288,7 +320,7 @@ flowchart TD C -->|Two| E[iPhone] C -->|Three| F[fa:fa-car Car]-+classDiagram Animal "1" <|-- Duck Animal <|-- Fish @@ -311,7 +343,7 @@ flowchart TD +run() }-+erDiagram CAR ||--o{ NAMED-DRIVER : allows CAR { @@ -357,6 +389,11 @@ flowchart TD document.getElementsByTagName('body')[0].appendChild(div); } + + mermaid.parseError = function (err, hash) { + console.error('In parse error:'); + console.error(err); + };