CLI runs on Node 20+: npx storebound (bunx keeps working)
- cli/lib/rt.ts: the runtime layer on node: modules (spawn / spawnSync with Bun's defaults, exit codes and "Executable not found", which with PATHEXT, sleep, serve, Glob, hasher, gunzip, stdin, SQLite, packageRoot, config imports with a type-stripping fallback for Node 20); every Bun.* use in cli/ goes through it - tools/gen-embedded.ts -> cli/lib/embedded.ts: templates/, cli/shots/*.html and Play's Data safety form embedded (replaces Bun text imports; the bundle and the compiled sidecar carry them), checked fresh by bun run ci - package.json: bin dist/storebound.mjs (Node ESM bundle, tools/build-node.ts, prepack), engines node >=20 - bun run ci: node bundle + node smoke (real node: --version, help, check same as Bun, scan, release plan --help, init, MCP handshake + tool call); Forgejo CI sets up Node 20; tests/node-compat.test.ts guards cli/ - install writes npx MCP commands when run with Node; bin/storebound falls back to Node - docs: npx storebound init (Node 20+) or bunx storebound init
This commit is contained in:
parent
614c32bcba
commit
36a8bf9625
73 changed files with 1195 additions and 251 deletions
43
tools/build-node.ts
Normal file
43
tools/build-node.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env bun
|
||||
// Builds the npm package's CLI: one Node ESM bundle, dist/storebound.mjs (package.json "bin"), so `npx storebound`
|
||||
// runs on Node 20+ and `bunx storebound` keeps working. Zero runtime dependencies: playwright stays an optional peer
|
||||
// resolved from the project at run time (playwright-core only in the compiled sidecar), so it is left out.
|
||||
// bun tools/build-node.ts write dist/storebound.mjs (npm pack / publish run it: prepack)
|
||||
// bun tools/build-node.ts --out <f> write elsewhere (tests)
|
||||
// The bundle is checked for this machine's home and checkout paths (like the sidecar): nothing private may ship.
|
||||
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { dirname, join, relative, resolve } from 'node:path';
|
||||
import { privateStrings, ROOT } from './build-sidecars';
|
||||
|
||||
/** Packages the CLI only imports at run time from the project (or never under Node). */
|
||||
export const NODE_EXTERNALS = ['playwright', 'playwright-core', 'electron', 'chromium-bidi'];
|
||||
export const NODE_BUNDLE = join(ROOT, 'dist', 'storebound.mjs');
|
||||
|
||||
export function buildNode(out = NODE_BUNDLE): { path: string; bytes: number } {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'sb-node-'));
|
||||
try {
|
||||
const tmp = join(dir, 'storebound.mjs');
|
||||
const p = Bun.spawnSync(['bun', 'build', 'cli/index.ts', '--target=node', '--format=esm', ...NODE_EXTERNALS.flatMap(e => ['--external', e]), '--outfile', tmp],
|
||||
{ cwd: ROOT, stdout: 'pipe', stderr: 'pipe' });
|
||||
if (p.exitCode !== 0) throw new Error(`bun build failed\n${p.stderr.toString()}${p.stdout.toString()}`);
|
||||
// the entry's own `#!/usr/bin/env bun` and Bun's `// @bun` marker go; node runs this file
|
||||
const body = readFileSync(tmp, 'utf8').replace(/^#!.*\n/, '').replace(/^\/\/ @bun[^\n]*\n/, '');
|
||||
const js = `#!/usr/bin/env node\n${body}`;
|
||||
const bunGlobal = /\bBun\.[a-zA-Z]/.exec(js);
|
||||
if (bunGlobal) throw new Error(`the Node bundle uses the Bun global (${bunGlobal[0]}...): go through cli/lib/rt.ts`);
|
||||
const leaked = privateStrings().filter(s => js.includes(s));
|
||||
if (leaked.length) throw new Error(`the Node bundle contains private paths (${leaked.length}); refusing to write it`);
|
||||
mkdirSync(dirname(out), { recursive: true });
|
||||
writeFileSync(out, js);
|
||||
chmodSync(out, 0o755);
|
||||
return { path: out, bytes: statSync(out).size };
|
||||
} finally { rmSync(dir, { recursive: true, force: true }); }
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
const i = process.argv.indexOf('--out');
|
||||
const t = Date.now();
|
||||
const r = buildNode(i >= 0 ? resolve(process.argv[i + 1]!) : NODE_BUNDLE);
|
||||
console.log(`node bundle -> ${relative(process.cwd(), r.path) || r.path} (${(r.bytes / 1e6).toFixed(2)} MB, ${((Date.now() - t) / 1000).toFixed(1)}s)`);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue