web-tree-sitter: bundlers fail to resolve `fs/promises` and `module` when targeting the browser · Issue #5545 · tree-sitter/tree-sitter · GitHub
Skip to content

web-tree-sitter: bundlers fail to resolve fs/promises and module when targeting the browser #5545

Description

@reteps

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)

  1. Add a "browser" condition in package.json#exports pointing at a Node-stripped entry.
  2. Add a top-level "browser" field mapping fs/promises and module to false.
  3. 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)

Metadata

Metadata

Assignees

No one assigned

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions