diff --git a/.esbuild/serve.cjs b/.esbuild/serve.cjs index c54ff1e9f..30fff7ec0 100644 --- a/.esbuild/serve.cjs +++ b/.esbuild/serve.cjs @@ -1,53 +1,71 @@ const esbuild = require('esbuild'); const http = require('http'); -const path = require('path'); -const { iifeBuild } = require('./util.cjs'); +const { iifeBuild, esmBuild, getEntryPoints } = require('./util.cjs'); +const express = require('express'); -// Start esbuild's server on a random local port -esbuild - .serve( - { - servedir: path.join(__dirname, '..'), +// 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: { + ...getEntryPoints(format === 'iife' ? '' : '.esm'), + e2e: 'cypress/platform/viewer.js', + 'bundle-test': 'cypress/platform/bundle-test.js', }, - iifeBuild({ minify: false }) - ) - .then((result) => { - // The result tells us where esbuild's local server is - const { host, port } = result; + outExtension: { '.js': format === 'iife' ? '.js' : '.mjs' }, + }; +}; - // Then start a proxy server on port 3000 - http - .createServer((req, res) => { - if (req.url.includes('mermaid.js')) { - req.url = '/dist/mermaid.js'; +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; } - const options = { - hostname: host, - port: port, - path: req.url, - method: req.method, - headers: req.headers, - }; + } + // 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 }); + }; +}; - // Forward each incoming request to esbuild - const proxyReq = http.request(options, (proxyRes) => { - // If esbuild returns "not found", send a custom 404 page - console.error('pp', req.url); - if (proxyRes.statusCode === 404) { - if (!req.url.endsWith('.html')) { - res.writeHead(404, { 'Content-Type': 'text/html' }); - res.end('

A custom 404 page

