diff --git a/.esbuild/esbuild.cjs b/.esbuild/esbuild.cjs
deleted file mode 100644
index ed2069b02..000000000
--- a/.esbuild/esbuild.cjs
+++ /dev/null
@@ -1,21 +0,0 @@
-const { esmBuild, esmCoreBuild, iifeBuild } = require('./util.cjs');
-const { build } = require('esbuild');
-
-const handler = (e) => {
- console.error(e);
- process.exit(1);
-};
-const watch = process.argv.includes('--watch');
-const pkg = 'mermaid';
-
-// mermaid.js
-build(iifeBuild(pkg, { minify: false, watch })).catch(handler);
-// mermaid.esm.mjs
-build(esmBuild(pkg, { minify: false, watch })).catch(handler);
-
-// mermaid.min.js
-build(iifeBuild(pkg)).catch(handler);
-// mermaid.esm.min.mjs
-build(esmBuild(pkg)).catch(handler);
-// mermaid.core.mjs (node_modules unbundled)
-build(esmCoreBuild(pkg)).catch(handler);
diff --git a/.esbuild/serve.cjs b/.esbuild/serve.cjs
deleted file mode 100644
index 374ddd8ce..000000000
--- a/.esbuild/serve.cjs
+++ /dev/null
@@ -1,79 +0,0 @@
-const esbuild = require('esbuild');
-const http = require('http');
-const { iifeBuild, esmBuild } = require('./util.cjs');
-const express = require('express');
-
-// Start 2 esbuild servers. One for IIFE and one for ESM
-// Serve 2 static directories: demo & cypress/platform
-// Have 3 entry points:
-// mermaid: './src/mermaid',
-// e2e: './cypress/platform/viewer.js',
-// 'bundle-test': './cypress/platform/bundle-test.js',
-
-const getEntryPointsAndExtensions = (format) => {
- return {
- entryPoints: {
- mermaid: './src/mermaid',
- e2e: '../../cypress/platform/viewer.js',
- 'bundle-test': '../../cypress/platform/bundle-test.js',
- },
- outExtension: { '.js': format === 'iife' ? '.js' : '.esm.mjs' },
- };
-};
-
-const generateHandler = (server) => {
- return (req, res) => {
- const options = {
- hostname: server.host,
- port: server.port,
- path: req.url,
- method: req.method,
- headers: req.headers,
- };
- // Forward each incoming request to esbuild
- const proxyReq = http.request(options, (proxyRes) => {
- // If esbuild returns "not found", send a custom 404 page
- if (proxyRes.statusCode === 404) {
- if (!req.url.endsWith('.html')) {
- res.writeHead(404, { 'Content-Type': 'text/html' });
- res.end('
A custom 404 page
');
- return;
- }
- }
- // Otherwise, forward the response from esbuild to the client
- res.writeHead(proxyRes.statusCode, proxyRes.headers);
- proxyRes.pipe(res, { end: true });
- });
- // Forward the body of the request to esbuild
- req.pipe(proxyReq, { end: true });
- };
-};
-
-(async () => {
- const iifeServer = await esbuild.serve(
- {},
- {
- ...iifeBuild({ minify: false, outfile: undefined, outdir: 'dist' }),
- ...getEntryPointsAndExtensions('iife'),
- }
- );
- const esmServer = await esbuild.serve(
- {},
- {
- ...esmBuild({ minify: false, outfile: undefined, outdir: 'dist' }),
- ...getEntryPointsAndExtensions('esm'),
- }
- );
- const app = express();
-
- app.use(express.static('demos'));
- app.use(express.static('cypress/platform'));
- app.all('/mermaid.js', generateHandler(iifeServer));
- app.all('/mermaid.esm.mjs', generateHandler(esmServer));
-
- app.all('/e2e.esm.mjs', generateHandler(esmServer));
- app.all('/bundle-test.esm.mjs', generateHandler(esmServer));
- app.listen(9000, () => {
- console.log(`Listening on http://localhost:9000`);
- });
-})();
diff --git a/.esbuild/util.cjs b/.esbuild/util.cjs
deleted file mode 100644
index cae261639..000000000
--- a/.esbuild/util.cjs
+++ /dev/null
@@ -1,108 +0,0 @@
-const { transformJison } = require('./jisonTransformer.cjs');
-const fs = require('fs');
-const { dependencies } = require('../package.json');
-
-/** @typedef {import('esbuild').BuildOptions} Options */
-
-const packageOptionsMap = {
- mermaid: {
- globalName: 'mermaid',
- entryPoints: ['packages/mermaid/src/mermaid.ts'],
- },
-};
-
-/**
- * @param {string} pkg - Package name.
- * @param {Options} override
- * @returns {Options}
- */
-const buildOptions = (pkg, override = {}) => {
- const packageOptions = packageOptionsMap[pkg];
- if (!packageOptions) {
- throw new Error(`Unknown package: ${pkg}`);
- }
- return {
- bundle: true,
- minify: true,
- keepNames: true,
- banner: { js: '"use strict";' },
- platform: 'browser',
- tsconfig: 'tsconfig.json',
- resolveExtensions: ['.ts', '.js', '.mjs', '.json', '.jison'],
- external: ['require', 'fs', 'path'],
- plugins: [jisonPlugin],
- sourcemap: 'external',
- outdir: 'dist',
- ...packageOptions,
- ...override,
- };
-};
-
-/**
- * Build options for mermaid.esm.* build.
- *
- * For ESM browser use.
- *
- * @param {string} pkg - Package name.
- * @param {Options} override - Override options.
- * @returns {Options} ESBuild build options.
- */
-exports.esmBuild = (pkg, override = { minify: true }) => {
- return buildOptions(pkg, {
- format: 'esm',
- outExtension: { '.js': `.esm${override.minify ? '.min' : ''}.mjs` },
- ...override,
- });
-};
-
-/**
- * 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 {string} pkg - Package name.
- * @param {Options} override - Override options.
- * @returns {Options} ESBuild build options.
- */
-exports.esmCoreBuild = (pkg, override) => {
- return buildOptions(pkg, {
- format: 'esm',
- outExtension: { '.js': '.core.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 {string} pkg - Package name.
- * @param {Options} override - Override options.
- * @returns {Options} ESBuild build options.
- */
-exports.iifeBuild = (pkg, override = { minify: true }) => {
- return buildOptions(pkg, {
- outExtension: { '.js': `${override.minify ? '.min' : ''}.js` },
- format: 'iife',
- footer: {
- js: 'mermaid = mermaid.default;',
- },
- ...override,
- });
-};
-
-const jisonPlugin = {
- name: 'jison',
- setup(build) {
- build.onLoad({ filter: /\.jison$/ }, async (args) => {
- // Load the file from the file system
- const source = await fs.promises.readFile(args.path, 'utf8');
- const contents = transformJison(source);
- return { contents, warnings: [] };
- });
- },
-};
diff --git a/.gitignore b/.gitignore
index 9641d2574..6e4fe723a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,11 @@ token
package-lock.json
-.vscode/
+# ignore files in /.vscode/ except for launch.json and extensions.json
+/.vscode/**
+!/.vscode/launch.json
+!/.vscode/extensions.json
+
cypress/platform/current.html
cypress/platform/experimental.html
local/
diff --git a/.vite/build.ts b/.vite/build.ts
new file mode 100644
index 000000000..327be2dcb
--- /dev/null
+++ b/.vite/build.ts
@@ -0,0 +1,81 @@
+import { build, InlineConfig } from 'vite';
+import { resolve } from 'path';
+import { fileURLToPath } from 'url';
+import jisonPlugin from './jisonPlugin.js';
+import pkg from '../package.json' assert { type: 'json' };
+import { OutputOptions } from 'vite/node_modules/rollup';
+const { dependencies } = pkg;
+const watch = process.argv.includes('--watch');
+const __dirname = fileURLToPath(new URL('.', import.meta.url));
+
+interface BuildOptions {
+ minify: boolean | 'esbuild';
+ core?: boolean;
+ watch?: boolean;
+}
+
+export const getBuildConfig = ({ minify, core, watch }: BuildOptions): InlineConfig => {
+ const external = ['require', 'fs', 'path'];
+ let output: OutputOptions | OutputOptions[] = [
+ {
+ name: 'mermaid',
+ format: 'esm',
+ sourcemap: true,
+ entryFileNames: `[name].esm${minify ? '.min' : ''}.mjs`,
+ },
+ {
+ name: 'mermaid',
+ format: 'umd',
+ sourcemap: true,
+ entryFileNames: `[name]${minify ? '.min' : ''}.js`,
+ },
+ ];
+
+ if (core) {
+ external.push(...Object.keys(dependencies));
+ output = {
+ name: 'mermaid',
+ format: 'esm',
+ sourcemap: true,
+ entryFileNames: `[name].core.mjs`,
+ };
+ }
+
+ const config: InlineConfig = {
+ configFile: false,
+ build: {
+ emptyOutDir: false,
+ lib: {
+ entry: resolve(__dirname, '../src/mermaid.ts'),
+ name: 'mermaid',
+ // the proper extensions will be added
+ fileName: 'mermaid',
+ },
+ minify,
+ rollupOptions: {
+ external,
+ output,
+ },
+ },
+ resolve: {
+ extensions: ['.jison', '.js', '.ts', '.json'],
+ },
+ plugins: [jisonPlugin()],
+ };
+
+ if (watch && config.build) {
+ config.build.watch = {
+ include: 'src/**',
+ };
+ }
+
+ return config;
+};
+
+if (watch) {
+ build(getBuildConfig({ minify: false, watch }));
+} else {
+ build(getBuildConfig({ minify: false }));
+ build(getBuildConfig({ minify: 'esbuild' }));
+ build(getBuildConfig({ minify: true, core: true }));
+}
diff --git a/.vite/jisonPlugin.ts b/.vite/jisonPlugin.ts
new file mode 100644
index 000000000..c21190784
--- /dev/null
+++ b/.vite/jisonPlugin.ts
@@ -0,0 +1,17 @@
+import { transformJison } from './jisonTransformer.js';
+const fileRegex = /\.(jison)$/;
+
+export default function jison() {
+ return {
+ name: 'jison',
+
+ transform(src: string, id: string) {
+ if (fileRegex.test(id)) {
+ return {
+ code: transformJison(src),
+ map: null, // provide source map if available
+ };
+ }
+ },
+ };
+}
diff --git a/.esbuild/jisonTransformer.cjs b/.vite/jisonTransformer.ts
similarity index 55%
rename from .esbuild/jisonTransformer.cjs
rename to .vite/jisonTransformer.ts
index 5f89c6647..a5734e125 100644
--- a/.esbuild/jisonTransformer.cjs
+++ b/.vite/jisonTransformer.ts
@@ -1,6 +1,9 @@
-const { Generator } = require('jison');
-exports.transformJison = (src) => {
- const parser = new Generator(src, {
+// @ts-ignore No typings for jison
+import jison from 'jison';
+
+export const transformJison = (src: string): string => {
+ // @ts-ignore No typings for jison
+ const parser = new jison.Generator(src, {
moduleType: 'js',
'token-stack': true,
});
diff --git a/.vite/server.ts b/.vite/server.ts
new file mode 100644
index 000000000..4cadc07cb
--- /dev/null
+++ b/.vite/server.ts
@@ -0,0 +1,26 @@
+import express from 'express';
+import { createServer as createViteServer } from 'vite';
+// import { getBuildConfig } from './build';
+
+async function createServer() {
+ const app = express();
+
+ // Create Vite server in middleware mode
+ const vite = await createViteServer({
+ configFile: './vite.config.ts',
+ server: { middlewareMode: true },
+ appType: 'custom', // don't include Vite's default HTML handling middlewares
+ });
+
+ app.use(vite.middlewares);
+ app.use(express.static('dist'));
+ app.use(express.static('demos'));
+ app.use(express.static('cypress/platform'));
+
+ app.listen(9000, () => {
+ console.log(`Listening on http://localhost:9000`);
+ });
+}
+
+// build(getBuildConfig({ minify: false, watch: true }));
+createServer();
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 000000000..9633bed66
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,8 @@
+{
+ "recommendations": [
+ "dbaeumer.vscode-eslint",
+ "esbenp.prettier-vscode",
+ "zixuanchen.vitest-explorer",
+ "luniclynx.bison"
+ ]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 000000000..92df7056e
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,17 @@
+{
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "node",
+ "request": "launch",
+ "name": "Debug Current Test File",
+ "autoAttachChildProcesses": true,
+ "skipFiles": ["/**", "**/node_modules/**"],
+ "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
+ "args": ["run", "${relativeFile}"],
+ "smartStep": true,
+ "console": "integratedTerminal"
+ }
+ ]
+}
diff --git a/cypress/platform/bundle-test.js b/cypress/platform/bundle-test.js
index 4b1f446a1..a991918c4 100644
--- a/cypress/platform/bundle-test.js
+++ b/cypress/platform/bundle-test.js
@@ -1,4 +1,4 @@
-import mermaid from '../../packages/mermaid/dist/mermaid.core';
+import mermaid from '../../packages/mermaid/src/mermaid';
let code = `flowchart LR
Power_Supply --> Transmitter_A
diff --git a/cypress/platform/e2e.html b/cypress/platform/e2e.html
index a23911334..c686afc88 100644
--- a/cypress/platform/e2e.html
+++ b/cypress/platform/e2e.html
@@ -2,7 +2,7 @@
-
+
-
+
diff --git a/cypress/platform/xss.html b/cypress/platform/xss.html
index 2d3661a4e..39b985ccc 100644
--- a/cypress/platform/xss.html
+++ b/cypress/platform/xss.html
@@ -1,6 +1,6 @@
-
+