Skip to content

djs: exit 1 when compile fails - #1577

Merged
sergey-shandar merged 2 commits into
mainfrom
claude/compile-error-exit-code-ifa6gb
Aug 15, 2026
Merged

djs: exit 1 when compile fails#1577
sergey-shandar merged 2 commits into
mainfrom
claude/compile-error-exit-code-ifa6gb

Conversation

@sergey-shandar

@sergey-shandar sergey-shandar commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

compile in fjs/djs/module.f.mjs printed the error and then returned
pure(0), so a failed compile was indistinguishable from a successful one by
exit status — the message text was the only failure signal, and scripts and CI
could not detect the failure at all. The argument-count check directly above it
already returned pure(1), so the branch was the odd one out.

That branch carries every transpile failure, not just parse errors: file not found and circular dependency reach it too, so none of the failure modes were
detectable. It now returns pure(1). The output file was already (correctly)
not written in this case, so nothing else about the failure path changes.

compile gains JSDoc stating the exit-code contract — 0 only once the output
file is written, 1 on every failure — since todo/README.md asks that a
deleted issue's design decision be captured in JSDoc or a README.md first. The
proofs for fileNotFound and parseError now assert code === 1 and, in
addition, that no output file was produced.

The one thing left behind: a metadata-less ParseError still prints
undefined:undefined:undefined as its location, the cosmetic half of the
original issue. Fixing it means either suppressing the prefix or giving those
errors real metadata — a design choice worth its own pull request rather than a
drive-by here, so it moves to fjs/djs/todo/parse-error-location-format.md
along with the circular dependency case, which has the same metadata: null.

Verified with npx tsc (clean), fjs t (2789 pass, 0 fail), and the issue's
own repro, where a parse error and a missing input now both exit 1 while a
successful compile still exits 0 and writes its output.

Changelog:

  • BREAKING CHANGES: djs: fjs compile exits with code 1 instead of 0
    when the input cannot be read or fails to parse, so a failed compile is
    detectable from the exit status

🤖 Generated with Claude Code

https://claude.ai/code/session_01ATUpzeifLPfhNZegwbJPVC

`compile` printed the parse error and then returned `pure(0)`, so a failed
compile was indistinguishable from a successful one by exit status. The same
branch also carries `transpile`'s `file not found` and `circular dependency`
errors, so none of the failure modes were detectable from a script or CI.

Return `pure(1)` instead, matching the argument-count check above it, and
document the exit-code contract on `compile`. The output file was already
(correctly) not written in this case.

The `undefined:undefined:undefined` location printed for metadata-less errors
is untouched here and moves to `fjs/djs/todo/parse-error-location-format.md`.

Changelog:
- **BREAKING CHANGES:** `djs`: `fjs compile` exits with code `1` instead of `0`
  when the input fails to parse or cannot be read

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATUpzeifLPfhNZegwbJPVC
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
functionalscript 91e7ddd Commit Preview URL

Branch Preview URL
Aug 15 2026, 01:36 AM

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ATUpzeifLPfhNZegwbJPVC
@sergey-shandar sergey-shandar changed the title Fix fjs compile to exit with code 1 on parse errors djs: exit 1 when compile fails Aug 15, 2026

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Draft is not a blocker. Baseline is origin/main = 104bfa34 (types/array: factor the emptiness guard out of head and tail (#1574)).

Exit codes, measured by driving the real entry point

Not via virtual — by running node fjs/module.mjs compile … in both trees and reading $?. fjs/effects/node/module.mjs:381 does process.exit(await runEffect(p)), so compile's return value is the process exit status.

invocation 104bfa34 91e7ddd5
no args / one arg 1 1
valid input → .mjs 0 0
valid input → .json 0 0
parse error (export default @) 0 1
input file missing 0 1

That is precisely the todo's complaint: the arg-count branch already returned pure(1) and the transpile-error branch was the odd one out. Both failure paths now agree with it, and neither success path moved. The output file is not written on either failure in either tree, so the only change is the status.

The new proof assertions aren't decorative

Two-sided mutation, running fjs/djs/proof.f.mjs:

mutant on 91e7ddd5 on 104bfa34
revert the branch to pure(0) diesfileNotFound, parseError (is main)
keep pure(1) but also writeUtf8File(outputFileName, …) before erroring dies — both cases survives, 5/5 pass

The second is the one worth calling out: it returns the right exit code and clobbers state, which is the shape that has slipped through before. The added assertEq(state.root['output.f.js'], undefined) is what catches it, and main's version of the same cases does not — so those two lines are a real strengthening, not padding.

**BREAKING CHANGES:** is the right call

fjs compile is a documented CLI command (README.md:56, fjs/README.md:39), and a shell doing fjs compile in out && next-step previously ran next-step on a parse error and now will not. That is observable to scripts in the way #1520's specifier change was, not merely a representation detail like #1524 — so it earns the prefix, and it has it. No in-repo consumer depends on the old code: nothing in .github/, package.json or any script invokes fjs compile (the only script-shaped occurrence is a proposal inside fjs/ci/todo/667-ci-self-test-script.md).

One nit on wording: the entry says "cannot be read or fails to parse", but the branch also covers circular dependency (transpiler/module.f.mjs:80), which now exits 1 as well. "fails to parse" arguably covers it; worth a word if you want the entry exhaustive.

Bookkeeping and gates

Deleting fjs/djs/todo/compile-error-exit-code.md and splitting the cosmetic half into parse-error-location-format.md is the right disposition — the undefined:undefined:undefined prefix does still reproduce at this head (fjs compile nope.f.mjs out.mjsundefined:undefined:undefined - error: file not found), and the new todo's ## <slug>. <Title> heading matches its siblings in that directory. Nothing referenced the deleted file.

  • tsc --noEmit → 0; npm run prepack from a clean tree → 0.
  • npm test: 2725 pass / 0 fail, identical to main — expected, since the PR strengthens existing cases rather than adding any.
  • Broken-link sets identical to main's (diff empty).

@sergey-shandar
sergey-shandar marked this pull request as ready for review August 15, 2026 03:58
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@sergey-shandar
sergey-shandar added this pull request to the merge queue Aug 15, 2026
Merged via the queue into main with commit 46e04ba Aug 15, 2026
19 checks passed
@sergey-shandar
sergey-shandar deleted the claude/compile-error-exit-code-ifa6gb branch August 15, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants