Skip to content

packages/cli e2e tests that spawn bin/run.js as "the SHIPPED entrypoint" never reach dist/ — oclif's ts-path reroutes them to src/ under NODE_ENV=test|development #11317

Description

@os-zhuang

Found while diagnosing the CI red on #11268 (issue #11113). Unassigned, filed for triage rather than fixed there — #11268's surface is ruled closed.

The claim three files make, and the measurement that contradicts it

packages/cli/test/serve-mcp-stdio-answers.e2e.test.ts:53, serve-mcp-capability-collision.e2e.test.ts:54 and serve-stdio-stdout-purity.e2e.test.ts:50 each carry the identical comment:

/** `bin/run.js` — the SHIPPED entrypoint, i.e. the one the card's repro names. */constCLI=resolve(HERE,'../bin/run.js');

They do spawn that stub. They do not reach packages/cli/dist.

Two facts compose:

  1. packages/cli/dist is never built for these tests.turbo.json declares "@objectstack/cli#test": { "dependsOn": ["^build"] } — dependencies only, not the package's own build. Measured: pnpm exec turbo run test --filter=@objectstack/cli --dry=json yields 58 tasks, 57 of them #build, and @objectstack/cli#test's resolved dependency list does not contain @objectstack/cli#build. invocation-loudness.e2e.test.ts:34-35 already states this fact in prose.

  2. All three spawns set NODE_ENV: 'development' in the child env (:115, :119, :109 respectively — for the --dev admin seed). @oclif/core@4.13.3's tsPath() skips the TypeScript path lookup only when isProd(), and lib/util/util.js:66 defines that as:

functionisProd(){return['development','test'].includes(process.env.NODE_ENV??'');}

So under development (or vitest's inherited test) oclif rewrites the command target from the declared ./dist/commands to ./src/commands and auto-transpiles. The compiled artifact is never consulted.

Measured directly, packages/cli/dist absent:

$ env -u NODE_ENV node packages/cli/bin/run.js serve …
› Error: command serve not found
$ NODE_ENV=production node packages/cli/bin/run.js serve …
› Error: command serve not found
$ NODE_ENV=test node packages/cli/bin/run.js serve …
(node:22544) [MODULE_NOT_FOUND] Warning: ModuleLoadError
task: findCommand (compile)
message: … import() failed to load …/packages/cli/src/commands/compile.ts …
$ NODE_ENV=development node packages/cli/bin/run.js serve …
(same — resolution proceeds from src/)

The test/development legs get past command resolution and into src/; unset and production do not.

Why it matters

Three flavours of loss, none of them loud:

  • The stated intent — exercise the artifact users install — is not met, and the comment asserting it is currently false. A tsc output difference (emit, exports resolution, a dist-only path bug of the cli: packages/cli/dist/index.js invoked directly exits 0 with zero output, and dev's unknown-flag failure presents as a crashed boot (carved from #10087) #10111 kind) is invisible to them.
  • packages/cli/vitest.config.ts's cost header reasons about "swapping the spawns to the built entry" as an unrealised speed lever, and quotes node bin/run.js --version 2.9-3.2s against tsx bin/run-dev.js --version 6.5-6.8s. These files already spell bin/run.js and do not get that 2.9s path — they pay the transpile cost anyway. The header's measurement stands; its framing of these files does not.
  • Anyone reading these three as the pattern for "test the built CLI" copies a shape that silently does not.

Not the same thing as #11267

#11267 is the inherited TEST=true making better-auth skip origin checks. This is oclif rerouting command resolution to source. Same family (a spawned child's env quietly changing what is under test), different mechanism, different remedy.

Shape of a fix, not prescribed

Either the three files stop claiming the built entry (spawn bin/run-dev.js like the other ~20 e2e files here, and delete the comment), or they genuinely get one (declare the build dependency and stop forcing a ts-path-enabling NODE_ENV — which means finding another way to reach the --dev admin seed, since that is why they set development). The first is honest and cheap; the second is what the comment currently promises. Whoever takes it should also decide whether packages/cli wants a lint rule making "spawns bin/run.js" and "sets a ts-path-enabling NODE_ENV" mutually exclusive, since the combination is silently self-cancelling.

Related

Every line and path above was re-derived in a dedicated worktree at adb1b9ea8c.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions