Skip to content

feat: switch tree-sitter from native N-API to web-tree-sitter (WASM)#76

Merged
HrushiBorhade merged 2 commits into
mainfrom
phase-2/web-tree-sitter
Mar 24, 2026
Merged

feat: switch tree-sitter from native N-API to web-tree-sitter (WASM)#76
HrushiBorhade merged 2 commits into
mainfrom
phase-2/web-tree-sitter

Conversation

@HrushiBorhade

@HrushiBorhade HrushiBorhade commented Mar 24, 2026

Copy link
Copy Markdown
Owner

Summary

Replaces native tree-sitter (N-API .node binaries) with web-tree-sitter (WASM) so AST parsing works in any environment — Node.js, Linux containers, Trigger.dev workers.

Changes

  • packages/core/src/chunker/ast.ts — complete rewrite: dynamic import('web-tree-sitter'), Parser.init(), Language.load() from WASM files
  • packages/core/package.json — removed 7 native deps (tree-sitter, tree-sitter-typescript, etc.), added web-tree-sitter@0.22.6 + tree-sitter-wasms@0.1.13
  • package.json — removed native tree-sitter from pnpm.onlyBuiltDependencies
  • chunkAST is now async (was sync) — callers already async, no breaking change

Why web-tree-sitter@0.22.6?

Pre-built WASM grammar files from tree-sitter-wasms@0.1.13 are compiled against tree-sitter 0.22.x. Using web-tree-sitter@0.26.7 (latest) causes getDylinkMetadata errors due to WASM format incompatibility. v0.22.6 is stable and widely used.

Test plan

  • All 32 chunker tests pass (TypeScript, TSX, JS, Python, Rust, Go, CSS)
  • 132/132 non-embedder tests pass
  • pnpm typecheck passes
  • Verified WASM parsing works in Node.js 22

Closes #69

Summary by CodeRabbit

  • Refactor

    • Switched internal code parsing to a WebAssembly-backed, async parser for more consistent parsing behavior and safer resource cleanup.
  • Chores

    • Reduced which native parsing packages are built during install and updated parsing-related dependencies.

@HrushiBorhade HrushiBorhade added phase-2 Phase 2: Indexing Pipeline indexing Indexing pipeline tasks labels Mar 24, 2026
@vercel

vercel Bot commented Mar 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
code-indexer-web Ready Ready Preview, Comment Mar 24, 2026 2:08pm

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Mar 24, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ec477d34-e863-4e0f-8a61-1e5e2808b317

📥 Commits

Reviewing files that changed from the base of the PR and between e985ae9 and dfeb294.

📒 Files selected for processing (1)
  • packages/core/src/chunker/ast.ts

📝 Walkthrough

Walkthrough

This PR switches Tree-sitter from native N-API to web-tree-sitter (WASM): root pnpm build allowlist updated, core package deps replaced with web-tree-sitter and tree-sitter-wasms, and the AST chunker rewritten to async WASM loading with per-language caching and explicit parser/tree cleanup.

Changes

Cohort / File(s) Summary
Root package config
package.json
Removed tree-sitter and all tree-sitter-* language packages from pnpm.onlyBuiltDependencies, leaving better-sqlite3 and esbuild.
Core package deps
packages/core/package.json
Replaced native tree-sitter + language packages with web-tree-sitter and tree-sitter-wasms entries.
AST chunker
packages/core/src/chunker/ast.ts
Converted chunkAST to async (Promise<Chunk[]>), added lazy ParserClass.init() via ensureInit(), per-language WASM loading/caching (getLanguage() using tree-sitter-wasms), switched to web-tree-sitter Parser API, and added explicit parser.delete() / tree.delete() cleanup and error/fallback handling.

Sequence Diagram(s)

sequenceDiagram
  participant Caller as Chunker
  participant Runtime as web-tree-sitter
  participant Loader as tree-sitter-wasms
  participant Parser as ParserInstance

  Caller->>Runtime: ensureInit() (ParserClass.init())
  Runtime-->>Caller: ready
  Caller->>Loader: resolve/load .wasm for language
  Loader-->>Caller: wasm path / buffer
  Caller->>Runtime: Language.load(wasm)
  Runtime-->>Caller: Language instance
  Caller->>Parser: instantiate ParserClass().setLanguage(Language)
  Caller->>Parser: parse(source)
  Parser-->>Caller: SyntaxTree
  Caller->>Caller: extract chunks
  Caller->>Parser: parser.delete()
  Caller->>Runtime: tree.delete()
  Caller-->>Caller: return chunks
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nudged the bindings, gave WASM wings,

