|
| 1 | +# grumpy_gen |
| 2 | + |
| 3 | +Source gen to enhance projects using [grumpy](https://github.com/necodeIT/grumpy) or [grumpy_flutter](https://github.com/necodeIT/grumpy_flutter). |
| 4 | + |
| 5 | +## Routes |
| 6 | + |
| 7 | +`grumpy_gen` can generate typed route helpers for any library that declares exactly one concrete `RootModule`. |
| 8 | + |
| 9 | +If the root also extends `AppModule`, the generated output includes Flutter navigation helpers on `BuildContext`. Otherwise it generates only the generic static path API. |
| 10 | + |
| 11 | +### Setup |
| 12 | + |
| 13 | +1. Add a `part` directive to the library that contains the root module. |
| 14 | +2. Run `dart run build_runner build`. |
| 15 | + |
| 16 | +Example: |
| 17 | + |
| 18 | +```dart |
| 19 | +import 'package:grumpy_flutter/grumpy_flutter.dart'; |
| 20 | +
|
| 21 | +part 'app.routes.dart'; |
| 22 | +
|
| 23 | +class App extends AppModule<AppConfig> { |
| 24 | + App(super.cfg); |
| 25 | +
|
| 26 | + @override |
| 27 | + List<FlutterRoute<AppConfig>> get routes => [ |
| 28 | + ModuleRoute(path: '/Settings', module: Settings()), |
| 29 | + ScreenRoute(path: '/dashboard', view: DashboardScreen()), |
| 30 | + ]; |
| 31 | +
|
| 32 | + @override |
| 33 | + Screen get notFoundScreen => NotFoundScreen(); |
| 34 | +
|
| 35 | + @override |
| 36 | + Widget buildApp() => const Widget(); |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +### Generated APIs |
| 41 | + |
| 42 | +For every `RootModule`, `grumpy_gen` emits a `GrumpyRoutes` static container with full-path strings. |
| 43 | + |
| 44 | +Example usage: |
| 45 | + |
| 46 | +```dart |
| 47 | +final dashboard = GrumpyRoutes.dashboard; |
| 48 | +final advancedSettings = GrumpyRoutes.settings.advanced; |
| 49 | +final settingsPrefix = GrumpyRoutes.settings.path; |
| 50 | +``` |
| 51 | + |
| 52 | +For roots that also extend `AppModule`, `grumpy_gen` additionally emits a typed `BuildContext` extension: |
| 53 | + |
| 54 | +```dart |
| 55 | +context.to.dashboard(); |
| 56 | +context.to.settings.advanced(); |
| 57 | +context.to.users.id(userId).details(); |
| 58 | +``` |
| 59 | + |
| 60 | +Parameterized path segments are represented as explicit segment methods. A route like `/users/:id/details` becomes: |
| 61 | + |
| 62 | +```dart |
| 63 | +GrumpyRoutes.users.id(userId).details |
| 64 | +context.to.users.id(userId).details() |
| 65 | +``` |
| 66 | + |
| 67 | +Module boundaries without a root leaf are still generated as typed namespace objects and expose `.path`, but Flutter `call()` is intentionally not generated for them. |
| 68 | + |
| 69 | +### What Gets Scanned |
| 70 | + |
| 71 | +The generator walks all routes reachable from the root and follows: |
| 72 | + |
| 73 | +- `LeafRoute` |
| 74 | +- `ScreenRoute` |
| 75 | +- `LeafRoute.root` |
| 76 | +- `ScreenRoute.root` |
| 77 | +- `ModuleRoute` |
| 78 | +- `ShellScreenRoute` |
| 79 | +- `Route.root([...])` |
| 80 | + |
| 81 | +Path joining follows the runtime router semantics: |
| 82 | + |
| 83 | +- `ShellScreenRoute` contributes no path segment |
| 84 | +- `ModuleRoute` descendants are rooted under the module boundary path |
| 85 | +- nested children are flattened into full absolute paths |
| 86 | + |
| 87 | +### Current Limitations |
| 88 | + |
| 89 | +Route generation is intentionally static in v1. The generator currently expects route trees to be declared with direct constructor calls and list literals. |
| 90 | + |
| 91 | +Supported: |
| 92 | + |
| 93 | +```dart |
| 94 | +@override |
| 95 | +List<FlutterRoute<AppConfig>> get routes => [ |
| 96 | + ScreenRoute(path: '/dashboard', view: DashboardScreen()), |
| 97 | + ModuleRoute(path: '/slots', module: Slots()), |
| 98 | +]; |
| 99 | +``` |
| 100 | + |
| 101 | +Not supported: |
| 102 | + |
| 103 | +```dart |
| 104 | +@override |
| 105 | +List<FlutterRoute<AppConfig>> get routes => buildRoutes(); |
| 106 | +``` |
| 107 | + |
| 108 | +or route lists built with control flow or spreads. |
| 109 | + |
| 110 | +If a library contains multiple concrete `RootModule` implementations, generation fails with a clear error. |
0 commit comments