From c62e9964a669e4dcf5c83554998a49720cc2d5c6 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Thu, 16 Jul 2026 17:03:21 -0400 Subject: [PATCH 1/6] Document MixWidget and Ack advanced features --- components/HomeContent.tsx | 3 + components/MixWidgetShowcase.tsx | 95 ++++++++++++++++ components/landing/ack/AckHome.tsx | 8 +- site-tests/ack-integration.test.mjs | 15 +++ site-tests/mix-codegen-integration.test.mjs | 23 ++++ .../ack/getting-started/overview.mdx | 30 +++++ .../mix/ecosystem/mix-generator.mdx | 107 ++++++++++++++---- 7 files changed, 257 insertions(+), 24 deletions(-) create mode 100644 components/MixWidgetShowcase.tsx create mode 100644 site-tests/mix-codegen-integration.test.mjs diff --git a/components/HomeContent.tsx b/components/HomeContent.tsx index 56df0f02..4c658e0e 100644 --- a/components/HomeContent.tsx +++ b/components/HomeContent.tsx @@ -10,6 +10,7 @@ import { HeroBackground } from "./HeroBackground"; import { HighlightedCode } from "./HighlightedCode"; import Layout from "./Layout"; import { Logo } from "./Logo"; +import { MixWidgetShowcase } from "./MixWidgetShowcase"; const fadeUp = { hidden: { opacity: 0, y: 20 }, @@ -202,6 +203,8 @@ Box(style: cardStyle, child: ...)`} /> {/* Install + CTA — constrained width */}
+ + {/* Install */} +
+ Code generation +

+ Turn a Styler into a widget API. +

+

+ Annotate the style you already use. Mix generates a typed{" "} + StatelessWidget that is ready for layouts, const constructors, + and your design system's public API. +

+
+ +
+
+ + +
+
+
+ + generates + + + AppCard + + + extends StatelessWidget + +
+
+ + +
+
+ + + See the annotation and generated Dart + + → + + + + ); +} + +function CodePanel({ + eyebrow, + title, + code, +}: { + eyebrow: string; + title: string; + code: string; +}) { + return ( +
+ + {eyebrow} + +

+ {title} +

+
+ +
+
+ ); +} diff --git a/components/landing/ack/AckHome.tsx b/components/landing/ack/AckHome.tsx index 75a2709a..7fd09879 100644 --- a/components/landing/ack/AckHome.tsx +++ b/components/landing/ack/AckHome.tsx @@ -47,8 +47,8 @@ export function AckHome() {
  • Runtime-first validation
  • -
  • Structured AI output
  • -
  • Provider-ready model
  • +
  • Bidirectional codecs
  • +
  • Optional code generation
@@ -240,7 +240,7 @@ export function AckHome() {
- OPTIONAL CODE GENERATION + TYPE-SAFE CODE GENERATION

Generate types, not duplicate models.

Annotate the schema you already trust. Ack generates a lightweight wrapper with typed getters and parse helpers—without changing the runtime schema.

Generate typed schemas @@ -253,7 +253,7 @@ export function AckHome() {
- AI-READY CODECS + BIDIRECTIONAL CODECS

Decode the wire. Keep rich Dart values.

AI providers exchange JSON strings. Ack.datetime() validates that wire value, decodes it to UTC DateTime, and encodes it back for the next boundary.

Meet codecs diff --git a/site-tests/ack-integration.test.mjs b/site-tests/ack-integration.test.mjs index a95486e5..c40c8763 100644 --- a/site-tests/ack-integration.test.mjs +++ b/site-tests/ack-integration.test.mjs @@ -104,3 +104,18 @@ test('resolves every Ack documentation and landing-page link', () => { assert.deepEqual(failures, []) }) + +test('features codecs and code generation on Ack entry pages', () => { + const landing = readFileSync(ackLanding, 'utf8') + const overview = readFileSync( + join(ackContentRoot, 'getting-started/overview.mdx'), + 'utf8', + ) + + for (const source of [landing, overview]) { + assert.match(source, /Codecs|CODECS/) + assert.match(source, /Code generation|CODE GENERATION/) + assert.match(source, /\/documentation\/ack\/advanced\/codecs/) + assert.match(source, /\/documentation\/ack\/advanced\/typesafe-schemas/) + } +}) diff --git a/site-tests/mix-codegen-integration.test.mjs b/site-tests/mix-codegen-integration.test.mjs new file mode 100644 index 00000000..7fbefa6f --- /dev/null +++ b/site-tests/mix-codegen-integration.test.mjs @@ -0,0 +1,23 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { join, resolve } from 'node:path' +import { test } from 'node:test' + +const root = resolve(import.meta.dirname, '..') + +test('shows MixWidget from the Mix homepage through generated output', () => { + const homepage = readFileSync(join(root, 'components/MixWidgetShowcase.tsx'), 'utf8') + const generatorGuide = readFileSync( + join(root, 'src/content/documentation/mix/ecosystem/mix-generator.mdx'), + 'utf8', + ) + + assert.match(homepage, /@MixWidget/) + assert.match(homepage, /AppCard/) + assert.match(homepage, /\/documentation\/mix\/ecosystem\/mix-generator#mixwidget/) + + assert.match(generatorGuide, /@MixWidget\(widgetParameters: \.only\(\{'child'\}\)\)/) + assert.match(generatorGuide, /class AppCard extends StatelessWidget/) + assert.match(generatorGuide, /return appCardStyle\.call\(/) + assert.match(generatorGuide, /dart run build_runner build --delete-conflicting-outputs/) +}) diff --git a/src/content/documentation/ack/getting-started/overview.mdx b/src/content/documentation/ack/getting-started/overview.mdx index 0d549e38..44b0c722 100644 --- a/src/content/documentation/ack/getting-started/overview.mdx +++ b/src/content/documentation/ack/getting-started/overview.mdx @@ -40,6 +40,36 @@ On success, `getOrThrow()` returns your **validated data** as a `Map + + Codecs +

JSON at the edge. Rich Dart values inside.

+
+ Validate and decode strings or numbers into DateTime, Uri, Duration, enums, or your own runtime type—then encode them for the next boundary. +
+ Ack.datetime() + Explore codecs → +
+ + + Code generation +

Keep the schema. Add a typed API.

+
+ Add @AckType() to the schema you already trust and generate lightweight wrappers with typed getters, parse helpers, and no duplicated model. +
+ UserType.parse(json) + Generate typed schemas → +
+
+ ## What do you want to do? - **Validate an API response** — [Quickstart Tutorial](/documentation/ack/getting-started/quickstart-tutorial), then [JSON Serialization](/documentation/ack/essentials/json-serialization) diff --git a/src/content/documentation/mix/ecosystem/mix-generator.mdx b/src/content/documentation/mix/ecosystem/mix-generator.mdx index e105ad8f..fe0d520f 100644 --- a/src/content/documentation/mix/ecosystem/mix-generator.mdx +++ b/src/content/documentation/mix/ecosystem/mix-generator.mdx @@ -1,29 +1,32 @@ --- title: mix_generator -description: "Generate Spec, Styler, and Mix property mixins with the mix_generator build_runner package" +description: "Generate Specs, Stylers, Mix properties, modifiers, and widget wrappers with mix_generator" --- import { Callout } from "nextra/components"; # mix_generator -`mix_generator` is a [build_runner](https://pub.dev/packages/build_runner) package that generates mixins and helpers for Mix’s core types. You annotate Spec, Styler, and property (“Mix”) classes in `mix_annotations`; the builder emits part files that implement `copyWith`, `lerp`, `merge`, `resolve`, setters, and related plumbing. +`mix_generator` is a [build_runner](https://pub.dev/packages/build_runner) package that turns Mix annotations into the repetitive code you should not have to maintain by hand. It can complete Specs, create Stylers, implement Mix properties and modifiers, and turn a named Styler into a reusable Flutter widget. ## What gets generated -The package registers **three builders** (see [`build.yaml` in the mix repo](https://github.com/btwld/mix/blob/main/packages/mix_generator/build.yaml)): +The package registers **six builders** (see [`build.yaml` in the Mix repo](https://github.com/btwld/mix/blob/main/packages/mix_generator/build.yaml)): -| Builder | Triggers on | Generated part (suffix) | Typical mixin role | +| Builder | Triggers on | Generated part (suffix) | Output | |---------|-------------|-------------------------|-------------------| -| `mix_generator` | `@MixableSpec` | `.mix_generator.g.part` | Spec: `copyWith`, equality, `lerp`, diagnostics | -| `styler_generator` | `@MixableStyler` | `.styler_generator.g.part` | Styler: setters, `merge`, `resolve`, `props`, optional `call` | -| `mixable_generator` | `@Mixable` | `.mixable_generator.g.part` | Property Mix: `merge`, `resolve`, `props`, diagnostics | +| `mix_generator` | `@MixableSpec` | `.mix_generator.g.part` | Completes the immutable Spec contract | +| `spec_styler_generator` | `@MixableSpec(target: …)` | `.spec_styler_generator.g.part` | Creates a full fluent Styler class | +| `styler_generator` | `@MixableStyler` | `.styler_generator.g.part` | Supports legacy handwritten Stylers | +| `mixable_generator` | `@Mixable` | `.mixable_generator.g.part` | Implements a compound Mix property | +| `mix_widget_generator` | `@MixWidget` | `.mix_widget_generator.g.part` | Creates a `StatelessWidget` around a Styler | +| `modifier_generator` | `@MixableModifier` | `.modifier_generator.g.part` | Completes a modifier and its Mix type | [`source_gen`](https://pub.dev/packages/source_gen) combines these parts into your hand-authored `part '…g.dart';` library. ```mermaid flowchart LR - A["Annotated Dart source"] --> B["mix_generator / styler_generator / mixable_generator"] + A["Annotated Dart source"] --> B["mix_generator builders"] B --> C["*.g.part fragments"] C --> D["combining_builder"] D --> E["*.g.dart part"] @@ -34,20 +37,20 @@ flowchart LR Add runtime and dev dependencies: ```bash -dart pub add mix mix_annotations -dart pub add dev:build_runner dev:mix_generator +flutter pub add mix mix_annotations +flutter pub add dev:build_runner dev:mix_generator ``` Minimal `pubspec.yaml` shape: ```yaml dependencies: - mix: ^2.0.0 - mix_annotations: ^2.0.0 + mix: ^2.1.0 + mix_annotations: ^2.1.3 dev_dependencies: - build_runner: ^2.4.0 - mix_generator: ^2.0.0 + build_runner: ^2.11.0 + mix_generator: ^2.1.3 ``` Versions should match what your Mix release expects; check the [changelog](https://github.com/btwld/mix/blob/main/packages/mix_generator/CHANGELOG.md) when upgrading. @@ -70,9 +73,69 @@ Contributors working inside the Mix repository often use `melos run gen:build` t ## Annotations (from `mix_annotations`) +### `@MixWidget` + +Use `@MixWidget` when a named Styler should become part of your widget API. The generator derives the widget name from a top-level lower-camel-case identifier ending in `Style`: `appCardStyle` becomes `AppCard`. + +Here is the complete handwritten file: + +```dart filename="app_card.dart" +import 'package:flutter/material.dart'; +import 'package:mix/mix.dart'; +import 'package:mix_annotations/mix_annotations.dart'; + +part 'app_card.g.dart'; + +@MixWidget(widgetParameters: .only({'child'})) +final appCardStyle = BoxStyler() + .color(const Color(0xFF6750A4)) + .paddingAll(16) + .borderRounded(12); +``` + +Run the builder: + +```bash +dart run build_runner build --delete-conflicting-outputs +``` + +The generated part contains a normal Flutter widget. This excerpt is the relevant output: + +```dart filename="app_card.g.dart" +class AppCard extends StatelessWidget { + const AppCard({super.key, this.child}); + + final Widget? child; + + @override + Widget build(BuildContext context) { + return appCardStyle.call( + key: this.key, + child: this.child, + ); + } +} +``` + +Use the generated type like any other widget: + +```dart +const AppCard( + child: Text('Generated from one Styler'), +); +``` + +The explicit `.only({'child'})` selection keeps the public constructor stable if `BoxStyler.call()` gains more optional parameters later. Use `@MixWidget()` when you deliberately want every supported call parameter, or `@MixWidget(name: 'Surface')` when the derived name is not the API you want. + + + `@MixWidget` requires an unprefixed Flutter widgets import because the generated part uses `Widget`, `StatelessWidget`, and `BuildContext` from the host library. + + +Function-backed styles work too. Factory parameters become widget constructor parameters, and a non-nullable enum parameter named `variant` can generate named constructors such as `Button.solid()` and `Button.ghost()`. + ### `@MixableSpec` -Use on immutable **Spec** classes that extend `Spec`. Controls which **methods** and **components** are generated via integer flags (see `GeneratedSpecMethods` and `GeneratedSpecComponents` in `mix_annotations`). +Use on immutable **Spec** classes. The generated mixin completes the `Spec` contract and can include `copyWith`, equality, interpolation, and diagnostics. Integer flags in `GeneratedSpecMethods` and `GeneratedSpecComponents` control the emitted surface. ```dart import 'package:flutter/foundation.dart'; @@ -83,8 +146,10 @@ part 'my_spec.g.dart'; @MixableSpec() @immutable -final class MySpec extends Spec with _$MySpecMethods { +final class MySpec with _$MySpec { + @override final String? name; + @override final int? age; const MySpec({this.name, this.age}); @@ -96,14 +161,16 @@ Flags let you skip generation you do not need, for example: - `GeneratedSpecMethods.skipLerp` — omit `lerp` - `GeneratedSpecMethods.skipCopyWith` — omit `copyWith` -### `@MixableStyler` +Add `target: YourWidget.new` when the generator should also create the matching Styler class from the Spec. + +### `@MixableStyler` (legacy) -Use on **Styler** classes. In Mix 2, concrete box-like Stylers typically extend `MixStyler` and combine the generated mixin with shared style mixins (padding, borders, and so on). The generated piece adds setters, `merge`, `resolve`, `debugFillProperties`, `props`, and optionally widget `call`. Configure with `GeneratedStylerMethods` flags if you need to omit pieces (for example `GeneratedStylerMethods.skipCall`). +`@MixableStyler` remains available for handwritten Styler classes created before spec-driven generation. New Stylers should normally come from `@MixableSpec(target: YourWidget.new)`, which generates the fields, constructors, fluent methods, `call()`, merge, resolve, diagnostics, and props together. -See [`BoxStyler`](https://github.com/btwld/mix/blob/main/packages/mix/lib/src/specs/box/box_style.dart) in the Mix repository for a full reference implementation (imports, hand-written mixins, `const` `.create`, and `part` wiring). +See [`BoxSpec`](https://github.com/btwld/mix/blob/main/packages/mix/lib/src/specs/box/box_spec.dart) and its generated part in the Mix repository for a current spec-driven reference. - Custom Stylers should follow the same constructor patterns as core Mix Stylers. The [mix_lint](/documentation/mix/ecosystem/mix-lint) rule `mix_mixable_styler_has_create` enforces a `const` `.create` constructor for `@MixableStyler` classes. + When maintaining a legacy handwritten Styler, follow the same constructor patterns as core Mix Stylers. The [mix_lint](/documentation/mix/ecosystem/mix-lint) rule `mix_mixable_styler_has_create` enforces a `const` `.create` constructor for `@MixableStyler` classes. ### `@MixableField` From 77f10d4e0b53b646895f73f8004cf888d55bf0d9 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Fri, 17 Jul 2026 05:37:18 -0400 Subject: [PATCH 2/6] Refine MixWidget codegen showcase --- components/MixWidgetShowcase.tsx | 117 +++++++++++++----- site-tests/mix-codegen-integration.test.mjs | 4 +- .../mix/ecosystem/mix-generator.mdx | 4 +- 3 files changed, 89 insertions(+), 36 deletions(-) diff --git a/components/MixWidgetShowcase.tsx b/components/MixWidgetShowcase.tsx index 8ff785d0..0bb00c46 100644 --- a/components/MixWidgetShowcase.tsx +++ b/components/MixWidgetShowcase.tsx @@ -2,18 +2,28 @@ import Link from "next/link"; import { HighlightedCode } from "./HighlightedCode"; -const STYLE_SOURCE = `@MixWidget( - widgetParameters: .only({'child'}), -) +const STYLE_SOURCE = `@MixWidget() final appCardStyle = BoxStyler() .color(const Color(0xFF6750A4)) .paddingAll(16) .borderRounded(12);`; +const GENERATED_WIDGET = `class AppCard extends StatelessWidget { + const AppCard({super.key, this.child}); + + final Widget? child; + + @override + Widget build(BuildContext context) { + return appCardStyle.call( + key: this.key, + child: this.child, + ); + } +}`; + const WIDGET_USAGE = `const AppCard( - child: Text( - 'A real Flutter widget', - ), + child: Text('A real Flutter widget'), );`; export function MixWidgetShowcase() { @@ -31,26 +41,62 @@ export function MixWidgetShowcase() {

-
-
- +
+
+
+ + + + + + + app_card.dart + +
-
-
-
- - generates - - - AppCard - - - extends StatelessWidget - -
+
+ + @MixWidget() + + + → + + + AppCard +
+
+ +
+ + +
- +
+
+ + 03 / Use it + +

+ Compose it anywhere. +

+
+
+ +
@@ -67,27 +113,32 @@ export function MixWidgetShowcase() { ); } -function CodePanel({ +function CodeStage({ + step, eyebrow, title, code, }: { + step: string; eyebrow: string; title: string; code: string; }) { return ( -
- - {eyebrow} - -

- {title} -

-
+
+
+ + {step} + + + {eyebrow} + +
+

{title}

+
diff --git a/site-tests/mix-codegen-integration.test.mjs b/site-tests/mix-codegen-integration.test.mjs index 7fbefa6f..0598eedc 100644 --- a/site-tests/mix-codegen-integration.test.mjs +++ b/site-tests/mix-codegen-integration.test.mjs @@ -15,8 +15,10 @@ test('shows MixWidget from the Mix homepage through generated output', () => { assert.match(homepage, /@MixWidget/) assert.match(homepage, /AppCard/) assert.match(homepage, /\/documentation\/mix\/ecosystem\/mix-generator#mixwidget/) + assert.doesNotMatch(homepage, /widgetParameters/) - assert.match(generatorGuide, /@MixWidget\(widgetParameters: \.only\(\{'child'\}\)\)/) + assert.match(generatorGuide, /@MixWidget\(\)/) + assert.doesNotMatch(generatorGuide, /widgetParameters/) assert.match(generatorGuide, /class AppCard extends StatelessWidget/) assert.match(generatorGuide, /return appCardStyle\.call\(/) assert.match(generatorGuide, /dart run build_runner build --delete-conflicting-outputs/) diff --git a/src/content/documentation/mix/ecosystem/mix-generator.mdx b/src/content/documentation/mix/ecosystem/mix-generator.mdx index fe0d520f..2a718c21 100644 --- a/src/content/documentation/mix/ecosystem/mix-generator.mdx +++ b/src/content/documentation/mix/ecosystem/mix-generator.mdx @@ -86,7 +86,7 @@ import 'package:mix_annotations/mix_annotations.dart'; part 'app_card.g.dart'; -@MixWidget(widgetParameters: .only({'child'})) +@MixWidget() final appCardStyle = BoxStyler() .color(const Color(0xFF6750A4)) .paddingAll(16) @@ -125,7 +125,7 @@ const AppCard( ); ``` -The explicit `.only({'child'})` selection keeps the public constructor stable if `BoxStyler.call()` gains more optional parameters later. Use `@MixWidget()` when you deliberately want every supported call parameter, or `@MixWidget(name: 'Surface')` when the derived name is not the API you want. +No parameter configuration is needed here: a variable-backed `BoxStyler` generates the `key` and `child` widget surface shown above. Use `@MixWidget(name: 'Surface')` when the derived name is not the API you want. `@MixWidget` requires an unprefixed Flutter widgets import because the generated part uses `Widget`, `StatelessWidget`, and `BuildContext` from the host library. From 150ffbde86707ceea8f9e04f860a828e2ee2594d Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Fri, 17 Jul 2026 06:01:34 -0400 Subject: [PATCH 3/6] Polish MixWidget editor layout --- components/MixWidgetShowcase.tsx | 120 ++++++++---------- .../mix/ecosystem/mix-generator.mdx | 2 +- 2 files changed, 57 insertions(+), 65 deletions(-) diff --git a/components/MixWidgetShowcase.tsx b/components/MixWidgetShowcase.tsx index 0bb00c46..10cba5bd 100644 --- a/components/MixWidgetShowcase.tsx +++ b/components/MixWidgetShowcase.tsx @@ -4,7 +4,7 @@ import { HighlightedCode } from "./HighlightedCode"; const STYLE_SOURCE = `@MixWidget() final appCardStyle = BoxStyler() - .color(const Color(0xFF6750A4)) + .color(Colors.deepPurple) .paddingAll(16) .borderRounded(12);`; @@ -23,7 +23,9 @@ const GENERATED_WIDGET = `class AppCard extends StatelessWidget { }`; const WIDGET_USAGE = `const AppCard( - child: Text('A real Flutter widget'), + child: Text( + 'A real Flutter widget', + ), );`; export function MixWidgetShowcase() { @@ -41,62 +43,67 @@ export function MixWidgetShowcase() {

-
-
+
+
- - app_card.dart + + AppCard code generation
-
+
@MixWidget() - + - + AppCard
-
- - -
+
+
+ +
+ +
+
-
-
- - 03 / Use it - -

- Compose it anywhere. -

-
-
- -
+
+ +
+ +
+ +
+
+ + 03 / Use it + +

+ Compose it anywhere. +

+
+ +
+
@@ -113,34 +120,19 @@ export function MixWidgetShowcase() { ); } -function CodeStage({ - step, - eyebrow, - title, - code, +function EditorFileHeader({ + filename, + label, }: { - step: string; - eyebrow: string; - title: string; - code: string; + filename: string; + label: string; }) { return ( -
-
- - {step} - - - {eyebrow} - -
-

{title}

-
- -
-
+
+ {filename} + + {label} + +
); } diff --git a/src/content/documentation/mix/ecosystem/mix-generator.mdx b/src/content/documentation/mix/ecosystem/mix-generator.mdx index 2a718c21..ac8610e5 100644 --- a/src/content/documentation/mix/ecosystem/mix-generator.mdx +++ b/src/content/documentation/mix/ecosystem/mix-generator.mdx @@ -88,7 +88,7 @@ part 'app_card.g.dart'; @MixWidget() final appCardStyle = BoxStyler() - .color(const Color(0xFF6750A4)) + .color(Colors.deepPurple) .paddingAll(16) .borderRounded(12); ``` From 5098fc8a80e858595490e54b67488bf0d06f0b11 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Fri, 17 Jul 2026 06:09:31 -0400 Subject: [PATCH 4/6] Move MixWidget usage to source column --- components/MixWidgetShowcase.tsx | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/components/MixWidgetShowcase.tsx b/components/MixWidgetShowcase.tsx index 10cba5bd..b41421a9 100644 --- a/components/MixWidgetShowcase.tsx +++ b/components/MixWidgetShowcase.tsx @@ -69,8 +69,8 @@ export function MixWidgetShowcase() {
-
-
+
+
-
+
+
-
-
- - 03 / Use it - -

- Compose it anywhere. -

-
- +
+
+ + 03 / Use it + +

+ Compose it anywhere. +

-
+ +
From 3dddaed47aef2af9e1ed9080162060ee645192d0 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Fri, 17 Jul 2026 06:14:29 -0400 Subject: [PATCH 5/6] Present MixWidget usage as editor file --- components/MixWidgetShowcase.tsx | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/components/MixWidgetShowcase.tsx b/components/MixWidgetShowcase.tsx index b41421a9..ccb0db77 100644 --- a/components/MixWidgetShowcase.tsx +++ b/components/MixWidgetShowcase.tsx @@ -90,20 +90,15 @@ export function MixWidgetShowcase() {
-
-
- - 03 / Use it - -

- Compose it anywhere. -

+
+ +
+
- -
+
From d57d9a6723137c2cb88cfdf7f5f4d75000bde878 Mon Sep 17 00:00:00 2001 From: Leo Farias Date: Fri, 17 Jul 2026 06:34:59 -0400 Subject: [PATCH 6/6] Document opt-in Mix Styler generation --- site-tests/mix-codegen-integration.test.mjs | 18 +++++++++++------- .../mix/ecosystem/mix-generator.mdx | 14 ++++++++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/site-tests/mix-codegen-integration.test.mjs b/site-tests/mix-codegen-integration.test.mjs index 0598eedc..612a4199 100644 --- a/site-tests/mix-codegen-integration.test.mjs +++ b/site-tests/mix-codegen-integration.test.mjs @@ -6,20 +6,24 @@ import { test } from 'node:test' const root = resolve(import.meta.dirname, '..') test('shows MixWidget from the Mix homepage through generated output', () => { - const homepage = readFileSync(join(root, 'components/MixWidgetShowcase.tsx'), 'utf8') + const homeContent = readFileSync(join(root, 'components/HomeContent.tsx'), 'utf8') + const showcase = readFileSync(join(root, 'components/MixWidgetShowcase.tsx'), 'utf8') const generatorGuide = readFileSync( join(root, 'src/content/documentation/mix/ecosystem/mix-generator.mdx'), 'utf8', ) - assert.match(homepage, /@MixWidget/) - assert.match(homepage, /AppCard/) - assert.match(homepage, /\/documentation\/mix\/ecosystem\/mix-generator#mixwidget/) - assert.doesNotMatch(homepage, /widgetParameters/) + assert.match(homeContent, /import \{ MixWidgetShowcase \} from "\.\/MixWidgetShowcase"/) + assert.match(homeContent, //) - assert.match(generatorGuide, /@MixWidget\(\)/) - assert.doesNotMatch(generatorGuide, /widgetParameters/) + assert.match(showcase, /@MixWidget/) + assert.match(showcase, /AppCard/) + assert.match(showcase, /\/documentation\/mix\/ecosystem\/mix-generator#mixwidget/) + assert.doesNotMatch(showcase, /widgetParameters/) + + assert.match(generatorGuide, /@MixWidget\(\)\s*\nfinal appCardStyle/) assert.match(generatorGuide, /class AppCard extends StatelessWidget/) assert.match(generatorGuide, /return appCardStyle\.call\(/) + assert.match(generatorGuide, /mix_generator:spec_styler_generator:\s*\n\s+enabled: true/) assert.match(generatorGuide, /dart run build_runner build --delete-conflicting-outputs/) }) diff --git a/src/content/documentation/mix/ecosystem/mix-generator.mdx b/src/content/documentation/mix/ecosystem/mix-generator.mdx index ac8610e5..cf979f98 100644 --- a/src/content/documentation/mix/ecosystem/mix-generator.mdx +++ b/src/content/documentation/mix/ecosystem/mix-generator.mdx @@ -16,7 +16,7 @@ The package registers **six builders** (see [`build.yaml` in the Mix repo](https | Builder | Triggers on | Generated part (suffix) | Output | |---------|-------------|-------------------------|-------------------| | `mix_generator` | `@MixableSpec` | `.mix_generator.g.part` | Completes the immutable Spec contract | -| `spec_styler_generator` | `@MixableSpec(target: …)` | `.spec_styler_generator.g.part` | Creates a full fluent Styler class | +| `spec_styler_generator` (opt-in) | `@MixableSpec(target: …)` | `.spec_styler_generator.g.part` | Creates a full fluent Styler class | | `styler_generator` | `@MixableStyler` | `.styler_generator.g.part` | Supports legacy handwritten Stylers | | `mixable_generator` | `@Mixable` | `.mixable_generator.g.part` | Implements a compound Mix property | | `mix_widget_generator` | `@MixWidget` | `.mix_widget_generator.g.part` | Creates a `StatelessWidget` around a Styler | @@ -163,6 +163,16 @@ Flags let you skip generation you do not need, for example: Add `target: YourWidget.new` when the generator should also create the matching Styler class from the Spec. +The `spec_styler_generator` builder does not apply automatically. Enable it explicitly in your package's `build.yaml`: + +```yaml filename="build.yaml" +targets: + $default: + builders: + mix_generator:spec_styler_generator: + enabled: true +``` + ### `@MixableStyler` (legacy) `@MixableStyler` remains available for handwritten Styler classes created before spec-driven generation. New Stylers should normally come from `@MixableSpec(target: YourWidget.new)`, which generates the fields, constructors, fluent methods, `call()`, merge, resolve, diagnostics, and props together. @@ -200,7 +210,7 @@ After a successful build, `box_spec.g.dart` contains the `part of` directive and - **No output / stale errors:** Run `build_runner` with `--delete-conflicting-outputs` if a previous run left incompatible `*.g.dart` files. - **Builder not running:** Ensure `mix_generator` is in `dev_dependencies` and that your package (or app) imports files that carry the annotations; builders only visit included sources. -- **Monorepo `build.yaml`:** The Mix framework repo narrows `generate_for` globs for its own layout. **Your app** typically relies on the builders’ `auto_apply: dependents` behavior unless you add a custom `build.yaml`. +- **Monorepo `build.yaml`:** The Mix framework repo narrows `generate_for` globs for its own layout. **Your app** typically relies on `auto_apply: dependents` for the standard builders; `spec_styler_generator` remains opt-in as shown above. ## Debugging the generator