From f0338cca5e44f4a85a764724d3597ff118aa143f Mon Sep 17 00:00:00 2001 From: Yvv Date: Sat, 28 Feb 2026 23:24:59 +0100 Subject: [PATCH] =?UTF-8?q?Fix=20d=C3=A9roulant=20PDF=20en=20production=20?= =?UTF-8?q?:=20polyfills=20DOMMatrix=20+=20worker=20pdfjs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pdfjs-dist en Node.js pur (hors Vite) nécessite : - DOMMatrix, Path2D, ImageData polyfills (pas de DOM en Node) - pdf.worker.mjs copié dans le build (traceInclude dans nitro config) Testé : 61 entrées retournées en mode production. Co-Authored-By: Claude Opus 4.6 --- nuxt.config.ts | 8 ++++++ server/api/admin/pdf-outline.get.ts | 43 ++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/nuxt.config.ts b/nuxt.config.ts index 45eeb2c..a3e20aa 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -45,4 +45,12 @@ export default defineNuxtConfig({ }, }, + nitro: { + externals: { + traceInclude: [ + 'node_modules/pdfjs-dist/legacy/build/pdf.worker.mjs', + ], + }, + }, + }) diff --git a/server/api/admin/pdf-outline.get.ts b/server/api/admin/pdf-outline.get.ts index 4d128f2..989a4c1 100644 --- a/server/api/admin/pdf-outline.get.ts +++ b/server/api/admin/pdf-outline.get.ts @@ -1,28 +1,63 @@ import { join } from 'node:path' import { readFileSync, existsSync } from 'node:fs' +// Polyfills nécessaires pour pdfjs-dist en Node.js pur (pas de DOM) +// On n'a besoin que du parsing, pas du rendu +if (typeof globalThis.DOMMatrix === 'undefined') { + // @ts-expect-error polyfill minimal pour pdfjs + globalThis.DOMMatrix = class DOMMatrix { + constructor() { return Object.assign(this, { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }) } + isIdentity = true + translate() { return new DOMMatrix() } + scale() { return new DOMMatrix() } + inverse() { return new DOMMatrix() } + multiply() { return new DOMMatrix() } + } +} +if (typeof globalThis.Path2D === 'undefined') { + // @ts-expect-error polyfill stub + globalThis.Path2D = class Path2D { constructor() {} } +} +if (typeof globalThis.ImageData === 'undefined') { + // @ts-expect-error polyfill stub + globalThis.ImageData = class ImageData { constructor(w: number, h: number) { this.width = w; this.height = h; this.data = new Uint8ClampedArray(w * h * 4) } } +} + export default defineEventHandler(async () => { const config = await readYaml('bookplayer.config.yml') const pdfFile = config?.book?.pdfFile || '/pdf/une-economie-du-don.pdf' - // En dev : public/, en prod : .output/public/ + // Résolution du chemin PDF : dev (public/) et prod (.output/public/) const cwd = process.cwd() const candidates = [ join(cwd, 'public', pdfFile), join(cwd, '.output', 'public', pdfFile), ] const pdfPath = candidates.find(p => existsSync(p)) - if (!pdfPath) return [] + + if (!pdfPath) { + console.warn('[pdf-outline] PDF non trouvé. cwd:', cwd, 'candidats:', candidates) + return [] + } let data: Uint8Array try { data = new Uint8Array(readFileSync(pdfPath)) - } catch { + } catch (err) { + console.warn('[pdf-outline] Erreur lecture PDF:', err) return [] } const pdfjsLib = await import('pdfjs-dist/legacy/build/pdf.mjs') - const doc = await pdfjsLib.getDocument({ data, useSystemFonts: true }).promise + + let doc + try { + doc = await pdfjsLib.getDocument({ data, useSystemFonts: true }).promise + } catch (err) { + console.warn('[pdf-outline] Erreur getDocument:', err) + return [] + } + const outline = await doc.getOutline() if (!outline || outline.length === 0) {