Skip to content

Support individual runtime asset URLs for hashed static files - #57

Merged
alekswebnet merged 4 commits into
missing-elements:masterfrom
nvi9:asset-attrs
Oct 19, 2026
Merged

alekswebnet merged 4 commits into
missing-elements:masterfrom
nvi9:asset-attrs

Conversation

@nvi9

@nvi9 nvi9 commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Allow PDF.js scripts and stylesheets to use URLs generated by static asset pipelines such as Django's ManifestStaticFilesStorage. assets-base configures a directory
but still assumes fixed filenames, so it cannot select those hashed runtime files individually.

This PR adds pdf-src, viewer-src, viewer-css-src, and theme-css-src alongside the existing worker-src attribute.

  • Each nonempty attribute overrides its corresponding runtime file,
    with or without assets-base.
  • Missing or empty attributes fall back to assets-base, then the
    bundled defaults.
  • Script and stylesheet attributes are read during initialization.
    Existing runtime worker option updates remain supported.
  • Worker URL resolution is centralized in getRuntimeFileUrls().

Validation

> pnpm test -- --run
$ pnpm sync:pdfjs && vitest -- --run
$ node scripts/sync-pdfjs-assets.mjs
[sync-pdfjs] Synced PDF.js assets for v6.4.299 (legacy build)

 DEV  v5.0.4 ./pdfjs-viewer

 ✓  firefox  tests/basic.test.ts (17 tests) 6512ms
   ✓ Basic tests (15)
     ✓ should render the PDF file 1421ms
     ✓ should not render PDF with wrong url 333ms
     ✓ should open the external file with base64 source 364ms
     ✓ should load the dark theme 315ms
     ✓ should hide the download button 243ms
     ✓ should add a title attribute to the iframe 240ms
     ✓ should apply hash-related attributes at runtime 259ms
     ✓ should apply worker-src option at runtime 246ms
     ✓ should include external worker origin in CSP script and worker directives 234ms
     ✓ should load runtime files from assets-base 465ms
     ✓ should prefer worker-src over assets-base and fall back to it on remove 169ms
     ✓ should use built-in worker by default 307ms
     ✓ should apply extended option attributes at runtime and restore defaults on remove 361ms
     ✓ should load individual runtime URLs without assets-base 1310ms
     ✓ should open a new document when src changes at runtime 238ms
   ✓ Runtime file URL overrides (2)
     ✓ resolves individual URLs with assets-base=undefined 1ms
     ✓ resolves individual URLs with assets-base=/static/pdfjs 2ms

 Test Files  1 passed (1)
      Tests  17 passed (17)
   Start at  10:59:09
   Duration  13.37s (tests 60%, worker 40%)

 PASS  Waiting for file changes...
       press h to show help, press q to quit
> pnpm build        
$ pnpm sync:pdfjs && tsc && vite build && node scripts/copy-worker.mjs
$ node scripts/sync-pdfjs-assets.mjs
[sync-pdfjs] Synced PDF.js assets for v6.4.299 (legacy build)
vite v8.3.4 building client environment for production...
✓ 4 modules transformed.
computing gzip size...
dist/locale-kKW4I85A.js                       2.57 kB │ gzip: 0.73 kB
dist/pdfjs-viewer-element.js                 11.05 kB │ gzip: 3.75 kB
dist/_virtual_pdfjs-viewer-html-2Y44YDES.js  44.91 kB │ gzip: 6.68 kB

✓ built in 491ms

Checklist

  • The change is focused and includes tests for behavior changes.
  • Relevant tests and the build pass locally.
  • Public API, behavior, or user-guidance documentation is updated where needed.
  • Accessibility impact has been considered, including keyboard navigation and focus visibility.
  • This pull request does not disclose a security vulnerability.

Allow PDF.js scripts and stylesheets to use URLs generated by static
asset pipelines such as Django's ManifestStaticFilesStorage.

Preserve assets-base and bundled fallbacks, centralize worker URL
resolution, and add documentation, a demo, and regression tests.
@alekswebnet

