Skip to content

Commit 83df2ef

Browse files
committed
feat: add static route gen
0 parents  commit 83df2ef

29 files changed

Lines changed: 2091 additions & 0 deletions

.fvmrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"flutter": "stable"
3+
}

.github/workflows/linting.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Linting
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
lint:
13+
name: Lint Check
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: 📚 Checkout code
18+
uses: actions/checkout@v3
19+
20+
- name: 🐦 Set up Dart
21+
uses: dart-lang/setup-dart@v1
22+
with:
23+
sdk: stable
24+
25+
- name: 📦 Install dependencies
26+
run: dart pub get
27+
28+
- name: 🔍 Dart Analyze
29+
uses: ValentinVignal/action-dart-analyze@v0.17
30+
with:
31+
fail-on: format

.github/workflows/tests.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Unit Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
test:
13+
name: Unit Testing
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: 📚 Checkout code
18+
uses: actions/checkout@v3
19+
20+
- name: 🐦 Set up Dart
21+
uses: dart-lang/setup-dart@v1
22+
with:
23+
sdk: stable
24+
25+
- name: 📦 Install dependencies
26+
run: dart pub get
27+
28+
- name: 🧪 Run Tests
29+
run: dart test -r json > test-results.json
30+
continue-on-error: true
31+
32+
- name: 📊 Upload Test Results
33+
uses: actions/upload-artifact@v4.6.2
34+
with:
35+
name: test-results
36+
path: test-results.json
37+
38+
- name: 📝 Check Test Results
39+
run: |
40+
if grep -q '"result":"failure"' test-results.json; then
41+
echo "❌ Tests failed"
42+
exit 1
43+
fi
44+
echo "✅ All tests passed"

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# https://dart.dev/guides/libraries/private-files
2+
# Created by `dart pub`
3+
.dart_tool/
4+
5+
# Avoid committing pubspec.lock for library packages; see
6+
# https://dart.dev/guides/libraries/private-files#pubspeclock.
7+
pubspec.lock
8+
9+
# FVM Version Cache
10+
.fvm/

.vscode/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"dart.flutterSdkPath": ".fvm/versions/stable",
3+
"dart.sdkPath": ".fvm/flutter_sdk/bin/cache/dart-sdk"
4+
}

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 1.0.0
2+
3+
- Initial version.

README.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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.

analysis_options.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This file configures the static analysis results for your project (errors,
2+
# warnings, and lints).
3+
#
4+
# This enables the 'recommended' set of lints from `package:lints`.
5+
# This set helps identify many issues that may lead to problems when running
6+
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
7+
# style and format.
8+
#
9+
# If you want a smaller set of lints you can change this to specify
10+
# 'package:lints/core.yaml'. These are just the most critical lints
11+
# (the recommended set includes the core lints).
12+
# The core lints are also what is used by pub.dev for scoring packages.
13+
14+
include: package:lints/recommended.yaml
15+
16+
# Uncomment the following section to specify additional rules.
17+
18+
# linter:
19+
# rules:
20+
# - camel_case_types
21+
22+
# analyzer:
23+
# exclude:
24+
# - path/to/excluded/files/**
25+
26+
# For more information about the core and recommended set of lints, see
27+
# https://dart.dev/go/core-lints
28+
29+
# For additional information about configuring this file, see
30+
# https://dart.dev/guides/language/analysis-options

build.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
builders:
2+
grumpy_routes:
3+
import: "package:grumpy_gen/grumpy_gen.dart"
4+
builder_factories: ["grumpyRoutesBuilder"]
5+
build_extensions: { ".dart": [".routes.dart"] }
6+
auto_apply: dependents
7+
build_to: source

lib/grumpy_gen.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
library;
2+
3+
export 'src/grumpy_flutter_routes.dart';
4+
export 'src/grumpy_routes.dart';
5+
export 'src/route_generation.dart'
6+
show
7+
BuildStepRouteAstResolver,
8+
RouteAstResolver,
9+
RouteGenerationSpec,
10+
RoutePathNode,
11+
analyzeRouteLibrary,
12+
emitFlutterRoutes,
13+
emitGenericRoutes;

0 commit comments

Comments
 (0)