Skip to content

feat(parsers): add Dart/Flutter public API surface parser via dartdoc_json#35

Closed
grdsdev wants to merge 15 commits into
mainfrom
claude/sweet-benz-56ab2d
Closed

feat(parsers): add Dart/Flutter public API surface parser via dartdoc_json#35
grdsdev wants to merge 15 commits into
mainfrom
claude/sweet-benz-56ab2d

Conversation

@grdsdev

@grdsdev grdsdev commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds Dart/Flutter public API surface parsing to the SDK compliance workflow, using dartdoc_json — a tool that runs the real Dart analyzer — instead of a handrolled regex parser.

Part of SDK-991. The reusable compliance workflow (validate-sdk-compliance.yml) already supports language: dart; this PR wires up the full implementation so supabase-flutter PRs are gated correctly.

Approach

A thin Node.js normalizer (normalize-dartdoc.ts) reads dartdoc_json JSON output and emits the ParseResult shape consumed by check-api-symbols. The CI workflow runs dartdoc_json per package, merges outputs with jq, then calls the normalizer CLI. Everything downstream (check-api-symbols, compliance validation) is unchanged.

supabase-flutter PR
  → validate-sdk-compliance.yml
      [dart-only] setup Flutter SDK (includes Dart + pub)
      [dart-only] activate dartdoc_json via dart pub global
      [dart-only] per package: flutter pub get + dartdoc_json → api.json
      [dart-only] jq -s '[.[][]]' → merged pr-raw.json
      → normalize-dartdoc pr-raw.json → pr-symbols.json
      → check-api-symbols pr-symbols.json base-symbols.json sdk-compliance.yaml

Changes

Added

  • src/normalize-dartdoc.ts — core normalizer: DartdocUnit[]ParseResult
  • src/normalize-dartdoc-cli.ts — CLI entry point (npm run normalize-dartdoc <path>)
  • test/normalize-dartdoc.test.ts — 20 tests (class/mixin/enum/extension kinds, constructor naming, getters/setters, privacy filter, multi-unit flattening, enum methods)
  • test/fixtures/dartdoc-sample.json — real dartdoc_json output used as test fixture
  • docs/specs/2026-06-19-dart-parser-dartdoc-json.md — design spec

Removed

  • src/dart-parser.ts, src/parse-dart.ts — handrolled regex parser
  • test/dart-parser.test.ts, test/fixtures/dart-sample/ — regex parser tests

Modified

  • .github/workflows/validate-sdk-compliance.yml — Dart-conditional steps: Flutter SDK setup, dartdoc_json activation, per-package generation with flutter pub get, jq merge, normalize-dartdoc dispatch
  • package.jsonnormalize-dartdoc replaces parse-dart

Why dartdoc_json over the regex parser

The regex parser required depth-tracking, arrow-continuation state, annotation-line suppression, and two production bug fixes before its first real use. Root cause: regex cannot reliably parse Dart without understanding the language. dartdoc_json uses the real Dart analyzer and gets it right by construction.

Correctness notes:

  • extension type (Dart 3.3+): dartdoc_json 0.5.0 does not support extension types — it throws on them. The normalizer has no dead extension_type handling. Verified empirically; supabase-flutter does not currently use extension types in its public API.
  • Enum methods (Dart 3+ enhanced enums): correctly captured — "enum" is included in the set of class-like declarations whose members are iterated.
  • Flutter packages: uses subosito/flutter-action (not dart-lang/setup-dart) and flutter pub get (not dart pub get) to handle the mix of pure-Dart and Flutter packages in supabase-flutter.
  • Defensive parsing: units without a declarations field are skipped gracefully (some Flutter build artifacts emit incomplete units).

Test plan

  • 119 tests pass (npm test)
  • TypeScript strict check clean (npm run typecheck)
  • Fixture is real dartdoc_json 0.5.0 output (not hand-authored)
  • E2E: supabase-flutter PR #1441 CI passes end-to-end (both validate and check jobs green) against this branch

Closes SDK-991

Adds a line-by-line regex-based Dart parser following the same design as
swift-parser.ts. Key differences from the Swift parser: Dart has no public
access modifier (privacy is _prefix), so member extraction is gated to the
immediate class-body brace depth to prevent method calls inside method bodies
from producing false symbols.

Handles: classes (abstract/final/sealed/mixin), enums, mixins, named extensions,
typedefs, getters, setters, factory constructors, and regular methods/constructors.
Skips build_runner generated files (*.g.dart, *.freezed.dart, etc.) and respects
sdk-parse-ignore. Scans lib/ by convention (Dart public library root).

Two production bugs fixed vs initial draft (verified against supabase-flutter):
- @annotation(params) on its own line no longer emits false symbols
- Multi-line arrow function continuations no longer emit false symbols from
  constructor calls in toString()/copyWith() bodies

