Repository navigation
fix: Next.js worker and stylesheets, strict page CSP, package.json export - #70
Merged
Merged
Conversation
Next.js, with webpack and Turbopack, returns `new URL('./file', import.meta.url).href` as a
root-relative path such as `/_next/static/media/pdf.worker.min.<hash>.mjs`. Inside the srcdoc
iframe, PDF.js can't resolve a relative worker URL and silently falls back to running on the main
thread, and the resource defaults derived from the viewer's URL degraded to bare `wasm/` and
`cmaps/`. The defaults now go through `getFullPath` like attribute values do.
Refs #66
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scripts that locate the package with `require.resolve('@missing-elements/pdfjs-viewer/package.json')`,
for example to copy `dist` next to the app, failed with ERR_PACKAGE_PATH_NOT_EXPORTED.
Refs #66
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The element referenced viewer.css and paper-and-ink.css with `new URL('./file', import.meta.url)`,
so consumer bundlers treated them as the app's own CSS. `next build --webpack` emitted both as
empty files, so the viewer never initialized ("The `container` must be absolutely positioned"),
and moved their contents into the host page's stylesheets, restyling the app with the viewer's
global rules. That happened with `assets-base` too, since it is a build-time effect.
The same bundled CSS text now ships as two JS modules, and the element applies it as constructed
stylesheets on the viewer document. `assets-base`, `viewer-css-src` and `theme-css-src` still load
`<link>` elements, and dist still has both `.css` files for them.
- `injectViewerStyles` styles are constructed stylesheets too, so they still come after the theme.
- A theme from `theme-css-src` is a `<link>`, which precedes every constructed stylesheet. The
bundled viewer stylesheet then goes into a cascade layer, so the theme still overrides it.
- The viewer document stays `hidden` until its stylesheets are applied, so it never paints unstyled.
- Constructed stylesheets aren't blocked by a page CSP without `'unsafe-inline'` for styles, which
the srcdoc iframe inherits.
Refs #66
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shadow root's `<style>` was blocked by a page CSP without `'unsafe-inline'` for styles, so the element was `inline` and its iframe kept the default 150px height. The host styles are now a constructed stylesheet, which such a CSP doesn't block. With the previous commit, the viewer and `injectViewerStyles` styles also apply under it. Older jsdom versions, such as the one Jest 29 uses, can't construct stylesheets. There the element keeps the `<style>` element, so importing it in an app's component tests doesn't throw. Refs #66 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vite, webpack 5 and Next.js bundle the viewer but not the folders PDF.js loads its data from by name. Without them, scans compressed with JBIG2, CCITT or JPEG 2000 render blank or black, CJK text in fonts the PDF doesn't embed goes missing, and JavaScript in PDFs doesn't run. Checked in Next.js (Turbopack and webpack) and Vite builds, which render all of it once a postinstall copy of dist is served and assets-base points at it. The note moves from "Why use it" to Install, where bundler users set the package up. Refs #66 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes three of the four problems in #66, plus a strict page CSP problem found while investigating it. There's one commit per fix, and each passes the suite on its own. The fourth problem, the PDF.js data folders in bundled apps, stays a requirement that the README now explains (203b6d9).
Fixes
Default runtime URLs in Next.js (39d3a0a). Next.js, with webpack and Turbopack, returns
new URL('./file', import.meta.url).hrefas a root-relative path such as/_next/static/media/pdf.worker.min.<hash>.mjs. Inside the srcdoc iframe, PDF.js couldn't resolve the relative worker URL and silently ran on the main thread ("Setting up fake worker"). The resource defaults derived from the viewer's URL degraded to barewasm/andcmaps/. The defaults now go throughgetFullPath, like attribute values../package.jsonexport (4f57b10).require.resolve('@missing-elements/pdfjs-viewer/package.json')failed withERR_PACKAGE_PATH_NOT_EXPORTED.Default stylesheets from JS modules (3d526fa). The element referenced
viewer.cssandpaper-and-ink.csswithnew URL('./file', import.meta.url), so consumer bundlers treated them as the app's own CSS.next build --webpackdid two things with them:containermust be absolutely positioned").html, body { height: 100% }, the body background, and.hidden, [hidden] { display: none !important }. This happened withassets-basetoo, because it's a build-time effect.The same bundled CSS text now ships as two JS modules, and the element applies it as constructed stylesheets on the viewer document.
assets-base,viewer-css-srcandtheme-css-srcstill use<link>elements, anddiststill has both.cssfiles for them.Strict page CSP (4f0bb74). The srcdoc iframe inherits the page's CSP. Under
style-src 'self'(no'unsafe-inline'), the shadow root's<style>was blocked: the element wasinlineand its iframe kept the default 150px height.injectViewerStyleswas blocked as well. The host styles are now a constructed stylesheet, and with fix 3 the viewer and injected styles are too. A CSP doesn't block constructed stylesheets.Behavior changes
_virtual_pdfjs-viewer-css-*.js(60 KB gzipped) and_virtual_pdfjs-viewer-theme-css-*.js(6.8 KB). Their text is byte-identical todist/viewer.cssanddist/paper-and-ink.css. The element entry grows from 3.95 to 4.17 KB gzipped.injectViewerStylesadds constructed stylesheets, not<style data-pdfjs-viewer-runtime-style>elements. They still come after the viewer and theme styles.theme-css-srcwithoutviewer-css-src: the linked theme precedes every constructed stylesheet, so the bundledviewer.cssgoes into@layer pdfjs-viewerto let the theme override it. In this setup the theme wins overviewer.cssregardless of specificity, andviewer.css's!importantdeclarations win over the theme's. Every other combination cascades as before.hiddenuntil its constructed stylesheets are applied, so it never paints unstyled.<style>element, so importing it in an app's component tests doesn't throw.Data folders
Bundlers still don't copy the folders PDF.js loads data from by name:
wasm/(JBIG2, CCITT, JPEG 2000, ICC, the scripting sandbox),cmaps/,standard_fonts/,iccs/and the annotation icons. They stay a requirement instead of getting theBinaryDataFactoryfrom #66. A factory can serve CMaps, fonts and WASM decoders, but PDF.js still loads the scripting sandbox, ICC profiles and annotation icons by folder, and switches ICC off when a custom factory is used. Bundled apps would need the folder anyway, and we'd maintain a second way of loading data.The README now says what breaks without the folders and points at
assets-base. Checked in Next.js (Turbopack and webpack) and Vite builds, with apostinstallcopy ofdistandassets-base="/pdfjs-viewer/":In the Vite build, the color-management WASM and the CMYK profile also load only with the copy.
Not covered
Verification
pnpm test -- --run: 36 passed (Firefox), and each commit passes on its own. The new tests fail onmaster:hrefs, as Next.js does.<link>.theme-css-srctheme overriding the bundledviewer.css. This one also fails with the cascade layer removed.style-src 'self'.brightness(.9). The bundled CSS is minified, and the dev build used to link the unminified source.tscandpnpm buildare clean.next build --webpack: viewer startscontainermust be absolutely positioned")next build --webpack: host pagenext build --webpack: CSS filesstatic/media, 2 extracted intostatic/cssnext build(Turbopack): workervite buildstyle-src 'self'inline, iframe 150px, injected styles blocked (Firefox)blockand all styles apply (Chromium and Firefox), no CSP violations (Chromium)require.resolveofpackage.jsonERR_PACKAGE_PATH_NOT_EXPORTEDRefs #66
🤖 Generated with Claude Code