Skip to content

fix(deps): build tree-sitter from source on linux-arm64 / Node ≥22#206

Merged
efenocchi merged 1 commit into
mainfrom
fix/tree-sitter-arm64-native-build
May 26, 2026
Merged

fix(deps): build tree-sitter from source on linux-arm64 / Node ≥22#206
efenocchi merged 1 commit into
mainfrom
fix/tree-sitter-arm64-native-build

Conversation

@khustup2
Copy link
Copy Markdown
Contributor

@khustup2 khustup2 commented May 26, 2026

Problem

A bare npm install hard-fails on linux-arm64 under Node ≥22:

.../include/node/v8config.h:13:2: error: #error "C++20 or later required."

Root cause (verified on this repo's pinned versions):

  • tree-sitter@0.21.x ships no linux-arm64 prebuild → it always compiles from source on arm64.
  • tree-sitter-typescript@0.23.x ships a linux-arm64 prebuild that is actually a mislabeled x86-64 binary (file reports x86-64), so it can't be dlopen'd on arm64 → also needs a source build.
  • Node ≥22's V8 headers require C++20, but tree-sitter@0.21's binding.gyp doesn't request it → the source compile fails.

Bumping out of this isn't possible: the latest tree-sitter-typescript (0.23.2) peer-pins tree-sitter: ^0.21.0, so core can't move to 0.22+ without orphaning the grammar.

Fix

  1. Move tree-sitter + tree-sitter-typescript to optionalDependencies — the expected arm64 build failure is then tolerated instead of aborting the whole install, so a postinstall step can heal it.
  2. scripts/ensure-tree-sitter.mjs (wired as postinstall): if the bindings fail to load, it re-fetches any dropped package, removes the absent/wrong-arch prebuilds, and recompiles from source with CXXFLAGS=-std=c++20. node-gyp-build loads build/Release ahead of prebuilds/, so the freshly compiled aarch64 binary always wins.
    • No-op where prebuilds work (x64 / darwin / CI) — sub-second, touches nothing.
    • Non-fatal: warns and exits 0 if no C/C++ toolchain is present, so it never breaks an install.
    • No deprecated config / no network: forces the source build purely by removing prebuilds (node-gyp-build builds locally when none match).
  3. npm run rebuild:native — manual escape hatch.

Testing (linux-arm64, Node 24)

  • rm -rf node_modules && npm install → completes (no longer aborts); heal logs OK — bindings compiled from source and loadable.
  • Both .node files verified ARM aarch64; node-gyp-build resolves to build/Release, not the bad prebuild; real TS parse works.
  • Re-running on a healthy tree is a 0.04s no-op.
  • No Unknown env config "build-from-source" warning.

Note (out of scope)

The committed package-lock.json already has pre-existing drift unrelated to this change — npm ci fails on main today with Missing: @emnapi/runtime / Missing: pg under both npm 10 and npm 11 (the lockfile was written by an older npm). This PR keeps the lockfile delta minimal (only the tree-sitter reclassification) and does not attempt to fix that drift.

Summary by CodeRabbit

Release Notes

  • Chores
    • Improved installation reliability with enhanced native dependency handling and automated verification.
    • Optimized package dependency structure for better maintainability.

Review Change Stack

tree-sitter@0.21.x ships no linux-arm64 prebuild and
tree-sitter-typescript@0.23.x ships a mislabeled (x86-64) one, so on
arm64 both must be compiled from source. Under Node >=22 that compile
requires C++20, which tree-sitter's binding.gyp doesn't request, so a
bare `npm install` hard-fails on arm64.

- Move tree-sitter + tree-sitter-typescript to optionalDependencies so
  the expected arm64 build failure no longer aborts the whole install.
- Add scripts/ensure-tree-sitter.mjs, a postinstall heal that compiles
  the bindings from source with CXXFLAGS=-std=c++20 when the shipped
  binaries fail to load. It removes the absent/wrong-arch prebuilds and
  rebuilds via node-gyp-build (build/Release is preferred at load time).
  No-op where prebuilds work (x64/darwin/CI); non-fatal with no toolchain.
- Add `rebuild:native` script as a manual escape hatch.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3136e4cb-a6b9-48ee-b99a-a010774ec106

📥 Commits

Reviewing files that changed from the base of the PR and between 66141c3 and cd8642e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json
  • scripts/ensure-tree-sitter.mjs

📝 Walkthrough

Walkthrough

This PR introduces an automatic native bindings recovery system for tree-sitter. A new CLI script verifies that tree-sitter and tree-sitter-typescript native modules load correctly, and if they fail to load, it automatically rebuilds them from source with proper C++ toolchain configuration. The script is wired into the npm install and build pipeline via package.json hooks.

Changes

Native bindings verification and recovery

Layer / File(s) Summary
Native bindings verification logic
scripts/ensure-tree-sitter.mjs
Defines a bindingsLoad() function that attempts to require tree-sitter packages, instantiate a parser, load the TypeScript language, and parse a snippet to confirm bindings are functional. The CLI entrypoint calls bindingsLoad() with a recursion guard to prevent loops.
Build environment setup and native rebuild
scripts/ensure-tree-sitter.mjs
On binding load failure, initializes build environment by reading versions from package.json, setting ENSURE_TS_RUNNING to prevent recursion, conditionally adding -std=c++20 to CXXFLAGS on non-Windows platforms, running npm install with --no-save --ignore-scripts, removing stale prebuilds and build artifacts, and invoking npm rebuild to force source compilation.
Final verification and package.json integration
scripts/ensure-tree-sitter.mjs, package.json
Re-attempts binding load after rebuild; on success, logs completion and exits. On continued failure, logs a non-fatal warning instructing users to install a C/C++ toolchain and direct them to re-run the rebuild script, exiting gracefully. package.json adds rebuild:native script and postinstall hook to run the ensure script, and moves yargs-parser and zod from optionalDependencies to regular dependencies (leaving only tree-sitter packages as optional).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A script hops in to check the native way,
And if the bindings don't work today,
It plants fresh code from the source so deep,
With C++ flags and a graceful leap!
No fear of missing tools, just a friendly tip—
The install will hum, not skip or trip. 🌳

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description provides comprehensive context (problem root causes, fix rationale, implementation details, testing results) but omits the required version bump field from the template. Complete the Version Bump section indicating whether a patch/minor/major version bump is needed and update package.json accordingly, or explicitly state 'no release needed.'
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the primary fix: enabling tree-sitter native builds on linux-arm64 with Node ≥22 by reclassifying dependencies and adding a postinstall healing script.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 fix/tree-sitter-arm64-native-build

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@github-actions
Copy link
Copy Markdown
Contributor

Coverage Report

No src/*.ts files changed in this PR.

Generated for commit 8c80b0a.

@khustup2 khustup2 requested a review from efenocchi May 26, 2026 01:22
@efenocchi efenocchi merged commit 1732489 into main May 26, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants