Skip to content

feat(mix_generator): derive nested styler types for StyleSpec fields by convention#961

Merged
leoafarias merged 4 commits into
mainfrom
feat/nested-styler-convention
Jul 11, 2026
Merged

feat(mix_generator): derive nested styler types for StyleSpec fields by convention#961
leoafarias merged 4 commits into
mainfrom
feat/nested-styler-convention

Conversation

@leoafarias

Copy link
Copy Markdown
Member

Problem

A @MixableSpec field typed StyleSpec<XSpec> only gets a styler-typed API (XStyler constructor param, setter, field factory, Prop.maybeMix merge semantics) when annotated @MixableField(setterType: XStyler). That override cannot work for same-package generated stylers: annotation type arguments are resolved by the analyzer while the generator runs, and build phases hide later-phase outputs from the resolver — so the type never resolves, setterType silently reads as unset, and the field degrades to a raw StyleSpec param with Prop.maybe value semantics (replacement instead of Mix merge accumulation). No error, wrong merge behavior.

This is the standard downstream-aggregator shape: any package composing its own specs (e.g. SuperDeck's SlideSpec nesting MarkdownAlertSpec, whose stylers this same generator emits) hits it. Reproduced end-to-end in a scratch package before writing this — first build and every rebuild silently degrade.

Change

Derive the nested styler type by the same XSpec → XStyler naming convention that already names generated styler classes (deriveStylerName). Resolution order in SpecStylerClassBuilder:

setterType overridecurated list mixcurated mixTypeForStyleSpec<XSpec> convention (new) → raw fallback

The derivation is pure string manipulation by design: it needs no analyzer resolution, so it works for same-package generated stylers. It cannot be validated at generation time (that's the point), so a non-conventional styler name surfaces as an analyzer error in the generated output — loud, at compile time — rather than a silent semantic change. setterType still overrides the convention for resolvable types.

Breaking

Specs with bare StyleSpec<XSpec> fields previously generated raw StyleSpec constructor params; those now take XStyler. Call sites passing resolved StyleSpec values must pass the styler instead. Flagged in the changelog under the unreleased 2.1.2.

Verification

  • All 292 mix_generator tests pass; two new smoke tests cover the convention, including the same-package case where the nested styler is emitted by this same builder run and can never resolve.
  • Existing setterType tests unchanged and passing (override path intact).
  • Regenerating packages/mix (78 outputs) produces zero diffFlexBoxStyler/StackBoxStyler keep their curated compound surfaces (partial-match test updated: nested field now gets its derived styler type, compound flattening stays inactive).
  • dart analyze + dart format clean on mix_generator and mix_annotations.

Docs

MixableField.setterType docstring now explains the convention and when the override is still needed.

Downstream

With this in a 2.1.2 release, conceptadev/superdeck#98 can drop its legacy @MixableStyler hand-written aggregator stylers and every @MixableField(setterType:) annotation, going fully spec-driven.

…by convention

Spec fields typed StyleSpec<XSpec> now generate styler-typed
constructors, setters, and field factories (XStyler, wrapped with
Prop.maybeMix) using the same XSpec -> XStyler naming convention that
already names generated styler classes. Previously these fields fell
back to raw StyleSpec params with Prop.maybe value semantics unless
annotated @MixableField(setterType:) — an override that cannot work for
same-package generated stylers, because annotation type arguments never
resolve while the generator runs (build phases hide later-phase outputs
from the resolver), silently degrading merge semantics.

The derivation is pure string manipulation by design: it needs no
resolution, so it works for same-package generated stylers, and a
non-conventional styler name surfaces as an analyzer error in the
generated output rather than a silent behavior change. setterType still
overrides the convention for resolvable types.

Breaking for specs that relied on raw StyleSpec constructor params:
pass the corresponding styler instead.

Verified: all 292 mix_generator tests pass (two new smoke tests cover
the convention, including the same-package downstream-aggregator
scenario); regenerating packages/mix produces zero diff (FlexBox and
StackBox keep their curated compound surfaces).
…dder

Unify _publicParamType/_propFactory into _publicApiFor so a field's public
parameter type and its Prop wrapper are decided by the same branch and can
never drift apart. Behavior-preserving: full workspace regen is
byte-identical.

Bump to 2.2.0: the unreleased changelog section carries a breaking FEAT,
so it must not ship as a patch over released 2.1.1.
…rings

Replace display-string parsing with a URL-checked TypeChecker and
InterfaceType inspection. One analyzer-derived fact,
FieldModel.styleSpecArgument, now feeds all three former detectors:
nested styler derivation, compound part matching, and lerp delegation.
This resolves typedef aliases, ignores same-named user classes, and
falls back cleanly on non-interface type arguments instead of emitting
invalid identifiers. Only the XSpec -> XStyler name stays a string
convention, since later-phase generated stylers are unresolvable by
design.

Test stubs now declare StyleSpec at its real package:mix path so the
URL checker matches, mirroring the existing Mix stub split. Full
workspace regen is byte-identical.
The 2.x rc cycle ships additive generator work in patch releases
(2.1.1 itself contained FEAT entries). The Breaking note stays
prominent in the changelog text.
@leoafarias
leoafarias merged commit e370896 into main Jul 11, 2026
4 checks passed
@leoafarias
leoafarias deleted the feat/nested-styler-convention branch July 11, 2026 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant