#!/usr/bin/env node // Makes the two ESM-only server dependencies loadable by the embedded Node 18 // (nodejs-mobile) runtime, which cannot require() ESM. Run AFTER // `npm install` inside nodejs-assets/nodejs-project (re-run after any install). // // • kysely (used: routes/categories.cts -> db/kysely.cts) — bundled to a // self-contained CommonJS file; its package entry is repointed at it. // • openid-client (UNUSED in single-user local mode, but eagerly require()'d // at boot via auth.cts/authOidc.cts -> oidcService.cts) — replaced with a // CommonJS stub so boot doesn't crash. Any actual use throws. // // Both are loaded on the web app (Node 22, native ESM); this only affects the // on-device copy. 'use strict'; const fs = require('fs'); const path = require('path'); const esbuild = require('esbuild'); // from the mobile project's own node_modules const PROJECT = path.join(__dirname, '..', 'nodejs-assets', 'nodejs-project'); const NM = path.join(PROJECT, 'node_modules'); function fail(msg) { console.error('prepare-local-mode-deps: ' + msg); process.exit(1); } // ── kysely → CommonJS ──────────────────────────────────────────────────────── (function bundleKysely() { const dir = path.join(NM, 'kysely'); if (!fs.existsSync(dir)) fail('kysely not installed — run `npm install` in nodejs-project first.'); const entry = path.join(dir, 'dist', 'index.js'); // ESM entry const outfile = path.join(dir, 'dist', 'index.cjs'); esbuild.buildSync({ entryPoints: [entry], bundle: true, format: 'cjs', platform: 'node', target: 'node18', outfile, logLevel: 'silent', }); const pkgPath = path.join(dir, 'package.json'); const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); pkg.main = './dist/index.cjs'; pkg.exports = { '.': { require: './dist/index.cjs', import: './dist/index.js', default: './dist/index.cjs' }, './package.json': './package.json', }; fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); console.log(' kysely -> bundled to CommonJS (dist/index.cjs)'); })(); // ── openid-client → CommonJS stub ──────────────────────────────────────────── (function stubOpenidClient() { const dir = path.join(NM, 'openid-client'); fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync( path.join(dir, 'package.json'), JSON.stringify({ name: 'openid-client', version: '0.0.0-local-stub', main: 'index.js' }, null, 2) + '\n', ); fs.writeFileSync( path.join(dir, 'index.js'), "'use strict';\n" + '// Local-mode stub. OIDC/SSO is not available when running on-device.\n' + 'function notSupported() { throw new Error("OIDC is not available in local mode"); }\n' + 'module.exports = new Proxy({}, { get: function () { return notSupported; } });\n', ); console.log(' openid-client -> CommonJS stub (OIDC disabled in local mode)'); })(); // ── ICU-less \p{} regex compatibility ──────────────────────────────────────── // nodejs-mobile's Node 18 ships WITHOUT ICU, so V8 cannot resolve Unicode // property escapes (\p{...}) — any regex using them throws "Invalid property // name" at compile time. path-to-regexp (express 5's router) uses // \p{ID_Start}/\p{ID_Continue} at module load to validate route-param names, // which crashes server boot. Rewrite to ASCII-safe classes; every route param // in this app is ASCII (:id, :userId, …). (minimatch also contains \p{} but only // as lazy POSIX-class strings that this app's globs never trigger — left as-is.) (function patchPathToRegexp() { const f = path.join(NM, 'path-to-regexp', 'dist', 'index.js'); if (!fs.existsSync(f)) return; let s = fs.readFileSync(f, 'utf8'); const before = s; s = s.replace(/\\p\{ID_Start\}/g, 'a-zA-Z').replace(/\\p\{ID_Continue\}/g, 'a-zA-Z0-9'); if (s !== before) { fs.writeFileSync(f, s); console.log(' path-to-regexp -> ASCII identifier regex (ICU-less \\p{} fix)'); } })(); console.log('prepare-local-mode-deps: done.');