Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(core): add Dart language support via workspace-vendored WASM#435

Open
thejesh23 wants to merge 18 commits into
Egonex-AI:main from
thejesh23:feat/dart-language-support
Open

feat(core): add Dart language support via workspace-vendored WASM #435
thejesh23 wants to merge 18 commits into
Egonex-AI:main from
thejesh23:feat/dart-language-support

Conversation

@thejesh23

@thejesh23 thejesh23 commented Jun 13, 2026

Copy link
Copy Markdown

Summary

Adds Dart to the tree-sitter pipeline so Flutter / Dart codebases get the same structural analysis (functions, classes with fields/constructors, mixins, extensions, enums, imports, exports) and call-graph edges that other languages already produce. Ships the freshly-built dylink.0-format wasm as a workspace-internal package so no upstream-republish or third-party-bundle dependency is required.

Linked issue(s)

Refs #226 — Dart is the third leg of the iOS-repo case (Swift + Objective-C + Flutter/Dart) where files currently group by domain but produce no reference edges.

Related PRs (this is the third Dart attempt — flagging explicitly)

There are already two open Dart PRs against main. This PR is opened to give maintainers a third angle with explicit trade-off comparison; happy to close it in favour of either of the others if that's the call.

What this PR does differently

  • No third-party grammar bundle dep, no loader changes. Ships the freshly-built wasm as a workspace-internal package @understand-anything/tree-sitter-dart-wasm/ consumed via workspace:*. The existing TreeSitterPlugin.init() resolves it through require.resolve() unchanged. If amaanq/tree-sitter-dart ever publishes a refreshed dylink.0 wasm, the workspace package is a single-commit delete + dependency-flip.
  • Implements the call graph. Uses startIndex to compare sibling-identity (web-tree-sitter's .child(i) returns a fresh JS wrapper per call, so === always fails — confirmed via probe; PR feat(core): add Dart structural analysis via tree-sitter (#226) #348 hit the same gotcha and uses .id for the same purpose).
  • Documents the AST surprises inline. function_signature / function_body are siblings not parent/child; abstract methods land at class_body > declaration > function_signature (no method_signature wrapper); anonymous extensions surface as ```on ``` so the graph builder doesn't drop them; hide combinators are skipped (not added as specifiers).
  • 22 vitest cases driven against the real wasm — coverage matrix is in the spec doc (docs/superpowers/specs/2026-06-13-dart-support-design.md).
  • Includes the design spec + bite-sized TDD implementation plan under docs/superpowers/ for review traceability.

Honest trade-offs vs. the alternatives

  • Adds a ~745 KB vendored binary to git (comparable to the wasms already pulled in at install time for Rust/Go/TS — those just aren't in-repo). Mitigated by the workspace package being trivially deletable when upstream republishes.
  • extractParams currently surfaces only required positional parameters; named ({...}) and optional positional ([...]) parameters are wrapped in container nodes and dropped. Documented inline in the helper's doc comment.
  • Getters / setters (int get value => count;) are not surfaced as methods — they appear as getter_signature, which the extractor doesn't currently route. Documented with an explicit test asserting the current (limited) behaviour so the gap can't silently regress.
  • Records and pattern matching are out of scope (function-local; no project-graph impact).

How I tested this

  • pnpm lint — clean
  • pnpm --filter @understand-anything/core build — tsc clean
  • pnpm --filter @understand-anything/core test — 720/720 (+ 22 new Dart cases, + 1 count-assertion update from 40 → 41 in language-registry.test.ts)
  • pnpm --filter @understand-anything/skill build — clean
  • pnpm test — 201/201 (one pre-existing failure in tests/skill/merge-recover-imports.test.mjs exists on origin/main unrelated to this branch)
  • Manual smoke against TreeSitterPlugin with the sample Flutter snippet (Counter class + main() calling increment()): analyzeFile returns the expected functions / classes / imports / exports, and extractCallGraph returns { caller: "main", callee: "increment" } plus { caller: "main", callee: "Counter" }.

Versioning

  • Version bumped 2.7.7 → 2.8.0 in all five manifests (per CLAUDE.md): understand-anything-plugin/package.json, understand-anything-plugin/.claude-plugin/plugin.json, .claude-plugin/plugin.json, .cursor-plugin/plugin.json, .copilot-plugin/plugin.json.

🤖 Generated with Claude Code

thejesh23 and others added 16 commits June 13, 2026 04:19
Adds the brainstormed design for landing deep Dart support at parity with
the recent Kotlin add (PR Egonex-AI#347): LanguageConfig + tree-sitter WASM grammar
(tree-sitter-dart@1.0.0, verified ships a prebuilt .wasm in its tarball) +
DartExtractor + ~22 vitest cases. Six file changes, no edits to shared
schemas/registries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Live verification during planning surfaced two facts that change the
shipping strategy:
1. tree-sitter-dart@1.0.0's prebuilt wasm uses the pre-`dylink.0` format
 and fails to load in web-tree-sitter@0.26.x (the version this project
 uses). Verified by directly loading the upstream wasm and catching
 the failure in getDylinkMetadata.
2. The grammar source itself is sound — rebuilding with the current
 tree-sitter-cli@0.26.x + wasi-sdk-29 toolchain produces a working
 dylink.0-format wasm that parses every construct the extractor needs.
Revised packaging: ship the freshly-built wasm as a workspace-internal
package (@understand-anything/tree-sitter-dart-wasm) rather than
depending on the broken upstream npm artifact. No loader changes
required; existing TreeSitterPlugin resolves it the same way it
resolves other tree-sitter packages.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Thirteen-task TDD plan walking from vendoring the workspace wasm package
through scaffolding the extractor and adding extraction logic in
test-first slices: functions, classes, constructors, mixins, extensions,
enums, imports, exports, visibility, and call graph.
Every code block reflects AST shapes confirmed via a live probe against
a freshly-built tree-sitter-dart wasm in the project's own
web-tree-sitter at 0.26.x. No placeholder code, no "fill in later"
references.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The upstream tree-sitter-dart@1.0.0 ships a pre-`dylink.0` wasm that
fails to load in web-tree-sitter@0.26.x. The grammar source itself is
sound — rebuilding with the current tree-sitter-cli + wasi-sdk produces
a working dylink.0 wasm. Vendor that artifact as a workspace-internal
package so @understand-anything/core can depend on it via workspace:*.
BUILD.md documents the provenance and rebuild instructions.
Adds the Dart language config and wires it into builtinLanguageConfigs
so .dart files are recognized by the language registry. References the
vendored @understand-anything/tree-sitter-dart-wasm package for grammar
loading.
No extractor yet — structural extraction lands in the next commit.
Empty extractor that satisfies the LanguageExtractor interface so the
plugin pipeline can load it. Real extraction logic lands in subsequent
TDD commits.
Add TDD tests and implement extractTopLevelFunction with helpers for
extracting function name, params, and return type (including generics
where the grammar emits type_identifier + type_arguments as siblings).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add constructorName() helper and extend collectClassBody() to surface
unnamed constructors as "ClassName", named constructors as "Class.named",
and factory named constructors as "Class.named" in methods[]/functions[].
Probe confirmed plan's AST shapes match exactly; extractReturnType returns
undefined for all constructor forms (factory keyword is an unnamed node).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add mixin_declaration handling to extractStructure, folding mixins into
classes[] (same convention as class_definition). The `on` constraint
sibling is intentionally ignored for graph purposes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds enum_declaration handling to DartExtractor: enum constants are surfaced
as properties[] so the structural graph captures Color.red / Color.green etc.
Implements Task 9 of the Dart language support plan (TDD, 16/16 dart tests
pass, full suite 708/708).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
...as) + export directives
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements extractCallGraph with a sibling-aware walk that pairs each
function_signature with its subsequent function_body sibling (Dart's
AST differs from Kotlin's: signature and body are siblings, not
parent/child). Detects call sites via selector nodes containing
argument_part; uses startIndex for sibling lookup (web-tree-sitter
returns new wrapper objects per child() call, making === unreliable).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copy link
Copy Markdown
Author

Opened #436 to retroactively satisfy the README's "please open an issue first for major changes" guideline — apologies for the order. The issue summarizes the three open Dart PRs (#348, #415, this one) and asks maintainers to pick a shipping approach before reviewing diffs. Happy to close this PR in favour of either alternative if that's the call.

thejesh23 added 2 commits June 13, 2026 07:49
Per maintainer feedback on Egonex-AI#436 — these are personal brainstorm/plan
artifacts produced via the superpowers skill flow, not repo
documentation. The BUILD.md provenance note in
packages/tree-sitter-dart-wasm/ stays since that's repo-level docs
about the vendored wasm.
Incorporates stronger pieces from the prior Dart attempts (Egonex-AI#348, Egonex-AI#415)
that @Lum1104 called out:
- `extractParams` now walks `optional_formal_parameters` (covers both
 optional positional `[...]` AND named `{...}` parameters — the Dart
 grammar uses one wrapper for both).
- New `extractParamName` helper extracts the user-visible field name
 from `this.field` and `super.field` initializer parameters by
 unwrapping `constructor_param` / `super_formal_parameter`.
- `collectClassBody` now routes `getter_signature` and `setter_signature`
 in both shapes:
 - concrete: `method_signature > getter_signature` + sibling function_body
 - abstract: `declaration > getter_signature`
 Setters use the same path. The previous limitation assertion
 (`methods).not.toContain("value")`) flipped to a positive
 `.toContain("value")`.
- Added import/export edge-case tests: `dart:` SDK URIs, multi-import
 declaration-order preservation, and `export ... show` clauses.
- Added a comma-list field test (`int a, b, c;`).
Underscore-prefix visibility carries through naturally to all new code
paths via the existing `isExported` gate inside `pushMethod`; explicit
test added for an underscore-prefixed getter.
Test counts: 28 → 41 dart cases; full core suite 720 → 733; no
regressions.

Copy link
Copy Markdown
Author

@Lum1104 thanks for the direction. Addressed in two new commits:

  • e4301ae — trimmed the process docs from docs/superpowers/ (the design + plan files were personal brainstorm artifacts, not repo documentation). BUILD.md next to the vendored wasm stays as that's actual repo-level provenance.
  • 68777fe — broadened extractor coverage per your asks, pulling the stronger pieces from feat(core): add Dart structural analysis via tree-sitter (#226) #348 and feat(core): add Dart language support #415 where they fit cleanly:
    • Optional + named parameters: extractParams walks optional_formal_parameters (the Dart grammar uses one wrapper for both [...] and {...} — the leading token differs but the contained formal_parameter children are the same).
    • this.field / super.field initializers: new extractParamName helper unwraps constructor_param / super_formal_parameter and surfaces just the field name.
    • Getters + setters: collectClassBody now routes both getter_signature and setter_signature in both the concrete (method_signature > ...) and abstract (declaration > ...) shapes. The previous limitation assertion (methods).not.toContain("value")) flipped to positive .toContain("value").
    • Import edge cases: added tests for dart: SDK URIs, multi-import declaration-order preservation, and export 'foo.dart' show X.
    • Comma-list fields: int a, b, c; now has explicit test coverage (the existing walk already handled it; locking it in).
    • Underscore-prefix visibility carries through to getters/setters via the existing isExported gate in pushMethod — explicit test added.

Verification (all green)

Before After
Dart-extractor tests 28 41 (+13)
Core suite 720 733
pnpm lint
pnpm --filter @understand-anything/core build
pnpm --filter @understand-anything/skill build

Branch is up-to-date with main (0 commits behind). Call graph stays in scope as you wanted. Ready for another look when you have a moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /