Context
#18814's third question asked whether generated dist/ bundles should ship in the npm package at all. It answered no change, on the grounds that none of them can be produced on the consumer side — every source package (parse5, marked, mermaid, monaco-editor, esbuild) is an engine devDependency.
That premise is false for the path most consumers take, and the correction is @tobiu's.
neomjs/create-app scaffolds a workspace whose postinstall is cd node_modules/neo.mjs && npm i, so the engine's own devDependencies are installed inside node_modules/neo.mjs. The engine's bundle scripts resolve their output relative to their own location — buildScripts/build/parse5.mjs writes to path.join(__dirname, '../../dist/parse5.mjs') — so running them from a scaffolded root repopulates the engine's own dist/ in place. Verified against create-app@d8ae9ffa41 and neomjs/neo@48021153ff.
So a npx neo-app workspace can rebuild these. A direct npm i neo.mjs cannot. The decision turns on which of those the package is for, and #18814 answered it without that distinction available.
Measured package composition, npm pack --dry-run --json on dev at 48021153ff, 46.63 MiB unpacked across 5655 files:
| tree |
size |
share |
resources/examples |
13.21 MiB |
28% |
test/playwright |
8.52 MiB |
18% |
apps/portal |
5.87 MiB |
13% |
dist/ — all five bundles |
5.45 MiB |
12% |
src/ |
5.39 MiB |
12% |
Inside dist/, the split is extreme:
| bundle |
size |
share of dist |
mermaid.mjs |
5156 KiB |
95% |
parse5.mjs |
163 KiB |
3% |
highlight/* (2 files) |
216 KiB |
4% |
marked.mjs |
46 KiB |
1% |
The Problem
Dropping the four small bundles saves 0.9% of the package and risks an engine that cannot boot: dist/parse5.mjs and dist/marked.mjs are imported at module scope by src/functional/util/HtmlTemplateProcessor.mjs and src/component/Markdown.mjs. That trade is not worth making, whatever the rebuild story.
mermaid.mjs is the only one where every property points the other way:
- 5.0 MB, 95% of
dist/ and 11% of the whole package
- dynamically imported, not at module scope —
src/main/addon/Mermaid.mjs sets useLazyLoading, so nothing fetches it until a page renders a diagram
- already redirectable —
main.addon.Mermaid exposes a mermaidPath config
- rebuildable in a scaffolded workspace, per the Context above
That combination — large, optional, lazily loaded, already redirectable, rebuildable — belongs to no other bundle here.
The Architectural Reality
src/main/addon/Mermaid.mjs:101 — the dynamic await import(), and the mermaidPath config above it.
buildScripts/build/mermaid.mjs:8 — outfile resolved from __dirname, which is why a scaffolded workspace's run lands in the engine package.
buildScripts/util/check-package-contents.mjs REQUIRED_ENTRIES — lists dist/mermaid.mjs as required, with a why that records the define-identifier substitution making the upstream package non-interchangeable. Any decision here has to move that entry, or the guard contradicts it.
neomjs/create-app#33 — the scaffolded workspace is missing bundle-mermaid entirely today, so "the consumer can rebuild it" is currently true only via cd node_modules/neo.mjs.
The Fix
Not prescribed — this ticket exists to make the decision, and the options differ in who absorbs the cost:
- Ship as today. Every consumer gets working diagrams with no setup, at 5.0 MB of install for the ones who never render one.
- Stop shipping it; the scaffold builds it. Requires
neomjs/create-app#33 first. Direct npm i neo.mjs consumers get a broken Mermaid addon until they redirect mermaidPath or run the bundle script — a support question the current design does not have.
- Default
mermaidPath to a CDN. No install cost, no build step, and a network dependency plus a supply-chain surface the engine does not have today.
Whichever lands, REQUIRED_ENTRIES and the .npmignore header move together with it.
Acceptance Criteria
Out of Scope
parse5, marked, highlight and monaco. Settled by the measurement: 0.9% combined, two of them imported at module scope.
resources/examples (13.21 MiB) and apps/portal (5.87 MiB), which are larger than all of dist/ and are a different question — what the package is FOR, rather than how a bundle is produced.
test/playwright (8.52 MiB). Not bloat: the scaffolded app's four test scripts point straight into node_modules/neo.mjs/test/playwright/, so it is the consumer's harness. Recorded here because it looks like an obvious cut and is not.
- #18814's release-composition repair, which is independent and must land regardless.
Avoided Traps
- ⛔ Do not treat the five bundles as one decision. One is 5.0 MB, optional and redirectable; two others are imported at module scope. A single verdict is wrong in one direction or the other.
- ⛔ Do not cite "the consumer can rebuild it" without naming which consumer. True for
npx neo-app once create-app#33 lands, false for a direct install, and #18814 got this wrong by not distinguishing them.
- ⛔ Do not remove the bundle before the scaffold can build it.
create-app#33 is the ordering constraint, not a nicety.
Related
- #18814 — where the question was raised and deferred; its AC-5 points here
neomjs/create-app#33 — the missing bundle-mermaid script, a prerequisite for option 2
neomjs/create-app#34 — the scaffold's version drift, which would ship a mermaid@11 bundle against a mermaid@12 engine
Origin Session ID: 2b78af80-54a8-4e14-afd8-85c6dfe41004
Retrieval Hint: "should dist/mermaid.mjs ship by default, 5MB 95% of dist, lazy and redirectable, create-app postinstall builds engine devDependencies"
Context
#18814's third question asked whether generated
dist/bundles should ship in the npm package at all. It answered no change, on the grounds that none of them can be produced on the consumer side — every source package (parse5,marked,mermaid,monaco-editor,esbuild) is an engine devDependency.That premise is false for the path most consumers take, and the correction is @tobiu's.
neomjs/create-appscaffolds a workspace whosepostinstalliscd node_modules/neo.mjs && npm i, so the engine's own devDependencies are installed insidenode_modules/neo.mjs. The engine's bundle scripts resolve their output relative to their own location —buildScripts/build/parse5.mjswrites topath.join(__dirname, '../../dist/parse5.mjs')— so running them from a scaffolded root repopulates the engine's owndist/in place. Verified againstcreate-app@d8ae9ffa41andneomjs/neo@48021153ff.So a
npx neo-appworkspace can rebuild these. A directnpm i neo.mjscannot. The decision turns on which of those the package is for, and #18814 answered it without that distinction available.Measured package composition,
npm pack --dry-run --jsonondevat48021153ff, 46.63 MiB unpacked across 5655 files:resources/examplestest/playwrightapps/portaldist/— all five bundlessrc/Inside
dist/, the split is extreme:mermaid.mjsparse5.mjshighlight/*(2 files)marked.mjsThe Problem
Dropping the four small bundles saves 0.9% of the package and risks an engine that cannot boot:
dist/parse5.mjsanddist/marked.mjsare imported at module scope bysrc/functional/util/HtmlTemplateProcessor.mjsandsrc/component/Markdown.mjs. That trade is not worth making, whatever the rebuild story.mermaid.mjsis the only one where every property points the other way:dist/and 11% of the whole packagesrc/main/addon/Mermaid.mjssetsuseLazyLoading, so nothing fetches it until a page renders a diagrammain.addon.Mermaidexposes amermaidPathconfigThat combination — large, optional, lazily loaded, already redirectable, rebuildable — belongs to no other bundle here.
The Architectural Reality
src/main/addon/Mermaid.mjs:101— the dynamicawait import(), and themermaidPathconfig above it.buildScripts/build/mermaid.mjs:8—outfileresolved from__dirname, which is why a scaffolded workspace's run lands in the engine package.buildScripts/util/check-package-contents.mjsREQUIRED_ENTRIES— listsdist/mermaid.mjsas required, with awhythat records thedefine-identifier substitution making the upstream package non-interchangeable. Any decision here has to move that entry, or the guard contradicts it.neomjs/create-app#33— the scaffolded workspace is missingbundle-mermaidentirely today, so "the consumer can rebuild it" is currently true only viacd node_modules/neo.mjs.The Fix
Not prescribed — this ticket exists to make the decision, and the options differ in who absorbs the cost:
neomjs/create-app#33first. Directnpm i neo.mjsconsumers get a broken Mermaid addon until they redirectmermaidPathor run the bundle script — a support question the current design does not have.mermaidPathto a CDN. No install cost, no build step, and a network dependency plus a supply-chain surface the engine does not have today.Whichever lands,
REQUIRED_ENTRIESand the.npmignoreheader move together with it.Acceptance Criteria
neomjs/create-app#33landed first,REQUIRED_ENTRIESupdated, and the.npmignoreheader's!/dist/mermaid.mjsremoved in the same change, so no guard asserts a file that no longer ships.whyon itsREQUIRED_ENTRIESrow, and it stops claiming a consumer cannot rebuild it.Out of Scope
parse5,marked,highlightandmonaco. Settled by the measurement: 0.9% combined, two of them imported at module scope.resources/examples(13.21 MiB) andapps/portal(5.87 MiB), which are larger than all ofdist/and are a different question — what the package is FOR, rather than how a bundle is produced.test/playwright(8.52 MiB). Not bloat: the scaffolded app's four test scripts point straight intonode_modules/neo.mjs/test/playwright/, so it is the consumer's harness. Recorded here because it looks like an obvious cut and is not.Avoided Traps
npx neo-apponcecreate-app#33lands, false for a direct install, and #18814 got this wrong by not distinguishing them.create-app#33is the ordering constraint, not a nicety.Related
neomjs/create-app#33— the missingbundle-mermaidscript, a prerequisite for option 2neomjs/create-app#34— the scaffold's version drift, which would ship amermaid@11bundle against amermaid@12engineOrigin Session ID: 2b78af80-54a8-4e14-afd8-85c6dfe41004
Retrieval Hint: "should dist/mermaid.mjs ship by default, 5MB 95% of dist, lazy and redirectable, create-app postinstall builds engine devDependencies"