Skip to content

fix(pdf): self-host worker as .js (prod .mjs MIME breaks module import)#101

Merged
lyfuci merged 1 commit into
mainfrom
fix/pdf-worker-mime
Jul 9, 2026
Merged

fix(pdf): self-host worker as .js (prod .mjs MIME breaks module import)#101
lyfuci merged 1 commit into
mainfrom
fix/pdf-worker-mime

Conversation

@lyfuci

@lyfuci lyfuci commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Problem

On production, opening a PDF fails with:

无法打开 PDF:Setting up fake worker failed: "Failed to fetch dynamically imported module: https://…/assets/pdf.worker.min-*.mjs"

Root cause: the pdf.js worker was emitted as a hashed .mjs asset. Prod nginx has no .mjs MIME mapping, so it serves the file as application/octet-stream — and browsers refuse to import() an octet-stream as a module. Confirmed on prod:

Asset Content-Type
…/pdf.worker.min-*.mjs application/octet-stream
…/index-*.js application/javascript
…/openjpeg.wasm application/wasm

Only the .mjs extension falls through.

Fix (repo-only, no server change)

Copy the worker into public/pdfjs/pdf.worker.min.js (alongside the cmaps/fonts/wasm already self-hosted there) and point GlobalWorkerOptions.workerSrc at that static .js path. pdf.js always spawns the worker with {type:"module"} regardless of file extension, and nginx serves .js as application/javascript, so the module worker loads. No .mjs asset is emitted anymore.

Verification

  • pnpm typecheck / lint / build green; pnpm test 393 passing.
  • Against pnpm preview: worker served as text/javascript, no .mjs in dist/, 3-page CJK PDF renders identically (correct dims, glyphs, white JPEG matte), zero console errors.

Note

The underlying nginx .mjs gap still exists and could bite any future .mjs. Optional hardening (server-side, not in this PR): map .mjs → text/javascript in nginx. This PR makes the app not depend on it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XBhzzooAQNwaM4vg1CmpFo

The pdf.js worker was emitted as a hashed `.mjs` asset via `?url`. Prod
nginx has no `.mjs` MIME mapping, so it served the file as
`application/octet-stream`, and the browser refused to import it as a
module — "Setting up fake worker failed: Failed to fetch dynamically
imported module …pdf.worker.min-*.mjs".

Copy the worker into `public/pdfjs/pdf.worker.min.js` (alongside the
cmaps/fonts/wasm already served from there) and point
`GlobalWorkerOptions.workerSrc` at that static `.js` path. pdf.js always
spawns the worker with `{type:"module"}` regardless of extension, and
nginx serves `.js` as `application/javascript`, so the module loads.
No server change required.

Verified against `pnpm preview`: worker served as text/javascript, no
`.mjs` asset emitted, 3-page CJK PDF renders identically, no console
errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBhzzooAQNwaM4vg1CmpFo
@lyfuci lyfuci merged commit 7984387 into main Jul 9, 2026
2 checks passed
@lyfuci lyfuci deleted the fix/pdf-worker-mime branch July 9, 2026 02:15
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.

1 participant