Mobile-BillTracker/scripts/transpile-node18.js

73 lines
2.3 KiB
JavaScript
Raw Permalink 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
// Transpiles the Bill Tracker server's TypeScript sources to plain CommonJS for
// the embedded Node 18.20.4 runtime (nodejs-mobile). Node 18 runs modern JS
// syntax natively, so — unlike the old Node-12 pass — this ONLY strips
// TypeScript; it does not rewrite dependency syntax and does not touch
// node_modules (their JS runs as-is on Node 18).
//
// The server is CommonJS-with-TS: files use require()/module.exports plus
// `import type`/`as typeof import()` for types, and require() each other with
// EXPLICIT `.cts`/`.mts` extensions (115 .cts + 40 .mts call sites). We therefore
// transpile each .cts/.mts file IN PLACE, keeping its original name: esbuild
// strips the types and emits CJS, and Node 18's CommonJS loader loads unknown
// extensions (.cts/.mts) through the default `.js` handler as CommonJS — so every
// explicit-extension require() path keeps resolving with zero path rewriting.
//
// Usage: node transpile-node18.js <serverDir> [<dir> ...]
'use strict';
const fs = require('fs');
const path = require('path');
const esbuild = require('esbuild');
const SKIP_DIRS = new Set(['.bin', 'node_modules', 'dist']);
function loaderFor(file) {
if (/\.(cts|mts|ts)$/.test(file)) return 'ts';
if (/\.(cjs|js)$/.test(file)) return 'js';
return null;
}
function walk(dir, cb) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (SKIP_DIRS.has(entry.name)) continue;
if (entry.isSymbolicLink()) continue;
const full = path.join(dir, entry.name);
if (entry.isDirectory()) walk(full, cb);
else if (loaderFor(entry.name)) cb(full);
}
}
let count = 0;
let failed = 0;
for (const dir of process.argv.slice(2)) {
if (!fs.existsSync(dir)) continue;
walk(dir, (file) => {
const src = fs.readFileSync(file, 'utf8');
let out;
try {
out = esbuild.transformSync(src, {
loader: loaderFor(file),
format: 'cjs',
target: 'node18',
sourcefile: file,
}).code;
} catch (err) {
console.error(`FAILED ${file}: ${err.message.split('\n')[0]}`);
failed++;
return;
}
if (out !== src) {
fs.writeFileSync(file, out);
count++;
}
});
}
if (failed) {
console.error(`Transpile finished with ${failed} failure(s).`);
process.exit(1);
}
console.log(`Transpiled ${count} file(s) to CommonJS for Node 18.`);