From 87da5c3cf5c89700ee2653f2d4f92b1b1e004b34 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 24 May 2026 20:23:41 -0400 Subject: [PATCH] fix(reader): fix PDF rendering broken by Vite bundling of foliate-js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PDFs failed to open with error: Invalid factory url: "http://localhost:8765/static/undefined" Root cause: foliate-js's pdfjsPath() uses new URL(dynamicPath, import.meta.url) to resolve runtime asset paths (standard_fonts/, cmaps/). Vite transforms this pattern into a static asset map lookup at build time, but can only resolve known static file paths — not dynamically-constructed directory paths. The lookup returns undefined, producing a broken URL. Fix (two parts): 1. foliate-js fork (commit d164d6f): Export an overridable config.pdfjsPath function. Module-level code (worker, CSS) continues using import.meta.url directly (works fine with Vite for static filenames). The makePDF function uses config.pdfjsPath for runtime paths, allowing consumers to override it. 2. Bookhoard changes: - Update foliate-js dependency to d164d6f - Override config.pdfjsPath in reader.ts to resolve to /static/vendor/pdfjs/ - Add Vite plugin (pdfjsAssets) that copies standard_fonts/ and cmaps/ from node_modules to the build output during vite build (the standard approach used by react-pdf and other pdfjs-dist consumers) - Remove manual cp commands from build:ts scripts --- package.json | 8 ++++---- vite.config.ts | 20 +++++++++++++++++++- web/src/reader/reader.ts | 7 ++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 4e25fa6..c5df508 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,14 @@ "scripts": { "build:css": "tailwindcss -i ./web/static/input.css -o ./web/static/style.css --watch", "build:css:prod": "tailwindcss -i ./web/static/input.css -o ./web/static/style.css --minify", - "build:ts": "cp node_modules/htmx.org/dist/htmx.min.js web/static/htmx.min.js && mkdir -p web/static/vendor/pdfjs && cp -r node_modules/@bookhoard/foliate-js/vendor/pdfjs/standard_fonts web/static/vendor/pdfjs/ && vite build", - "build:ts:dev": "cp node_modules/htmx.org/dist/htmx.min.js web/static/htmx.min.js && mkdir -p web/static/vendor/pdfjs && cp -r node_modules/@bookhoard/foliate-js/vendor/pdfjs/standard_fonts web/static/vendor/pdfjs/ && vite build --mode development", - "build:ts:watch": "cp node_modules/htmx.org/dist/htmx.min.js web/static/htmx.min.js && mkdir -p web/static/vendor/pdfjs && cp -r node_modules/@bookhoard/foliate-js/vendor/pdfjs/standard_fonts web/static/vendor/pdfjs/ && vite build --watch", + "build:ts": "cp node_modules/htmx.org/dist/htmx.min.js web/static/htmx.min.js && vite build", + "build:ts:dev": "cp node_modules/htmx.org/dist/htmx.min.js web/static/htmx.min.js && vite build --mode development", + "build:ts:watch": "cp node_modules/htmx.org/dist/htmx.min.js web/static/htmx.min.js && vite build --watch", "build": "npm run build:ts && npm run build:css:prod", "dev": "npm run build:ts:dev && npm run build:css" }, "dependencies": { - "@bookhoard/foliate-js": "github:john-okeefe/foliate-js#74c317d58c2cedb53811da2973d0bf9bb6a70dcb", + "@bookhoard/foliate-js": "github:john-okeefe/foliate-js#d164d6f", "alpinejs": "^3.15.8", "chart.js": "^4.5.1", "highlight.js": "^11.11.1", diff --git a/vite.config.ts b/vite.config.ts index c951ac5..772b272 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,12 +1,30 @@ import { defineConfig } from "vite"; +import { cpSync, mkdirSync } from "node:fs"; +import { join } from "node:path"; + +const pdfjsAssets = () => ({ + name: "pdfjs-assets", + closeBundle() { + const outDir = "web/static/vendor/pdfjs"; + const srcDir = + "node_modules/@bookhoard/foliate-js/vendor/pdfjs"; + mkdirSync(outDir, { recursive: true }); + cpSync(join(srcDir, "standard_fonts"), join(outDir, "standard_fonts"), { + recursive: true, + }); + cpSync(join(srcDir, "cmaps"), join(outDir, "cmaps"), { recursive: true }); + }, +}); + export default defineConfig({ resolve: { alias: { "@": "/web/src", - "foliate-js": "/node_modules/@bookhoard/foliate-js", // Updated path + "foliate-js": "/node_modules/@bookhoard/foliate-js", }, }, base: "/static/", + plugins: [pdfjsAssets()], build: { outDir: "web/static", emptyOutDir: false, diff --git a/web/src/reader/reader.ts b/web/src/reader/reader.ts index 1093c41..e96b488 100644 --- a/web/src/reader/reader.ts +++ b/web/src/reader/reader.ts @@ -1,8 +1,11 @@ import "foliate-js/view.js"; +import { config as foliateConfig } from "@bookhoard/foliate-js/pdf.js"; import { Alpine } from "../alpine"; import { loadSettings, saveSettings } from "./settings-manager"; import { getToken } from "../storage"; +foliateConfig.pdfjsPath = (path) => `/static/vendor/pdfjs/${path}`; + const FONT_MAP: Record = { literata: '"Literata"', crimson: '"Crimson Pro"', @@ -437,9 +440,7 @@ document.addEventListener("alpine:init", () => { const fileName = new URL(config.fileUrl, window.location.origin).pathname; const file = new File([blob], fileName, { type: blob.type }); await this.view.open(file, { - pdf: { - standardFontDataUrl: "/static/vendor/pdfjs/standard_fonts/", - }, + pdf: {}, }); this.renderer = this.view.renderer; this.book = this.view.book;