Grammars now wander on universal springs.
Async and tidy, caches snug in rows,
Parsers prune branches where syntax grows.
Hooray — the forest of code freely flows.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The PR implements the core WASM migration objectives: replaced native tree-sitter with web-tree-sitter, updated package dependencies, converted chunkAST to async, and all tests pass. However, lib/languages.ts grammar loading updates are not documented in the changeset summary. Clarify whether lib/languages.ts was modified to load WASM grammars as required by issue #69, or if those changes are handled elsewhere in the codebase.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and accurately describes the main change: switching tree-sitter from native N-API binaries to web-tree-sitter (WASM) for cross-platform AST parsing.
Out of Scope Changes check ✅ Passed All changes are directly related to the WASM migration and issue #69: dependency updates, chunkAST async conversion, memory leak fixes, and logger consistency improvements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch phase-2/web-tree-sitter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/core/src/chunker/ast.ts (2)

96-99: ⚠️ Potential issue | 🟡 Minor

Use the Pino logger instead of console.warn in this module.

console.warn is used here while the file already uses createLogger. Keep warning output consistent with structured logging.

🧹 Proposed fix
-    console.warn(
-      `[chunker] No AST node types configured for "${language}", using fallback: ${filePath}`,
-    );
+    log.warn(
+      `[chunker] No AST node types configured for "${language}", using fallback: ${filePath}`,
+    );
@@
-    console.warn(
-      `[chunker] AST parsed but found 0 matching nodes for "${language}" in: ${filePath}`,
-    );
+    log.warn(
+      `[chunker] AST parsed but found 0 matching nodes for "${language}" in: ${filePath}`,
+    );

As per coding guidelines **/*.{ts,tsx}: Use Pino for logging throughout the application.

Also applies to: 137-139

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/chunker/ast.ts` around lines 96 - 99, Replace the
console.warn calls with the module's Pino logger so warnings are emitted as
structured logs: locate the console.warn usages in this file (the warning that
starts with `[chunker] No AST node types configured for "${language}"` and the
similar calls around the 137-139 range) and change them to use the existing
logger instance created via createLogger (e.g., logger.warn) and pass the same
message and context (language and filePath) as structured fields where possible;
ensure you use logger.warn(...) consistently throughout the module so all
warnings come through Pino.

102-113: ⚠️ Potential issue | 🟠 Major

Ensure parser cleanup runs on parse failures.

Parser allocated at line 102 with new ParserClass() is not freed when parse fails and early returns at line 112, skipping cleanup at lines 133-134. With native tree-sitter N-API bindings, this causes a memory leak.

♻️ Proposed fix (move cleanup to finally)
-  let tree;
-  try {
-    tree = parser.parse(source);
-  } catch (err: unknown) {
-    log.error(
-      `tree-sitter parse failed for ${filePath}: ${err instanceof Error ? err.message : err}`,
-    );
-    return [fallbackChunk(source, filePath, language)];
-  }
-
-  const chunks: Chunk[] = [];
-
-  for (const node of tree.rootNode.children) {
-    if (!nodeTypes.has(node.type)) continue;
-
-    const content = node.text.trim();
-    if (content.length === 0) continue;
-
-    chunks.push({
-      content,
-      filePath,
-      lineStart: node.startPosition.row + 1,
-      lineEnd: node.endPosition.row + 1,
-      language,
-      type: 'ast',
-    });
-  }
-
-  parser.delete();
-  tree.delete();
+  let tree;
+  const chunks: Chunk[] = [];
+  try {
+    tree = parser.parse(source);
+
+    for (const node of tree.rootNode.children) {
+      if (!nodeTypes.has(node.type)) continue;
+
+      const content = node.text.trim();
+      if (content.length === 0) continue;
+
+      chunks.push({
+        content,
+        filePath,
+        lineStart: node.startPosition.row + 1,
+        lineEnd: node.endPosition.row + 1,
+        language,
+        type: 'ast',
+      });
+    }
+  } catch (err: unknown) {
+    log.error(
+      `tree-sitter parse failed for ${filePath}: ${err instanceof Error ? err.message : err}`,
+    );
+    return [fallbackChunk(source, filePath, language)];
+  } finally {
+    tree?.delete();
+    parser.delete();
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/chunker/ast.ts` around lines 102 - 113, Parser instance
created with ParserClass and assigned to parser must be cleaned up even on parse
failure; move the parser cleanup (the code currently at lines 133-134) into a
finally block so that after calling parser.parse(source) (and on the early
return that returns fallbackChunk(source, filePath, language)) the parser is
always released. Specifically, ensure the new try/catch/finally around
parser.parse calls log.error on failure (keeping the existing message using
filePath) and then in finally call the parser cleanup method used in this module
(e.g., parser.delete(), parser.free(), or parser.close()) so no native
tree-sitter resources leak.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/chunker/ast.ts`:
- Around line 17-23: ensureInit currently always imports web-tree-sitter (WASM);
change it to select the parser based on environment by first trying to load the
native Node N-API binding (e.g., require/import 'tree-sitter' or the native
package your project uses) for CLI/node environments and only fall back to
import('web-tree-sitter') for Lambda/container/trigger environments; update the
logic inside ensureInit (referencing ParserClass, initialized and the
ParserClass.init() call) to attempt native load, call ParserClass.init() if
needed, and set initialized=true, with a clear fallback to the WASM import when
native loading fails or an environment variable indicates a container/lambda
runtime.

---

Outside diff comments:
In `@packages/core/src/chunker/ast.ts`:
- Around line 96-99: Replace the console.warn calls with the module's Pino
logger so warnings are emitted as structured logs: locate the console.warn
usages in this file (the warning that starts with `[chunker] No AST node types
configured for "${language}"` and the similar calls around the 137-139 range)
and change them to use the existing logger instance created via createLogger
(e.g., logger.warn) and pass the same message and context (language and
filePath) as structured fields where possible; ensure you use logger.warn(...)
consistently throughout the module so all warnings come through Pino.
- Around line 102-113: Parser instance created with ParserClass and assigned to
parser must be cleaned up even on parse failure; move the parser cleanup (the
code currently at lines 133-134) into a finally block so that after calling
parser.parse(source) (and on the early return that returns fallbackChunk(source,
filePath, language)) the parser is always released. Specifically, ensure the new
try/catch/finally around parser.parse calls log.error on failure (keeping the
existing message using filePath) and then in finally call the parser cleanup
method used in this module (e.g., parser.delete(), parser.free(), or
parser.close()) so no native tree-sitter resources leak.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b4592548-5e31-4bf2-bc72-c997352fab23

📥 Commits

Reviewing files that changed from the base of the PR and between d172d99 and 8be27de.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • package.json
  • packages/core/package.json
  • packages/core/src/chunker/ast.ts

Comment on lines +17 to +23
async function ensureInit(): Promise<void> {
if (initialized) return;
const mod = await import('web-tree-sitter');
ParserClass = mod.default;
await ParserClass.init();
initialized = true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add environment-aware parser selection instead of always forcing WASM.

Current flow unconditionally initializes WASM parser for all environments. This drops the CLI native-N-API path required by project policy and can regress CLI performance/startup characteristics.

As per coding guidelines **/chunker/**/*.{ts,tsx}: Use tree-sitter with native N-API bindings for AST chunking in CLI, but switch to web-tree-sitter (WASM) for Trigger.dev Lambda/container environments.

