diff --git a/compute/core/src/pdf/parse.ts b/compute/core/src/pdf/parse.ts index f0bde04..69d10d2 100644 --- a/compute/core/src/pdf/parse.ts +++ b/compute/core/src/pdf/parse.ts @@ -3,7 +3,7 @@ import type { ParsedPdfDocument, ParsedPdfPage } from '../types/parsed-pdf'; import { ensureModel } from './model'; import { runLayoutModel } from './runLayoutModel'; import { mergeTextWithRegions } from './merge'; -import { resolvePdfjsStandardFontDataUrl } from './pdfjs-runtime'; +import { configurePdfjsNodeRuntime, resolvePdfjsStandardFontDataUrl } from './pdfjs-runtime'; import { PDF_PARSER_VERSION } from './parser-version'; import { stitchCrossPageBlocks } from './stitch'; import { renderPage } from './render'; @@ -36,10 +36,7 @@ export async function parsePdf(input: ParsePdfInput): Promise // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs'); - if (pdfjs.GlobalWorkerOptions) { - pdfjs.GlobalWorkerOptions.workerSrc = 'pdfjs-dist/legacy/build/pdf.worker.mjs'; - pdfjs.GlobalWorkerOptions.workerPort = null; - } + configurePdfjsNodeRuntime(pdfjs); const standardFontDataUrl = resolvePdfjsStandardFontDataUrl(); const loadingTask = pdfjs.getDocument({ diff --git a/compute/core/src/pdf/pdfjs-runtime.ts b/compute/core/src/pdf/pdfjs-runtime.ts index 233d9c3..4f041c5 100644 --- a/compute/core/src/pdf/pdfjs-runtime.ts +++ b/compute/core/src/pdf/pdfjs-runtime.ts @@ -1,14 +1,39 @@ import fs from 'node:fs'; -import { createRequire } from 'node:module'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; let cachedStandardFontDataUrl: string | null = null; -const require = createRequire(import.meta.url); +const pdfjsPackageName = 'pdfjs-dist'; + +function candidatePackageRoots(): string[] { + const roots = new Set(); + let dir = process.cwd(); + + for (let i = 0; i < 8; i += 1) { + roots.add(path.join(dir, 'node_modules', pdfjsPackageName)); + const next = path.dirname(dir); + if (next === dir) break; + dir = next; + } + + return [...roots]; +} + +function resolvePdfjsPackageFile(relativePath: string): string { + const normalizedRelativePath = relativePath.replace(/^\/+/, ''); + const candidates = candidatePackageRoots().map((root) => path.join(root, normalizedRelativePath)); + const found = candidates.find((candidate) => fs.existsSync(candidate)); + if (found) return found; + + throw new Error( + `pdfjs-dist file not found: ${normalizedRelativePath}; checked ${candidates.join(', ')}`, + ); +} export function resolvePdfjsStandardFontDataUrl(): string { if (cachedStandardFontDataUrl) return cachedStandardFontDataUrl; - const pdfjsPackageDir = path.dirname(require.resolve('pdfjs-dist/package.json')); + const pdfjsPackageDir = path.dirname(resolvePdfjsPackageFile('package.json')); const standardFontDir = path.join(pdfjsPackageDir, 'standard_fonts'); if (!fs.existsSync(standardFontDir)) { @@ -18,3 +43,18 @@ export function resolvePdfjsStandardFontDataUrl(): string { cachedStandardFontDataUrl = `${standardFontDir.replace(/\/?$/, '/')}`; return cachedStandardFontDataUrl; } + +export function resolvePdfjsWorkerSrc(): string { + return pathToFileURL(resolvePdfjsPackageFile('legacy/build/pdf.worker.mjs')).href; +} + +export function configurePdfjsNodeRuntime(pdfjs: { + GlobalWorkerOptions?: { + workerSrc?: string; + workerPort?: unknown; + }; +}): void { + if (!pdfjs.GlobalWorkerOptions) return; + pdfjs.GlobalWorkerOptions.workerSrc = resolvePdfjsWorkerSrc(); + pdfjs.GlobalWorkerOptions.workerPort = null; +} diff --git a/compute/core/src/pdf/render.ts b/compute/core/src/pdf/render.ts index 89ffaff..33ec1a1 100644 --- a/compute/core/src/pdf/render.ts +++ b/compute/core/src/pdf/render.ts @@ -1,5 +1,5 @@ import type { Canvas } from '@napi-rs/canvas'; -import { resolvePdfjsStandardFontDataUrl } from './pdfjs-runtime'; +import { configurePdfjsNodeRuntime, resolvePdfjsStandardFontDataUrl } from './pdfjs-runtime'; type CanvasRuntime = { DOMMatrixCtor: unknown; @@ -106,11 +106,7 @@ export async function renderPage({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const pdfjs = await import('pdfjs-dist/legacy/build/pdf.mjs'); - - if (pdfjs.GlobalWorkerOptions) { - pdfjs.GlobalWorkerOptions.workerSrc = 'pdfjs-dist/legacy/build/pdf.worker.mjs'; - pdfjs.GlobalWorkerOptions.workerPort = null; - } + configurePdfjsNodeRuntime(pdfjs); const standardFontDataUrl = resolvePdfjsStandardFontDataUrl(); diff --git a/next.config.ts b/next.config.ts index 1e2f4b7..78947e8 100644 --- a/next.config.ts +++ b/next.config.ts @@ -16,13 +16,18 @@ const securityHeaders = [ ]; const bundleWorkerCompute = true; +const pdfjsTraceFiles = [ + './node_modules/pdfjs-dist/package.json', + './node_modules/pdfjs-dist/legacy/build/pdf.mjs', + './node_modules/pdfjs-dist/legacy/build/pdf.worker.mjs', + './node_modules/pdfjs-dist/standard_fonts/**/*', +]; const serverExternalPackages = [ '@napi-rs/canvas', 'better-sqlite3', 'ffmpeg-static', - // Keep pdfjs-dist out of the bundle: it resolves its own on-disk assets - // (standard_fonts) at runtime via require.resolve, which only works if it - // stays a real package in node_modules rather than being inlined. + // Keep pdfjs-dist as a real package in node_modules. Server-side preview + // rendering resolves pdf.js runtime assets from the filesystem at runtime. 'pdfjs-dist', ...(!bundleWorkerCompute ? ['onnxruntime-node', '@huggingface/tokenizers'] : []), ]; @@ -56,19 +61,15 @@ const nextConfig: NextConfig = { './node_modules/ffmpeg-static/ffmpeg', ], '/api/documents/blob/preview/ensure': [ - // standard_fonts + the worker are loaded by path/specifier, not a static - // import, so trace them explicitly. The rest of pdfjs-dist is pulled in - // automatically now that it is a server-external package. - './node_modules/pdfjs-dist/legacy/build/pdf.worker.mjs', - './node_modules/pdfjs-dist/standard_fonts/**/*', + // pdf.js runtime assets are resolved through filesystem paths at runtime, + // so trace them explicitly for Vercel/standalone serverless bundles. + ...pdfjsTraceFiles, ], '/api/documents/blob/preview/presign': [ - './node_modules/pdfjs-dist/legacy/build/pdf.worker.mjs', - './node_modules/pdfjs-dist/standard_fonts/**/*', + ...pdfjsTraceFiles, ], '/api/documents/blob/preview/fallback': [ - './node_modules/pdfjs-dist/legacy/build/pdf.worker.mjs', - './node_modules/pdfjs-dist/standard_fonts/**/*', + ...pdfjsTraceFiles, ], }, outputFileTracingExcludes: {