Skip to content

Commit d79d758

Browse files
committed
Embed tree-sitter.wasm in CLI binary to fix bun --compile resolution
web-tree-sitter@0.25.10 split its package exports into separate `import` and `require` conditions. In bun --compile binaries, require.resolve from init-node.ts now returns the build-time absolute path of tree-sitter.cjs, which doesn't exist on user machines, causing freebuff to crash on startup with "Cannot find module .../tree-sitter.cjs". Fix by embedding tree-sitter.wasm into the binary via Bun's \`import ... with { type: 'file' }\` and stashing the resulting bunfs path in process.env so all copies of init-node.ts (including the SDK pre-built bundle's inlined copy) can pick it up.
1 parent 35569fc commit d79d758

6 files changed

Lines changed: 80 additions & 18 deletions

File tree

cli/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
"test:tmux-poc": "bun run src/__tests__/tmux-poc.ts",
2424
"typecheck": "tsc --noEmit -p ."
2525
},
26-
"sideEffects": false,
26+
"sideEffects": [
27+
"./src/pre-init/*.ts"
28+
],
2729
"engines": {
2830
"bun": "1.3.11"
2931
},

cli/src/index.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
#!/usr/bin/env bun
22

3+
// Embed tree-sitter.wasm into the bun-compile binary at a bunfs path the runtime
4+
// can find. Without this, web-tree-sitter resolves the wasm via require.resolve,
5+
// which (since 0.25.10's split exports map) returns the build-time absolute path
6+
// of tree-sitter.cjs and fails on user machines. Must run before the SDK / code-map
7+
// import chain triggers Parser.init.
8+
import './pre-init/tree-sitter-wasm'
9+
310
import fs from 'fs'
411
import { createRequire } from 'module'
512
import os from 'os'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Embed tree-sitter.wasm into the bun-compile binary at a bunfs path the runtime
2+
// can find. Must be the very first import in `index.tsx`: subsequent imports
3+
// (the SDK / code-map) eagerly construct a tree-sitter parser singleton, and its
4+
// `locateFile` callback reads `CODEBUFF_TREE_SITTER_WASM_PATH` from `process.env`.
5+
//
6+
// Without this, web-tree-sitter@0.25.10 falls back to `require.resolve` which —
7+
// per the package's split `import`/`require` exports map — returns the build-time
8+
// absolute path of `tree-sitter.cjs` and fails on user machines.
9+
10+
import treeSitterWasmPath from 'web-tree-sitter/tree-sitter.wasm' with {
11+
type: 'file',
12+
}
13+
14+
if (treeSitterWasmPath) {
15+
process.env.CODEBUFF_TREE_SITTER_WASM_PATH = treeSitterWasmPath
16+
}

packages/code-map/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import './types'
22
export * from './parse'
33
export * from './languages'
4+
export { setTreeSitterWasmPath } from './init-node'

packages/code-map/src/init-node.ts

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,53 @@ import * as path from 'path'
33

44
import { Parser } from 'web-tree-sitter'
55

6+
const TREE_SITTER_WASM_ENV_VAR = 'CODEBUFF_TREE_SITTER_WASM_PATH'
7+
8+
/**
9+
* Override the path to `tree-sitter.wasm` used during {@link initTreeSitterForNode}.
10+
*
11+
* Needed for `bun build --compile` binaries: the embedded `tree-sitter.js` reports a
12+
* `scriptDir` like `/$bunfs/root/`, but the runtime wasm isn't auto-embedded next to
13+
* it, and `require.resolve('web-tree-sitter')` resolves to the build-time absolute
14+
* path of `tree-sitter.cjs` (per the package's `require` exports condition added in
15+
* 0.25.10), which doesn't exist on the end user's machine. Callers building binaries
16+
* should embed the wasm via Bun's `import ... with { type: 'file' }` and pass the
17+
* resulting path here before any tree-sitter use.
18+
*
19+
* Stored on `process.env` so it reaches every copy of this module — the SDK
20+
* pre-built bundle inlines its own copy of `init-node.ts`, so a module-level
21+
* variable here wouldn't be visible to the singleton initialized via the SDK.
22+
*/
23+
export function setTreeSitterWasmPath(wasmPath: string): void {
24+
process.env[TREE_SITTER_WASM_ENV_VAR] = wasmPath
25+
}
26+
27+
function resolveTreeSitterWasm(scriptDir: string): string {
28+
const override = process.env[TREE_SITTER_WASM_ENV_VAR]
29+
if (override && fs.existsSync(override)) {
30+
return override
31+
}
32+
33+
const fallback = path.join(scriptDir, 'tree-sitter.wasm')
34+
if (fs.existsSync(fallback)) {
35+
return fallback
36+
}
37+
38+
try {
39+
const pkgDir = path.dirname(require.resolve('web-tree-sitter'))
40+
const wasm = path.join(pkgDir, 'tree-sitter.wasm')
41+
if (fs.existsSync(wasm)) {
42+
return wasm
43+
}
44+
} catch {
45+
// Package not resolvable; fall through.
46+
}
47+
48+
throw new Error(
49+
`Internal error: tree-sitter.wasm not found (looked at scriptDir=${scriptDir} and via web-tree-sitter package). Set ${TREE_SITTER_WASM_ENV_VAR} or ensure the file is included in your deployment bundle.`,
50+
)
51+
}
52+
653
/**
754
* Initialize web-tree-sitter for Node.js environments with proper WASM file location
855
*/
@@ -11,22 +58,7 @@ export async function initTreeSitterForNode(): Promise<void> {
1158
await Parser.init({
1259
locateFile: (name: string, scriptDir: string) => {
1360
if (name === 'tree-sitter.wasm') {
14-
// Fallback to script directory
15-
const fallback = path.join(scriptDir, name)
16-
if (fs.existsSync(fallback)) {
17-
return fallback
18-
}
19-
20-
// Find the installed package root
21-
const pkgDir = path.dirname(require.resolve('web-tree-sitter'))
22-
// The wasm ships at: node_modules/web-tree-sitter/tree-sitter.wasm
23-
const wasm = path.join(pkgDir, 'tree-sitter.wasm')
24-
if (fs.existsSync(wasm)) {
25-
return wasm
26-
}
27-
throw new Error(
28-
`Internal error: web-tree-sitter/tree-sitter.wasm not found at ${wasm}. Ensure the file is included in your deployment bundle.`,
29-
)
61+
return resolveTreeSitterWasm(scriptDir)
3062
}
3163

3264
// For other files, use default behavior

sdk/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ export {
8282
export type { CodebuffFileSystem } from '@codebuff/common/types/filesystem'
8383

8484
// Tree-sitter / code-map exports
85-
export { getFileTokenScores, setWasmDir } from '@codebuff/code-map'
85+
export {
86+
getFileTokenScores,
87+
setWasmDir,
88+
setTreeSitterWasmPath,
89+
} from '@codebuff/code-map'
8690
export type { FileTokenData, TokenCallerMap } from '@codebuff/code-map'
8791

8892
export { runTerminalCommand } from './tools/run-terminal-command'

0 commit comments

Comments
 (0)