Skip to content

Commit 7d58294

Browse files
jahoomaclaude
andcommitted
Move --smoke-tree-sitter handler to main() to bypass commander
Last attempt put the handler at top-level in the pre-init module behind a top-level await, on the theory that ESM would pause subsequent module evaluation until it resolved. That worked on macOS locally but not on Windows in CI: smoke-binary: spawning ./codebuff.exe for 10s… error: tree-sitter smoke failed with exit code 1 error: unknown option '--smoke-tree-sitter' So commander.parse() ran before our handler exited, which means top-level await is not actually blocking parent-module evaluation in the bun --compile output on Windows (or it's getting transformed away by `--production` minification). Move the handler to the top of main() in cli/src/index.tsx, before parseArgs(). At that point commander hasn't run yet, so we can short- circuit cleanly. The pre-init module's only job is now to publish the embedded wasm bytes (globalThis) and path (env var); the handler reads those out of the same channels the production runtime uses. Verified locally: ./codebuff --smoke-tree-sitter prints "tree-sitter smoke ok (wasmBinary, 205488 bytes)" and exits 0; full smoke-binary.ts run passes both the tree-sitter pre-check and the boot-screen window. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 01fefda commit 7d58294

2 files changed

Lines changed: 46 additions & 47 deletions

File tree

cli/src/index.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,44 @@ function parseArgs(): ParsedArgs {
186186
}
187187

188188
async function main(): Promise<void> {
189+
// CI gate: `<binary> --smoke-tree-sitter` proves the embedded wasm boots
190+
// through Parser.init end-to-end. Has to live BEFORE commander.parse() —
191+
// an earlier attempt put this in a pre-init module with top-level await,
192+
// and on Windows that didn't actually pause module evaluation (commander
193+
// still ran first and rejected the unknown flag).
194+
if (process.argv.includes('--smoke-tree-sitter')) {
195+
const wasmBinary = (
196+
globalThis as { __CODEBUFF_TREE_SITTER_WASM_BINARY__?: Uint8Array }
197+
).__CODEBUFF_TREE_SITTER_WASM_BINARY__
198+
const wasmPath = process.env.CODEBUFF_TREE_SITTER_WASM_PATH
199+
try {
200+
const { Parser } = await import('web-tree-sitter')
201+
if (wasmBinary) {
202+
await Parser.init({ wasmBinary })
203+
// Marker grepped by cli/scripts/smoke-binary.ts — keep this exact text.
204+
console.log(
205+
`tree-sitter smoke ok (wasmBinary, ${wasmBinary.byteLength} bytes)`,
206+
)
207+
} else if (wasmPath) {
208+
await Parser.init({
209+
locateFile: (name: string) =>
210+
name === 'tree-sitter.wasm' ? wasmPath : name,
211+
})
212+
console.log(`tree-sitter smoke ok (locateFile, path=${wasmPath})`)
213+
} else {
214+
console.error(
215+
'tree-sitter smoke FAIL: pre-init published neither globalThis bytes nor an env path. ' +
216+
'The `with { type: \'file\' }` import returned falsy.',
217+
)
218+
process.exit(1)
219+
}
220+
process.exit(0)
221+
} catch (err) {
222+
console.error('tree-sitter smoke FAIL:', err)
223+
process.exit(1)
224+
}
225+
}
226+
189227
// Run OSC theme detection BEFORE anything else.
190228
// This MUST happen before OpenTUI starts because OSC responses come through stdin,
191229
// and OpenTUI also listens to stdin. Running detection here ensures stdin is clean.

cli/src/pre-init/tree-sitter-wasm.ts

Lines changed: 8 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -50,50 +50,11 @@ if (treeSitterWasmPath) {
5050
}
5151
}
5252

53-
// Deterministic CI gate: `<binary> --smoke-tree-sitter` proves the embed
54-
// shipped end-to-end. Lives here, in the very first import, on purpose:
55-
//
56-
// - We're testing whether the *embed* works. Going through commander +
57-
// initTreeSitterForNode would pass via the path-resolution fallback
58-
// when the embed is empty (e.g. dev mode), giving false positives that
59-
// mask a broken production build.
60-
// - Failing here, before any other module loads, gives a sharp signal:
61-
// either the wasm reached the runtime or it didn't.
62-
//
63-
// Top-level await (not a fire-and-forget IIFE) because subsequent module
64-
// evaluation has to *wait* — otherwise `commander.parse()` runs first and
65-
// fails on the unknown flag before our handler can exit cleanly.
66-
if (process.argv.includes('--smoke-tree-sitter')) {
67-
try {
68-
const { Parser } = await import('web-tree-sitter')
69-
// Prefer the wasmBinary path (no filesystem step). Fall back to
70-
// letting Parser.init resolve the path via its locateFile callback,
71-
// which init-node.ts wires up to accept bunfs paths even when
72-
// fs.existsSync says otherwise.
73-
if (embeddedWasm) {
74-
await Parser.init({ wasmBinary: embeddedWasm })
75-
console.log(
76-
`tree-sitter smoke ok (wasmBinary, ${embeddedWasm.byteLength} bytes)`,
77-
)
78-
} else if (treeSitterWasmPath) {
79-
await Parser.init({
80-
locateFile: (name: string) =>
81-
name === 'tree-sitter.wasm' ? treeSitterWasmPath : name,
82-
})
83-
console.log(
84-
`tree-sitter smoke ok (locateFile, path=${treeSitterWasmPath})`,
85-
)
86-
} else {
87-
console.error(
88-
'tree-sitter smoke FAIL: no embedded wasm path. The `with { type: ' +
89-
"'file' }` import returned a falsy value, which means the bundler " +
90-
'did not embed the asset.',
91-
)
92-
process.exit(1)
93-
}
94-
process.exit(0)
95-
} catch (err) {
96-
console.error('tree-sitter smoke FAIL:', err)
97-
process.exit(1)
98-
}
99-
}
53+
// `--smoke-tree-sitter` is the deterministic CI gate. We can't handle it
54+
// here with top-level await — bun --compile on Windows didn't preserve the
55+
// blocking semantics in our last attempt, so commander still ran and
56+
// rejected the unknown flag. Instead, the handler lives at the top of
57+
// main() in cli/src/index.tsx (before parseArgs), where we can synchronously
58+
// short-circuit before commander parses argv. This module's job is just to
59+
// publish the wasm bytes / path on globalThis + process.env so that the
60+
// handler (and the SDK's eager Parser.init) can find them.

0 commit comments

Comments
 (0)