I ran into a subtle PDF.js worker mismatch that still allows the build to succeed. The host application installs `[email protected]`, while a PDF renderer depends on `[email protected]`. When asset-copy code resolves the worker from the application root, it may copy version 6 even though the renderer is using version 5.4.624. The browser then reports a worker version mismatch at runtime.
The fragile lookup was:
```js
const worker = require.resolve('pdfjs-dist/legacy/build/pdf.worker.mjs')
```
I changed it so resolution starts from the renderer package that owns the dependency, and the build now fails if the resolved asset version does not match the renderer's PDF.js version. I tested npm and pnpm with nested and hoisted layouts, Vite development and production builds, and clean installs. The Worker, CMaps, WASM files, and fonts consistently needed to come from `5.4.624`, while the host application continued using `6.1.200`. The generated asset manifest also records the source package and version.
Should build tooling always resolve runtime assets from the package that owns them, or is it better to force one PDF.js version across the entire application?
2 Answers
Resolving from the application root is doing exactly what it was asked to do, so it is not a safe way to identify a renderer’s dependency. The stronger design is to make ownership explicit: each renderer can expose the assets it requires, and presets can combine the manifests from the renderers they include. The build plugin then consumes those declarations instead of guessing through a chain of possible resolution paths. If the owning package cannot be identified, failing the build is much safer than copying whichever version happens to resolve first.
I would avoid forcing a single PDF.js version just to make asset resolution easier. A global override can silently change the version used by one renderer and create a different runtime failure. Keeping each renderer paired with its own dependency, then resolving the Worker and related files from that dependency, preserves the package contract and makes mismatches visible during the build instead of when a document is opened.

Agreed. The fallback chain is brittle. Renderer-owned manifests are cleaner, although discovering renderers can be awkward. Package metadata may be the least problematic option, but an unknown owner should still cause a build failure.