Problem
Bundling web-tree-sitter@0.26.x for the browser fails because two await import() calls in environment-guarded branches are statically discoverable by bundlers:
await import("fs/promises") at web-tree-sitter.js:1487 (inside Language.load — guarded by globalThis.process?.versions.node)
await import("module") at web-tree-sitter.js:1527 (inside the emscripten module init — guarded by ENVIRONMENT_IS_NODE)
Even though those branches never execute in a browser, the bundler tries to resolve the specifiers at build time and errors out.
Minimal repro
package.json:
{
"name": "wts-bundle-repro",
"private": true,
"type": "module",
"scripts": {
"repro": "esbuild --bundle --platform=browser --format=esm --outfile=out.js entry.js"
},
"dependencies": {
"esbuild": "^0.27.0",
"web-tree-sitter": "^0.26.6"
}
}
entry.js:
import 'web-tree-sitter';
Run npm install && npm run repro:
✘ [ERROR] Could not resolve "fs/promises"
node_modules/web-tree-sitter/web-tree-sitter.js:1487:31:
1487 │ const fs2 = await import("fs/promises");
╵ ~~~~~~~~~~~~~
The package "fs/promises" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "--platform=node" to do that, which will remove this error.
✘ [ERROR] Could not resolve "module"
node_modules/web-tree-sitter/web-tree-sitter.js:1527:43:
1527 │ const { createRequire } = await import("module");
╵ ~~~~~~~~
The package "module" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "--platform=node" to do that, which will remove this error.
2 errors
Tested with web-tree-sitter@0.26.8 and esbuild@0.27.7. Other bundlers (vite, webpack) likely fail similarly — see historical reports #466, #681, #1121.
Workaround
Mark them external:
esbuild --bundle --platform=browser --format=esm \
--external:fs/promises --external:module --outfile=out.js entry.js
This succeeds (bundle ~150KB) and is safe at runtime: the imports stay as bare specifiers in the output but are guarded by Node-only checks that never fire in a browser.
Suggested fixes (any of)
- Add a
"browser" condition in package.json#exports pointing at a Node-stripped entry.
- Add a top-level
"browser" field mapping fs/promises and module to false.
- Move the Node-only loader into a separate file imported through a specifier that bundlers can't statically resolve (e.g.
await import(/* @vite-ignore */ nodeOnlyPath) with nodeOnlyPath a runtime-built string).
Environment
web-tree-sitter 0.26.8
esbuild 0.27.7
- macOS 24.6.0 (also reproduces on Linux)
Problem
Bundling
web-tree-sitter@0.26.xfor the browser fails because twoawait import()calls in environment-guarded branches are statically discoverable by bundlers:await import("fs/promises")atweb-tree-sitter.js:1487(insideLanguage.load— guarded byglobalThis.process?.versions.node)await import("module")atweb-tree-sitter.js:1527(inside the emscripten module init — guarded byENVIRONMENT_IS_NODE)Even though those branches never execute in a browser, the bundler tries to resolve the specifiers at build time and errors out.
Minimal repro
package.json:{ "name": "wts-bundle-repro", "private": true, "type": "module", "scripts": { "repro": "esbuild --bundle --platform=browser --format=esm --outfile=out.js entry.js" }, "dependencies": { "esbuild": "^0.27.0", "web-tree-sitter": "^0.26.6" } }entry.js:Run
npm install && npm run repro:Tested with
web-tree-sitter@0.26.8andesbuild@0.27.7. Other bundlers (vite, webpack) likely fail similarly — see historical reports #466, #681, #1121.Workaround
Mark them external:
This succeeds (bundle ~150KB) and is safe at runtime: the imports stay as bare specifiers in the output but are guarded by Node-only checks that never fire in a browser.
Suggested fixes (any of)
"browser"condition inpackage.json#exportspointing at a Node-stripped entry."browser"field mappingfs/promisesandmoduletofalse.await import(/* @vite-ignore */ nodeOnlyPath)withnodeOnlyPatha runtime-built string).Environment
web-tree-sitter0.26.8esbuild0.27.7