diff --git a/.esbuild/esbuild.cjs b/.esbuild/esbuild.cjs
deleted file mode 100644
index 393dadcf31..0000000000
--- a/.esbuild/esbuild.cjs
+++ /dev/null
@@ -1,20 +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');
-
-// mermaid.js
-build(iifeBuild({ minify: false, watch })).catch(handler);
-// mermaid.esm.mjs
-build(esmBuild({ minify: false, watch })).catch(handler);
-
-// mermaid.min.js
-build(iifeBuild()).catch(handler);
-// mermaid.esm.min.mjs
-build(esmBuild()).catch(handler);
-// mermaid.core.mjs (node_modules unbundled)
-build(esmCoreBuild()).catch(handler);
diff --git a/.esbuild/serve.cjs b/.esbuild/serve.cjs
deleted file mode 100644
index 4c06572f3e..0000000000
--- 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 4dbcd01c37..0000000000
--- a/.esbuild/util.cjs
+++ /dev/null
@@ -1,94 +0,0 @@
-const { transformJison } = require('./jisonTransformer.cjs');
-const fs = require('fs');
-const { dependencies } = require('../package.json');
-
-/** @typedef {import('esbuild').BuildOptions} Options */
-
-/**
- * @param {Options} override
- * @returns {Options}
- */
-const buildOptions = (override = {}) => {
- return {
- bundle: true,
- minify: true,
- keepNames: true,
- banner: { js: '"use strict";' },
- globalName: 'mermaid',
- platform: 'browser',
- tsconfig: 'tsconfig.json',
- resolveExtensions: ['.ts', '.js', '.mjs', '.json', '.jison'],
- external: ['require', 'fs', 'path'],
- entryPoints: ['src/mermaid.ts'],
- outfile: 'dist/mermaid.min.js',
- plugins: [jisonPlugin],
- sourcemap: 'external',
- ...override,
- };
-};
-
-/**
- * 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`,
- ...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 {Options} override - Override options.
- * @returns {Options} ESBuild build options.
- */
-exports.esmCoreBuild = (override) => {
- return buildOptions({
- format: 'esm',
- outfile: `dist/mermaid.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 {Options} override - Override options.
- * @returns {Options} ESBuild build options.
- */
-exports.iifeBuild = (override = { minify: true }) => {
- return buildOptions({
- outfile: `dist/mermaid${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/.vite/build.ts b/.vite/build.ts
new file mode 100644
index 0000000000..c4df49ea24
--- /dev/null
+++ b/.vite/build.ts
@@ -0,0 +1,89 @@
+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' };
+type OutputOptions = Exclude<
+ Exclude['rollupOptions'],
+ undefined
+>['output'];
+
+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 = [
+ {
+ 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) {
+ // Core build is used to generate file without bundled dependencies.
+ // This is used by downstream projects to bundle dependencies themselves.
+ external.push(...Object.keys(dependencies));
+ // This needs to be an array. Otherwise vite will build esm & umd with same name and overwrite esm with umd.
+ output = [
+ {
+ 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: false, core: true }));
+}
diff --git a/.vite/jisonPlugin.ts b/.vite/jisonPlugin.ts
new file mode 100644
index 0000000000..c211907841
--- /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 5f89c6647b..a5734e1259 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 0000000000..4cadc07cb7
--- /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/.vite/tsconfig.json b/.vite/tsconfig.json
new file mode 100644
index 0000000000..915436da1e
--- /dev/null
+++ b/.vite/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "module": "ES2022"
+ }
+}
diff --git a/cypress/helpers/util.js b/cypress/helpers/util.js
index d138dfcfef..bee9a59f0b 100644
--- a/cypress/helpers/util.js
+++ b/cypress/helpers/util.js
@@ -1,4 +1,6 @@
-import { Base64 } from 'js-base64';
+const utf8ToB64 = (str) => {
+ return window.btoa(unescape(encodeURIComponent(str)));
+};
export const mermaidUrl = (graphStr, options, api) => {
const obj = {
@@ -6,7 +8,7 @@ export const mermaidUrl = (graphStr, options, api) => {
mermaid: options,
};
const objStr = JSON.stringify(obj);
- let url = 'http://localhost:9000/e2e.html?graph=' + Base64.encodeURI(objStr);
+ let url = 'http://localhost:9000/e2e.html?graph=' + utf8ToB64(objStr);
if (api) {
url = 'http://localhost:9000/xss.html?graph=' + graphStr;
}
diff --git a/cypress/integration/other/xss.spec.js b/cypress/integration/other/xss.spec.js
index 4911dd8df5..76b2c47f2c 100644
--- a/cypress/integration/other/xss.spec.js
+++ b/cypress/integration/other/xss.spec.js
@@ -15,7 +15,7 @@ describe('XSS', () => {
it('should not allow tags in the css', () => {
const str =
- 'eyJjb2RlIjoiJSV7aW5pdDogeyAnZm9udEZhbWlseSc6ICdcXFwiPjwvc3R5bGU-PGltZyBzcmM9eCBvbmVycm9yPXhzc0F0dGFjaygpPid9IH0lJVxuZ3JhcGggTFJcbiAgICAgQSAtLT4gQiIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0IiwiZmxvd2NoYXJ0Ijp7Imh0bWxMYWJlbHMiOmZhbHNlfX0sInVwZGF0ZUVkaXRvciI6ZmFsc2V9';
+ 'eyJjb2RlIjoiJSV7aW5pdDogeyAnZm9udEZhbWlseSc6ICdcXFwiPjwvc3R5bGU+PGltZyBzcmM9eCBvbmVycm9yPXhzc0F0dGFjaygpPid9IH0lJVxuZ3JhcGggTFJcbiAgICAgQSAtLT4gQiIsIm1lcm1haWQiOnsidGhlbWUiOiJkZWZhdWx0IiwiZmxvd2NoYXJ0Ijp7Imh0bWxMYWJlbHMiOmZhbHNlfX0sInVwZGF0ZUVkaXRvciI6ZmFsc2V9';
const url = mermaidUrl(
str,
diff --git a/cypress/platform/bundle-test.js b/cypress/platform/bundle-test.js
index 373f8741a0..074dd8c635 100644
--- a/cypress/platform/bundle-test.js
+++ b/cypress/platform/bundle-test.js
@@ -1,4 +1,4 @@
-import mermaid from '../../dist/mermaid.core';
+import mermaid from '../../src/mermaid';
let code = `flowchart LR
Power_Supply --> Transmitter_A
diff --git a/cypress/platform/e2e.html b/cypress/platform/e2e.html
index a239113345..c686afc886 100644
--- a/cypress/platform/e2e.html
+++ b/cypress/platform/e2e.html
@@ -2,7 +2,7 @@
-
+
0) {
pos = pos + 7;
const graphBase64 = document.location.href.substr(pos);
- const graphObj = JSON.parse(Base64.decode(graphBase64));
+ const graphObj = JSON.parse(b64ToUtf8(graphBase64));
if (graphObj.mermaid && graphObj.mermaid.theme === 'dark') {
document.body.style.background = '#3f3f3f';
}
@@ -67,7 +70,7 @@ const contentLoadedApi = function () {
if (pos > 0) {
pos = pos + 7;
const graphBase64 = document.location.href.substr(pos);
- const graphObj = JSON.parse(Base64.decode(graphBase64));
+ const graphObj = JSON.parse(b64ToUtf8(graphBase64));
// const graph = 'hello'
if (Array.isArray(graphObj.code)) {
const numCodes = graphObj.code.length;
diff --git a/cypress/platform/webpackUsage.html b/cypress/platform/webpackUsage.html
index ec6edb4ea5..23df19f49e 100644
--- a/cypress/platform/webpackUsage.html
+++ b/cypress/platform/webpackUsage.html
@@ -12,6 +12,8 @@
-
+