Skip to content

Let the Node build be bundled: export registerStdFonts, resolve standard fonts and the ICC profile on first use - #1802

Open
dbartholomae wants to merge 2 commits into
foliojs:masterfrom
dbartholomae:dbartholomae/bundle-node-build
Open

dbartholomae wants to merge 2 commits into
foliojs:masterfrom
dbartholomae:dbartholomae/bundle-node-build

Conversation

@dbartholomae

Copy link
Copy Markdown

Fixes #1801

What changes

  • lib/document.node.js: createRequire falls back to file:/// when import.meta.url is undefined (CommonJS bundles), so importing a bundled pdfkit no longer throws. A #standard-fonts/<Name> lookup that fails with MODULE_NOT_FOUND now throws an error that names the font and the two fixes (mark pdfkit external, or registerStdFonts() with data from pdfkit/standard-fonts/<Name>), with the original error as cause. registerStdFonts is exported from the Node build, as it already is from the browser build.
  • lib/mixins/pdfa.js: the ICC profile URL is computed on first PDF/A use (getIccProfilePath()) instead of at import time, with a clear error when import.meta.url is unavailable. The browser build registers its inline profile under the same path as before.
  • rollup.config.mjs: the CommonJS footer exposes registerStdFonts too.

What does not change

  • The require('#standard-fonts/<Name>') calls stay literal and bound to createRequire, so file tracers keep working. @vercel/nft traces the same 18 files from the built js/pdfkit.node.mjs before and after this change: all 14 fonts, their shared chunk, the ICC profile and package.json.
  • Unbundled use is unchanged: the standard fonts still load lazily from js/standard-fonts/*.cjs on first use, and npm run test:package still passes.

Tests

  • New tests/unit/bundling.spec.js bundles lib/document.node.js with rollup into a directory that has its own package.json (under node_modules/.cache, so pdfkit's dependencies still resolve and Vitest leaves the file to Node), once as ESM and once as CommonJS with import.meta.url replaced by undefined, which is what esbuild produces. It asserts that both bundles load, that an unregistered standard font throws the new error with MODULE_NOT_FOUND as cause, that a document renders after registerStdFonts(Helvetica), and the PDF/A behaviour of each output. On master it fails in both formats (ESM: registerStdFonts missing and the bare Cannot find module error; CommonJS: Invalid URL while loading). It runs in about half a second.
  • tests/package-resolution.{cjs,mjs} assert the new export.
  • Checked end to end with @react-pdf/renderer 4.9 bundled by esbuild 0.28 into ESM and CommonJS: without registration both bundles fail with the new message, with the 14 fonts registered before react-pdf is imported both render.
  • npm test, npm run lint, npm run prettier -- --check and npm run build pass locally on Node 24.

Docs

README gains a "Bundling for Node" section; CHANGELOG has an entry under Unreleased.


Disclosure: this pull request and #1801 were written with an AI coding assistant (Claude Code). The tests, the tracer comparison and the end-to-end check described above were run, not only written.

A bundler that inlines pdfkit leaves the bundle in a package without the
`#standard-fonts/*` mapping and without `data/`, and a CommonJS output has no
`import.meta.url` at all. The Node build bound `createRequire(import.meta.url)`
and the ICC profile URL at import time, so an ESM bundle threw
`Cannot find module '#standard-fonts/Helvetica'` on the first document and a
CommonJS bundle threw `Invalid URL` while being imported, with no way for the
application to supply the font data.

Resolve both on first use, name the font and the fix when a standard font
cannot be loaded, and export `registerStdFonts` from the Node build as the
browser build already does. The `require('#standard-fonts/<Name>')` calls stay
literal so file tracers still pick the font files up.

Fixes foliojs#1801

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@blikblum

Copy link
Copy Markdown
Member

#standard-fonts mapping is only used in node and AFAICS can be replaced by a direct path.

Can you try this route and see if simplifies a bit the required changes?

…ping

The `#standard-fonts/*` entry in package.json was only ever referenced from
the Node entry, so the fourteen loaders now require
`./standard-fonts/<Name>.cjs` relative to the built file. That drops the
mapping, and with it the check that kept its import and require conditions
pointing at the same file, and lets an ESM bundle load the fonts from a copy
of `js/standard-fonts` placed next to it. A CommonJS bundle still has no
`import.meta.url`, so the createRequire fallback, the named error and the
`registerStdFonts` export stay as they are.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dbartholomae

Copy link
Copy Markdown
Author

Done in fff40ec. The loaders now do require('./standard-fonts/<Name>.cjs') relative to the built file, so the #standard-fonts/* entry in package.json is gone, and so is the check from #1782 that kept its two conditions in sync (nothing left to check). Tests, lint and test:package pass, and nft still picks up the 14 fonts, the chunk and the ICC profile.

Honestly it doesn't shrink the diff (+63/−43 instead of +45/−6 on the code files), because the 14 loader lines change and the test block is deleted, but the repo ends up smaller and package.json simpler. The rest of document.node.js I kept as it was: a CJS bundle still has no import.meta.url, and an ESM bundle still has no standard-fonts/ next to it, so the fallback, the error message and the registerStdFonts export are needed regardless.

Nice side effect: copying js/standard-fonts next to an ESM bundle now just works, same as js/data for the ICC profile. Added that to the README. Only catch is that the path is relative to js/, so the source entry only works via the build.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Node build cannot be bundled: standard fonts and import.meta are resolved relative to pdfkit's own package directory

2 participants