diff --git a/.build/types.ts b/.build/types.ts
index 419240782..9dec05a68 100644
--- a/.build/types.ts
+++ b/.build/types.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-console */
import { packageOptions } from './common.js';
import { execSync } from 'child_process';
@@ -5,11 +6,17 @@ const buildType = (packageName: string) => {
console.log(`Building types for ${packageName}`);
try {
const out = execSync(`tsc -p ./packages/${packageName}/tsconfig.json --emitDeclarationOnly`);
- out.length > 0 && console.log(out.toString());
+ if (out.length > 0) {
+ console.log(out.toString());
+ }
} catch (e) {
console.error(e);
- e.stdout.length > 0 && console.error(e.stdout.toString());
- e.stderr.length > 0 && console.error(e.stderr.toString());
+ if (e.stdout.length > 0) {
+ console.error(e.stdout.toString());
+ }
+ if (e.stderr.length > 0) {
+ console.error(e.stderr.toString());
+ }
}
};
diff --git a/.cspell/code-terms.txt b/.cspell/code-terms.txt
index 2670a9521..cf47a86a8 100644
--- a/.cspell/code-terms.txt
+++ b/.cspell/code-terms.txt
@@ -13,6 +13,7 @@ bqstring
BQUOTE
bramp
BRKT
+brotli
callbackargs
callbackname
classdef
@@ -113,6 +114,7 @@ STYLECLASS
STYLEOPTS
subcomponent
subcomponents
+subconfig
SUBROUTINEEND
SUBROUTINESTART
Subschemas
@@ -127,6 +129,7 @@ titlevalue
topbar
TRAPEND
TRAPSTART
+treemap
ts-nocheck
tsdoc
typeof
diff --git a/.cspell/contributors.txt b/.cspell/contributors.txt
index bd3ad9da2..b7f52f8d0 100644
--- a/.cspell/contributors.txt
+++ b/.cspell/contributors.txt
@@ -4,5 +4,6 @@ cpettitt
Dong Cai
Nikolay Rozhkov
Peng Xiao
+Per Brolin
subhash-halder
Vinod Sidharth
diff --git a/.cspell/libraries.txt b/.cspell/libraries.txt
index 83bea2d09..7eabb8b29 100644
--- a/.cspell/libraries.txt
+++ b/.cspell/libraries.txt
@@ -20,6 +20,7 @@ dagre-d3
Deepdwn
Docsify
Docsy
+Doctave
DokuWiki
dompurify
elkjs
@@ -56,12 +57,14 @@ pyplot
redmine
rehype
rscratch
+shiki
sparkline
sphinxcontrib
ssim
stylis
Swimm
tsbuildinfo
+tseslint
Tuleap
Typora
unocss
diff --git a/.cspell/misc-terms.txt b/.cspell/misc-terms.txt
index 467e48891..0efd1dcc0 100644
--- a/.cspell/misc-terms.txt
+++ b/.cspell/misc-terms.txt
@@ -1 +1,4 @@
+BRANDES
+handdrawn
+KOEPF
newbranch
diff --git a/.esbuild/build.ts b/.esbuild/build.ts
index 3c87f9d62..505c18405 100644
--- a/.esbuild/build.ts
+++ b/.esbuild/build.ts
@@ -2,7 +2,8 @@ import { build } from 'esbuild';
import { mkdir, writeFile } from 'node:fs/promises';
import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.js';
-import { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
+import type { MermaidBuildOptions } from './util.js';
+import { defaultOptions, getBuildConfig } from './util.js';
const shouldVisualize = process.argv.includes('--visualize');
@@ -35,11 +36,11 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
if (shouldVisualize) {
for (const { metafile } of results) {
- if (!metafile) {
+ if (!metafile?.outputs) {
continue;
}
const fileName = Object.keys(metafile.outputs)
- .filter((file) => !file.includes('chunks') && file.endsWith('js'))[0]
+ .find((file) => !file.includes('chunks') && file.endsWith('js'))
.replace('dist/', '');
// Upload metafile into https://esbuild.github.io/analyze/
await writeFile(`stats/${fileName}.meta.json`, JSON.stringify(metafile));
@@ -48,13 +49,14 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
};
const handler = (e) => {
+ // eslint-disable-next-line no-console
console.error(e);
process.exit(1);
};
const main = async () => {
await generateLangium();
- await mkdir('stats').catch(() => {});
+ await mkdir('stats', { recursive: true });
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
// it should build `parser` before `mermaid` because it's a dependency
for (const pkg of packageNames) {
diff --git a/.esbuild/jisonPlugin.ts b/.esbuild/jisonPlugin.ts
index de801ea9f..007516f08 100644
--- a/.esbuild/jisonPlugin.ts
+++ b/.esbuild/jisonPlugin.ts
@@ -1,6 +1,6 @@
import { readFile } from 'node:fs/promises';
import { transformJison } from '../.build/jisonTransformer.js';
-import { Plugin } from 'esbuild';
+import type { Plugin } from 'esbuild';
export const jisonPlugin: Plugin = {
name: 'jison',
diff --git a/.esbuild/server.ts b/.esbuild/server.ts
index 9102c7de8..ef61ebec2 100644
--- a/.esbuild/server.ts
+++ b/.esbuild/server.ts
@@ -1,11 +1,12 @@
-import express from 'express';
-import type { NextFunction, Request, Response } from 'express';
-import cors from 'cors';
-import { getBuildConfig, defaultOptions } from './util.js';
-import { context } from 'esbuild';
+/* eslint-disable no-console */
import chokidar from 'chokidar';
-import { generateLangium } from '../.build/generateLangium.js';
+import cors from 'cors';
+import { context } from 'esbuild';
+import type { Request, Response } from 'express';
+import express from 'express';
import { packageOptions } from '../.build/common.js';
+import { generateLangium } from '../.build/generateLangium.js';
+import { defaultOptions, getBuildConfig } from './util.js';
const configs = Object.values(packageOptions).map(({ packageName }) =>
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: packageName })
@@ -19,16 +20,28 @@ const mermaidIIFEConfig = getBuildConfig({
});
configs.push(mermaidIIFEConfig);
-const contexts = await Promise.all(configs.map((config) => context(config)));
+const contexts = await Promise.all(
+ configs.map(async (config) => ({ config, context: await context(config) }))
+);
+let rebuildCounter = 1;
const rebuildAll = async () => {
- console.time('Rebuild time');
- await Promise.all(contexts.map((ctx) => ctx.rebuild())).catch((e) => console.error(e));
- console.timeEnd('Rebuild time');
+ const buildNumber = rebuildCounter++;
+ const timeLabel = `Rebuild ${buildNumber} Time (total)`;
+ console.time(timeLabel);
+ await Promise.all(
+ contexts.map(async ({ config, context }) => {
+ const buildVariant = `Rebuild ${buildNumber} Time (${Object.keys(config.entryPoints!)[0]} ${config.format})`;
+ console.time(buildVariant);
+ await context.rebuild();
+ console.timeEnd(buildVariant);
+ })
+ ).catch((e) => console.error(e));
+ console.timeEnd(timeLabel);
};
let clients: { id: number; response: Response }[] = [];
-function eventsHandler(request: Request, response: Response, next: NextFunction) {
+function eventsHandler(request: Request, response: Response) {
const headers = {
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
@@ -45,19 +58,20 @@ function eventsHandler(request: Request, response: Response, next: NextFunction)
});
}
-let timeoutId: NodeJS.Timeout | undefined = undefined;
+let timeoutID: NodeJS.Timeout | undefined = undefined;
/**
* Debounce file change events to avoid rebuilding multiple times.
*/
function handleFileChange() {
- if (timeoutId !== undefined) {
- clearTimeout(timeoutId);
+ if (timeoutID !== undefined) {
+ clearTimeout(timeoutID);
}
- timeoutId = setTimeout(async () => {
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ timeoutID = setTimeout(async () => {
await rebuildAll();
sendEventsToAll();
- timeoutId = undefined;
+ timeoutID = undefined;
}, 100);
}
@@ -74,15 +88,16 @@ async function createServer() {
ignoreInitial: true,
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
})
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
.on('all', async (event, path) => {
// Ignore other events.
if (!['add', 'change'].includes(event)) {
return;
}
- if (/\.langium$/.test(path)) {
+ console.log(`${path} changed. Rebuilding...`);
+ if (path.endsWith('.langium')) {
await generateLangium();
}
- console.log(`${path} changed. Rebuilding...`);
handleFileChange();
});
@@ -99,4 +114,4 @@ async function createServer() {
});
}
-createServer();
+void createServer();
diff --git a/.esbuild/util.ts b/.esbuild/util.ts
index 5c21cbf45..6d424ab17 100644
--- a/.esbuild/util.ts
+++ b/.esbuild/util.ts
@@ -56,7 +56,7 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const { name, file, packageName } = packageOptions[entryName];
const outFileName = getFileName(name, options);
- let output: BuildOptions = buildOptions({
+ const output: BuildOptions = buildOptions({
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
entryPoints: {
[outFileName]: `src/${file}`,
diff --git a/.eslintignore b/.eslintignore
deleted file mode 120000
index 3e4e48b0b..000000000
--- a/.eslintignore
+++ /dev/null
@@ -1 +0,0 @@
-.gitignore
\ No newline at end of file
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
deleted file mode 100644
index c9428c9f5..000000000
--- a/.eslintrc.cjs
+++ /dev/null
@@ -1,190 +0,0 @@
-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: 2022,
- 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-legacy',
- 'plugin:@cspell/recommended',
- 'prettier',
- ],
- plugins: [
- '@typescript-eslint',
- 'no-only-tests',
- 'html',
- 'jest',
- 'jsdoc',
- 'json',
- '@cspell',
- 'lodash',
- 'unicorn',
- ],
- ignorePatterns: [
- // this file is automatically generated by `pnpm run --filter mermaid types:build-config`
- 'packages/mermaid/src/config.type.ts',
- ],
- rules: {
- curly: 'error',
- 'no-console': 'error',
- 'no-prototype-builtins': 'off',
- 'no-unused-vars': 'off',
- 'cypress/no-async-tests': 'off',
- '@typescript-eslint/consistent-type-imports': 'error',
- '@typescript-eslint/no-explicit-any': 'warn',
- '@typescript-eslint/no-floating-promises': 'error',
- '@typescript-eslint/no-misused-promises': 'error',
- '@typescript-eslint/no-unused-vars': 'warn',
- '@typescript-eslint/consistent-type-definitions': '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,
- },
- ],
- '@typescript-eslint/naming-convention': [
- 'error',
- {
- selector: 'typeLike',
- format: ['PascalCase'],
- custom: {
- regex: '^I[A-Z]',
- match: false,
- },
- },
- ],
- 'json/*': ['error', 'allowComments'],
- '@cspell/spellchecker': [
- 'error',
- {
- checkIdentifiers: true,
- checkStrings: true,
- checkStringTemplates: true,
- },
- ],
- '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: {
- 'no-restricted-syntax': [
- 'error',
- {
- selector: 'TSEnumDeclaration',
- message:
- 'Prefer using TypeScript union types over TypeScript enum, since TypeScript enums have a bunch of issues, see https://dev.to/dvddpl/whats-the-problem-with-typescript-enums-2okj',
- },
- ],
- 'tsdoc/syntax': 'error',
- },
- },
- {
- files: ['*.spec.{ts,js}', 'cypress/**', 'demos/**', '**/docs/**'],
- rules: {
- 'jsdoc/require-jsdoc': 'off',
- '@typescript-eslint/no-unused-vars': 'off',
- },
- },
- {
- files: ['*.spec.{ts,js}', 'tests/**', 'cypress/**/*.js'],
- rules: {
- '@cspell/spellchecker': [
- 'error',
- {
- checkIdentifiers: false,
- checkStrings: false,
- checkStringTemplates: false,
- },
- ],
- },
- },
- {
- 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/.github/lychee.toml b/.github/lychee.toml
index c5a2f0e45..2e3b08c41 100644
--- a/.github/lychee.toml
+++ b/.github/lychee.toml
@@ -41,7 +41,10 @@ exclude = [
"https://bundlephobia.com",
# Chrome webstore migration issue. Temporary
-"https://chromewebstore.google.com"
+"https://chromewebstore.google.com",
+
+# Drupal 403
+"https://(www.)?drupal.org"
]
# Exclude all private IPs from checking.
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index 87607bc2f..0ce778957 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -18,7 +18,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index bf54772bc..c6e96912e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -18,7 +18,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml
index 1238fe371..5e5407a23 100644
--- a/.github/workflows/e2e-applitools.yml
+++ b/.github/workflows/e2e-applitools.yml
@@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 6477c9eb5..df487494d 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -1,9 +1,3 @@
-# We use github cache to save snapshots between runs.
-# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
-# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
-# These are then downloaded before running the E2E, providing the reference snapshots.
-# If there are any errors, the diff image is uploaded to artifacts, and the user is notified.
-
name: E2E
on:
@@ -38,7 +32,7 @@ jobs:
options: --user 1001
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
@@ -72,16 +66,6 @@ jobs:
mkdir -p cypress/snapshots/stats/base
mv stats cypress/snapshots/stats/base
- - name: Cypress run
- uses: cypress-io/github-action@v6
- id: cypress-snapshot-gen
- if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
- with:
- install: false
- start: pnpm run dev
- wait-on: 'http://localhost:9000'
- browser: chrome
-
e2e:
runs-on: ubuntu-latest
container:
@@ -95,7 +79,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
@@ -146,6 +130,10 @@ jobs:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
VITEST_COVERAGE: true
CYPRESS_COMMIT: ${{ github.sha }}
+ ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
+ ARGOS_PARALLEL: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
+ ARGOS_PARALLEL_TOTAL: 4
+ ARGOS_PARALLEL_INDEX: ${{ matrix.containers }}
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v4
@@ -158,55 +146,3 @@ jobs:
fail_ci_if_error: false
verbose: true
token: 6845cc80-77ee-4e17-85a1-026cd95e0766
-
- # We upload the artifacts into numbered archives to prevent overwriting
- - name: Upload Artifacts
- uses: actions/upload-artifact@v4
- if: ${{ always() }}
- with:
- name: snapshots-${{ matrix.containers }}
- retention-days: 1
- path: ./cypress/snapshots
-
- combineArtifacts:
- needs: e2e
- runs-on: ubuntu-latest
- if: ${{ always() }}
- steps:
- # Download all snapshot artifacts and merge them into a single folder
- - name: Download All Artifacts
- uses: actions/download-artifact@v4
- with:
- path: snapshots
- pattern: snapshots-*
- merge-multiple: true
-
- # For successful push events, we save the snapshots cache
- - name: Save snapshots cache
- id: cache-upload
- if: ${{ github.event_name == 'push' && needs.e2e.result != 'failure' }}
- uses: actions/cache/save@v4
- with:
- path: ./snapshots
- key: ${{ runner.os }}-snapshots-${{ github.event.after }}
-
- - name: Flatten images to a folder
- if: ${{ needs.e2e.result == 'failure' }}
- run: |
- mkdir errors
- cd snapshots
- find . -mindepth 2 -type d -name "*__diff_output__*" -exec sh -c 'mv "$0"/*.png ../errors/' {} \;
-
- - name: Upload Error snapshots
- if: ${{ needs.e2e.result == 'failure' }}
- uses: actions/upload-artifact@v4
- id: upload-artifacts
- with:
- name: error-snapshots
- retention-days: 10
- path: errors/
-
- - name: Notify Users
- if: ${{ needs.e2e.result == 'failure' }}
- run: |
- echo "::error title=Visual tests failed::You can view images that failed by downloading the error-snapshots artifact: ${{ steps.upload-artifacts.outputs.artifact-url }}"
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 8f5995d71..ac34067f6 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -19,7 +19,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml
index fb70a90ec..4ff5f4117 100644
--- a/.github/workflows/publish-docs.yml
+++ b/.github/workflows/publish-docs.yml
@@ -25,7 +25,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
diff --git a/.github/workflows/release-preview-publish.yml b/.github/workflows/release-preview-publish.yml
index c763430b0..91e3ac981 100644
--- a/.github/workflows/release-preview-publish.yml
+++ b/.github/workflows/release-preview-publish.yml
@@ -13,7 +13,7 @@ jobs:
with:
fetch-depth: 0
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml
index dce461cf5..4dcf709c0 100644
--- a/.github/workflows/release-publish.yml
+++ b/.github/workflows/release-publish.yml
@@ -11,7 +11,7 @@ jobs:
- uses: actions/checkout@v4
- uses: fregante/setup-git-user@v2
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index a4bd264e0..a0b284a68 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -11,7 +11,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
diff --git a/.github/workflows/update-browserlist.yml b/.github/workflows/update-browserlist.yml
index 9aac3d7b3..f8f7696cd 100644
--- a/.github/workflows/update-browserlist.yml
+++ b/.github/workflows/update-browserlist.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2
+ - uses: pnpm/action-setup@v4
- run: npx update-browserslist-db@latest
- name: Commit changes
uses: EndBug/add-and-commit@v9
diff --git a/.vite/build.ts b/.vite/build.ts
index 7ce93a497..486d59452 100644
--- a/.vite/build.ts
+++ b/.vite/build.ts
@@ -1,4 +1,5 @@
-import { build, InlineConfig, type PluginOption } from 'vite';
+import type { InlineConfig } from 'vite';
+import { build, type PluginOption } from 'vite';
import { resolve } from 'path';
import { fileURLToPath } from 'url';
import jisonPlugin from './jisonPlugin.js';
@@ -46,9 +47,10 @@ interface BuildOptions {
export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions): InlineConfig => {
const external: (string | RegExp)[] = ['require', 'fs', 'path'];
+ // eslint-disable-next-line no-console
console.log(entryName, packageOptions[entryName]);
const { name, file, packageName } = packageOptions[entryName];
- let output: OutputOptions = [
+ const output: OutputOptions = [
{
name,
format: 'esm',
@@ -83,7 +85,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
plugins: [
jisonPlugin(),
jsonSchemaPlugin(), // handles `.schema.yaml` files
- // @ts-expect-error According to the type definitions, rollup plugins are incompatible with vite
typescript({ compilerOptions: { declaration: false } }),
istanbul({
exclude: ['node_modules', 'test/', '__mocks__', 'generated'],
@@ -121,10 +122,10 @@ await generateLangium();
if (watch) {
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
- build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
+ void build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
if (!mermaidOnly) {
- build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
- build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
+ void build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
+ void build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
}
} else if (visualize) {
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
diff --git a/.vite/jsonSchemaPlugin.ts b/.vite/jsonSchemaPlugin.ts
index 2e5b5cc0b..3614d5b45 100644
--- a/.vite/jsonSchemaPlugin.ts
+++ b/.vite/jsonSchemaPlugin.ts
@@ -1,4 +1,4 @@
-import { PluginOption } from 'vite';
+import type { PluginOption } from 'vite';
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
/**
diff --git a/.vite/server.ts b/.vite/server.ts
index 99d16f6f2..078599dc1 100644
--- a/.vite/server.ts
+++ b/.vite/server.ts
@@ -23,8 +23,9 @@ async function createServer() {
app.use(express.static('cypress/platform'));
app.listen(9000, () => {
+ // eslint-disable-next-line no-console
console.log(`Listening on http://localhost:9000`);
});
}
-createServer();
+void createServer();
diff --git a/README.md b/README.md
index d368a4349..8d5eebfeb 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@ Try Live Editor previews of future releases:
diff --git a/cypress.config.ts b/cypress.config.ts
index 4182d92a8..3346b5549 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -2,6 +2,8 @@ import { defineConfig } from 'cypress';
import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin';
import coverage from '@cypress/code-coverage/task';
import eyesPlugin from '@applitools/eyes-cypress';
+import { registerArgosTask } from '@argos-ci/cypress/task';
+
export default eyesPlugin(
defineConfig({
projectId: 'n2sma2',
@@ -17,10 +19,17 @@ export default eyesPlugin(
}
return launchOptions;
});
- addMatchImageSnapshotPlugin(on, config);
// copy any needed variables from process.env to config.env
config.env.useAppli = process.env.USE_APPLI ? true : false;
+ config.env.useArgos = !!process.env.CI;
+ if (config.env.useArgos) {
+ registerArgosTask(on, config, {
+ token: 'fc3a35cf5200db928d65b2047861582d9444032b',
+ });
+ } else {
+ addMatchImageSnapshotPlugin(on, config);
+ }
// do not forget to return the changed config object!
return config;
},
diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts
index aed5d7973..3ffba697a 100644
--- a/cypress/helpers/util.ts
+++ b/cypress/helpers/util.ts
@@ -35,7 +35,7 @@ export const mermaidUrl = (
};
const objStr: string = JSON.stringify(codeObject);
let url = `http://localhost:9000/e2e.html?graph=${utf8ToB64(objStr)}`;
- if (api) {
+ if (api && typeof graphStr === 'string') {
url = `http://localhost:9000/xss.html?graph=${graphStr}`;
}
@@ -54,16 +54,15 @@ export const imgSnapshotTest = (
): void => {
const options: CypressMermaidConfig = {
..._options,
- fontFamily: _options.fontFamily || 'courier',
+ fontFamily: _options.fontFamily ?? 'courier',
// @ts-ignore TODO: Fix type of fontSize
- fontSize: _options.fontSize || '16px',
+ fontSize: _options.fontSize ?? '16px',
sequence: {
- ...(_options.sequence || {}),
+ ...(_options.sequence ?? {}),
actorFontFamily: 'courier',
- noteFontFamily:
- _options.sequence && _options.sequence.noteFontFamily
- ? _options.sequence.noteFontFamily
- : 'courier',
+ noteFontFamily: _options.sequence?.noteFontFamily
+ ? _options.sequence.noteFontFamily
+ : 'courier',
messageFontFamily: 'courier',
},
};
@@ -95,18 +94,7 @@ export const openURLAndVerifyRendering = (
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,
- });
- }
+ const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
@@ -116,11 +104,27 @@ export const openURLAndVerifyRendering = (
cy.get('svg').should(validation);
}
+ verifyScreenshot(name);
+};
+
+export const verifyScreenshot = (name: string): void => {
+ const useAppli: boolean = Cypress.env('useAppli');
+ const useArgos: boolean = Cypress.env('useArgos');
+
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.log(`Check eyes ${Cypress.spec.name}`);
cy.eyesCheckWindow('Click!');
cy.log(`Closing eyes ${Cypress.spec.name}`);
cy.eyesClose();
+ } else if (useArgos) {
+ cy.argosScreenshot(name);
} else {
cy.matchImageSnapshot(name);
}
diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js
index 544eab40f..ad6b21e29 100644
--- a/cypress/integration/other/configuration.spec.js
+++ b/cypress/integration/other/configuration.spec.js
@@ -1,4 +1,4 @@
-import { renderGraph } from '../../helpers/util.ts';
+import { renderGraph, verifyScreenshot } from '../../helpers/util.ts';
describe('Configuration', () => {
describe('arrowMarkerAbsolute', () => {
it('should handle default value false of arrowMarkerAbsolute', () => {
@@ -119,8 +119,7 @@ describe('Configuration', () => {
const url = 'http://localhost:9000/regression/issue-1874.html';
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
- cy.get('svg').should('be.visible');
- cy.matchImageSnapshot(
+ verifyScreenshot(
'configuration.spec-should-not-taint-initial-configuration-when-using-multiple-directives'
);
});
@@ -145,7 +144,7 @@ describe('Configuration', () => {
// none of the diagrams should be error diagrams
expect($svg).to.not.contain('Syntax error');
});
- cy.matchImageSnapshot(
+ verifyScreenshot(
'configuration.spec-should-not-render-error-diagram-if-suppressErrorRendering-is-set'
);
});
@@ -162,7 +161,7 @@ describe('Configuration', () => {
// some of the diagrams should be error diagrams
expect($svg).to.contain('Syntax error');
});
- cy.matchImageSnapshot(
+ verifyScreenshot(
'configuration.spec-should-render-error-diagram-if-suppressErrorRendering-is-not-set'
);
});
diff --git a/cypress/integration/other/xss.spec.js b/cypress/integration/other/xss.spec.js
index d041fa5f4..1e51d2f23 100644
--- a/cypress/integration/other/xss.spec.js
+++ b/cypress/integration/other/xss.spec.js
@@ -10,7 +10,6 @@ describe('XSS', () => {
cy.wait(1000).then(() => {
cy.get('.mermaid').should('exist');
});
- cy.get('svg');
});
it('should not allow tags in the css', () => {
diff --git a/cypress/integration/rendering/c4.spec.js b/cypress/integration/rendering/c4.spec.js
index 59af6504b..f699bd429 100644
--- a/cypress/integration/rendering/c4.spec.js
+++ b/cypress/integration/rendering/c4.spec.js
@@ -30,7 +30,6 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a simple C4Container diagram', () => {
imgSnapshotTest(
@@ -50,7 +49,6 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a simple C4Component diagram', () => {
imgSnapshotTest(
@@ -69,7 +67,6 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a simple C4Dynamic diagram', () => {
imgSnapshotTest(
@@ -93,7 +90,6 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a simple C4Deployment diagram', () => {
imgSnapshotTest(
@@ -117,6 +113,5 @@ describe('C4 diagram', () => {
`,
{}
);
- cy.get('svg');
});
});
diff --git a/cypress/integration/rendering/classDiagram.spec.js b/cypress/integration/rendering/classDiagram.spec.js
index cab3649df..a98a359ed 100644
--- a/cypress/integration/rendering/classDiagram.spec.js
+++ b/cypress/integration/rendering/classDiagram.spec.js
@@ -32,7 +32,6 @@ describe('Class diagram', () => {
`,
{ logLevel: 1 }
);
- cy.get('svg');
});
it('2: should render a simple class diagrams with cardinality', () => {
@@ -61,7 +60,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('3: should render a simple class diagram with different visibilities', () => {
@@ -79,7 +77,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('4: should render a simple class diagram with comments', () => {
@@ -109,7 +106,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('5: should render a simple class diagram with abstract method', () => {
@@ -121,7 +117,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('6: should render a simple class diagram with static method', () => {
@@ -133,7 +128,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('7: should render a simple class diagram with Generic class', () => {
@@ -153,7 +147,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('8: should render a simple class diagram with Generic class and relations', () => {
@@ -174,7 +167,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('9: should render a simple class diagram with clickable link', () => {
@@ -196,7 +188,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('10: should render a simple class diagram with clickable callback', () => {
@@ -218,7 +209,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('11: should render a simple class diagram with return type on method', () => {
@@ -233,7 +223,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('12: should render a simple class diagram with generic types', () => {
@@ -249,7 +238,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('13: should render a simple class diagram with css classes applied', () => {
@@ -267,7 +255,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('14: should render a simple class diagram with css classes applied directly', () => {
@@ -283,7 +270,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('15: should render a simple class diagram with css classes applied to multiple classes', () => {
@@ -298,7 +284,6 @@ describe('Class diagram', () => {
`,
{}
);
- cy.get('svg');
});
it('16: should render multiple class diagrams', () => {
@@ -351,7 +336,6 @@ describe('Class diagram', () => {
],
{}
);
- cy.get('svg');
});
// it('17: should render a class diagram when useMaxWidth is true (default)', () => {
@@ -421,7 +405,6 @@ describe('Class diagram', () => {
`,
{ logLevel: 1 }
);
- cy.get('svg');
});
it('should render class diagram with newlines in title', () => {
@@ -439,7 +422,6 @@ describe('Class diagram', () => {
+quack()
}
`);
- cy.get('svg');
});
it('should render class diagram with many newlines in title', () => {
diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js
index 578f5a398..1a2340906 100644
--- a/cypress/integration/rendering/erDiagram.spec.js
+++ b/cypress/integration/rendering/erDiagram.spec.js
@@ -218,7 +218,6 @@ describe('Entity Relationship Diagram', () => {
`,
{ loglevel: 1 }
);
- cy.get('svg');
});
it('should render entities with keys', () => {
diff --git a/cypress/integration/rendering/quadrantChart.spec.js b/cypress/integration/rendering/quadrantChart.spec.js
index 83a1455c6..4830db656 100644
--- a/cypress/integration/rendering/quadrantChart.spec.js
+++ b/cypress/integration/rendering/quadrantChart.spec.js
@@ -8,7 +8,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render a complete quadrant chart', () => {
imgSnapshotTest(
@@ -30,7 +29,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render without points', () => {
imgSnapshotTest(
@@ -46,7 +44,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should able to render y-axix on right side', () => {
imgSnapshotTest(
@@ -63,7 +60,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should able to render x-axix on bottom', () => {
imgSnapshotTest(
@@ -80,7 +76,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should able to render x-axix on bottom and y-axis on right', () => {
imgSnapshotTest(
@@ -97,7 +92,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render without title', () => {
imgSnapshotTest(
@@ -112,7 +106,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should use all the config', () => {
imgSnapshotTest(
@@ -135,7 +128,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should use all the theme variable', () => {
imgSnapshotTest(
@@ -158,7 +150,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render x-axis labels in the center, if x-axis has two labels', () => {
imgSnapshotTest(
@@ -180,7 +171,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render y-axis labels in the center, if y-axis has two labels', () => {
imgSnapshotTest(
@@ -202,7 +192,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('should render both axes labels on the left and bottom, if both axes have only one label', () => {
imgSnapshotTest(
@@ -224,7 +213,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('it should render data points with styles', () => {
@@ -249,7 +237,6 @@ describe('Quadrant Chart', () => {
`,
{}
);
- cy.get('svg');
});
it('it should render data points with styles + classes', () => {
diff --git a/cypress/integration/rendering/requirement.spec.js b/cypress/integration/rendering/requirement.spec.js
index f33ae7a0c..343441848 100644
--- a/cypress/integration/rendering/requirement.spec.js
+++ b/cypress/integration/rendering/requirement.spec.js
@@ -44,6 +44,5 @@ describe('Requirement diagram', () => {
`,
{}
);
- cy.get('svg');
});
});
diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js
index 1285a0832..f18e99abf 100644
--- a/cypress/integration/rendering/sequencediagram.spec.js
+++ b/cypress/integration/rendering/sequencediagram.spec.js
@@ -1,8 +1,6 @@
-/// )
B(Bold text!)`;
-if (location.href.match('test-html-escaping')) {
+if (/test-html-escaping/.exec(location.href)) {
code = code3;
}
diff --git a/cypress/platform/viewer.js b/cypress/platform/viewer.js
index 482a90646..c397f0e16 100644
--- a/cypress/platform/viewer.js
+++ b/cypress/platform/viewer.js
@@ -132,7 +132,7 @@ if (typeof document !== 'undefined') {
window.addEventListener(
'load',
function () {
- if (this.location.href.match('xss.html')) {
+ if (/xss.html/.exec(this.location.href)) {
this.console.log('Using api');
void contentLoadedApi().finally(markRendered);
} else {
diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js
index ef985866e..aa888465b 100644
--- a/cypress/support/e2e.js
+++ b/cypress/support/e2e.js
@@ -15,6 +15,7 @@
import '@cypress/code-coverage/support';
import '@applitools/eyes-cypress/commands';
+import '@argos-ci/cypress/support';
// Import commands.js using ES2015 syntax:
import './commands';
diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json
index baa9a7217..b01ce9bac 100644
--- a/cypress/tsconfig.json
+++ b/cypress/tsconfig.json
@@ -2,7 +2,7 @@
"compilerOptions": {
"target": "es2020",
"lib": ["es2020", "dom"],
- "types": ["cypress", "node"],
+ "types": ["cypress", "node", "@argos-ci/cypress/dist/support.d.ts"],
"allowImportingTsExtensions": true,
"noEmit": true
},
diff --git a/demos/sequence.html b/demos/sequence.html
index abdc84f04..eca935ff5 100644
--- a/demos/sequence.html
+++ b/demos/sequence.html
@@ -238,6 +238,17 @@
Alice-xJohn: Hello John, how are you?
John--xAlice: Great!
+
+
+ sequenceDiagram + participant Alice + participant Bob + Alice<<->>Bob: Hello! + Alice<<->>Bob: Wow, we said that at the same time! + Bob<<-->>Alice: Bidirectional Arrows are so cool +