Repository navigation
feat(*)!: publish ES module builds with exports maps and drop the UMD bundles - #682
Conversation
An ES module build cannot follow a require() call: esbuild keeps it as a runtime require, which throws in browsers (the concerto-core one in markdown-transform runs at load time). Convert the five remaining calls to imports and declare the two untyped modules. dayjs has no exports map, so its plugin subpath carries the .js extension Node ESM needs. Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com>
formulaName() was the only use of Node crypto, which pulled about 1 MB of crypto-browserify into browser bundles. @noble/hashes 1.x is a pure JavaScript SHA-256 (5 KB minified) with CommonJS and ESM builds. Names are unchanged; new tests compare against Node crypto. Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com>
scripts/build-esm.js, modelled on Concerto v5, emits lib/esm (import condition) and lib/esm-browser (browser condition) next to the CommonJS lib/. Dependencies stay bare imports so consumers share one copy of each; the browser build stubs Node builtins and jsdom. scripts/smoke-esm.mjs runs after the root build. BREAKING CHANGE: each package now has an exports map, so deep imports into lib/ no longer resolve, and Node import loads the ESM build. Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com>
Bundles the packages through the browser export condition with esbuild, as an application would, and runs the result in Chromium. Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com>
The exports maps already route webpack 5, Vite, Rollup and esbuild to lib/esm-browser, so the UMD bundles only served script-tag users and tools that do not read exports. Remove them with their webpack configs, the browser field, the Node polyfill devDependencies and the process dependency (113 packages out of the lockfile). The UMD e2e cases move into esm-browser.spec.ts. MIGRATION.md describes the 2.0 changes. BREAKING CHANGE: the umd/markdown-html.js, umd/markdown-template.js and umd/markdown-transform.js bundles are no longer published; bundle the packages instead (see MIGRATION.md). Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com>
|
Hi @mttrbrts, four choices in this PR are mine and I'd like your call on them before it's merged:
This is the first of the section 2 PRs. Next are concerto-codegen (which can go in parallel), cicero-core once markdown 2.0 is released, and then template-engine. |
|
Hi @mttrbrts, gentle reminder on this one. The CI workflow is still waiting for maintainer approval (first-time contributor here), so the Build and E2E checks haven't run yet. Could you approve it when you get a chance? Whenever you have time, I'd also like your call on the four questions above, since they decide how I build the next PR in concerto-codegen. Thanks! |
|
Hi @ujjwalv01 👋 Thank you for this, and welcome to the project! It's a substantial, carefully done PR. The before/after bundle numbers (4.5 MB to 689 KB minified), MIGRATION.md, the ESM smoke checks and the browser e2e suite make it much easier to review. Sorry for the slow reply on CI; I'll get the workflow approved so the Build and E2E checks can run. Dependencies and supply chain
Tests
Smaller things
Your four questions (my take):
Thanks again! This sets a really nice pattern for the concerto-codegen and cicero-core PRs that follow. 🙏 Generated by Claude Code |
Address review: esm-browser.spec.ts imports esbuild, so the e2e package now declares it rather than relying on hoisting. Restore the ToHtmlStringVisitor export check and the Paragraph check in the toCiceroMark test, which did not make it across from the UMD specs. Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com>
The UMD bundles are no longer built, so nothing writes to umd/ any more. Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com>
|
Thanks @mttrbrts, and thanks for approving CI! Pushed two commits for the review:
arethetypeswrong ( Noted on |
|
@mttrbrts thanks for merging #682! Next in section 2:
|
… drop the UMD bundle (#954) * refactor(cicero-core): import util.promisify instead of requiring it An ES module build cannot follow a runtime require(); esbuild would emit a __require that throws in browsers and Node ES modules. Refs accordproject/template-engine#185 Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com> * refactor(cicero-core): hash with @noble/hashes and import Buffer explicitly Node's crypto and the global Buffer do not exist in browsers. SHA-256 now uses @noble/hashes (already installed through markdown-template 2.0), Buffer comes from the buffer package, now a runtime dependency, and JSZip reads and writes Uint8Arrays. Hashes and archive bytes are unchanged. Refs accordproject/template-engine#185 Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com> * feat(cicero-core): publish ES module builds with an exports map Adds lib/esm (Node import) and lib/esm-browser (browser condition) built with esbuild, following accordproject/markdown-transform#682. Dependencies stay bare imports; the browser build stubs Node builtins except the buffer polyfill. Adds an explicit exports map, module and sideEffects: false, an ESM smoke test and a CI step that runs it. Refs accordproject/template-engine#185 Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com> * test(e2e): bundle cicero-core through the browser condition and run it in Chromium esbuild bundles the lib/esm-browser build the way a web application would. Covers archive loading, signature verification, wrong passphrases, browser hashes matching Node's, logos as Buffers and toArchive in the browser. Declares esbuild in the e2e package. Refs accordproject/template-engine#185 Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com> * feat(cicero-core)!: drop the UMD bundle The exports map routes webpack 5, Vite, Rollup and esbuild to lib/esm-browser, so the UMD bundle only served script-tag users and tools that do not read exports. Remove it with its webpack config, the browser field and the webpack and Node polyfill devDependencies (110 packages out of the lockfile). The UMD e2e cases move into esm-browser.spec.ts. MIGRATION.md describes the changes. BREAKING CHANGE: umd/cicero-core.js is no longer published and deep imports no longer resolve; bundle the package instead (see MIGRATION.md). Refs accordproject/template-engine#185 Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com> * chore(cicero-core): remove the stale nested package-lock.json The workspace is installed from the root package-lock.json; this leftover still listed the webpack toolchain removed in this PR. Refs accordproject/template-engine#185 Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com> --------- Signed-off-by: Ujjwal Verma <ujjwalverma010305@gmail.com>
Part of accordproject/template-engine#185 (section 2)
Every library package now ships two ES module builds next to the CommonJS
lib/:lib/esmfor Node andlib/esm-browserfor bundlers targeting the web. An explicitexportsmap selects between them. The webpack UMD bundles are removed. A browser bundler now takes each package'slib/esm-browserand shares one copy of markdown-it, dayjs and Concerto with the rest of the application. Before, each UMD bundle carried its own copy of Concerto plus about 1 MB of Node polyfills, and the markdown-transform UMD nested the markdown-template and markdown-html UMDs inside itself. The approach follows Concerto v5 (accordproject/concerto#1306).What template-engine imports from these packages (
{ TemplateMarkTransformer }and{ transform }), bundled for the browser with esbuild, Concerto included:lib/esm-browserChanges
refactor(*): the five remainingrequire()calls insrc/becomeimportstatements: markdown-it-cicero, markdown-it-template, concerto-core'sModelLoader, dijkstrajs and type-of. Type declarations are added for the last two. An ES module build cannot follow arequire(); the one in markdown-transform would have thrown in browsers as soon as the module loaded.dayjs/plugin/utcbecomesdayjs/plugin/utc.js, because dayjs has no exports map and Node ESM needs the extension.feat(markdown-template):formulaName()uses@noble/hashesinstead of Nodecrypto. Nodecryptowas the only reason crypto-browserify was in the browser bundles. Formula names are unchanged; new tests compare them against Nodecrypto.feat(*)!:scripts/build-esm.jsemitslib/esmandlib/esm-browserwith esbuild, one output module per source module. Dependencies stay as imports; the browser build replaces Node builtins and jsdom with empty modules. The seven library packages getexports(.and./package.jsononly),moduleandsideEffects: false. markdown-cli is a Node-only CLI and is unchanged.scripts/smoke-esm.mjsruns after the root build with 9 checks:importtolib/esm;require()(other than jsdom in the Node build);process.test(e2e):esm-browser.spec.tsbundles the packages through thebrowsercondition, as an application's bundler would, and runs the result in Chromium.feat(*)!: the UMD bundles are dropped, along with:browserfield;processdependency (113 packages out of the lockfile);The UMD e2e cases move into
esm-browser.spec.ts. MIGRATION.md is added, and the e2e README, the markdown-html README and.github/copilot-instructions.mdare updated.Flags
Breaking, needs a major release (2.0):
.../lib/...) no longer resolve;importnow loads the ESM build, so a process using bothimportandrequire()holds two copies;processdependency are gone.MIGRATION.md covers each case. I found no deep imports of these packages in other accordproject repos; template-engine, template-playground, cicero-core, template-cli, template-compiler and vscode-extension all import package roots.
New dependencies:
@noble/hashes^1.8 (runtime, markdown-template) is a pure JavaScript SHA-256: 5 KB minified, CommonJS and ESM builds. I used v1 because v2 is ESM-only.esbuild^0.27.7 (root dev dependency) is the same tool Concerto uses for its ESM builds.Differs from Concerto: Concerto bundles third-party dependencies into its browser build; this PR leaves them as imports. That gives one copy of markdown-it instead of three, and 689 KB instead of 977 KB minified for the imports above. The cost is that
lib/esm-browsercannot be loaded without a bundler using an import map alone, because dayjs and @xmldom/xmldom are CommonJS.Differs from (feat) convert commonmark dom to plain text #185: item 1 proposed keeping the UMD as a
./umdsubpath; this PR removes it, as Concerto v5 did.The design choices above are listed as questions in the first comment.
Screenshots or Video
N/A: build and packaging change.
Related Issues
Testing
npm test: 1,866 passed (1,860 before plus 6 new), 1,602 snapshots unchanged; lint shows 0 errors and licchk passes.npm run test:esm: 9/9. I confirmed the guard works: putting onerequire()back makes it fail in both builds.Author Checklist
--signoffoption of git commit.mainfromujjwalv01:build/esm-exports