Also applies to: 85-88

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/chunker/ast.ts` around lines 17 - 23, ensureInit currently
always imports web-tree-sitter (WASM); change it to select the parser based on
environment by first trying to load the native Node N-API binding (e.g.,
require/import 'tree-sitter' or the native package your project uses) for
CLI/node environments and only fall back to import('web-tree-sitter') for
Lambda/container/trigger environments; update the logic inside ensureInit
(referencing ParserClass, initialized and the ParserClass.init() call) to
attempt native load, call ParserClass.init() if needed, and set
initialized=true, with a clear fallback to the WASM import when native loading
fails or an environment variable indicates a container/lambda runtime.

- Replaced native tree-sitter + 6 grammar packages with web-tree-sitter + tree-sitter-wasms
- WASM grammars loaded at runtime via Language.load() (TypeScript, TSX, JS, Python, Rust, Go, CSS)
- Parser initialized async via Parser.init() — chunkAST is now async
- Removed native N-API dependencies from pnpm.onlyBuiltDependencies
- Compatible versions: web-tree-sitter@0.22.6 + tree-sitter-wasms@0.1.13
- All 32 chunker tests pass, 132/132 non-embedder tests pass
- WASM runs anywhere: Node.js, Linux containers, Trigger.dev workers

Closes #69
- Wrapped parser/tree in try/finally to ensure cleanup on all code paths
- Replaced console.warn with log.warn for consistent structured logging
@HrushiBorhade HrushiBorhade merged commit 2e26e58 into main Mar 24, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

indexing Indexing pipeline tasks phase-2 Phase 2: Indexing Pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: switch tree-sitter from native N-API to web-tree-sitter (WASM)

1 participant