A framework-agnostic, universal document renderer with optional chunked loading polyrender.wisp.place/
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 147 lines 5.1 kB view raw
1/** 2 * Minimal esbuild build script — replaces Vite entirely. 3 * 4 * The only "magic" is an esbuild plugin that rewrites the variable-based 5 * dynamic `import(moduleName)` in @polyrender/core's `requirePeerDep` into 6 * static import() calls that esbuild can bundle. 7 * 8 * This is the same fundamental fix needed in Vite, webpack, or any other 9 * bundler — browsers cannot resolve bare specifiers from `import(variable)`. 10 */ 11import { build, context } from "esbuild"; 12import { cpSync, existsSync, mkdirSync } from "fs"; 13import { resolve, dirname } from "path"; 14import { fileURLToPath } from "url"; 15 16const __dirname = dirname(fileURLToPath(import.meta.url)); 17const isWatch = process.argv.includes("--watch"); 18 19/** 20 * Plugin that provides empty shims for Node.js built-in modules when bundling 21 * for the browser. Some WASM-based packages (e.g. 7z-wasm) include conditional 22 * Node code paths that are never reached in a browser, but esbuild still tries 23 * to resolve the `require("fs")` / `require("crypto")` calls at bundle time. 24 */ 25const shimNodeBuiltins = { 26 name: "shim-node-builtins", 27 setup(build) { 28 const builtins = 29 /^(fs|crypto|path|os|module|stream|util|events|buffer|assert|http|https|net|tls|url|zlib|readline|child_process|worker_threads|perf_hooks)$/; 30 build.onResolve({ filter: builtins }, (args) => ({ 31 path: args.path, 32 namespace: "node-builtin-shim", 33 })); 34 build.onLoad({ filter: /.*/, namespace: "node-builtin-shim" }, () => ({ 35 contents: "module.exports = {}", 36 loader: "js", 37 })); 38 }, 39}; 40 41/** Plugin to resolve @polyrender/core's dynamic peer-dep imports. */ 42const resolvePeerDeps = { 43 name: "resolve-peer-deps", 44 setup(build) { 45 build.onLoad({ filter: /packages[\\/]core.*\.(js|ts)$/ }, async (args) => { 46 const fs = await import("fs"); 47 let contents = fs.readFileSync(args.path, "utf8"); 48 49 if ( 50 contents.includes("moduleName") && 51 contents.includes("import(") 52 ) { 53 // Map of peer dep names → static imports 54 const peerDeps = [ 55 "pdfjs-dist", 56 "epubjs", 57 "docx-preview", 58 "papaparse", 59 "highlight.js", 60 "jszip", 61 "xlsx", 62 "node-unrar-js", 63 "@jsquash/jxl", 64 "utif", 65 ]; 66 // 7z-wasm's default ESM build imports Node's 'module' built-in, 67 // so we redirect to its UMD build which is browser-safe. 68 const sevenZipCase = ` case '7z-wasm': return import('7z-wasm/7zz.umd.js').then(m => m.default || m);`; 69 const cases = peerDeps 70 .map( 71 (d) => 72 ` case '${d}': return import('${d}').then(m => m.default || m);`, 73 ) 74 .join("\n"); 75 76 const replacement = [ 77 "(async (name) => { switch(name) {", 78 cases, 79 sevenZipCase, 80 " default: throw new Error(`Unknown peer dep: ${name}`);", 81 " }})(moduleName)", 82 ].join("\n"); 83 84 contents = contents.replace( 85 /await\s+import\(\s*(?:\/\*.*?\*\/\s*)?moduleName\s*\)/g, 86 `await ${replacement}`, 87 ); 88 } 89 90 return { 91 contents, 92 loader: args.path.endsWith(".ts") ? "ts" : "js", 93 }; 94 }); 95 }, 96}; 97 98// Ensure dist directory exists 99const distDir = resolve(__dirname, "dist"); 100if (!existsSync(distDir)) mkdirSync(distDir, { recursive: true }); 101 102// Copy index.html to dist 103cpSync(resolve(__dirname, "index.html"), resolve(distDir, "index.html")); 104 105// Copy styles.css to dist 106const stylesPath = resolve(__dirname, "../../packages/core/src/styles.css"); 107if (existsSync(stylesPath)) { 108 cpSync(stylesPath, resolve(distDir, "styles.css")); 109} 110 111// Copy pdfjs worker to dist 112const workerGlob = resolve(__dirname, "node_modules/pdfjs-dist/build"); 113if (existsSync(workerGlob)) { 114 const workerFiles = [ 115 "pdf.worker.min.mjs", 116 "pdf.worker.mjs", 117 "pdf.worker.min.js", 118 ]; 119 for (const wf of workerFiles) { 120 const src = resolve(workerGlob, wf); 121 if (existsSync(src)) { 122 cpSync(src, resolve(distDir, wf)); 123 break; 124 } 125 } 126} 127 128const buildOptions = { 129 entryPoints: [resolve(__dirname, "src/main.ts")], 130 bundle: true, 131 format: "esm", 132 platform: "browser", 133 outdir: distDir, 134 sourcemap: true, 135 target: "es2022", 136 plugins: [shimNodeBuiltins, resolvePeerDeps], 137 logLevel: "info", 138}; 139 140if (isWatch) { 141 const ctx = await context(buildOptions); 142 await ctx.watch(); 143 console.log("Watching for changes..."); 144} else { 145 await build(buildOptions); 146 console.log(`\nBuild complete! Serve with:\n npx serve dist\n`); 147}