Problem
Every CLI invocation re-parses and re-compiles the CLI's own module graph: the ~600 files under dist/, @oclif/core, and the rest of the dependency closure. On a production server (slow host, Node v22.22.2) this costs about 1.2s of every command, measured while decomposing time bitsocial community list (see the breakdown in pkc-js docs/protocol/import-performance.md, "Decomposition of the remaining ~5.5s"):
| Layer |
Median (steady state) |
bare node -e 0 |
~0.05s |
import("@pkcprotocol/pkc-js") alone |
~1.8s |
command graph (list.js -> BaseCommand -> pkc-js) |
~2.5s (~0.7s CLI dist on top of pkc-js) |
oclif boot (bitsocial --version) |
~0.5s |
pkc-js already enables the compile cache for its own graph: its npm import entry calls module.enableCompileCache() and then dynamic-imports the real graph. But that only covers modules compiled after the call. Everything the CLI loads before reaching pkc-js (its own dist/ files, oclif, other deps) is compiled first, uncached, on every run.
Proposed fix
Call enableCompileCache() at the very top of bin/run, before importing @oclif/core. bin/run already delays oclif behind a dynamic import (for DEBUG stripping), so the whole framework + command graph compiles after the cache is on:
#!/usr/bin/env node
// Enable the V8 compile cache before anything is imported so the CLI's own dist/,
// oclif and all deps get bytecode-cached across invocations (no-op on Node < 22.8).
const { enableCompileCache } = await import("node:module");
enableCompileCache?.();
// ... existing DEBUG stripping ...
const oclif = await import("@oclif/core");
Notes:
module.enableCompileCache() exists since Node 22.8; the optional call keeps older Node working (no-op).
- Default cache dir is
os.tmpdir()/node-compile-cache, overridable with NODE_COMPILE_CACHE, opt-out with NODE_DISABLE_COMPILE_CACHE=1.
- First run after an upgrade pays a small one-time cache-population cost; every later run skips parse/compile for the whole graph.
Validation
On a slow host, compare time bitsocial --version (oclif boot only) and time bitsocial community list before/after, second-and-later runs. Expected: most of the ~1.2s CLI-graph share recovered; pkc-js numbers unchanged (it caches itself already).
Problem
Every CLI invocation re-parses and re-compiles the CLI's own module graph: the ~600 files under
dist/,@oclif/core, and the rest of the dependency closure. On a production server (slow host, Node v22.22.2) this costs about 1.2s of every command, measured while decomposingtime bitsocial community list(see the breakdown in pkc-jsdocs/protocol/import-performance.md, "Decomposition of the remaining ~5.5s"):node -e 0import("@pkcprotocol/pkc-js")alonelist.js->BaseCommand-> pkc-js)bitsocial --version)pkc-js already enables the compile cache for its own graph: its npm
importentry callsmodule.enableCompileCache()and then dynamic-imports the real graph. But that only covers modules compiled after the call. Everything the CLI loads before reaching pkc-js (its owndist/files, oclif, other deps) is compiled first, uncached, on every run.Proposed fix
Call
enableCompileCache()at the very top ofbin/run, before importing@oclif/core.bin/runalready delays oclif behind a dynamic import (for DEBUG stripping), so the whole framework + command graph compiles after the cache is on:Notes:
module.enableCompileCache()exists since Node 22.8; the optional call keeps older Node working (no-op).os.tmpdir()/node-compile-cache, overridable withNODE_COMPILE_CACHE, opt-out withNODE_DISABLE_COMPILE_CACHE=1.Validation
On a slow host, compare
time bitsocial --version(oclif boot only) andtime bitsocial community listbefore/after, second-and-later runs. Expected: most of the ~1.2s CLI-graph share recovered; pkc-js numbers unchanged (it caches itself already).