Copy link
Copy Markdown
Collaborator

Thanks for this — the motivation is clear and the fallback order (attribute → assets-base → bundled) is the right design. Tests and build pass locally for me as well. Two things I'd like fixed before merging:

1. Runtime worker-src updates bypass getFullPath

The init path now normalizes worker-src through getRuntimeFileUrls() → getFullPath(), but the runtime branch in attributeChangedCallback still passes the raw value:

viewerOptions?.set('workerSrc', newValue || this.getRuntimeFileUrls().workerSrc)

With a root-relative value such as /static/pdfjs/pdf.worker.abc.mjs (what {% static %} emits), the markup form works but a later setAttribute('worker-src', ...) does not: inside the srcdoc iframe window.location is about:srcdoc, so PDF.js's new URL(workerSrc, window.location) throws and it silently falls back to the fake main-thread worker. Since getRuntimeFileUrls() already reads and normalizes the attribute, this can simply be:

viewerOptions?.set('workerSrc', this.getRuntimeFileUrls().workerSrc)

A runtime test with a relative value would cover it — the existing one only uses an absolute URL.

2. Whitespace-only values are treated as set

resolveFile checks if (attr), so pdf-src=" " (e.g. a blank template variable) passes, and getFullPath(' ') resolves to the host page URL — the iframe then tries to load the HTML page as a module and initPromise rejects. getAssetsBase() already trims; doing the same here makes it fall back like an empty value:

const attr = this.getAttribute(...)?.trim()

Everything else looks good to me.

nvi9 and others added 3 commits October 19, 2026 12:20
`pdf-src` reads as the PDF document URL next to `src`. `pdfjs-lib-src`
names the PDF.js core library module (`pdf.mjs`, which sets
`globalThis.pdfjsLib`) and avoids confusion with the pdf-lib package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alekswebnet

Copy link
Copy Markdown
Collaborator

Thanks for the quick fixes, both look good.

One more change, which I've pushed to your branch myself (c1eb859): pdf-src is renamed to pdfjs-lib-src.

Next to src, which is the PDF document, pdf-src reads like "the PDF file's URL". pdfjs-lib-src names what it actually points to: the PDF.js core library (pdf.mjs, which exposes globalThis.pdfjsLib). It also can't be confused with the separate pdf-lib package.

The commit renames the internal key pdfSrc → pdfjsLibSrc (the attribute name is derived from it), and updates the tests, README and demo to match. Tests and build pass locally. If you make further changes, please pull first so you don't overwrite it.

@alekswebnet
alekswebnet merged commit 8fa8fc3 into missing-elements:master Oct 19, 2026
5 checks passed
@nvi9

nvi9 commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Next to src, which is the PDF document, pdf-src reads like "the PDF file's URL". pdfjs-lib-src names what it actually points to: the PDF.js core library (pdf.mjs, which exposes globalThis.pdfjsLib). It also can't be confused with the separate pdf-lib package.

I've also noticed that pdf-src might be a bit confusing. Thanks for the fix and the review!

@nvi9
nvi9 deleted the asset-attrs branch October 19, 2026 11:43
nvi9 pushed a commit to nvi9/pdfjs-viewer that referenced this pull request Oct 19, 2026
Follow-ups from the review of missing-elements#57:

- Replace the regex that derived attribute names from object keys with
  an explicit RUNTIME_FILES table (attribute, dist file name, default).
- Read runtime file URLs once per build, so the srcdoc CSP and the files
  it loads always come from the same values.
- Escape stylesheet URLs written into the srcdoc markup; values that are
  not valid URLs are passed through as written.
- Document that all runtime files must come from the same release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alekswebnet

Copy link
Copy Markdown
Collaborator

Released in v4.3.0, now on npm as @missing-elements/pdfjs-viewer@4.3.0.

Thank you for this one, @nvi9. The motivation was clear, the fallback order was right from the start, and the quick turnaround on the review points made it easy to land. The Django example in the demo is a nice touch for anyone else on a hashed-static-files setup.

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.

2 participants