1
- import 'dart:io' ;
1
+ import 'dart:async' ;
2
+ import 'dart:io' as io;
2
3
3
- import 'package:mason/mason.dart' ;
4
4
import 'package:dart_frog_gen/dart_frog_gen.dart' ;
5
+ import 'package:mason/mason.dart' ;
6
+ import 'package:path/path.dart' as path;
7
+
8
+ typedef RouteConfigurationBuilder = RouteConfiguration Function (
9
+ io.Directory directory,
10
+ );
5
11
6
- Future <void > run (HookContext context) async {
12
+ Future <void > run (HookContext context) async => preGen (context);
13
+
14
+ Future <void > preGen (
15
+ HookContext context, {
16
+ RouteConfigurationBuilder buildConfiguration = buildRouteConfiguration,
17
+ void Function (int exitCode)? exit,
18
+ }) async {
7
19
final RouteConfiguration configuration;
8
20
try {
9
- configuration = buildRouteConfiguration ( Directory .current);
21
+ configuration = buildConfiguration (io. Directory .current);
10
22
} catch (error) {
11
23
context.logger.err ('$error ' );
12
- exit (1 );
24
+ final _exit = exit ?? ExitOverrides .current? .exit ?? io.exit;
25
+ return _exit (1 );
13
26
}
14
27
28
+ reportRouteConflicts (context, configuration);
29
+
15
30
context.vars = {
16
31
'port' : context.vars['port' ] ?? '8080' ,
17
32
'directories' : configuration.directories
@@ -27,3 +42,55 @@ Future<void> run(HookContext context) async {
27
42
'serveStaticFiles' : configuration.serveStaticFiles,
28
43
};
29
44
}
45
+
46
+ void reportRouteConflicts (
47
+ HookContext context,
48
+ RouteConfiguration configuration,
49
+ ) {
50
+ final conflictingEndpoints =
51
+ configuration.endpoints.entries.where ((entry) => entry.value.length > 1 );
52
+ if (conflictingEndpoints.isNotEmpty) {
53
+ context.logger.info ('' );
54
+ for (final conflict in conflictingEndpoints) {
55
+ final originalFilePath = path.normalize (
56
+ path.join ('routes' , conflict.value.first.path),
57
+ );
58
+ final conflictingFilePath = path.normalize (
59
+ path.join ('routes' , conflict.value.last.path),
60
+ );
61
+ context.logger.err (
62
+ '''Route conflict detected. ${lightCyan .wrap (originalFilePath )} and ${lightCyan .wrap (conflictingFilePath )} both resolve to ${lightCyan .wrap (conflict .key )}.''' ,
63
+ );
64
+ }
65
+ context.logger.info ('' );
66
+ }
67
+ }
68
+
69
+ const _asyncRunZoned = runZoned;
70
+
71
+ abstract class ExitOverrides {
72
+ static final _token = Object ();
73
+
74
+ static ExitOverrides ? get current {
75
+ return Zone .current[_token] as ExitOverrides ? ;
76
+ }
77
+
78
+ static R runZoned <R >(R Function () body, {void Function (int )? exit}) {
79
+ final overrides = _ExitOverridesScope (exit);
80
+ return _asyncRunZoned (body, zoneValues: {_token: overrides});
81
+ }
82
+
83
+ void Function (int exitCode) get exit => io.exit;
84
+ }
85
+
86
+ class _ExitOverridesScope extends ExitOverrides {
87
+ _ExitOverridesScope (this ._exit);
88
+
89
+ final ExitOverrides ? _previous = ExitOverrides .current;
90
+ final void Function (int exitCode)? _exit;
91
+
92
+ @override
93
+ void Function (int exitCode) get exit {
94
+ return _exit ?? _previous? .exit ?? super .exit;
95
+ }
96
+ }
0 commit comments