fix: zilliztech/claude-context#421 - #423
Conversation
| expect(splitter).toBeInstanceOf(AstCodeSplitter); | ||
| }); | ||
|
|
||
| it('falls back to the LangChain splitter for a language whose grammar failed to load', async () => { |
There was a problem hiding this comment.
packages/core/src/splitter/ast-splitter.test.ts line:16
Medium ---- This test proves that a broken C++ grammar falls back, but it does not verify the new failedLanguages memoization contract: call split (or isLanguageSupported) twice and assert the loader/warning runs only once. Without that assertion, a regression that retries the failing native require for every file would still pass while reintroducing repeated dlopen work and warning noise.
519c85a to
c7f2270
Compare
|
Addressed review comment 3772389966 (failedLanguages memoization): added a test that reloads the module fresh ( |
Summary
Grammar loaders in
packages/core/src/splitter/ast-splitter.tswere required eagerly at module load, unconditionally and without error handling. On macOS arm64, the unsignedtree-sitter-cpp/tree-sitter-scalaprebuilds faildlopenwithERR_DLOPEN_FAILED, crashing the whole MCP server at startup for every user — even ones with pure TypeScript/JavaScript codebases and zero C++/Scala files.This PR makes per-language grammar loading lazy and defensive. Grammars are now
require()d on first use (inside a try/catch), so a broken grammar only degrades that one language:[ASTSplitter] ⚠️ Failed to load ...)The rest of the pipeline (and every other language) keeps working normally.
Why this fixes the issue
The crash was self-inflicted: any single broken parser took down the server for all languages, regardless of what the user's codebase actually contains. Lazy, guarded loading confines the failure to exactly the language whose grammar is broken. The upstream unsigned-binary problem (arguably a
tree-sitter-cpp/tree-sitter-scalapackaging bug) now becomes a graceful degradation instead of a hard, unrecoverable startup crash.Implementation details
requireblock with aLANGUAGE_PARSER_LOADERSmap of lazy loader functions.loadLanguageParser()with per-language memoization (loadedParsers) and afailedLanguagesset so a grammar is only attempted once per process — no repeateddlopenfailures at split time.getLanguageConfig()now routes throughLANGUAGE_ALIASES→ canonical key → loader, keeping alias handling (js/ts/c++/cs, …) intact.isLanguageSupported()is now accurate per-platform: it actually attempts the load rather than hardcoding a static language list, so a language with a broken grammar correctly reports unsupported.getSupportedLanguages()exposes the canonical languages with AST node definitions.Verification
ast-splitter.test.tscovering lazy loading, failed-load fallback (returnsnull+ logs warning, doesn't throw), memoization, and language alias resolution.pnpm --filter @zilliz/claude-context-core test.pnpm typecheckandpnpm lintpass.Closes #421