diff --git a/.eslintignore b/.eslintignore
index e1957aef9..04348c410 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -3,4 +3,5 @@ dist/**
docs/Setup.md
cypress.config.js
cypress/plugins/index.js
-coverage
\ No newline at end of file
+coverage
+*.json
\ No newline at end of file
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 000000000..e6f99a8bf
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,150 @@
+module.exports = {
+ env: {
+ browser: true,
+ es6: true,
+ 'jest/globals': true,
+ node: true,
+ },
+ root: true,
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ ecmaFeatures: {
+ experimentalObjectRestSpread: true,
+ jsx: true,
+ },
+ tsconfigRootDir: __dirname,
+ sourceType: 'module',
+ ecmaVersion: 2020,
+ allowAutomaticSingleRunInference: true,
+ project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'],
+ parser: '@typescript-eslint/parser',
+ },
+ extends: [
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:json/recommended',
+ 'plugin:markdown/recommended',
+ 'plugin:@cspell/recommended',
+ 'prettier',
+ ],
+ plugins: [
+ '@typescript-eslint',
+ 'no-only-tests',
+ 'html',
+ 'jest',
+ 'jsdoc',
+ 'json',
+ '@cspell',
+ 'lodash',
+ 'unicorn',
+ ],
+ rules: {
+ curly: 'error',
+ 'no-console': 'error',
+ 'no-prototype-builtins': 'off',
+ 'no-unused-vars': 'off',
+ 'cypress/no-async-tests': 'off',
+ '@typescript-eslint/no-floating-promises': 'error',
+ '@typescript-eslint/no-misused-promises': 'error',
+ '@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'],
+ '@cspell/spellchecker': [
+ 'error',
+ {
+ checkIdentifiers: false,
+ checkStrings: false,
+ checkStringTemplates: false,
+ },
+ ],
+ 'no-empty': [
+ 'error',
+ {
+ allowEmptyCatch: true,
+ },
+ ],
+ 'no-only-tests/no-only-tests': 'error',
+ 'lodash/import-scope': ['error', 'method'],
+ 'unicorn/better-regex': 'error',
+ 'unicorn/no-abusive-eslint-disable': 'error',
+ 'unicorn/no-array-push-push': 'error',
+ 'unicorn/no-for-loop': 'error',
+ 'unicorn/no-instanceof-array': 'error',
+ 'unicorn/no-typeof-undefined': 'error',
+ 'unicorn/no-unnecessary-await': 'error',
+ 'unicorn/no-unsafe-regex': 'warn',
+ 'unicorn/no-useless-promise-resolve-reject': 'error',
+ 'unicorn/prefer-array-find': 'error',
+ 'unicorn/prefer-array-flat-map': 'error',
+ 'unicorn/prefer-array-index-of': 'error',
+ 'unicorn/prefer-array-some': 'error',
+ 'unicorn/prefer-default-parameters': 'error',
+ 'unicorn/prefer-includes': 'error',
+ 'unicorn/prefer-negative-index': 'error',
+ 'unicorn/prefer-object-from-entries': 'error',
+ 'unicorn/prefer-string-starts-ends-with': 'error',
+ 'unicorn/prefer-string-trim-start-end': 'error',
+ 'unicorn/string-content': 'error',
+ 'unicorn/prefer-spread': 'error',
+ 'unicorn/no-lonely-if': 'error',
+ },
+ overrides: [
+ {
+ files: ['cypress/**', 'demos/**'],
+ rules: {
+ 'no-console': 'off',
+ },
+ },
+ {
+ files: ['*.{js,jsx,mjs,cjs}'],
+ extends: ['plugin:jsdoc/recommended'],
+ rules: {
+ 'jsdoc/check-indentation': 'off',
+ 'jsdoc/check-alignment': 'off',
+ 'jsdoc/check-line-alignment': 'off',
+ 'jsdoc/multiline-blocks': 'off',
+ 'jsdoc/newline-after-description': 'off',
+ 'jsdoc/tag-lines': 'off',
+ 'jsdoc/require-param-description': 'off',
+ 'jsdoc/require-param-type': 'off',
+ 'jsdoc/require-returns': 'off',
+ 'jsdoc/require-returns-description': 'off',
+ },
+ },
+ {
+ files: ['*.{ts,tsx}'],
+ plugins: ['tsdoc'],
+ rules: {
+ 'tsdoc/syntax': 'error',
+ },
+ },
+ {
+ files: ['*.spec.{ts,js}', 'cypress/**', 'demos/**', '**/docs/**'],
+ rules: {
+ 'jsdoc/require-jsdoc': 'off',
+ '@typescript-eslint/no-unused-vars': 'off',
+ },
+ },
+ {
+ files: ['*.html', '*.md', '**/*.md/*'],
+ rules: {
+ 'no-var': 'error',
+ 'no-undef': 'off',
+ '@typescript-eslint/no-unused-vars': 'off',
+ '@typescript-eslint/no-floating-promises': 'off',
+ '@typescript-eslint/no-misused-promises': 'off',
+ },
+ parserOptions: {
+ project: null,
+ },
+ },
+ ],
+};
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 9d7eacecd..000000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,137 +0,0 @@
-{
- "env": {
- "browser": true,
- "es6": true,
- "jest/globals": true,
- "node": true
- },
- "parser": "@typescript-eslint/parser",
- "parserOptions": {
- "ecmaFeatures": {
- "experimentalObjectRestSpread": true,
- "jsx": true
- },
- "sourceType": "module"
- },
- "extends": [
- "eslint:recommended",
- "plugin:@typescript-eslint/recommended",
- "plugin:json/recommended",
- "plugin:markdown/recommended",
- "plugin:@cspell/recommended",
- "prettier"
- ],
- "plugins": [
- "@typescript-eslint",
- "no-only-tests",
- "html",
- "jest",
- "jsdoc",
- "json",
- "@cspell",
- "lodash",
- "unicorn"
- ],
- "rules": {
- "curly": "error",
- "no-console": "error",
- "no-prototype-builtins": "off",
- "no-unused-vars": "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"],
- "@cspell/spellchecker": [
- "error",
- {
- "checkIdentifiers": false,
- "checkStrings": false,
- "checkStringTemplates": false
- }
- ],
- "no-empty": [
- "error",
- {
- "allowEmptyCatch": true
- }
- ],
- "no-only-tests/no-only-tests": "error",
- "lodash/import-scope": ["error", "method"],
- "unicorn/better-regex": "error",
- "unicorn/no-abusive-eslint-disable": "error",
- "unicorn/no-array-push-push": "error",
- "unicorn/no-for-loop": "error",
- "unicorn/no-instanceof-array": "error",
- "unicorn/no-typeof-undefined": "error",
- "unicorn/no-unnecessary-await": "error",
- "unicorn/no-unsafe-regex": "warn",
- "unicorn/no-useless-promise-resolve-reject": "error",
- "unicorn/prefer-array-find": "error",
- "unicorn/prefer-array-flat-map": "error",
- "unicorn/prefer-array-index-of": "error",
- "unicorn/prefer-array-some": "error",
- "unicorn/prefer-default-parameters": "error",
- "unicorn/prefer-includes": "error",
- "unicorn/prefer-negative-index": "error",
- "unicorn/prefer-object-from-entries": "error",
- "unicorn/prefer-string-starts-ends-with": "error",
- "unicorn/prefer-string-trim-start-end": "error",
- "unicorn/string-content": "error",
- "unicorn/prefer-spread": "error",
- "unicorn/no-lonely-if": "error"
- },
- "overrides": [
- {
- "files": ["cypress/**", "demos/**"],
- "rules": {
- "no-console": "off"
- }
- },
- {
- "files": ["*.{js,jsx,mjs,cjs}"],
- "extends": ["plugin:jsdoc/recommended"],
- "rules": {
- "jsdoc/check-indentation": "off",
- "jsdoc/check-alignment": "off",
- "jsdoc/check-line-alignment": "off",
- "jsdoc/multiline-blocks": "off",
- "jsdoc/newline-after-description": "off",
- "jsdoc/tag-lines": "off",
- "jsdoc/require-param-description": "off",
- "jsdoc/require-param-type": "off",
- "jsdoc/require-returns": "off",
- "jsdoc/require-returns-description": "off"
- }
- },
- {
- "files": ["*.{ts,tsx}"],
- "plugins": ["tsdoc"],
- "rules": {
- "tsdoc/syntax": "error"
- }
- },
- {
- "files": ["*.spec.{ts,js}", "cypress/**", "demos/**", "**/docs/**"],
- "rules": {
- "jsdoc/require-jsdoc": "off",
- "@typescript-eslint/no-unused-vars": "off"
- }
- },
- {
- "files": ["*.html", "*.md", "**/*.md/*"],
- "rules": {
- "no-var": "error",
- "no-undef": "off",
- "@typescript-eslint/no-unused-vars": "off"
- }
- }
- ]
-}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 95e4256b1..a21fbc005 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -37,7 +37,20 @@ jobs:
CYPRESS_CACHE_FOLDER: .cache/Cypress
- name: Run Linting
- run: pnpm run lint
+ shell: bash
+ run: |
+ if ! pnpm run lint; then
+ # print a nice error message on lint failure
+ ERROR_MESSAGE='Running `pnpm run lint` failed.'
+ ERROR_MESSAGE+=' Running `pnpm run lint:fix` may fix this issue. '
+ ERROR_MESSAGE+=" If this error doesn't occur on your local machine,"
+ ERROR_MESSAGE+=' make sure your packages are up-to-date by running `pnpm install`.'
+ ERROR_MESSAGE+=' You may also need to delete your prettier cache by running'
+ ERROR_MESSAGE+=' `rm ./node_modules/.cache/prettier/.prettier-cache`.'
+ echo "::error title=Lint failure::${ERROR_MESSAGE}"
+ # make sure to return an error exitcode so that GitHub actions shows a red-cross
+ exit 1
+ fi
- name: Verify Docs
id: verifyDocs
diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs
index ff1d8c107..ac2623093 100644
--- a/.lintstagedrc.mjs
+++ b/.lintstagedrc.mjs
@@ -1,5 +1,11 @@
export default {
- '!(docs/**/*)*.{ts,js,json,html,md,mts}': ['eslint --fix', 'prettier --write'],
+ '!(docs/**/*)*.{ts,js,json,html,md,mts}': [
+ 'eslint --cache --cache-strategy content --fix',
+ // don't cache prettier yet, since we use `prettier-plugin-jsdoc`,
+ // and prettier doesn't invalidate cache on plugin updates"
+ // https://prettier.io/docs/en/cli.html#--cache
+ 'prettier --write',
+ ],
'cSpell.json': ['ts-node-esm scripts/fixCSpell.ts'],
'**/*.jison': ['pnpm -w run lint:jison'],
};
diff --git a/README.md b/README.md
index 9a500283c..9fed47a69 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# mermaid
-[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
+[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
English | [简体中文](./README.zh-CN.md)
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 6b3e28b19..0f0cef7a8 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -1,6 +1,6 @@
# mermaid
-[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
+[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
[English](./README.md) | 简体中文
diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js
index b5ff92c8c..1f063c13e 100644
--- a/cypress/integration/rendering/sequencediagram.spec.js
+++ b/cypress/integration/rendering/sequencediagram.spec.js
@@ -3,6 +3,42 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
context('Sequence diagram', () => {
+ it('should render a sequence diagram with boxes', () => {
+ renderGraph(
+ `
+ sequenceDiagram
+ box LightGrey Alice and Bob
+ participant Alice
+ participant Bob
+ end
+ participant John as John
Second Line
+ Alice ->> Bob: Hello Bob, how are you?
+ Bob-->>John: How about you John?
+ Bob--x Alice: I am good thanks!
+ Bob-x John: I am good thanks!
+ Note right of John: Bob thinks a long
long time, so long
that the text does
not fit on a row.
+ Bob-->Alice: Checking with John...
+ alt either this
+ Alice->>John: Yes
+ else or this
+ Alice->>John: No
+ else or this will happen
+ Alice->John: Maybe
+ end
+ par this happens in parallel
+ Alice -->> Bob: Parallel message 1
+ and
+ Alice -->> John: Parallel message 2
+ end
+ `,
+ { sequence: { useMaxWidth: false } }
+ );
+ cy.get('svg').should((svg) => {
+ const width = parseFloat(svg.attr('width'));
+ expect(width).to.be.within(830 * 0.95, 830 * 1.05);
+ expect(svg).to.not.have.attr('style');
+ });
+ });
it('should render a simple sequence diagram', () => {
imgSnapshotTest(
`
diff --git a/cypress/platform/viewer.js b/cypress/platform/viewer.js
index c10ae73b1..01b49435f 100644
--- a/cypress/platform/viewer.js
+++ b/cypress/platform/viewer.js
@@ -151,7 +151,7 @@ if (typeof document !== 'undefined') {
contentLoadedApi();
} else {
this.console.log('Not using api');
- contentLoaded();
+ void contentLoaded();
}
},
false
diff --git a/demos/er.html b/demos/er.html
index 34e06acf8..662b8cbed 100644
--- a/demos/er.html
+++ b/demos/er.html
@@ -71,6 +71,44 @@
+ erDiagram + "HOSPITAL" { + int id PK + int doctor_id PK, FK + string address UK + string name + string phone_number + string fax_number + } ++
+ erDiagram + CAR ||--o{ NAMED-DRIVER : allows + CAR { + string registrationNumber PK + string make + string model + string[] parts + } + PERSON ||--o{ NAMED-DRIVER : is + PERSON { + string driversLicense PK "The license #" + string(99) firstName "Only 99 characters are allowed" + string lastName + string phone UK + int age + } + NAMED-DRIVER { + string carRegistrationNumber PK, FK + string driverLicence PK,FK + } + MANUFACTURER only one to zero or more CAR : makes ++