139 tests pass (36 new + 103 existing).

Linear: SDK-991
@grdsdev grdsdev requested review from a team as code owners June 19, 2026 09:08

@spydon spydon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should handroll this, we could use something like dartdoc_json --output api_members.json --root and just parse that file instead

grdsdev added 8 commits June 19, 2026 08:11
dartdoc_json 0.5.0 throws AssertionError on extension type declarations
(verified empirically), so "extension_type" never appears in its output
and the matching branches were dead code. Removed from CLASS_LIKE and
resolveTopKind, with a comment explaining why.

Added "enum" to CLASS_LIKE so methods/getters on Dart 3+ enhanced enums
are emitted as symbols instead of being silently dropped.

supabase-flutter does not use extension types in its public API surface.

Added two tests covering enum member emission (method and getter kinds).
@grdsdev grdsdev changed the title feat(parsers): add Dart/Flutter public API surface parser feat(parsers): add Dart/Flutter public API surface parser via dartdoc_json Jun 19, 2026
@grdsdev grdsdev requested a review from spydon June 19, 2026 12:44
grdsdev added 5 commits June 19, 2026 10:05
dart pub get fails with exit 69 on Flutter packages (supabase_flutter)
because they require the Flutter toolchain. Replace dart-lang/setup-dart
with subosito/flutter-action and use flutter pub get universally — it
works for both pure-Dart and Flutter packages in the monorepo.
dartdoc_json can emit units without a declarations array for certain
Flutter package artifacts. Guard with ?? [] to skip them gracefully.
Also mark the field optional in the DartdocUnit interface.
grdsdev added a commit to supabase/supabase-flutter that referenced this pull request Jun 19, 2026
grdsdev pushed a commit that referenced this pull request Jun 22, 2026
…native to #35) (#41)

* feat(parsers): add Dart public API parser via package:analyzer

Alternative to #35: extract the Dart/Flutter public API surface with a small
package:analyzer tool instead of the dartdoc_json CLI plus a Node normalizer.

The tool parses lib/**.dart syntactically (no pub get, no network), emits the
existing ParseResult shape, and is invoked via a parse-dart npm script so the
reusable workflow's generic parse step is unchanged. It handles extension types
and enhanced enums by construction, and honors .sdk-parse-ignore for parity with
the TypeScript and Swift parsers.

Removes the need for dartdoc_json activation, per-package flutter pub get, the jq
merge, and the Node normalizer.

* ci: run dart_symbol_extractor format, analyze and test in capability CI

* ci: install Dart SDK via direct download instead of marketplace action

The supabase/sdk Actions policy only permits GitHub-owned and verified-creator
actions, so dart-lang/setup-dart caused a workflow startup failure. Install the
SDK with a plain download instead, which also keeps the reusable workflow
independent of each calling repo's allowed-actions policy.

* fix(dart-parser): correct class-type-alias kind and directory ignores

Self-review found three issues:
- A class-type alias (class C = A with B;) was emitted with kind variable
  instead of class, because ClassTypeAlias is a subtype of TypeAlias.
- Directory ignore patterns (build/, examples/) excluded only the directory
  entry itself, not the files nested beneath it, so contents leaked into the
  surface.
- A single unreadable or unparseable lib file aborted the whole extraction;
  it is now skipped with a stderr warning.

Adds regression tests for each.

* refactor: use dart-lang/setup-dart action and drop SymbolKind extension

Install the Dart SDK with the dart-lang/setup-dart action instead of a manual
download. Replace the SymbolKindJson extension with SymbolKind.name, special
casing only classKind since 'class' is a reserved word.

* Fix alignment

* docs: remove design spec doc

* refactor(dart-parser): collapse container dispatch with pattern matching

Replace the if-else type ladder in _visitTopLevel with a switch on Dart 3
patterns: the five class-like containers share one case, and a small _emit
helper centralizes the private-name guard for simple symbols. Output is
unchanged.

* refactor: move dart_symbol_extractor up to scripts/

Relocate the package from scripts/capability-matrix/dart_symbol_extractor to
scripts/dart_symbol_extractor as a sibling of the Node validator. Update the
parse-dart npm script path, both workflows' working directories, the
validate-capabilities paths filter, and the CLAUDE.md reference.

* ci: invoke dart extractor directly, drop parse-dart npm script

The reusable workflow now runs 'dart run bin/extract.dart' from the extractor
package for the dart language, instead of dispatching through an npm script.
Removes the parse-dart entry from package.json.
@grdsdev grdsdev closed this Jun 22, 2026
grdsdev added a commit to supabase/supabase-flutter that referenced this pull request Jun 22, 2026
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