diff --git a/frontend/node-polyfill.cjs b/frontend/node-polyfill.cjs index 6cd7861..08641af 100644 --- a/frontend/node-polyfill.cjs +++ b/frontend/node-polyfill.cjs @@ -1,4 +1,11 @@ -// Polyfill markAsUncloneable on node:worker_threads for Node 20 runtime compatibility (undici/jsdom) +/** + * Polyfill worker_threads.markAsUncloneable untuk Node 20 runtime compatibility (undici/jsdom). + * + * Catatan: + * File CJS ini dimuat via NODE_OPTIONS="--require /app/node-polyfill.cjs" di Docker runner + * (require-time sebelum entrypoint Node dievaluasi). + * Untuk penggunaan import-time di TypeScript/ESM, lihat src/lib/services/markdown/nodeCompat.ts. + */ try { const wt = require('node:worker_threads'); if (wt && typeof wt.markAsUncloneable === 'undefined') { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7d968cc..4d603fc 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,9 +12,7 @@ "@codemirror/lang-markdown": "^6.3.0", "@codemirror/lang-python": "^6.1.0", "@codemirror/theme-one-dark": "^6.1.0", - "@types/dompurify": "^3.0.5", "codemirror": "^6.0.1", - "dompurify": "^3.4.14", "highlight.js": "^11.11.0", "isomorphic-dompurify": "^3.23.0", "katex": "^0.16.9", @@ -1575,15 +1573,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/dompurify": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", - "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", - "license": "MIT", - "dependencies": { - "@types/trusted-types": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -1619,6 +1608,7 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "devOptional": true, "license": "MIT" }, "node_modules/@vitest/expect": { diff --git a/frontend/package.json b/frontend/package.json index c5285c5..d6cc09d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,9 +27,7 @@ "@codemirror/lang-markdown": "^6.3.0", "@codemirror/lang-python": "^6.1.0", "@codemirror/theme-one-dark": "^6.1.0", - "@types/dompurify": "^3.0.5", "codemirror": "^6.0.1", - "dompurify": "^3.4.14", "highlight.js": "^11.11.0", "isomorphic-dompurify": "^3.23.0", "katex": "^0.16.9", diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index 5eb958f..6321164 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -6,16 +6,8 @@ * elemes-ts network namespace, so the backend lives on localhost. */ -// Polyfill markAsUncloneable on node:worker_threads for Node 20 runtime compatibility (undici/jsdom) -try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const wt = require('node:worker_threads'); - if (wt && typeof wt.markAsUncloneable === 'undefined') { - wt.markAsUncloneable = () => {}; - } -} catch { - // ignore -} +import { patchWorkerThreadsCompat } from '$lib/services/markdown/nodeCompat'; +patchWorkerThreadsCompat(); import type { Handle } from '@sveltejs/kit'; diff --git a/frontend/src/lib/services/markdown/nodeCompat.ts b/frontend/src/lib/services/markdown/nodeCompat.ts new file mode 100644 index 0000000..a0e3c28 --- /dev/null +++ b/frontend/src/lib/services/markdown/nodeCompat.ts @@ -0,0 +1,30 @@ +/** + * Polyfill worker_threads.markAsUncloneable untuk kompatibilitas Node 20 + * dengan isomorphic-dompurify/jsdom/undici. + * + * Root Cause: + * isomorphic-dompurify@3.23.0 -> jsdom@30.0.1 -> undici@8.10.0 memanggil + * `markAsUncloneable` dari `node:worker_threads`. Pada Node 20 runtime (v20.20.2), + * method ini belum ada (ditambahkan pada Node 22+), menyebabkan + * `TypeError: webidl.util.markAsUncloneable is not a function` saat SSR. + * + * Catatan: + * File ini adalah helper TypeScript/ESM (import-time). Terdapat juga salinan CJS di + * `frontend/node-polyfill.cjs` yang digunakan khusus untuk `NODE_OPTIONS="--require ..."` + * pada Docker container (require-time sebelum entrypoint Node). + * + * TODO: hapus polyfill ini kalau isomorphic-dompurify/jsdom sudah rilis versi + * yang kompatibel penuh dengan Node 20 tanpa markAsUncloneable, atau base image + * Docker sudah di-upgrade ke Node 22+. + */ +export function patchWorkerThreadsCompat(): void { + try { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const wt = require('node:worker_threads'); + if (wt && typeof wt.markAsUncloneable === 'undefined') { + wt.markAsUncloneable = () => {}; + } + } catch { + // ignore - bukan environment Node (misal di browser runtime) + } +} diff --git a/frontend/src/lib/services/markdown/processEmbeds.ts b/frontend/src/lib/services/markdown/processEmbeds.ts index f9f979f..39a84f3 100644 --- a/frontend/src/lib/services/markdown/processEmbeds.ts +++ b/frontend/src/lib/services/markdown/processEmbeds.ts @@ -1,13 +1,5 @@ -// Polyfill markAsUncloneable on node:worker_threads for Node 20 runtime compatibility (undici/jsdom) -try { - // eslint-disable-next-line @typescript-eslint/no-require-imports - const wt = require('node:worker_threads'); - if (wt && typeof wt.markAsUncloneable === 'undefined') { - wt.markAsUncloneable = () => {}; - } -} catch { - // ignore -} +import { patchWorkerThreadsCompat } from './nodeCompat'; +patchWorkerThreadsCompat(); import DOMPurify from 'isomorphic-dompurify';