perf(core): lazy-load web-tree-sitter runtime - #6747
Conversation
|
Thanks for the PR! Template looks good ✓ — all required headings present, bilingual, test plan included. Problem: This is an observed optimization with measurable evidence. The PR shows a 162,719-byte (1.01%) reduction in the static startup closure by deferring Direction: Aligned with the project's performance goals. Part of #3225 (startup optimization). Deferring heavy dependencies from the static startup path is the standard approach for this kind of work. No auth/sandbox/telemetry concerns. Size: 13 production lines (9 additions, 4 deletions in Approach: The scope feels right — minimal, focused change. Converts Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ — 所有必要标题齐全,双语,包含测试计划。 问题:这是一个有可测量证据的优化。PR 显示通过将 方向:与项目的性能目标一致。属于 #3225(启动优化)的一部分。将重型依赖从静态启动路径中延迟加载是此类工作的标准方法。无 auth/sandbox/telemetry 相关顾虑。 规模:13 行生产代码( 方案:范围合理——最小化的聚焦改动。将 进入代码审查 🔍 — Qwen Code · qwen3.7-max |
Code ReviewThe diff is clean and minimal. Two changes to
The existing error handling already covers the dynamic import (the catch block on the IIFE marks The new test file ( Reuse check: No new abstractions or utilities introduced. The PR uses the standard dynamic import pattern — nothing to extract or consolidate. Test ResultsAll 151 tests pass (148 existing + 3 new):
Real-Scenario Testing (tmux)Drove the CLI in tmux with a shell command that exercises the parser's permission-check path. Both builds produce identical behavior. Before (installed qwen 0.19.9, static import)After (PR bundle, dynamic import)Both builds started, parsed the shell command through the AST parser, determined it was read-only, and executed it successfully. The dynamic import is transparent to the end user. 中文说明代码审查Diff 简洁且最小化。
现有的错误处理已覆盖动态导入(IIFE 上的 catch 块会设置 新测试文件( 复用检查: 未引入新的抽象或工具函数。PR 使用标准动态导入模式——无需提取或整合。 测试结果全部 151 个测试通过(148 个现有 + 3 个新增)。 真实场景测试(tmux)在 tmux 中通过 CLI 执行了一个会触发解析器权限检查路径的 shell 命令。两个版本行为一致。动态导入对终端用户完全透明。 — Qwen Code · qwen3.7-max |
|
This PR is a clean, minimal optimization that does exactly what it says. The change is 13 production lines — convert a static import to a dynamic import inside the already-async parser initializer. It ships with solid test coverage (mock tests for lazy loading and failure modes, plus a full esbuild integration test proving the runtime is deferred from the static closure). My independent proposal would have been identical: change the import to type-only, add The before/after tmux test confirms the dynamic import is transparent at runtime — the shell parser initializes correctly in both builds. Unit tests all pass (151/151). The tradeoff is real and acknowledged: the first shell parse now pays the dynamic import cost. But Approving. ✅ 中文说明这个 PR 是一个干净、最小化的优化,完全如其所述。改动只有 13 行生产代码——在已有的异步解析器初始化 IIFE 中将静态导入转为动态导入。配套了扎实的测试覆盖(延迟加载和失败模式的 mock 测试,以及一个完整的 esbuild 集成测试证明运行时从静态闭包中被延迟)。 我的独立方案会完全一致:将导入改为仅类型,在 Before/after tmux 测试确认动态导入在运行时完全透明——两个版本的 shell 解析器都正确初始化。所有单元测试通过(151/151)。 取舍是真实且已被承认的:首次 shell 解析会承担动态导入成本。但 通过 ✅ — Qwen Code · qwen3.7-max |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No review findings. Downgraded from Approve to Comment: CI failing: web-shell E2E Smoke (ubuntu-latest, Node 22.x), Test (ubuntu-latest, Node 22.x).
— qwen3.7-max via Qwen Code /review
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
| */ | ||
|
|
||
| import Parser from 'web-tree-sitter'; | ||
| import type Parser from 'web-tree-sitter'; |
There was a problem hiding this comment.
[Suggestion] Consider adding an eslint no-restricted-imports rule for web-tree-sitter value imports (outside this file). Without it, someone adding import Parser from 'web-tree-sitter' in another module would silently pull the ~163KB runtime back into the synchronous startup closure, undoing this optimization with no CI check to catch it.
— qwen3.7-max via Qwen Code /review
| parserInstance = new Parser(); | ||
| await ParserClass.init({ wasmBinary: treeSitterWasm }); | ||
| parserInstance = new ParserClass(); | ||
| const bashWasm = await loadWasmBinary( |
There was a problem hiding this comment.
[Suggestion] The new test file covers dynamic import failure and deduplication, but does not cover the case where web-tree-sitter imports successfully yet a WASM binary (tree-sitter.wasm or tree-sitter-bash.wasm) fails to load. Both paths share this .catch() handler, but the code leading to it differs. Adding a test that mocks a successful import but a failing loadWasmBinary would give defense-in-depth for the WASM-specific failure path.
— qwen3.7-max via Qwen Code /review
✅ Maintainer local verification — LGTM (merge reference)I built and tested this PR locally in an isolated worktree at the PR head ( 1. Reviewer test plan — all greenThe new Guard check (does the test actually protect the behavior?): I overlaid the pre-PR 2. Independent byte-deferral proof — reproduces the exact −162,719 BI rebuilt the real production esbuild config (
That is an exact match to the PR's claimed Worth noting for reviewers: the two WASM binaries ( 3. Scope & risk
Verdict: builds clean, full test plan passes, the new test genuinely guards the behavior, and the headline byte saving reproduces to the byte. No concerns from local verification — good to merge from a correctness/perf standpoint. Verified in an isolated worktree; screenshots are faithful renders of the actual local command output. 🇨🇳 中文版本(点击展开)✅ 维护者本地验证 —— 通过(合并参考)我在隔离的 worktree 中基于 PR HEAD( 1. Reviewer 测试计划 —— 全部通过(见上图 tests)新增的 守护性验证(测试是否真的保护了该行为?): 我覆盖回 PR 之前的 2. 独立的体积延迟加载验证 —— 精确复现 −162,719 B(见上图 bytes)我在两个版本上分别用真实生产 esbuild 配置(
与 PR 声明的 提醒 reviewer:两个 WASM 二进制( 3. 范围与风险
结论: 构建干净、完整测试计划通过、新测试真正守护了该行为、核心体积收益精确复现到字节。本地验证无顾虑,从正确性/性能角度可以合并。 |


What this PR does
This changes the
web-tree-sitterJavaScript runtime from a static import to a first-use dynamic import inside the existing asynchronous parser initializer. The runtime remains bundled, parser initialization remains deduplicated, and existing regex fallback behavior is preserved when loading fails.It also adds focused coverage for lazy loading, concurrent initialization, import-failure latching, fallback behavior, emitted-chunk execution with the real parser and WASM assets, and reset recovery.
Why it's needed
Shell parsing is already asynchronous, but the JavaScript runtime was still part of the CLI's static startup closure before any shell command needed it. Deferring that runtime removes
162,719bytes from the measured startup closure while keeping it available on first parse.This implementation was found while running autoresearch with Weco, then reduced, tested, and reviewed for submission.
Reviewer Test Plan
How to verify
cd packages/core npx vitest run src/utils/shellAstParser.test.ts src/utils/shell-ast-parser-lazy.test.ts npm run build npm run typecheck npm run lintThe production CLI bundle evaluator builds the normal esbuild configuration, runs bundled
--versionand--helpsmoke checks, verifies that the runtime remains emitted rather than removed or externalized, and measures the static closure containing the CLI entry and default route.01d406f1)16,156,432bytes15,993,713bytes-162,719bytes (-1.01%)This measures bytes deferred from startup, not total bundle size or wall-clock startup time.
Evidence (Before & After)
N/A - no user-visible UI change.
Tested on
Environment (optional)
Node.js 22 with the repository's locked npm dependencies.
Risk & Scope
Linked Issues
Part of #3225.