tom_d4rt_generator 1.26.0
tom_d4rt_generator: ^1.26.0 copied to clipboard
D4rt bridge generator for creating BridgedClass implementations from Dart source. Supports per-package generation, deduplication, and cross-package bridging.
1.26.0 #
Added — d4rtgen --verify-output (scd13_ahcm) #
GEN-121 put the generator's output under dart analyze inside the generator's
own test suite. That protects the generator, not its consumers: a consumer's
analysis_options.yaml excludes the generated bridge directory, so dart analyze reports the project clean while a generated file imports a URI that
resolves nowhere. GEN-119 and GEN-120 both shipped through that gap and were
found by hand afterwards.
d4rtgen --verify-output runs dart analyze --format=machine over the files
the run just wrote and fails the run on any error, or on any warning not
explicitly allowlisted. Opt-in for now.
Only diagnostics inside generated files can fail a run. A consumer may be mid-refactor or carry an unrelated warning of its own; none of that is the generator's emission. This scoping is what makes the flag safe to point at any package, and it is the precondition for ever defaulting it on.
Changed — one severity policy, shared by the tool and the gate #
The severity policy, the machine-format parser and the allowlist move from
test/gen121_generated_output_analyze_gate_test.dart into
lib/src/verification/generated_output_analysis.dart; the gate now consumes
them. Two copies would drift, and the whole point of a gate is that the tool
and the test agree on what a bad emission is.
Note for anyone adding to allowedWarningCodes: entries are spelled as the
machine format spells them (UPPER_SNAKE_CASE), and are matched
case-insensitively.
1.25.0 #
Fixed — the orchestrated relaxer path emitted a file that did not compile (scd12_ahcm) #
Extending the GEN-121 dart analyze gate to a second, orchestrated package —
generated through bridge_api.generateBridges, the path that also emits the
relaxers, barrel and dartscript files — put lib/src/relaxers.b.dart under the
analyzer for the first time. Three defects surfaced at once, each of which made
the emitted package fail to compile:
- User-relaxer imports were collected and then dropped. The scan built an
import block into a local buffer that nothing ever read, so every call to a
user relaxer landed in the output as
Undefined name 'relaxZomBox'. registerGenericConstructors()was not emitted when no RC-2 class was eligible.dartscript.b.dartcalls it unconditionally, so a package with relaxers but no generic constructor factory produced an undefined-function reference. The function is now always defined, empty when nothing is reachable.scanUserRelaxersbuilt the package URI without the package name, so it emittedpackage:src/user_relaxers/x.dart(URI_DOES_NOT_EXIST) — the GEN-119 failure mode, in a path GEN-119's guard does not cover. The owning package is now taken fromconfig.name, falling back to the pubspec'sname:.
Also adds unused_element to the emitted // ignore_for_file: list, for the
_coerceToV helper that is emitted unconditionally.
Gate coverage: G-GEN121-05 analyses the orchestrated package, G-GEN121-06
pins that its relaxer file really does carry a user relaxer and its import, so
the analyze assertion cannot go vacuous.
1.24.0 #
Fixed — directory mode bypassed both the extension dedupe and the source exclusion (scd11_ahcm) #
GEN-120 deduplicated extensions in SINGLE-FILE mode. Directory mode — one
<source>_bridge.dart per source file — grouped them straight off
globals.extensions: before the excludeSourcePatterns filter, before the
GEN-064/GEN-120 dedupe, and keyed on the unnormalised sourceFile. A
part-declared extension was therefore emitted into TWO output files, one named
after the part and one after the parent library, each carrying a complete
BridgedExtensionDefinition, and both were registered; and
excludeSourcePatterns never reached extensions in that mode at all.
Both pipelines now call one helper, _bridgeableExtensions. Directory mode maps
the canonicalised parent URI back to the input path before grouping, so the
extension joins that file's own group instead of opening a second one whose
output file would collide by basename and silently overwrite it.
Fixed — an exclusion naming a parent library now excludes its parts' extensions #
Found while fixing the above, and not directory-mode-specific: measured in single-file mode too. A part-declared extension arrives twice — tagged with the parent library, and with the part itself — and the exclusion was matched only against each copy's raw URI. Excluding the parent left the part's copy standing, which the dedupe then renamed to the parent, so the exclusion had no effect. Either spelling now excludes it.
Note for callers: excludeSourcePatterns globs the WHOLE source URI. A bare
thing.dart matches nothing; write package:pkg/src/thing.dart or **/thing.dart.
This is unchanged behaviour, now documented — the first version of the test for
this fix used a bare file name and failed for that reason rather than the one it
was written for.
1.23.0 #
Added — compareFixtureResolution: an example that resolves is not yet an example that resolves CORRECTLY (scd9_aicx) #
An example project reaches the package it exercises by path: ../.., and a
path dependency supplies the SOURCE but not the RESOLUTION: the example
compiles that package's current lib/ against the EXAMPLE's lock. A stale
example lock therefore type-checks current first-party source against an old
third-party dependency, and the error lands inside ../../lib/ — a directory
the example does not own and whose contents are correct. Measured instance:
every tom_d4rt_exec/example/*/pubspec.lock pinned tom_d4rt_ast 0.19.0 while
exec's lib needed 0.20.x, surfacing as The getter 'Logger' isn't defined for the type 'ModuleLoader' plus Bad state: Generating AOT kernel dill failed!.
Lock files are gitignored, so the drift never appears in git status.
compareFixtureResolution(hostProjectPath:, fixtureProjectPath:) reports each
package the fixture resolves differently, and describeMismatches renders them
with the reason the fixture's lock governs someone else's compile, plus the
repair. Exported from package:tom_d4rt_generator/testing.dart, alongside
resolveIfUnresolved, which answers the neighbouring question.
Only the host's RUNTIME dependencies are compared — the ones its lib/ is
compiled against (direct main and transitive in the lock). Comparing every
shared hosted package instead reports benign differences: measured across the
three packages with examples, all seven were lints, a dev-only ruleset no
library imports.
1.22.0 #
Fixed — extensionSourceUris() identifies an extension instead of just naming it (scd8_ahcm) #
The emitted map was keyed by the extension NAME alone, and registerBridges()
looked the source URI back up by that same name. Two genuinely distinct
extensions sharing a name — different on-types, different libraries — therefore
collapsed to one entry: Dart keeps the last, so one extension was registered
against the other's source URI, and the map literal itself carried an
equal_keys_in_map warning. This is independent of GEN-120, which removed a
duplicate of a single extension; this was two extensions sharing one key, and
the only surviving route to that warning in a generated bridge.
The key is now <name>@<onType> (<unnamed>@<onType> as before for unnamed
extensions), spelled identically in the map and in the registerBridges()
lookup — the two halves of that wire format move together. BridgeGenerator.extensionSourceUriKey
is the single place it is built.
This changes generated output, so every committed *.b.dart whose
extensionSourceUris() has entries is stale until regenerated — 17 files in
this workspace, including both Flutter twins. Regenerate with the fleet sweep;
an un-regenerated file keeps working, because both halves of its own key live
in it.
Added — a generation-time warning for extensions that still cannot be told apart #
Two extensions sharing a name AND an on-type are indistinguishable at the
lookup site, which reads a BridgedExtensionDefinition and has nothing else to
key on. The generator now emits one entry (a duplicate key would fail the
GEN-121 analyze gate and decide the winner by source order anyway) and reports
the loser as a warning naming both libraries, so the collision surfaces where
it can still be acted on.
1.21.1 #
Fixed — bridges with a List<FunctionTypedef> parameter compile (scd7_ahcm) #
A parameter typed as a list of function typedefs — List<BridgeRegistrar>,
List<VoidCallback> and the rest of the known-typedef list — is not bridged:
the adapter throws UnimplementedError. After the throw the generator emitted
a placeholder local so the call below it would still compile, typed
<dynamic>[]. It did not compile: List<dynamic> is not assignable to
List<BridgeRegistrar>?, and the // ignore: dead_code above it silences the
dead-code diagnostic on the declaration, not the type error on the call.
Consumers never saw it because their analysis_options.yaml excludes the
generated folder; tom_vscode_bridge carried the error in
VSCodeBridgeServer's constructor adapter.
The placeholder is now final dynamic <name> = null;. dynamic is assignable
to any parameter type, and the generator has nothing narrower it can safely
spell there: every source library is imported under a $pkg_N prefix, and a
typedef the generator declined to bridge may not be imported at all. The
runtime behaviour is unchanged — the throw comes first. The named-parameter and
positional-parameter paths, which each emitted their own copy, now share one
emitter.
The GEN-121 analyze gate's fixture now declares such a parameter on both paths,
so dart analyze over the generated output covers the placeholder.
1.21.0 #
Fixed — d4rtgen --dry-run / -n writes nothing (scd5_ahcm) #
The flag was accepted — tom_build_base parses -n for every tool — and then
ignored: d4rtgen -n regenerated every *.b.dart in place, exactly like a
normal run. A flag whose whole contract is "change nothing" silently changed
everything, and the operator reaching for it to inspect a package whose
bridges must not move was the one it hurt.
A dry run now runs the full generation with its writes redirected into a
scratch directory under the project's .dart_tool/ (the overlay behind
checkBridgeFreshness), then prints every file a real run would write and how
it relates to the project's copy — would create, would change, or
unchanged apart from the // Generated: line — and deletes the scratch
tree. Because the redirection happens at the dart:io level, all six writers
(module bridges, relaxers, proxies, barrel, dartscript, test runner) are
covered without any of them checking a flag. The tool now advertises
--dry-run in its help.
Added — previewGeneration #
The shared mechanism, exported from package:tom_d4rt_generator/tom_d4rt_generator.dart
(lib/src/generation_preview.dart): run a generation callback against a
package inside the scratch overlay and get back its result plus every file it
wrote, as PreviewedWrite(path, PreviewedChange). checkBridgeFreshness is
now built on it, so the gate and the dry run classify a change identically.
normaliseGeneratedContent moved to the same file and is still exported.
1.20.0 #
Added — every generated file names the generator that wrote it #
The // Generated: line in each file's header now carries the generator
version as well as the time:
// Generated: 2026-09-11T14:30:00.000 by tom_d4rt_generator 1.20.0
"Which generator produced these committed bridges?" could be answered only by
reading each consumer's .dart_tool/package_config.json, and that says which
generator a regeneration would use today, not which one wrote the files. A
sweep once produced a baseline from three different generators without
anything showing it. The version in the file makes the committed tree answer
the question on its own.
Relaxer, proxy and barrel files had no // Generated: line at all; they now
carry one, as do module bridges, the dartscript file and the test runner.
The version goes on the existing line on purpose. Every comparison of generated
output — checkBridgeFreshness, the example ratchet, the flutter corpus —
ignores lines starting with // Generated:, so a release that produces the
same code does not make every consumer's bridges read as stale. Regenerating
with this release changes only that line in files that were otherwise current,
and adds it to relaxer, proxy and barrel files.
New public API (package:tom_d4rt_generator/tom_d4rt_generator.dart):
generatedStampLine(), parseGeneratedStamp(content) returning a
GeneratedStamp (generatedAt, generatorVersion — null for files written
before this release), and generatedStampPrefix.
1.19.0 #
Changed — resolveIfUnresolved asks pub whether a project resolves #
A project that stops resolving keeps its last .dart_tool/package_config.json,
and whatever reads it next fails on a downstream symptom. The recorded case is
an example project whose config still named analyzer 8.4.1, long gone from the
pub cache: DiagnosticSeverity is not defined, an apparent analyzer API break.
When this release was prepared, 7 of the 25 example projects across
tom_d4rt_generator, tom_d4rt_exec and tom_ast_generator named a lints
version the cache no longer held.
resolveIfUnresolved used to act only when the package config was missing, so
it passed exactly those projects. It now runs dart pub get --offline — about a
second, no network, and no writes when the resolution is current — and falls
back to dart pub get only if that fails, so a config that merely went stale is
repaired and a project that cannot resolve fails with pub's own message. It
then reads the config once more, because a cached directory that exists without
its pubspec.yaml passes a resolve.
The obvious cheaper test — pubspec.yaml newer than the config — was measured
and rejected: dart pub get rewrites neither the config nor pubspec.lock when
the resolution is unchanged, so that comparison reports "stale" forever after
any edit that does not change the resolution.
D4rtTester.prepareBridges runs the same step instead of checking only that a
config exists, so its generation errors now name the resolution failure.
Added #
resolutionProblems(projectPath)— what is wrong with a project's package config, read from disk without running pub: missing, unreadable, a package root that no longer exists, or one without apubspec.yaml.findDartProjects(root)— every directory underrootwith apubspec.yaml, for tests that cover projects without ad4rtgen:section.
This package's test/example_resolution_test.dart resolves every example/
project with it.
1.18.0 #
Added — building blocks for an example/ bridge ratchet (testing.dart) #
Example projects carry buildkit_skip.yaml, so no workspace scan reaches them
and nothing notices when their committed bridges fall behind the generator.
Measured with checkBridgeFreshness, 17 of the 21 example projects across
tom_d4rt_generator, tom_d4rt_exec and tom_ast_generator were stale. The
package that owns them is the only place a test can notice, and three packages
need the same test, so its logic ships here:
findD4rtgenProjects(root)— the directories underrootwhosebuildkit.yamlhas ad4rtgen:section, skipping.dart_toolandbuild.resolveIfUnresolved(projectPath)—dart pub getfor a project that has never been resolved.checkBridgeFreshnessrefuses to resolve, because that writes to the package; an owning package's test may do it as setup.freshnessRatchetViolation(project, freshness, knownStale: …)— the verdict under a ratchet: a project on the caller's known-stale list must still be stale, any other project must be fresh, and a report with errors is always a violation. So the list can only shrink, and no new staleness lands while the backlog is paid down.
This package's own suite applies it to its seven examples
(test/example_bridges_fresh_test.dart).
1.17.0 #
Added — checkBridgeFreshness, a test-time gate for stale bridges #
Regeneration is not a side effect of any non-Flutter test suite; it happens
when somebody runs d4rtgen by hand. So a generator change can land, every
suite go green, and every committed *.b.dart stay stale — the suites then
exercise the old generated code. GEN-123 went unnoticed that way: a relative
scan root dropped all four user bridges in d4rt_userbridges_sample, and no
suite regenerated.
checkBridgeFreshness(projectPath) regenerates a package's bridges from its
own buildkit.yaml and compares the result with what the package has
committed, ignoring the // Generated: timestamp line. It reports each
disagreeing file as differs or notCommitted, and generation errors
separately — a report with errors has measured nothing, and isFresh is false.
A consumer asserts it in one test:
test('committed bridges match the generator', () async {
final freshness = await checkBridgeFreshness(Directory.current.path);
expect(freshness.errors, isEmpty);
expect(freshness.stale, isEmpty);
});
It does not write to the package. Generation runs unmodified, inside a
dart:io overlay that sends writes to *.b.dart files into a scratch tree
under the package's .dart_tool/, while reads see the scratch copy only where
this run wrote one. Two cheaper designs were measured and rejected: rewriting
the configured output paths changes the generated content (the test runner
picks its import form and usage comments from its own path), so every package
reads as stale; and redirecting reads as well breaks any package that imports
its own generated files, which the analyzer then meets under two URIs.
An unresolved package — no .dart_tool/package_config.json — is refused with an
error instead of being resolved: generateBridges would run dart pub get, a
subprocess no overlay can redirect, and rewrite pubspec.lock in place.
Measured against eight non-Flutter consumers before release, the gate agreed with an in-place regeneration on every file, and a content-and-directory snapshot of each package was identical before and after.
1.16.0 #
Changed — the tom_d4rt floor moves from >=1.11.0 to >=1.30.1 #
Two reasons, and the second is why this is a minor rather than a patch.
1.27.0 through 1.30.0 cannot run a generated bridge set. 1.27.0 (tcca19)
made two same-named bridged classes reject the bare name instead of silently
picking whichever registered last, and applied that rule to dart:*
declarations as well. Dart does not treat them as peers — it applies
platform-library precedence, under which a dart:* name is shadowed by a
non-platform one with no ambiguity. So under 1.27.0 any script naming a type
that a dart:* library also declares failed:
Ambiguous Name Error: The name 'TextStyle' is declared by more than one
library in scope, so it cannot be used unqualified.
dart:ui declares TextStyle, which is most Flutter scripts. 1.30.1 restores
precedence. All three affected versions are published, so a floor below 1.30.1
admits them.
The floor is load-bearing for someone else's test suite. tom_d4rt_exec
declares no tom_d4rt of its own; the interpreter reaches it only
transitively, through this package as a dev dependency. So this line is the
only constraint in that graph with any say over the version, and exec's
test/generator_tests/ suite — which exists to compare the analyzer-free tree
against the analyzer tree — was comparing against whatever this floor admitted.
At >=1.11.0 that meant a reference nineteen minors old, weak in exactly the
direction the suite is meant to be strong: every conformance case the analyzer
tree changed in between was being checked against the old answer.
The example fixtures under example/ move to the same floor, so the
generator's own end-to-end tests measure the interpreter the generator is
developed against rather than an arbitrary older one.
No generator behaviour changes. Suite: 936 passed / 3 failed at tom_d4rt
1.22.0 before the raise, 939 passed / 0 failed at 1.30.1 after it. Same 939
tests both times; the three that failed before were 30-second
TimeoutExceptions under full-suite load, not assertions. So the interpreter
changes across 1.23.0..1.30.1 are all invisible to this package.
1.15.3 #
Fixed — a relative scan root emitted unrooted source URIs (GEN-125) #
GEN-123 let d4rtgen -s . reach the analyzer; this fixes what it then wrote.
_getPackageUri maps a source file to the URI embedded in the generated
bridge, and every probe it uses looks for a leading-separator segment:
normalizedPath.indexOf('/lib/')
normalizedPath.indexOf('/test/')
normalizedPath.contains('/sky_engine/lib/')
A relative path can never contain one, so lib/tom_d4rt_cli_api.dart matched
nothing and fell through to the raw-path fallback. The visible damage was in
bridgeReExports(), whose source is the key registerLibraryReExport looks
up when a script imports the barrel:
- (source: 'package:tom_d4rt_dcli/tom_d4rt_cli_api.dart', …)
+ (source: 'lib/tom_d4rt_cli_api.dart', …)
No importer can ever match lib/…, so every re-export declared by a barrel
regenerated from a relative scan root was silently dead.
This is GEN-123's rule at a second call site — normalizing is not rooting —
so the fix is the same shape: root a relative path against
Directory.current before the probes run. Only genuine filesystem paths are
rooted; callers that pass an already-resolved package:/dart: URI are left
alone, and the scheme test requires two or more characters before the colon so
a Windows drive prefix (C:/Code/x) still reads as a path.
Covered by G-PURI-03 (relative path → package URI) and G-PURI-04
(resolved URIs pass through untouched).
1.15.2 #
Fixed — a relative scan root silently dropped every user bridge (GEN-123) #
d4rtgen -s . aborted on the one corpus project that carries user bridges:
Error processing ./d4rt_userbridges_sample: Invalid argument(s):
Only absolute normalized paths are supported: d4rt_userbridges_sample
The same command with -s "$(pwd)" succeeded, so the defect was never in the
project — it was that a relative project directory reached the analyzer.
The analyzer requires every path it is given to be absolute and normalized. That is one rule with two independent halves, and it was written out by hand at five call sites. Three of them satisfied only the second half:
final normalizedProjectDir = p.normalize(projectDir); // WRONG
p.normalize collapses . and ..; it does not make a path absolute. The two
correct sites additionally joined against Directory.current.path. Only a
project carrying lib/src/d4rt_user_bridges/ could trip it, because everywhere
else the context is built from an already-absolutised path.
Fixed by extracting analysisIncludedPath() (lib/src/analysis_paths.dart),
which states the whole contract once, and using it at every site. Applying it
turned up a sixth site the audit had missed — the per-package context in
bridge_generator.dart, which derives its root by walking up from a file path
and so inherits that path's shape.
The first fix was incomplete, in the more dangerous direction. Absolutising
includedPaths stopped the crash but merely moved the failure downstream:
contextFor and getResolvedLibrary were still handed relative paths, threw
the same ArgumentError, and the throw was swallowed by a verbose-gated
catch. A relative-root run then found 0 user bridges where an absolute
run found 4, and emitted bridges missing every override — no error, no warning,
wrong output. Three changes close that:
- The scan directories are derived from the absolutised root, so every collected file path is absolute by construction rather than by remembering to convert it at three later call sites.
- Resolution failures are no longer
verbose-gated. A user-bridge file that exists on disk but does not resolve is a wrong-output condition, not a debug detail. - Finding files but registering nothing now warns. Under the previous
> 0-only reporting that state was indistinguishable from a project with no user bridges at all, which is precisely why the regression was silent.
Changed — the two user-bridge pre-scans are one function #
bridge_api.dart and v2/d4rtgen_executor.dart each carried their own ~60-line
copy of the pre-scan, differing only in log wording and in which warnings were
verbose-gated — which is how one copy came to swallow errors the other
reported. Both now call preScanUserBridges()
(lib/src/user_bridge_prescan.dart).
Tests #
test/gen123_analysis_included_path_test.dart (6 tests). G-GEN123-01..03 cover
the helper, including a behavioural check that the analyzer still rejects
p.normalize(relative) — so the suite cannot pass for the wrong reason if the
helper is ever reduced back. G-GEN123-06 pins the property the silent-zero
violated: a relative and an absolute project dir must find the same number of
user bridges. Asserting "does not throw" would have passed throughout the
broken window. G-GEN123-04/05 are structural gates: a seventh hand-derived
includedPaths argument, or a bare p.normalize on a project path, fails the
suite.
1.15.0 #
Added — the generated bridge is now really analysed, not proxied (GEN-121) #
test/gen121_generated_output_analyze_gate_test.dart generates a bridge into a
throwaway package and runs dart analyze on it, with no
analysis_options.yaml in a position to exclude the output.
This replaces a standing compromise. The GEN-119 smoke gate asserts a property
of the emitted file — every import URI must be absolute — because analysing an
emitted file standalone appeared unviable: without a package config, every
package: import fails too and the report is all noise (measured: 38 issues,
every one of them downstream of a missing import). The proxy caught the defect
that shipped, but GEN-120 then walked straight past it, so the gap was not
hypothetical.
Two things turn that noise into signal, and only the first is obvious:
- A synthesised
.dart_tool/package_config.jsonbeside the output. It is built by copying the generator's own resolved config — whose entries carry absolutefile://roots and so stay valid anywhere — and appending the fixture package. - A fixture that is a real package. The corpus under
test/fixtures/is not: those files live outside any packagelib/, so the generator cannot emit an import for the very types it bridges and no package config can rescue the output.
The fixture is created in Directory.systemTemp, and that is load-bearing
rather than tidiness: an ancestor pubspec.yaml in the workspace tree hijacks
resolution for anything nested under it. The identical fixture reports four
URI_DOES_NOT_EXIST errors under <ws-root>/ztmp/ and zero in the system temp
directory, so a gate placed inside the tree would have been permanently and
invisibly red.
Warnings fail the gate unless explicitly allowlisted. equal_keys_in_map —
the GEN-120 defect — is a warning, not an error, so a gate that failed only on
errors would have been blind to the defect class that motivated it. The
allowlist is currently empty; the fixture analyses completely clean.
Two of the four tests assert that the gate detects, by feeding it known-bad
output: a bare-path import must be reported (URI_DOES_NOT_EXIST, the GEN-119
shape) and a duplicate map key must be reported (EQUAL_KEYS_IN_MAP, the
GEN-120 shape). Without them a silently-neutered gate would keep reporting green.
Verified against a real regression, not just injected strings: reverting the
GEN-120 fix in bridge_generator.dart turns the gate red, while the GEN-119
property suite stays green at 9/9 — the gap, demonstrated.
The GEN-119 property assertions are kept. They are cheap enough to run over the whole fixture corpus; the analyze gate is heavier and runs on one fixture.
Fixed — an extension declared in a part of file was registered twice (GEN-120) #
tom_dist_ledger's generated bridge carried
static Map<String, String> extensionSourceUris() {
return {
'DLLogLevelExtension': 'package:tom_dist_ledger/src/ledger_api/call_callback.dart',
'DLLogLevelExtension': 'package:tom_dist_ledger/src/ledger_api/ledger_api.dart',
...
DLLogLevelExtension is declared in call_callback.dart, which is
part of 'ledger_api.dart'. The analyzer flags the duplicate map key
(equal_keys_in_map), but that is only the visible half: bridgedExtensions()
emitted the same extension twice as two complete
BridgedExtensionDefinition entries, and a duplicate element in a list literal
is perfectly legal Dart — nothing complained at all. Deduplicating the map alone
would have silenced the warning and left the double registration in place, so
the fix is at collection time.
The extension arrives by two routes that tag it with different URIs. The local
extractor sees it while extracting the parent library and tags it with the
parent's path; GEN-049 import discovery sees it again while extracting a file
that imports the parent, and tags it with
extElement.firstFragment.libraryFragment.source.uri — which for a
part-declared extension is the part's URI, not the library's. GEN-064's
dedupe key was name|sourceUri, so the two spellings read as two distinct
extensions and both survived.
The dedupe now canonicalises the URI through the part of → parent-library
resolver before keying on it, and pins that canonical URI onto the survivor.
The parent library is the only correct answer: a part file is not
independently importable, so its URI is useless to any consumer acting on it.
Deduplication also now prefers the copy carrying full MemberInfo. Import
discovery populates only methodNames, never methods, so keeping whichever
copy happened to arrive first could silently degrade callback wrapping
(GEN-052) depending on source-file ordering.
test/gen120_part_declared_extension_test.dart pins all of it, including an
anti-vacuity guard — "exactly one" passes just as happily when the extension
was dropped entirely as when it was correctly deduplicated.
1.14.0 #
Fixed — auxiliary imports could be emitted as bare file paths (GEN-119) #
tom_dist_ledger's generated bridge carried
import 'lib/src/ledger_api/ledger_api.dart' as $aux_aux;
a project-relative path where a package: URI belongs. It resolves against the
generated file's directory, so it pointed at a file that does not exist —
while the very same library was correctly emitted as
package:tom_dist_ledger/src/ledger_api/ledger_api.dart a few lines above.
The $aux_aux prefix is the fingerprint: both prefix factories derive their
base name from a package: URI and fall back to the literal aux otherwise,
so a doubled token proves a non-package URI reached the factory.
The chain: d4rtgen is invoked as d4rtgen -s ., so workspacePath is
relative. The own package's rootUri in .dart_tool/package_config.json is
../, which normalised against a relative workspace path collapses to ..
_getFilePathForPackageUri therefore returned a relative path, and when the
requested type lived in a part of file (DLLogLevel in call_callback.dart)
the part-of resolver derived the parent path relatively too. _getPackageUri
maps a path back to a package by scanning for a /lib/ segment — which a path
that starts with lib/ does not have — and returns its input unchanged when
it cannot, so a bare path entered the auxiliary-import map and was written
verbatim.
Fixed in three layers:
- Root cause —
_packageRootSyncnow returns an absolute path regardless of howworkspacePathwas spelled. - Guard — the
part of→ parent-library resolver keeps the part file's own (importable)package:URI when the parent cannot be expressed as one, instead of propagating a bare path. - Last resort — the auxiliary-import writer drops any import whose URI is
neither
package:nordart:, with a warning. Dropping degrades loudly (an undefined prefix is a compile error at the use site) rather than silently shipping an import that resolves nowhere.
Added — regen smoke gate for emitted import URIs #
test/gen119_auxiliary_import_uri_test.dart regenerates five fixtures into a
temp directory and asserts that every emitted import URI is absolute
(package: or dart:). Analysing an emitted file standalone is not viable —
without a package_config.json every package: import would fail too — so the
gate asserts the property that actually separates good output from bad. The
defect shipped precisely because the consumer's analysis_options.yaml excludes
lib/src/d4rt_bridges/**, so dart analyze reported the project clean.
G-GEN119-05 pins that user_proxy_relaxer_source.dart still drives the
auxiliary-import writer, so the gate cannot quietly become vacuous.
1.13.0 #
Added — configurable typeMappings escape hatch + additionalImports (DGU3) #
- New top-level
d4rtgen:config keys mirroring upstreamGeneratorConfig:typeMappings: Map<String,String>— substitute an awkward source type with another type at emission time. Keyed by source type name (SomeType) or its exact nullable spelling (SomeType?); the value is emitted verbatim wherever that type would appear (parameters, fields, return types, and each type argument inside generics). Applied at the single type-resolution chokepoint (_resolveTypeArgument), so it covers bare types and generic arguments. The mapped type's own bridge registration is left intact.additionalImports: List<String>— fullimportURIs added to every generated bridge file, pairing withtypeMappingswhen a substitute type lives in a package the generator would not otherwise import.
- This is the config seam behind the "fix the generator, not the generated code"
rule: a downstream package can resolve a hard-to-bridge type through
buildkit.yamlinstead of patching the generator. Both keys default to empty, so generated*.b.dartoutput stays byte-identical until a config opts in. - Wired through
BridgeConfig(field + constructor +fromJson/toJson/copyWith), theBridgeGeneratorconstructor, and both executor paths (bridge_api.dart,v2/d4rtgen_executor.dart).
Fixed — the v2 executor forwards recursiveBoundTypes (DGUB2) #
Recorded 2026-09-12 (scd11_aicx). The fix shipped in this release —
799c16893, a week before it — but no entry was written for it, so a consumer
reading the log could not tell the bug had been fixed.
D4rtgenExecutorconstructed itsBridgeGeneratorwithout passingrecursiveBoundTypes, so a project generating through the v2 path silently ignored itsbuildkit.yamlrecursiveBoundTypes:entries and fell back to the built-in defaults (num,String,DateTime,Duration,BigInt). SinceD4rtgenExecutoris the default v2 executor, this was a correctness bug rather than hygiene: the escape hatch existed in the config and did nothing.
Added — generated-code-quality regression guards (DGU4) #
- Cross-checked upstream 0.2.1's four generated-code-quality fixes against our
output. Our analyzer-driven emitter produces none of the four
anti-patterns (they targeted a heuristic generator): generic collection
extraction is already well-formed, abstract classes already strip their
generative constructors (GEN-051) and carry
isAbstract: true, no redundant?? nullis emitted, and a param namedkeykeeps its declared type rather than being force-inferred. No production change was needed. - Locked those properties in with
test/generated_code_quality_test.dart(G-DGU4-1..4) + fixture so a future emitter change cannot silently reintroduce one.
1.12.5 #
Changed — make the tom_analyzer_shared >=0.7.2 floor authoritative on pub.flutter-io.cn #
- RCJ3/RCJ6 bumped the
tom_analyzer_sharedconstraint to>=0.7.2(SDK-version partitioned summary cache + transitive-dependency bundle invalidation), but the version published on pub.flutter-io.cn (1.12.4) still carried the old>=0.6.0floor, so a fresh consumer resolving this generator from pub.flutter-io.cn was only allowed — not required — to pick a fixedtom_analyzer_shared. This patch republish makes the>=0.7.2floor the authoritative constraint every downstream consumer inherits. No code change; hardening only. (RCL2)
1.12.4 #
Fixed — silence GEN-079 warnings for dart:async SDK generics #
- The relaxer generator only builds type-relaxing wrappers for application-
package generics scanned from module barrels.
dart:corecollections (List/Set/Map/…) were already skipped silently, but thedart:asyncSDK generics that appear in bridged signatures —FutureOr,StreamSubscription,StreamConsumer,StreamTransformer,EventSink,StreamSink— were not, so each produced a noisyNo ClassInfo for generic base type "…"/… — skipping wrapperwarning even though the outcome (no wrapper) was correct and expected. These types are provided by D4rt's stdlib bridges (tom_d4rt/lib/src/stdlib/async/), not by relaxer wrappers;FutureOris a union type, not a class, and can never be wrapped at all. The relaxer skip-set (renamed_dartCoreGenericTypes→_sdkGenericTypesWithoutRelaxers) now covers both families, so these SDK generics are skipped silently. No change to generated output. New regression coverage:relaxer_sdk_generic_skip_test.dart.
1.12.3 #
Fixed — escape $-prefixed member names in all generated bridge maps #
- Completes 1.12.2: the
$-escaping was applied to the getter/setter function maps but not to the signature maps (getterSignatures,setterSignatures,methodSignatures,constructor/static*variants) nor the instance/static method and static getter/setter function-map keys. Those still emitted a raw'$sectionId'key (the signature value was already escaped), so a bridge for a class exposing a$-prefixed member still failed to compile with "Undefined name 'sectionId'". Every member-name map key now routes through_escapeString. Regression coverage added: G-CLS-7d (getter/setter signature keys) and G-CLS-7e (method map + signature keys) over a new$compute()method on the fixture.
1.12.2 #
Fixed — escape $-prefixed member names in generated bridges #
- Members whose name begins with
$(e.g. a structural accessor deliberately named$sectionIdto avoid colliding with a model field literally calledsectionId) were emitted verbatim into the single-quoted, interpolating Dart string literals the bridge uses for its getter/setter/signature map keys and for the arg-name passed toD4.extractBridgedArg*. The unescaped'$sectionId'was read by the analyzer as an interpolation and failed to compile with "Undefined name 'sectionId'"._escapeStringnow escapes$(and the getter/setter map-key and setter-cast arg-name sites route their names through it), so the key is emitted as'\$sectionId'while the member access stays the raw identifier.$sectionId. Regression coverage:class_bridge_generation_testG-CLS-7a/7b/7c.
1.12.1 #
- Track our latest published components:
tom_analyzer_sharedfloor raised to>=0.6.0(ToolCacheLocator shared tool-cache root), and all in-workspace dependencies now use lower-bound-only constraints (no upper cap) sopub upgraderesolves to our latest versions during active development.
1.12.0 #
Added — emit static enum methods into BridgedEnumDefinition (GitHub issue #2) #
- The enum extractor now collects an enum's public static methods (these
were previously dropped) and emits them into a
staticMethods:block on the generatedBridgedEnumDefinition, dispatched on the enum type (not an instance receiver). This makes static factory/helper methods likePageFormat.fromString(name)reachable from interpreted code. - Collection-typed static parameters reuse the existing coercion path via a
parameterized receiver, so
D4.coerceList/map coercion applies the same way it does for instance methods. - Requires
tom_d4rt^1.11.0(which accepts the newstaticMethodsparameter); the floor was raised accordingly.
1.11.0 #
Lazy bridge factories (import-optimization) #
- Generated per-package bridges now emit factory thunks instead of a
pre-built
List<BridgedClass>:static Map<String, BridgedClass Function()> bridgeClassThunks()— class name → deferred factory that builds one class's member maps + adapter closures on demand.static Map<String, Type> bridgeClassTypes()— class name → nativeType, so the same thunk registers into both the name-keyed and type-keyed registries with the correctsourceUri.registerBridgesnow loops the thunks through the interpreter'sregisterBridgedClassLazy(name, type, thunk, importPath, sourceUri:)instead of constructing everyBridgedClasseagerly. A script that uses N of M generated classes materializes ≈N objects, not M.bridgeClasses()is retained (eager, diagnostic) for callers that still want the full materialized list.
- The aggregator barrel (
per_package_orchestrator) now spreadsbridgeClassThunks()/bridgeClassTypes()from each per-package bridge. - Bumped
tom_d4rtconstraint to^1.9.0: the generatedregisterBridgestargets the import-optimization API (registerBridgedClassLazy) introduced intom_d4rt1.9.0 /tom_d4rt_ast0.1.9.
1.10.0 #
Analyzer 10 migration #
- Upgraded
analyzerto^10.0.0(from^8.4.1). Applied the analyzer-10 API renames the generator relied on:AnalysisContextCollectionImpl(..., packagesFile: …)→packageConfigFile: …(constructor parameter rename) inbridge_generator.dart.NamedType.name2→NamedType.nameincorpus_type_scanner.dart.- Tidied a now-flagged null-aware spread (
...?externalClassLookup) inbridge_generator.dart.Element.isSyntheticremains deprecated-but-functional under analyzer 10; it is left in place because this package already setsdeprecated_member_use: ignoreproject-wide, so no per-call migration was needed.
- Bumped
tom_d4rtto^1.8.25(first analyzer-10 build on pub.flutter-io.cn; earlier1.8.xcarryanalyzer ^8and would conflict) andtom_analyzer_sharedto^0.4.0. - Stale analyzer-8
.sumsummary bundles underexample/d4/.tom/analyzer-cache/are undecodable by analyzer 10'sbundle_reader(RangeErrorin_decodeVariance) and were poisoning the test suites. Added a.gitignorefor**/.tom/analyzer-cache/and untracked the 92 regenerable bundles; they rebuild on demand under the active analyzer.
Incorporates 1.9.9 (published out-of-band, skipping local's 1.9.8): the
tom_analyzer_shared ^0.3.0shared-tool-cache change (analyzer summaries resolved viaToolCacheLocatorinto the shared Tom tool-cache directory) is superseded here by the^0.4.0constraint, so its behaviour is retained. This release also carries the 1.9.8 const-arg suppression that 1.9.9 omitted.
1.9.8 #
Generation quality (suppress unavoidable const-arg warning) #
- Bridge
.b.dartfiles emit constructor calls that forward script-supplied runtime values intoconstconstructors (e.g.IconData(codePoint, …)). Because those arguments are only known at run time they can never be const, so the analyzer reportsnon_const_argument_for_const_parameter— an unavoidable, cosmetic warning for bridged code. Addednon_const_argument_for_const_parameterto the generated// ignore_for_file:directive so regenerated bridge surfaces analyze cleanly (previously surfaced as 3 warnings againstIconDatainwidgets_bridges.b.dartfor bothtom_d4rt_flutterandtom_d4rt_flutter_ast).
1.9.7 #
Bug fixes (AllBridge import surface dropped under single-package inline) #
- GEN-077 — the generated dartscript helper calls
AllBridge.getImportBlock()andAllBridge.subPackageBarrels()unconditionally, but the AllBridge emitter gated both methods behindif (importBlockUri != null)whilesourceLibraries()was always emitted. In the single-package inline strategy (sourceImportunset, soimportBlockUriresolves tonull), AllBridge declaredsourceLibraries()but neithergetImportBlock()norsubPackageBarrels()— and thedartscript.b.darthalf that calls them still compiled, breaking downstream consumers (tom_brain_procedure,tom_brain_run, Observatory) with "method isn't defined" errors after regeneration. The emitter now writes both methods unconditionally: when there is no resolved barrel URI it derives the import block straight from the canonicalsourceLibraries()URIs and returns an emptysubPackageBarrels(). Adds the GEN-077 regression tests (G-ISS-37/38/39) that forceimportBlockUri == null.
1.9.6 #
Bug fixes (empty-Set default coercion) #
- GEN-SET — a
Setparameter whose default was a bareconst {}(e.g.TomCommandParser({Set<String> additionalCommands = const {}}),ParsedCommand({Set<String> flags = const {}})) emitted the default unchanged. Dart parses bareconst {}as an empty Map (static typeObject), so the generated constructor call failed withargument_type_not_assignable("The argument type 'Object' can't be assigned to the parameter type 'Set
1.9.5 #
Bug fixes (AOT-only bridge compile defects) #
Two defects surfaced by tom_core_d4rt's bridge corpus, visible only at
dart compile exe (masked from dart analyze by the generated
ignore_for_file header):
- B3b — generic methods whose callback parameter returns the method's
own type parameter (e.g.
FutureOr<T> Function(MySQLConnection)inMySQLConnectionPool.withConnection<T>/transactional<T>) failed withFutureOr<Object?> Function(X) can't be assigned to FutureOr<Object> Function(X): the callback wrapper resolvesTtoObject?but generic inference picked a non-nullableObject. The generated method call now pins explicit type arguments (each parameter's bound, orObject?when unbounded) via_methodCallTypeArgswhen a function-typed parameter references the method type parameter, bypassing inference. - B4 — package-URI resolution used a hardcoded sub-workspace
searchDirslist that omitteddistributed/, sopart offiles in packages outside that list were imported directly ("has a 'part of' declaration"). Resolution now goes through.dart_tool/package_config.json(_packageRootSync) first, covering every package in the resolution graph, with the directory scan kept as a fallback.
Also removes leftover GEN-060 debug prints. Adds the G-CB-13 regression test.
1.9.4 #
- Housekeeping: test artifacts now live in a gitignored
testlog/folder;doc/no longer ships machine-generated baselines or last_testrun.json. No code changes.
1.9.3 #
Generated-code hygiene #
- Emit expanded
// ignore_for_file:headers in generated*.b.dartbridges so generated bridge corpora (includingtom_d4rt_flutter) are analyzer-clean without per-file hand edits.
Documentation #
- Consolidated proxy/relaxer manual-intervention guidance into
doc/user_proxy_relaxer_annotations.mdand the baseline docs; README aligned with the source-primary documentation reframe.
1.9.2 #
Generator features #
- Annotation-driven proxy/relaxer directive core + scanner (
@D4rtUserProxy/@D4rtUserRelaxer) with a variant-pattern engine. - Template families: B3 generic-constructor reifiers, A4 RenderBox-proxy, super-constructor-arg capture factories, generic-type-arg proxy variants, State-proxy mixin variants, generic interceptor re-dispatch.
genericInterceptorsconfig wired intoBridgeConfig; VM↔web signature-skew coercion table.yieldVoidCallbacksswitch for cooperative input/frame yield (OPEN B.14): void callback wrappers emitted as async closures awaiting a 1ms delay.- Per-symbol
@Deprecatedallowlist; opt-invector_math_64bridge.
Dependency #
- Require
tom_d4rt ^1.8.21.
1.9.1 #
Fix — build_runner path emits a compiling dartscript.b.dart #
The build_runner / orchestrator code path previously produced a
dartscript.b.dart that could not compile, because:
- the delegating barrel (
<Module>Bridge) was missing thesubPackageBarrels()method the shared dartscript template calls unconditionally, and relaxers.b.dart(imported by the dartscript template whenever the config has modules) was never generated on the build_runner path.
Both halves are fixed: the orchestrator's delegating barrel now emits
subPackageBarrels() (primary package excluded, sub-package barrel URIs
listed), and the build_runner path now generates relaxers.b.dart —
falling back to a resolvable no-op stub (registerRelaxers() /
registerGenericConstructors()) when there are no extraction sites. The
standalone/CLI and build_runner paths now emit interchangeable
registration code. Covered by a new regression test
(test/build_runner_dartscript_compile_test.dart) that assembles the
build_runner artifacts and asserts dart analyze reports no errors.
1.9.0 #
Refactoring — summary-backed extraction migration (Phases 1–6) #
Completes the multi-phase migration from dual-path (AST + element) bridge
extraction to a single element-mode code path backed by analyzer .sum
summaries. See doc/summary_refactoring_plan.md for the full plan and
doc/baseline_summary_refactor.md for the regression oracle.
- Phase 1:
ElementModeExtractorreaches output parity with the legacy AST_ResolvedClassVisitor(type aliases, inheritance resolution, default values, metadata, inherited members, substitution). - Phase 2:
BridgeGeneratorroutes every package through the element walker by default; summary-cache stage runs before scanning so external deps resolve from.sumbundles. - Phase 3: Default-value rendering and annotation-arg serialization unit tests lock the extractor API.
- Phase 4:
ProxyGeneratormigrated to the shared element-mode path. - Phase 5:
UserBridgeScannermigrated toLibraryElementwalker; legacyRecursiveAstVisitor<void>path removed. - Phase 6: Deleted the AST extraction path entirely —
_ResolvedClassVisitor(~2,200 lines),_ClassVisitor,_ParsedClass, theuseLegacyAstWalkerdebug flag, andsummary_exclusion.dart.lib/src/bridge_generator.dartdropped from 16,678 → 13,602 lines (−3,076 lines vs the plan's ≥1,800-line target).TOM_D4RT_BRIDGE_USE_SUMMARIESenv-var scaffolding removed.
Maintenance #
- Update dependency on tom_d4rt 1.8.19 (type matching, enum handling, isSubtypeOf, stdlib fixes) — carried over from 1.8.24.
Compatibility #
- Public generator API is unchanged. Generated bridge output for
tom_d4rt_fluttermis byte-identical to the pre-migration baseline modulo theGenerated: <timestamp>header (per Phase 6 exit check). - All five consumers documented in
baseline_summary_refactor.md(flutterm, dcli, exec, dcli_exec, tom_d4rt) match their Phase 0 test baselines — no new regressions.
1.8.24 #
Maintenance #
- Update dependency on tom_d4rt 1.8.19 (type matching, enum handling, isSubtypeOf, stdlib fixes)
1.8.23 #
Bug Fixes #
- RC-2: Fix inline function types with type params (e.g.,
Object? Function(T)) — cast todynamicto bypass static type checking since analyzer expands typedef aliases to inline form which can't be cast back to the typedef - RC-2: This fixes the remaining 3,208 compile errors in flutter_relaxers.b.dart (total error reduction: 441,443 → 0)
1.8.22 #
Bug Fixes #
- RC-2: Comprehensive fix for generic constructor param type handling — reduced compile errors from 441K to 3K (99.3%)
- RC-2: Extended
_rc2SkipTypeswithFutureOr, type param names (T/E/K/V/R/S), and vector_math types - RC-2: Improved
isTypeParamTypedto detect types containing type params (e.g.,MessageCodec<T>) - RC-2: Fixed bounded type params —
Objectbound now correctly excludesdynamicfallback - RC-2: Add
!assertion for non-nullable params since extraction produces nullable values - RC-2: Proper type substitution and casting for params containing type params
1.8.21 #
Bug Fixes #
- RC-2: Fix nullable param passing in
_writeRC2Case()— add!assertion for required non-nullable params - RC-2: Add missing types to
_rc2SkipTypes(meta annotations, vector_math types not imported in relaxer output)
1.8.20 #
1.8.19 #
Bug Fixes #
- RC-2: Wire
registerGenericConstructors()andregisterRelaxers()into dartscript registration - RC-5: Fix 11 misleading comments in annotation filtering (actual: @internal/@visibleForOverriding/@mustBeOverridden only)
1.8.17 #
Features #
- GEN-100d: Auto-generate function typedef registrations from source
- GEN-083: Proxy/adapter class generator for abstract delegates (CustomPainter, CustomClipper, etc.)
- GEN-082: Setter
sourceFilePathfix + resolved 14 skipped and 3 failed tests - GEN-081: Generator now emits
isAssignablecallback inBridgedClassconstructors
Bug Fixes #
- GEN-100: Follow-up fix for secondary_classes_test failures
- Auto
dart pub getfor barrel resolution - Pass
d4rtImportfrom config ingenerateBridges() - Dart format cleanup
1.8.15 #
Bug Fixes #
- GEN-075: Fixed required nullable argument handling — generates null-safe parameter extraction
- GEN-076: Raised non-wrappable default threshold from 4 to 8 to reduce combinatorial explosion
1.8.14 #
Bug Fixes #
- GEN-077: Skip same-package re-exports to prevent duplicate bridge generation (e.g., Tween only in animation module)
- GEN-078: Collect deprecated non-function type aliases (e.g.,
MaterialStateProperty)
1.8.13 #
Added #
- GEN-074 (
bridge_generator.dart) — Added support for type aliases (non-function typedefs) bridging:- New
visitGenericTypeAliasin_ResolvedClassVisitorcollects type aliases liketypedef MaterialStateProperty<T> = WidgetStateProperty<T> - Generated
classAliases()method returns a map of alias name to target class name registerBridges()now automatically registers class aliases with the interpreter- Enables D4rt scripts to use type aliases like
MaterialStatePropertythat resolve to their target classes
- New
Tests #
gen074_type_alias_test.dart— Unit tests for type alias detection and code generation (8 tests)
1.8.12 #
Fixed #
- GEN-073 (
bridge_generator.dart) — AddedIteratorto the list of built-in types that don't need import prefixes. This fixes compile errors in generated code whereIterator<E>was incorrectly prefixed with Flutter imports (e.g.,$flutter_3.Iterator).
1.8.11 #
Fixed #
- GEN-072 (
bridge_generator.dart) — Fixed export detection bug where a direct export (no show/hide clause) didn't override a restrictive re-export from the same package when processed in wrong order. This caused classes like Flutter'sCurvesto be incorrectly marked as "not exported from barrel file" even though they were directly exported. The fix addsisCurrentMorePermissive && !isExistingMorePermissivecheck toshouldOverridelogic.
Added #
gen072_permissive_override_test.dart— Unit tests for GEN-072 fix, verifying permissive exports override restrictive same-package re-exports.
1.8.10 #
Fixed #
- GEN-071 (
bridge_generator.dart) — Fixed required nullable parameters incorrectly rejecting null values. Parameters marked asrequired+ nullable now correctly accept explicit null.
1.8.8 #
Added #
bridge_generator.dart— Dynamic member dispatch for ~24 Flutter access-restricted members (e.g.initState,dispose,build,activate) using(t as dynamic).memberfallback to avoid compile errors in generated bridge code.bridge_generator.dart— Protected override filtering: skips unannotated overrides of protected/visibleForTesting base methods.bridge_generator.dart— Extendedignore_for_filedirective withimplementation_imports,sort_child_properties_last,non_constant_identifier_names,avoid_function_literals_in_foreach_calls.file_generators.dart— Addedignore_for_file: avoid_printto generated test runner files.
Fixed #
- Callback wrapper return cast: only skips redundant
as Object?cast when original return type isdynamic/Object/Object?, preventing type errors on typed callbacks.
1.8.5 #
Added #
bridge_config.dart— Newd4rtImportfield onBridgeConfigto configure the D4rt runtime import path. Defaults topackage:tom_d4rt/d4rt.dart. Enables generating bridges for alternative runtimes (e.g.package:tom_d4rt_exec/d4rt.dart).d4rtgen_tool.dart— AddedworksWithNatures: {DartProjectFolder}to tool definition.
Changed #
bridge_generator.dart— Uses configurabled4rtImportinstead of hardcodedpackage:tom_d4rt/d4rt.dartimport.file_generators.dart— Dartscript file generator usesconfig.d4rtImportfor the runtime import.per_package_orchestrator.dart— Minor formatting cleanup.d4rtgen_executor.dart— Minor formatting cleanup.- Renamed
version.g.dart→version.versioner.dart.
1.8.4 #
Bug Fixes #
- GEN-070: Fixed barrel export bug for multi-chain re-exports — when a symbol is exported through multiple barrel chains (e.g.,
Findclass via direct dcli_core export AND indirect find.dart re-export), the show clauses are now unioned instead of the second chain being blocked by the visited set
Tests #
- Added
gen070_reexport_show_test.dartwith 3 tests for multi-chain re-export scenarios - All 464 tests pass
1.8.3 #
Architecture #
- v2 ToolRunner migration: Refactored d4rtgen CLI to use v2 ToolRunner framework (
D4rtgenTool,D4rtgenExecutor) for better code organization and testability
Bug Fixes #
- GEN-064: Fixed duplicate extension keys in generated bridge files — extensions are now deduplicated by fully qualified key before generation
- GEN-065: Fixed type resolution for cross-file references — types defined in one file but used in another now correctly resolve prefixes
- GEN-066: Fixed extension target resolution when the target type is parameterized with types from other files
- GEN-067: Fixed resolution of types in generic bounds that reference cross-file definitions
- GEN-068: Fixed method return type resolution when the return type is from a different source file than the method declaration
- GEN-069: Fixed parameter type resolution for callbacks and function types that reference cross-file types
Tests #
- Added
cross_file_type_resolution_test.dartwith 132 lines of new test coverage - Added
d4rtgen_traversal_test.dartwith 236 lines validating v2 traversal logic - All 461 tests pass
1.8.1 #
Bug Fixes #
- GEN-058: Fixed nullable generic type resolution — types like
List<RuntimeType>?now correctly retain the?suffix when resolved through_resolveGenericTypeWithPrefixes - GEN-059: Fixed extension filtering — extensions whose target type (
onTypeName) isn't among bridged classes/enums or built-in types are now filtered out before generation, preventing runtime errors for unresolvable extension targets - Multi-barrel registration: Added
subPackageBarrels()static method to bridge classes and registration loop in dartscript.b.dart. This enables imports likeimport 'package:dcli_core/dcli_core.dart'to work when the primary package isdcli— the module loader now finds content under sub-package URIs - Content-based barrel filtering:
getImportBlock()andsubPackageBarrels()now use content-based filtering (derived from actual bridged class/enum/function/extension source URIs) instead of type-reference-based filtering. This prevents including packages that are only type-referenced but not bridged (e.g., crypto with skipReExports)
1.8.0 #
Architecture #
- Direct source file imports: Generator now imports source files directly (
import 'package:<pkg>/<path>.dart' as $<pkgname>_<N>) instead of relying solely on barrel exports. This resolves issues with types not being accessible through barrel files and eliminates prefix collisions across packages.
Bug Fixes #
- GEN-055/056: Fixed type dependency resolution and extension on-type URI resolution for cross-package types
- GEN-057: Fixed return type bridging and prefix stripping in API surface dependencies — return types now correctly use the source file's own import alias
- Part-of files: Fixed prefix resolution for
part offiles and extensions whose on-type comes from a different package - G-DCLI-05/07/08/11/12/13/14: All DCli bridge issues resolved — show/hide clause propagation, callback bridging, and DCli-specific type handling
Tests #
- Updated 46 test expectations to match new direct source import generation patterns (
$<pkgname>_<N>prefixes andD4.callInterpreterCallback) - All 444 tests pass
1.7.0 #
Bug Fixes #
- G-DCLI-07/11: Show/hide clause propagation: Fixed export parsing to properly propagate show/hide clauses when following re-exports. When a barrel file re-exports from another package with a
showclause (e.g.,export 'package:dcli_core/dcli_core.dart' show FindItem), nested exports now correctly filter symbols. This fixes cases where dcli'sfind()was incorrectly bridged from dcli_core (callback-based) instead of dcli's own version (returnsFindProgress).- Added
mergeWithParent()method toExportInfofor clause merging - Added
parentShowClause/parentHideClauseparameters toparseExportFiles() - Show clauses merge via intersection; hide clauses merge via union
- Added
1.6.1 #
Bug Fixes #
- SDK path detection: Compiled d4rtgen binaries now correctly locate the Dart SDK. The analyzer's default SDK detection fails for compiled binaries because
Platform.resolvedExecutablereturns the binary path instead of the Dart executable. Added_getSdkPath()method that checksDART_SDKenvironment variable first, then derives SDK fromdartin PATH (handles Flutter's embedded SDK structure).
1.6.0 #
Features #
- Record type support (G-TYPE-1, G-TYPE-2): Full support for Dart records as function parameters and return types. The generator emits inline conversion code:
- Parameters:
InterpretedRecord→ native Dart record at call sites - Returns: Native Dart record →
InterpretedRecordfor interpreter access - New helpers:
_isRecordType(),_parseRecordType(),_generateRecordParamExtraction(),_generateRecordReturnWrapper()
- Parameters:
Bug Fixes #
- G-TE-1: Added
sourceFilePathparameter to global function type resolution. Type bounds in generic parameters now resolve correctly for global functions. - G-TE-2: Fixed type erasure test expectations — import prefixes for non-barrel-exported types now correctly use auxiliary prefixes.
- G-OP-8: Fixed barrel export collision —
Pointclass now exports fromrun_static_object_methods.dart(which hasoperator ==,hashCode,toString) instead ofrun_constructors.dart. - GEN-045: Barrel name collision for constrained mixins resolved as side effect of G-OP-8 fix.
Tests #
- All 431 tests now pass (was 430 pass, 1 fail)
- Full dart_overview coverage suite validated
1.5.2 #
Bug Fixes #
- GEN-049: Extension methods on bridged classes from imported libraries are now discovered. The generator walks the import tree of each source file to collect extensions from imported packages. This enables D4rt scripts to call extension methods from packages like
package:collectionwhen they are in scope. - GEN-048: Pure
mixindeclarations are now bridged. Previously onlymixin classdeclarations were handled. Mixins are bridged as abstract classes without constructors, including their methods, getters, setters, and fields. - GEN-020: Global exclusions no longer merge across modules. Each module's exclusions now apply only to packages belonging to that module, preventing accidental cross-filtering.
- GEN-046: GlobalsUserBridge overrides now work correctly. Fixed example project annotations and method signatures. The generator already correctly wired up overrides—the issue was missing
@D4rtGlobalsUserBridgeannotations in user code. - GEN-007: Expanded
_knownFunctionTypeAliasesfrom 7 to ~50 common function type aliases. Now covers D4rt, Dart core, Flutter, and async package types for better function type detection in syntactic fallback. - GEN-009: Improved
_isGenericTypeParameter()heuristic to recognize multi-character type parameter patterns likeT1,T2,K2,V2andTValue,TOutput,TState, etc. Eliminates false "Missing export" warnings. - GEN-021: Verified this issue is already resolved — no builder-skip logic exists in the current codebase.
- GEN-011: Global function/variable generation counts now report actual values instead of hardcoded 0.
- GEN-013: Verified already resolved — approximate class count (files × 10) pattern no longer exists.
- GEN-019: Barrel preference now prioritizes primary barrel (
barrelImport) over same-package barrels for consistent$pkgprefix usage. - GEN-008: Expanded
mapPrivateSdkLibrary()from 6 to 20+ entries covering common SDK private libraries. Added optional warning callback for unknown libraries. - GEN-025: Enhanced record type resolution to handle named field groups
({int x, String y})and mixed positional/named fields. - GEN-027: Added explicit
InvalidTypehandling in_collectInfoFromDartType()to gracefully skip analyzer resolution failures.
New Features #
_collectExtensionsFromImports(): New function that walks library imports and collects visible extensionsvisitMixinDeclaration(): Added to both visitors to handle pure mixin declarations_getExclusionsForPackage(): New helper that returns exclusions scoped to a package's owning modules- Verbose mode shows
GEN-049: Discovered extension {name} on {type} from import {uri}messages
Example Fixes #
userbridge_override: Added missing@D4rtGlobalsUserBridgeand@D4rtUserBridgeannotationsuserbridge_override: FixedMyListUserBridgeoperator override signatures
Tests #
- Added
test/import_extension_discovery_test.dart— 5 tests for import-based extension discovery - Added
test/fixtures/external_extensions.dartandtest/fixtures/imports_external_extensions.darttest fixtures - Added
test/mixin_bridge_generation_test.dart— 12 tests for mixin bridging - Added
test/fixtures/mixin_test_source.darttest fixture with pure mixin declarations
1.5.1 #
Documentation #
- Config filename standardization: Updated all documentation references from
tom_build.yamltobuildkit.yaml. All CLI help text, README, user guides, and code comments now use the current filename.
Internal #
d4rt_gen.dart: CLI help text and print statements referencebuildkit.yaml_printBuildYamlSection(): UsesTomBuildConfig.projectFilenameconstantBuildConfigLoader: Updated doc comments
Dependencies #
- Updated
tom_build_baseto^1.3.2(buildkit.yaml references)
1.5.0 #
Features #
- Test infrastructure: New
testing.dartlibrary withD4rtTester— run D4rt test scripts that verify bridge correctness by executing DartScript code against real bridges. - D4rtTestResult: Structured pass/fail/skip/error results with detailed assertion messages for programmatic test evaluation.
- IssueTestHelper: Specialized test helper for writing regression tests against known generator issues (GEN-xxx).
- 94 D4rt test scripts: Comprehensive test coverage across 6 example projects — constructors, fields, methods, operators, generics, inheritance, parameters, async, enums, and UserBridge overrides.
- Test coverage documentation:
doc/test_coverage.mdwith feature inventory across 10 categories.
Refactoring #
- CLI scanning replaced with ProjectDiscovery: Eliminated ~200 lines of manual directory traversal in
d4rt_gen.dart, replaced withProjectDiscovery.resolveProjectPatterns()andscanForProjects()fromtom_build_base. - Removed dead CLI code: Deleted unused
d4rt_generator_cli.dart(274 lines) andcli.dartbarrel export. - Shared YAML utilities: Replaced private
_yamlToJson/_yamlListToJsoninBuildConfigLoaderwith sharedyamlToMap()fromtom_build_base.
Documentation #
- Expanded
doc/issues.mdto 46 documented issues (GEN-001 through GEN-046). - Added
doc/test_coverage.md— full bridge generator feature inventory with pass/fail status. - Added project-level
_copilot_guidelines/testing.md.
Dependencies #
- Updated
tom_build_baseto^1.2.0(addsyamlToMap/yamlListToListutilities).
1.4.0 #
Features #
- CLI: buildkit.yaml support: The
d4rtgenCLI now reads configuration frombuildkit.yamlfiles (in addition tobuild.yamlandd4rt_bridging.json), using the sharedtom_build_baseinfrastructure. - CLI: Multi-project and glob support:
--projectoption now accepts comma-separated lists and glob patterns (e.g.,--project=tom_*_builder,xternal/tom_module_*/*). - CLI:
--listflag: List discovered projects without generating bridges. - CLI: ProjectDiscovery integration: Proper scan vs recursive semantics — scans directories until a project boundary, recursive mode also looks inside projects for nested subprojects.
- Known issues documentation: Comprehensive
doc/issues.mddocumenting 30 known issues and limitations with concrete cause→effect examples from real generated bridge code.
Bug Fixes #
- Multi-barrel registration (GEN-030): Modules with multiple barrel files (e.g.,
dcli.dart+dcli_core.dart) now register bridges under ALL barrel import paths. Previously only the primarybarrelImportwas registered, causingSourceCodeException: Module source not preloaded for URIwhen scripts imported secondary barrels. - CLI export filtering params (GEN-028): CLI code path now passes
followAllReExports,skipReExports,followReExports, andexcludeSourcePatternsfrom module config to the generator. Previously these were silently ignored, causing the CLI to follow all re-exports regardless of configuration. - CLI global export filtering (GEN-029): CLI code path now filters global functions, variables, and enums by barrel export show/hide clauses, matching the build_runner path behavior. Previously the CLI would generate bridges for non-exported globals, causing compile errors.
- Import block for multi-barrel modules:
getImportBlock()now returns import statements for all barrel files, not just the primary barrel.
Dependencies #
- Added
tom_build_base: ^1.0.0as a pub.flutter-io.cn dependency (replaces path dependency).
Documentation #
- Added
doc/issues.mdwith 30 documented issues (GEN-001 through GEN-030) including concrete source→bridge→problem examples. - Updated
doc/d4rt_generator_cli_user_guide.mdwithbuildkit.yamlconfiguration and multi-project/glob support.
1.3.0 #
Features #
- Per-package bridge generation: Generate separate bridge files per package to improve code organization and enable deduplication
- Cross-package support: Package URI support for generating bridges that reference types from other packages
- Bridge deduplication: Automatic deduplication for enums, variables, and global functions with
sourceUritracking - Element-aware exclusions: Exclude specific elements by source file pattern
- Show/hide filtering: Filter enums, functions, and variables with show/hide lists
- Callback wrapping: Automatic wrapping of function-type parameters for proper bridge integration
- Improved dartscript generation: Generated file headers and stdlib imports in dartscript output
Bug Fixes #
- Fixed type erasure for complex generic types
- Fixed dartscript.dart generation for cross-package scenarios
- Fixed unwrappable defaults using combinatorial dispatch
- Fixed typedef callback wrapping
- Fixed auxiliary import resolution for complex dependency graphs
- Fixed Windows filename compatibility (renamed files with invalid characters)
Breaking Changes #
- Per-package generation is now the default behavior
- Generator output structure may differ from 1.2.x for multi-package projects
Internal #
- Consolidated duplicated file generation code into file_generators.dart
- Improved error aggregation for bridge registration failures
- Refactored type resolution for better accuracy
1.2.0 #
Features #
- GlobalsUserBridge: New override system for top-level global variables, getters, and functions
overrideGlobalVariableXxx- override global variable valuesoverrideGlobalGetterXxx- override global getters with lazy evaluation functionsoverrideGlobalFunctionXxx- override global function implementations
- Getter vs Variable distinction: Generator now correctly uses
registerGlobalGetterfor top-level getters (lazy evaluation) andregisterGlobalVariablefor constants/variables - Operator overrides enabled: Removed outdated skip for operator UserBridge overrides - operators are now fully supported
Documentation #
- Updated bridgegenerator_user_guide.md with GlobalsUserBridge documentation
- Updated bridgegenerator_user_reference.md with global override reference
- Added global overrides section to userbridge_override_design.md
1.1.2 #
Changes #
- Repository reorganization: Moved to tom_module_d4rt repository as part of modular workspace structure
- Updated repository URL to https://github.com/al-the-bear/tom_module_d4rt
- Package now published to pub.flutter-io.cn (removed
publish_to: none) - followReExports feature: Bridge generator can now follow re-exports from external packages
1.1.1 #
Fixes #
- Operator argument typing: Operator bridges now use
D4.getRequiredArg<T>()for properly typed argument extraction - This ensures type-safe operator implementations (e.g.,
operator+extractsotheras the correct type) - Affected operators:
[],[]=, and all binary operators (+,-,*,/,%,&,|,^,<<,>>,>>>,<,>,<=,>=,==)
1.1.0 #
Features #
- Operator bridging: Full support for all Dart operators (+, -, *, /, [], []=, ==, <, >, etc.)
- UserBridge override system: Selective method overrides via
*UserBridgecompanion classes extendingD4UserBridge - Override individual constructors, getters, setters, methods, and operators while generating the rest
Documentation #
- Added comprehensive operator override reference
- Added UserBridge override design documentation
1.0.1 #
- Fix: BuildRunnerFileWriter now writes directly to filesystem for
build_to: sourcecompatibility - This fixes
UnexpectedOutputExceptionwhen using build_runner integration
1.0.0 #
- Initial version.