'); - return; - } - } +(async () => { + const iifeServer = await esbuild.serve( + {}, + { + ...iifeBuild({ minify: false }), + ...getEntryPointsAndExtensions('iife'), + } + ); + const esmServer = await esbuild.serve( + {}, + { ...esmBuild({ minify: false }), ...getEntryPointsAndExtensions('esm') } + ); + const app = express(); - // 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 }); - }) - .listen(3000); - }); + 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.listen(3000); +})(); diff --git a/.esbuild/util.cjs b/.esbuild/util.cjs index 0cddb7e45..d9b783747 100644 --- a/.esbuild/util.cjs +++ b/.esbuild/util.cjs @@ -26,12 +26,14 @@ const buildOptions = (override = {}) => { }; }; -const getOutFiles = (extension) => { +const getEntryPoints = (extension) => { return { [`mermaid${extension}`]: 'src/mermaid.ts', [`diagramAPI${extension}`]: 'src/diagram-api/diagramAPI.ts', }; }; +exports.getEntryPoints = getEntryPoints; + /** * Build options for mermaid.esm.* build. * @@ -43,7 +45,7 @@ const getOutFiles = (extension) => { exports.esmBuild = (override = { minify: true }) => { return buildOptions({ format: 'esm', - entryPoints: getOutFiles(`.esm${override.minify ? '.min' : ''}`), + entryPoints: getEntryPoints(`.esm${override.minify ? '.min' : ''}`), outExtension: { '.js': '.mjs' }, ...override, }); @@ -61,7 +63,7 @@ exports.esmBuild = (override = { minify: true }) => { exports.esmCoreBuild = (override) => { return buildOptions({ format: 'esm', - entryPoints: getOutFiles(`.core`), + entryPoints: getEntryPoints(`.core`), outExtension: { '.js': '.mjs' }, external: ['require', 'fs', 'path', ...Object.keys(dependencies)], platform: 'neutral', @@ -79,7 +81,7 @@ exports.esmCoreBuild = (override) => { */ exports.iifeBuild = (override = { minify: true }) => { return buildOptions({ - entryPoints: getOutFiles(override.minify ? '.min' : ''), + entryPoints: getEntryPoints(override.minify ? '.min' : ''), format: 'iife', ...override, }); diff --git a/.webpack/loaders/jison.js b/.webpack/loaders/jison.js deleted file mode 100644 index 0d5ebc7e5..000000000 --- a/.webpack/loaders/jison.js +++ /dev/null @@ -1,25 +0,0 @@ -const { Generator } = require('jison'); -const validate = require('schema-utils'); - -const schema = { - title: 'Jison Parser options', - type: 'object', - properties: { - 'token-stack': { - type: 'boolean', - }, - debug: { - type: 'boolean', - }, - }, - additionalProperties: false, -}; - -module.exports = function jisonLoader(source) { - const options = this.getOptions(); - (validate.validate || validate)(schema, options, { - name: 'Jison Loader', - baseDataPath: 'options', - }); - return new Generator(source, options).generate(); -}; diff --git a/.webpack/webpack.config.babel.js b/.webpack/webpack.config.babel.js deleted file mode 100644 index 15760b19b..000000000 --- a/.webpack/webpack.config.babel.js +++ /dev/null @@ -1,46 +0,0 @@ -import { merge, mergeWithCustomize, customizeObject } from 'webpack-merge'; -import nodeExternals from 'webpack-node-externals'; -import baseConfig from './webpack.config.base'; - -export default (_env, args) => { - 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/.webpack/webpack.config.base.js b/.webpack/webpack.config.base.js deleted file mode 100644 index 3ebd1863d..000000000 --- a/.webpack/webpack.config.base.js +++ /dev/null @@ -1,71 +0,0 @@ -import path from 'path'; -const esbuild = require('esbuild'); -const { ESBuildMinifyPlugin } = require('esbuild-loader'); -export const resolveRoot = (...relativePath) => path.resolve(__dirname, '..', ...relativePath); - -export default { - amd: false, // https://github.com/lodash/lodash/issues/3052 - target: 'web', - entry: { - mermaid: './src/mermaid', - }, - resolve: { - extensions: ['.wasm', '.mjs', '.js', '.ts', '.json', '.jison'], - fallback: { - fs: false, // jison generated code requires 'fs' - path: require.resolve('path-browserify'), - }, - }, - output: { - path: resolveRoot('./dist'), - filename: '[name].js', - library: { - name: 'mermaid', - type: 'umd', - export: 'default', - }, - globalObject: 'typeof self !== "undefined" ? self : this', - }, - module: { - rules: [ - { - test: /\.ts$/, - use: 'ts-loader', - exclude: /node_modules/, - }, - { - test: /\.js$/, - include: [resolveRoot('./src'), resolveRoot('./node_modules/dagre-d3-renderer/lib')], - use: { - loader: 'esbuild-loader', - options: { - implementation: esbuild, - target: 'es2015', - }, - }, - }, - { - // load scss to string - test: /\.scss$/, - use: ['css-to-string-loader', 'css-loader', 'sass-loader'], - }, - { - test: /\.jison$/, - use: { - loader: path.resolve(__dirname, './loaders/jison.js'), - options: { - 'token-stack': true, - }, - }, - }, - ], - }, - devtool: 'source-map', - optimization: { - minimizer: [ - new ESBuildMinifyPlugin({ - target: 'es2015', - }), - ], - }, -}; diff --git a/.webpack/webpack.config.e2e.babel.js b/.webpack/webpack.config.e2e.babel.js deleted file mode 100644 index 7f26b8aed..000000000 --- a/.webpack/webpack.config.e2e.babel.js +++ /dev/null @@ -1,26 +0,0 @@ -import baseConfig, { resolveRoot } from './webpack.config.base'; -import { merge } from 'webpack-merge'; - -export default merge(baseConfig, { - mode: 'development', - entry: { - mermaid: './src/mermaid', - e2e: './cypress/platform/viewer.js', - 'bundle-test': './cypress/platform/bundle-test.js', - }, - output: { - globalObject: 'window', - }, - devServer: { - compress: true, - port: 9000, - static: [ - { directory: resolveRoot('cypress', 'platform') }, - { directory: resolveRoot('dist') }, - { directory: resolveRoot('demos') }, - ], - }, - externals: { - mermaid: 'mermaid', - }, -}); diff --git a/package.json b/package.json index 94c94ab1a..cf8eab56b 100644 --- a/package.json +++ b/package.json @@ -26,22 +26,20 @@ "clean": "rimraf dist", "build:code": "node .esbuild/esbuild.cjs", "build:types": "tsc -p ./tsconfig.json --emitDeclarationOnly", - "build:webpack": "webpack --mode production --progress --color", "build:watch": "yarn build:code --watch", - "build:new": "concurrently \"yarn build:code\" \"yarn build:types\"", - "build": "yarn clean; yarn build:webpack", + "build:esbuild": "concurrently \"yarn build:code\" \"yarn build:types\"", + "build": "yarn clean; yarn build:esbuild", + "dev": "node .esbuild/serve.cjs", "docs:build": "ts-node-esm src/docs.mts", "docs:verify": "ts-node-esm src/docs.mts --verify", "postbuild": "documentation build src/mermaidAPI.ts src/config.ts src/defaultConfig.ts --shallow -f md --markdown-toc false > src/docs/Setup.md && prettier --write src/docs/Setup.md && yarn docs:build", "release": "yarn build", "lint": "eslint --cache --ignore-path .gitignore . && prettier --check .", "lint:fix": "eslint --fix --ignore-path .gitignore . && prettier --write .", - "e2e:depr": "yarn lint && jest e2e --config e2e/jest.config.js", "cypress": "cypress run", "cypress:open": "cypress open", "e2e": "start-server-and-test dev http://localhost:9000/ cypress", "e2e-upd": "yarn lint && jest e2e -u --config e2e/jest.config.js", - "dev": "webpack serve --config ./.webpack/webpack.config.e2e.babel.js", "ci": "jest src/.*", "test": "yarn lint && jest src/.*", "test:watch": "jest --watch src", @@ -89,6 +87,7 @@ "@commitlint/config-conventional": "^17.0.0", "@types/d3": "^7.4.0", "@types/dompurify": "^2.3.4", + "@types/express": "^4.17.13", "@types/jest": "^28.1.7", "@types/lodash": "^4.14.184", "@types/stylis": "^4.0.2", @@ -112,6 +111,7 @@ "eslint-plugin-jsdoc": "^39.3.6", "eslint-plugin-json": "^3.1.0", "eslint-plugin-markdown": "^3.0.0", + "express": "^4.18.1", "globby": "^13.1.2", "husky": "^8.0.0", "identity-obj-proxy": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 1967b28b1..ce417808e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3746,6 +3746,24 @@ body-parser@1.19.2: raw-body "2.4.3" type-is "~1.6.18" +body-parser@1.20.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" + integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.10.3" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + body@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" @@ -4598,6 +4616,11 @@ cookie@0.4.2: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -5241,6 +5264,11 @@ dequal@^2.0.0: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d" integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug== +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" @@ -6269,6 +6297,43 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" +express@^4.18.1: + version "4.18.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" + integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.0" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.10.3" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -6436,6 +6501,19 @@ filter-obj@^1.1.0: resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -9676,6 +9754,13 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -10319,6 +10404,13 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +qs@6.10.3: + version "6.10.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== + dependencies: + side-channel "^1.0.4" + qs@6.9.7: version "6.9.7" resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" @@ -11065,6 +11157,25 @@ send@0.17.2: range-parser "~1.2.1" statuses "~1.5.0" +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + serialize-javascript@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -11095,6 +11206,16 @@ serve-static@1.14.2: parseurl "~1.3.3" send "0.17.2" +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"