Tree-sitter never loaded in a packaged install (#47) - #48
Merged
Conversation
`registry.ts` reached the analyzer through an indirected dynamic import:
const modulePath = './treeSitterAnalyzer.js';
const mod = await import(modulePath).catch(() => null);
The indirection existed to stop static resolution, with a comment saying the
module is "optional — it only exists when tree-sitter grammars are installed".
src/parsing/treeSitterAnalyzer.ts is tracked in git and always present, so that
premise was stale. What the indirection still did was stop esbuild bundling it,
leaving a runtime specifier in dist/extension.js pointing at a file `dist/` does
not contain — it holds extension.js and its sourcemap, nothing else.
Every packaged install therefore threw ERR_MODULE_NOT_FOUND there, and
`.catch(() => null)` sat INSIDE the try block and absorbed exactly the error the
outer handler existed to report. No warning, ever. The extension shipped 28 MB
of .wasm grammars it could not load and parsed everything with the regex
fallback.
Confirmed three ways rather than inferred:
static dist/ and the VSIX contain no treeSitterAnalyzer.js
runtime reproducing the call site from dist/ gives ERR_MODULE_NOT_FOUND
empirical the graph 0.122.4 actually cached matches the regex analyzer's
count over src/ (6,541) and not tree-sitter's (5,836)
Consequences worth recording: the perDeclarator mapping that fixed 221 invisible
exported constants, and the generator mapping in #45, never ran in production.
Both had regex counterparts that did the real work — luck, not design.
The specifier is now literal so esbuild bundles the module; grammar loading
stays lazy because the expensive part is inside createTreeSitterAnalyzer, not
the import. The swallow is gone, and the fallback now names what degrades.
The new test reads dist/extension.js, because every existing test in this
directory calls createTreeSitterAnalyzer directly and so passed throughout —
they verify a path no install reached. Mutation-checked: restoring the old load
path and rebuilding fails two of the three.
Closes #47
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
✅ All checks passed
Posted by SideCarAI-Bot |
This was referenced Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #47.
The bug
The indirection existed to stop static resolution — its comment says the module
is "optional, it only exists when tree-sitter grammars are installed".
src/parsing/treeSitterAnalyzer.tsis tracked in git and always present, so thatpremise was stale. What the indirection still did was stop esbuild bundling
it, leaving a runtime specifier pointing at a file
dist/does not contain..catch(() => null)sat inside the try block and absorbed exactly the error theouter
logger.warnexisted to report. Silent, every time.Confirmed three ways
dist/and the VSIX contain notreeSitterAnalyzer.js— onlyextension.jsand its mapdist/→ERR_MODULE_NOT_FOUNDsrc/), not tree-sitter (5,836)What this means retroactively
The
perDeclaratormapping that fixed 221 invisible exported constants, and thegenerator_function_declarationmapping from #45, never ran in production.Both happened to have regex counterparts doing the real work — luck, not design.
The extension shipped 28 MB of
.wasmgrammars it could not load, and parsedeverything with the less precise fallback (regex yields ~13% more symbols on the
same tree; that gap is false positives, not coverage).
The fix
Literal specifier, so esbuild bundles the module. Grammar loading stays lazy —
the expensive work is inside
createTreeSitterAnalyzer, not the import. Theswallow is gone and the fallback now names what degrades.
The test reads the build output
Every existing test in
src/parsing/callscreateTreeSitterAnalyzerdirectlyand passed throughout — they verify a path no install reached. That is the
sharpest form of this repo's recurring failure: not a check that measures
nothing, but one that measures something real which users never execute.
The new suite asserts on
dist/extension.js: the analyzer is present, nodist-relative runtime import survives, and the failure path is not silent. It
throws in CI if the bundle is missing, mirroring
grammarsTestSupport— askipped suite here would cover nothing while reporting green.
Mutation-checked: restoring the old load path and rebuilding fails two of three.
Verified in a real package
npm run check: 8622 passed, 2 skipped, 465 files. Integration: 45 passing.🤖 Generated with Claude Code