Mobile-BillTracker/scripts/prepare-local-mode-deps.js

96 lines
4.2 KiB
JavaScript
Raw Normal View History

feat(mobile): run the TypeScript backend on Node 18 in local mode (batch 7-9) The web app migrated its server to TypeScript (server.cts) on Express 5 / openid-client 6 / kysely, none of which ran on the old embedded Node 12.19. Rebuild the local-mode pipeline for Node 18: - sync-nodejs-project.sh: sync server.cts + .cts/.mts + setup/ + seed data. - transpile-node18.js (replaces transpile-node12.js): esbuild TS->CJS in place, keeping .cts/.mts filenames (Node 18's CJS loader loads unknown extensions via the .js handler, so the explicit-extension require() graph keeps resolving). - prepare-local-mode-deps.js: bundle ESM-only kysely to CJS, stub openid-client (unused on-device), and rewrite path-to-regexp's \p{} regexes to ASCII (the no-ICU Node 18 build can't compile unicode-property escapes). - build-better-sqlite3-node18.sh (replaces the Node-12 build/fix scripts): cross-compile for all three ABIs against the Node 18 ABI, injecting libnode.so as a NEEDED entry so V8 symbols resolve at dlopen. - main.js: entry -> server.cts; drop the now-native randomUUID/hkdfSync polyfills (keep the Intl shim); add TMPDIR + a startup-error relay to the WebView. - nodejs-project/package.json: server dep set matched to the web app. Verified end-to-end on the x86_64 emulator: boot -> migrations -> seed -> login -> create bill -> force-stop -> relaunch -> data persists. arm64/armv7 prebuilds are built and link-verified; a physical-device run is still pending. Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-11 09:42:53 -05:00
#!/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.');