From 06bbb16610d2b1216de3a5da37a47b9b4f5e16b4 Mon Sep 17 00:00:00 2001 From: Aaron Culich Date: Tue, 14 Apr 2026 05:14:15 -0700 Subject: [PATCH] fix(personal-server): bundle JS deps with esbuild to fix MODULE_NOT_FOUND crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The personal server binary crashes on startup with: Error: Cannot find module '@opendatalabs/personal-server-ts-core/config' Root cause: The build script marks @opendatalabs/*, @hono/node-server, and hono as esbuild externals, converting their imports to require() calls in the CJS bundle. These modules are copied to dist/node_modules/ beside the binary, but two issues prevent runtime resolution: 1. pkg's virtual filesystem doesn't search beside process.execPath, and the _resolveFilename patch only redirects native addons (better-sqlite3 etc.) 2. The @opendatalabs packages only export ESM ("import" condition) — Node 22.10.0 (embedded by pkg) can't resolve ESM-only subpath exports via require() without --experimental-require-module Fix: Let esbuild bundle all JS dependencies directly (only native addons remain external). This eliminates runtime module resolution for JS packages entirely. The binary is also ~44% smaller (66MB vs 118MB) since duplicate copies in dist/node_modules/ are no longer needed. Made-with: Cursor --- personal-server/package.json | 7 ------- personal-server/scripts/build.js | 34 ++++++-------------------------- 2 files changed, 6 insertions(+), 35 deletions(-) diff --git a/personal-server/package.json b/personal-server/package.json index e9723e79..cafd7aa0 100644 --- a/personal-server/package.json +++ b/personal-server/package.json @@ -10,13 +10,6 @@ "build": "node scripts/build.js" }, "pkg": { - "assets": [ - "node_modules/@opendatalabs/personal-server-ts-*/dist/**/*", - "node_modules/@opendatalabs/personal-server-ts-*/package.json", - "node_modules/@hono/node-server/dist/**/*", - "node_modules/@hono/node-server/package.json", - "node_modules/hono/**/*" - ], "targets": [ "node22-macos-arm64", "node22-macos-x64", diff --git a/personal-server/scripts/build.js b/personal-server/scripts/build.js index 2b719166..35ea134d 100644 --- a/personal-server/scripts/build.js +++ b/personal-server/scripts/build.js @@ -90,22 +90,15 @@ async function build() { // Also provide import.meta.url shim for ESM code bundled to CJS // Must redirect better-sqlite3, bindings, and file-uri-to-path to external node_modules const nativeModulesList = ['better-sqlite3', 'bindings', 'file-uri-to-path']; - const runtimeExternalModules = [ - '@opendatalabs/personal-server-ts-core/config', - '@opendatalabs/personal-server-ts-server', - '@opendatalabs/personal-server-ts-mcp', - '@hono/node-server', - 'hono', - ]; const nativeBanner = [ 'var _M=require("module"),_P=require("path"),_U=require("url"),_R=_M._resolveFilename;', // Shim for import.meta.url 'if(typeof globalThis.__importMetaUrl==="undefined"){globalThis.__importMetaUrl=_U.pathToFileURL(__filename).href;}', - // Patch require resolution for native modules - // _resolveFilename(request, parent, isMain, options) - paths goes in options (4th param) + // Patch require resolution so native addons load from beside the executable `var _NM=${JSON.stringify(nativeModulesList)};`, + 'var _np=_P.join(_P.dirname(process.execPath),"node_modules");', '_M._resolveFilename=function(r,p,m,o){', - 'if(_NM.includes(r)){var _np=_P.join(_P.dirname(process.execPath),"node_modules");', + 'if(_NM.includes(r)){', 'try{return _R.call(this,r,p,m,Object.assign({},o||{},{paths:[_np]}));}catch(e){}}', 'return _R.call(this,r,p,m,o);};', ].join(''); @@ -174,7 +167,7 @@ async function build() { platform: 'node', format: 'cjs', outfile: bundlePath, - external: runtimeExternalModules, + external: nativeModulesList, plugins: [inlinePackageJsonPlugin, dynamicNativeRequirePlugin], banner: { js: nativeBanner }, inject: [shimPath], @@ -211,23 +204,8 @@ async function build() { } } - const runtimePackagesToCopy = [ - '@opendatalabs/personal-server-ts-core', - '@opendatalabs/personal-server-ts-server', - '@opendatalabs/personal-server-ts-mcp', - '@hono/node-server', - 'hono', - ]; - for (const mod of runtimePackagesToCopy) { - const src = join(ROOT, 'node_modules', ...mod.split('/')); - if (existsSync(src)) { - const dest = join(DIST, 'node_modules', ...mod.split('/')); - log(`Copying ${mod}...`); - cpSync(src, dest, { recursive: true }); - } else { - log(`WARNING: ${mod} not found in node_modules`); - } - } + // @opendatalabs/*, @hono/node-server, and hono are bundled by esbuild + // (no longer external), so no copy step is needed for them. // Re-download the better-sqlite3 prebuilt binary for the pkg target Node version. // The local npm install compiles for the host Node.js, which may differ from the