diff --git a/.esbuild/build.ts b/.esbuild/build.ts index 05002cb16..6bf5f6017 100644 --- a/.esbuild/build.ts +++ b/.esbuild/build.ts @@ -1,5 +1,5 @@ import { build } from 'esbuild'; -import { mkdir, writeFile } from 'node:fs/promises'; +import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'; import { packageOptions } from '../.build/common.js'; import { generateLangium } from '../.build/generateLangium.js'; import type { MermaidBuildOptions } from './util.js'; @@ -31,7 +31,15 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => { // mermaid.js { ...iifeOptions }, // mermaid.min.js - { ...iifeOptions, minify: true, metafile: shouldVisualize } + { ...iifeOptions, minify: true, metafile: shouldVisualize }, + // mermaid.tiny.min.js + { + ...iifeOptions, + minify: true, + includeLargeFeatures: false, + metafile: shouldVisualize, + sourcemap: false, + } ); } if (entryName === 'mermaid-zenuml') { @@ -70,6 +78,20 @@ const handler = (e) => { process.exit(1); }; +const buildTinyMermaid = async () => { + await mkdir('./packages/tiny/dist', { recursive: true }); + await rename( + './packages/mermaid/dist/mermaid.tiny.min.js', + './packages/tiny/dist/mermaid.tiny.js' + ); + // Copy version from mermaid's package.json to tiny's package.json + const mermaidPkg = JSON.parse(await readFile('./packages/mermaid/package.json', 'utf8')); + const tinyPkg = JSON.parse(await readFile('./packages/tiny/package.json', 'utf8')); + tinyPkg.version = mermaidPkg.version; + + await writeFile('./packages/tiny/package.json', JSON.stringify(tinyPkg, null, 2) + '\n'); +}; + const main = async () => { await generateLangium(); await mkdir('stats', { recursive: true }); @@ -78,6 +100,7 @@ const main = async () => { for (const pkg of packageNames) { await buildPackage(pkg).catch(handler); } + await buildTinyMermaid(); }; void main(); diff --git a/.esbuild/util.ts b/.esbuild/util.ts index dde0352af..3a0ec6b41 100644 --- a/.esbuild/util.ts +++ b/.esbuild/util.ts @@ -14,6 +14,7 @@ export interface MermaidBuildOptions extends BuildOptions { metafile: boolean; format: 'esm' | 'iife'; options: PackageOptions; + includeLargeFeatures: boolean; } export const defaultOptions: Omit = { @@ -21,6 +22,7 @@ export const defaultOptions: Omit metafile: false, core: false, format: 'esm', + includeLargeFeatures: true, } as const; const buildOptions = (override: BuildOptions): BuildOptions => { @@ -39,12 +41,18 @@ const buildOptions = (override: BuildOptions): BuildOptions => { }; }; -const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOptions) => { +const getFileName = ( + fileName: string, + { core, format, minify, includeLargeFeatures }: MermaidBuildOptions +) => { if (core) { fileName += '.core'; } else if (format === 'esm') { fileName += '.esm'; } + if (!includeLargeFeatures) { + fileName += '.tiny'; + } if (minify) { fileName += '.min'; } @@ -54,25 +62,27 @@ const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOpt export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => { const { core, - metafile, format, - minify, options: { name, file, packageName }, globalName = 'mermaid', + includeLargeFeatures, + ...rest } = options; + const external: string[] = ['require', 'fs', 'path']; const outFileName = getFileName(name, options); const output: BuildOptions = buildOptions({ + ...rest, absWorkingDir: resolve(__dirname, `../packages/${packageName}`), entryPoints: { [outFileName]: `src/${file}`, }, - metafile, - minify, globalName, logLevel: 'info', chunkNames: `chunks/${outFileName}/[name]-[hash]`, define: { + // This needs to be stringified for esbuild + includeLargeFeatures: `${includeLargeFeatures}`, 'import.meta.vitest': 'undefined', }, }); diff --git a/.vite/build.ts b/.vite/build.ts index 486d59452..480dd6b30 100644 --- a/.vite/build.ts +++ b/.vite/build.ts @@ -94,6 +94,10 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions) }), ...visualizerOptions(packageName, core), ], + define: { + // Needs to be string + includeLargeFeatures: 'true', + }, }; if (watch && config.build) { diff --git a/docs/config/usage.md b/docs/config/usage.md index 2e207213c..e157d1827 100644 --- a/docs/config/usage.md +++ b/docs/config/usage.md @@ -98,6 +98,12 @@ Mermaid can load multiple diagrams, in the same page. > Try it out, save this code as HTML and load it using any browser. > (Except Internet Explorer, please don't use Internet Explorer.) +## Tiny Mermaid + +We offer a smaller version of Mermaid that's approximately half the size of the full library. This tiny version doesn't support Mindmap Diagrams, Architecture Diagrams, KaTeX rendering, or lazy loading. + +If you need a more lightweight version without these features, you can use [Mermaid Tiny](https://github.com/mermaid-js/mermaid/tree/develop/packages/tiny). + ## Enabling Click Event and Tags in Nodes A `securityLevel` configuration has to first be cleared. `securityLevel` sets the level of trust for the parsed diagrams and limits click functionality. This was introduced in version 8.2 as a security improvement, aimed at preventing malicious use. diff --git a/docs/intro/index.md b/docs/intro/index.md index 06a408268..51dd9f9d4 100644 --- a/docs/intro/index.md +++ b/docs/intro/index.md @@ -354,6 +354,7 @@ To Deploy Mermaid: - [Mermaid Live Editor](https://github.com/mermaid-js/mermaid-live-editor) - [Mermaid CLI](https://github.com/mermaid-js/mermaid-cli) +- [Mermaid Tiny](https://github.com/mermaid-js/mermaid/tree/develop/packages/tiny) - [Mermaid Webpack Demo](https://github.com/mermaidjs/mermaid-webpack-demo) - [Mermaid Parcel Demo](https://github.com/mermaidjs/mermaid-parcel-demo) diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index 8f2b76abb..d18aab957 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -27,6 +27,7 @@ import block from '../diagrams/block/blockDetector.js'; import architecture from '../diagrams/architecture/architectureDetector.js'; import { registerLazyLoadedDiagrams } from './detectType.js'; import { registerDiagram } from './diagramAPI.js'; +import '../type.d.ts'; let hasLoadedDiagrams = false; export const addDiagrams = () => { @@ -69,6 +70,11 @@ export const addDiagrams = () => { return text.toLowerCase().trimStart().startsWith('---'); } ); + + if (includeLargeFeatures) { + registerLazyLoadedDiagrams(flowchartElk, mindmap, architecture); + } + // Ordering of detectors is important. The first one to return true will be used. registerLazyLoadedDiagrams( c4, @@ -81,10 +87,8 @@ export const addDiagrams = () => { pie, requirement, sequence, - flowchartElk, flowchartV2, flowchart, - mindmap, timeline, git, stateV2, @@ -95,7 +99,6 @@ export const addDiagrams = () => { packet, xychart, block, - architecture, radar ); }; diff --git a/packages/mermaid/src/diagrams/common/common.ts b/packages/mermaid/src/diagrams/common/common.ts index fd76d0a45..14c88a0fc 100644 --- a/packages/mermaid/src/diagrams/common/common.ts +++ b/packages/mermaid/src/diagrams/common/common.ts @@ -341,29 +341,36 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise< return text.replace(katexRegex, 'MathML is unsupported in this environment.'); } - const { default: katex } = await import('katex'); - const outputMode = - config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML) - ? 'htmlAndMathml' - : 'mathml'; - return text - .split(lineBreakRegex) - .map((line) => - hasKatex(line) - ? `
${line}
` - : `
${line}
` - ) - .join('') - .replace(katexRegex, (_, c) => - katex - .renderToString(c, { - throwOnError: true, - displayMode: true, - output: outputMode, - }) - .replace(/\n/g, ' ') - .replace(//g, '') - ); + if (includeLargeFeatures) { + const { default: katex } = await import('katex'); + const outputMode = + config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML) + ? 'htmlAndMathml' + : 'mathml'; + return text + .split(lineBreakRegex) + .map((line) => + hasKatex(line) + ? `
${line}
` + : `
${line}
` + ) + .join('') + .replace(katexRegex, (_, c) => + katex + .renderToString(c, { + throwOnError: true, + displayMode: true, + output: outputMode, + }) + .replace(/\n/g, ' ') + .replace(//g, '') + ); + } + + return text.replace( + katexRegex, + 'Katex is not supported in @mermaid-js/tiny. Please use the full mermaid library.' + ); }; export default { diff --git a/packages/mermaid/src/diagrams/info/infoDb.ts b/packages/mermaid/src/diagrams/info/infoDb.ts index 5616a0ab9..169ceaf27 100644 --- a/packages/mermaid/src/diagrams/info/infoDb.ts +++ b/packages/mermaid/src/diagrams/info/infoDb.ts @@ -1,7 +1,9 @@ import type { InfoFields, InfoDB } from './infoTypes.js'; import packageJson from '../../../package.json' assert { type: 'json' }; -export const DEFAULT_INFO_DB: InfoFields = { version: packageJson.version } as const; +export const DEFAULT_INFO_DB: InfoFields = { + version: packageJson.version + (includeLargeFeatures ? '' : '-tiny'), +} as const; export const getVersion = (): string => DEFAULT_INFO_DB.version; diff --git a/packages/mermaid/src/docs/config/usage.md b/packages/mermaid/src/docs/config/usage.md index 0886a0e44..9e82ab87a 100644 --- a/packages/mermaid/src/docs/config/usage.md +++ b/packages/mermaid/src/docs/config/usage.md @@ -92,6 +92,12 @@ Mermaid can load multiple diagrams, in the same page. > Try it out, save this code as HTML and load it using any browser. > (Except Internet Explorer, please don't use Internet Explorer.) +## Tiny Mermaid + +We offer a smaller version of Mermaid that's approximately half the size of the full library. This tiny version doesn't support Mindmap Diagrams, Architecture Diagrams, KaTeX rendering, or lazy loading. + +If you need a more lightweight version without these features, you can use [Mermaid Tiny](https://github.com/mermaid-js/mermaid/tree/develop/packages/tiny). + ## Enabling Click Event and Tags in Nodes A `securityLevel` configuration has to first be cleared. `securityLevel` sets the level of trust for the parsed diagrams and limits click functionality. This was introduced in version 8.2 as a security improvement, aimed at preventing malicious use. diff --git a/packages/mermaid/src/docs/intro/index.md b/packages/mermaid/src/docs/intro/index.md index ff37d549b..f8aef36f1 100644 --- a/packages/mermaid/src/docs/intro/index.md +++ b/packages/mermaid/src/docs/intro/index.md @@ -109,6 +109,7 @@ To Deploy Mermaid: - [Mermaid Live Editor](https://github.com/mermaid-js/mermaid-live-editor) - [Mermaid CLI](https://github.com/mermaid-js/mermaid-cli) +- [Mermaid Tiny](https://github.com/mermaid-js/mermaid/tree/develop/packages/tiny) - [Mermaid Webpack Demo](https://github.com/mermaidjs/mermaid-webpack-demo) - [Mermaid Parcel Demo](https://github.com/mermaidjs/mermaid-parcel-demo) diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index 8fa02679b..cf08d02f8 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -5,7 +5,6 @@ // @ts-ignore TODO: Investigate D3 issue import { select } from 'd3'; import { compile, serialize, stringify } from 'stylis'; -// @ts-ignore: TODO Fix ts errors import DOMPurify from 'dompurify'; import isEmpty from 'lodash-es/isEmpty.js'; import packageJson from '../package.json' assert { type: 'json' }; diff --git a/packages/mermaid/src/type.d.ts b/packages/mermaid/src/type.d.ts new file mode 100644 index 000000000..0c88e4866 --- /dev/null +++ b/packages/mermaid/src/type.d.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-var +declare var includeLargeFeatures: boolean; diff --git a/packages/tiny/README.md b/packages/tiny/README.md new file mode 100644 index 000000000..a877a95da --- /dev/null +++ b/packages/tiny/README.md @@ -0,0 +1,43 @@ +# Tiny Mermaid + +This is a tiny version of mermaid that is optimized for the web. It is a subset of the mermaid library and is designed to be used in the browser via CDN. + +## Lazy loading + +The original mermaid library supports lazy loading, so it will be faster on the initial load, and only load the required diagrams. +This is not supported in the tiny mermaid library. So it's always recommended to use the full mermaid library unless you have a very specific reason to reduce the bundle size. + +## Removals from mermaid + +This does not support + +- Mindmap Diagram +- Architecture Diagram +- Katex rendering +- Lazy loading + +## Usage via NPM + +This package is not meant to be installed directly from npm. It is designed to be used via CDN. +If you need to use mermaid in your project, please install the full [`mermaid` package](https://www.npmjs.com/package/mermaid) instead. + +## Usage via CDN + +### Latest version + +```html + +``` + +### Specific version + +```html + + + + + + + + +``` diff --git a/packages/tiny/package.json b/packages/tiny/package.json new file mode 100644 index 000000000..0460850c6 --- /dev/null +++ b/packages/tiny/package.json @@ -0,0 +1,25 @@ +{ + "name": "@mermaid-js/tiny", + "version": "11.6.0", + "description": "Tiny version of mermaid", + "type": "commonjs", + "main": "./dist/mermaid.tiny.js", + "scripts": { + "clean": "rimraf dist" + }, + "repository": { + "type": "git", + "url": "https://github.com/mermaid-js/mermaid" + }, + "author": "Sidharth Vinod", + "license": "MIT", + "dependencies": {}, + "devDependencies": {}, + "files": [ + "dist/", + "README.md" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 14d4e0032..ca3d69848 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -518,6 +518,8 @@ importers: specifier: ^11.0.3 version: 11.0.3 + packages/tiny: {} + tests/webpack: dependencies: '@mermaid-js/mermaid-example-diagram': diff --git a/vite.config.ts b/vite.config.ts index 923e2d24e..0930de5b6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -35,6 +35,8 @@ export default defineConfig({ }, }, define: { + // Needs to be string + includeLargeFeatures: 'true', 'import.meta.vitest': 'undefined', }, });