Skip to content

Commit e9e85bd

Browse files
authored
feat(dart_frog_dev_server): report route conflicts (#200)
1 parent b4d455b commit e9e85bd

File tree

6 files changed

+372
-6
lines changed

6 files changed

+372
-6
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: dart_frog_dev_server
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- ".github/workflows/dart_frog_dev_server.yaml"
7+
- "bricks/dart_frog_dev_server/hooks/**"
8+
push:
9+
branches:
10+
- main
11+
paths:
12+
- ".github/workflows/dart_frog_dev_server.yaml"
13+
- "bricks/dart_frog_dev_server/hooks/**"
14+
15+
jobs:
16+
build:
17+
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1
18+
with:
19+
working_directory: bricks/dart_frog_dev_server/hooks
20+
analyze_directories: .
21+
report_on: pre_gen.dart
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.dart_tool
22
.packages
33
pubspec.lock
4+
coverage
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include: package:very_good_analysis/analysis_options.3.0.1.yaml

bricks/dart_frog_dev_server/hooks/pre_gen.dart

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
1-
import 'dart:io';
1+
import 'dart:async';
2+
import 'dart:io' as io;
23

3-
import 'package:mason/mason.dart';
44
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+
);
511

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 {
719
final RouteConfiguration configuration;
820
try {
9-
configuration = buildRouteConfiguration(Directory.current);
21+
configuration = buildConfiguration(io.Directory.current);
1022
} catch (error) {
1123
context.logger.err('$error');
12-
exit(1);
24+
final _exit = exit ?? ExitOverrides.current?.exit ?? io.exit;
25+
return _exit(1);
1326
}
1427

28+
reportRouteConflicts(context, configuration);
29+
1530
context.vars = {
1631
'port': context.vars['port'] ?? '8080',
1732
'directories': configuration.directories
@@ -27,3 +42,55 @@ Future<void> run(HookContext context) async {
2742
'serveStaticFiles': configuration.serveStaticFiles,
2843
};
2944
}
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+
}

bricks/dart_frog_dev_server/hooks/pubspec.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,10 @@ environment:
55
sdk: ">=2.12.0 <3.0.0"
66

77
dependencies:
8-
dart_frog_gen: ^0.0.2-dev.4
8+
dart_frog_gen: ^0.0.2-dev.5
99
mason: ^0.1.0-dev.30
10+
11+
dev_dependencies:
12+
mocktail: ^0.3.0
13+
test: ^1.19.2
14+
very_good_analysis: ^3.0.1

0 commit comments

Comments
 (0)