Skip to content

Commit bfe9675

Browse files
authored
feat(dart_frog_dev): report external path dependencies (#339)
1 parent 3b4661c commit bfe9675

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

bricks/dart_frog_dev_server/hooks/pre_gen.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'dart:io' as io;
44
import 'package:dart_frog_gen/dart_frog_gen.dart';
55
import 'package:mason/mason.dart';
66
import 'package:path/path.dart' as path;
7+
import 'package:pubspec_parse/pubspec_parse.dart';
78

89
typedef RouteConfigurationBuilder = RouteConfiguration Function(
910
io.Directory directory,
@@ -27,6 +28,7 @@ Future<void> preGen(
2728

2829
reportRouteConflicts(context, configuration);
2930
reportRogueRoutes(context, configuration);
31+
await reportExternalPathDependencies(context, io.Directory.current);
3032

3133
context.vars = {
3234
'port': context.vars['port'] ?? '8080',
@@ -45,6 +47,39 @@ Future<void> preGen(
4547
};
4648
}
4749

50+
Future<void> reportExternalPathDependencies(
51+
HookContext context,
52+
io.Directory directory,
53+
) async {
54+
final pubspec = Pubspec.parse(
55+
await io.File(path.join(directory.path, 'pubspec.yaml')).readAsString(),
56+
);
57+
58+
final dependencies = pubspec.dependencies;
59+
final devDependencies = pubspec.devDependencies;
60+
final pathDependencies = [...dependencies.entries, ...devDependencies.entries]
61+
.where((entry) => entry.value is PathDependency)
62+
.map((entry) {
63+
final value = entry.value as PathDependency;
64+
return [entry.key, value.path];
65+
}).toList();
66+
final externalDependencies = pathDependencies.where(
67+
(dep) => !path.isWithin(directory.path, dep.last),
68+
);
69+
70+
if (externalDependencies.isNotEmpty) {
71+
context.logger
72+
..info('')
73+
..err('All path dependencies must be within the project.')
74+
..err('External path dependencies detected:');
75+
for (final dependency in externalDependencies) {
76+
final dependencyName = dependency.first;
77+
final dependencyPath = path.normalize(dependency.last);
78+
context.logger.err(' \u{2022} $dependencyName from $dependencyPath');
79+
}
80+
}
81+
}
82+
4883
void reportRouteConflicts(
4984
HookContext context,
5085
RouteConfiguration configuration,

bricks/dart_frog_dev_server/hooks/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ environment:
77
dependencies:
88
dart_frog_gen: ^0.1.0
99
mason: ^0.1.0-dev.31
10+
pubspec_parse: ^1.2.0
1011

1112
dev_dependencies:
1213
mocktail: ^0.3.0
14+
path: ^1.8.2
1315
test: ^1.19.2
1416
very_good_analysis: ^3.0.1

bricks/dart_frog_dev_server/hooks/test/pre_gen_test.dart

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:io';
33
import 'package:dart_frog_gen/dart_frog_gen.dart';
44
import 'package:mason/mason.dart';
55
import 'package:mocktail/mocktail.dart';
6+
import 'package:path/path.dart' as path;
67
import 'package:test/test.dart';
78

89
import '../pre_gen.dart';
@@ -339,6 +340,111 @@ void main() {
339340
});
340341
});
341342

343+
group('reportExternalPathDependencies', () {
344+
late HookContext context;
345+
late Logger logger;
346+
347+
setUp(() {
348+
context = _MockHookContext();
349+
logger = _MockLogger();
350+
351+
when(() => context.logger).thenReturn(logger);
352+
});
353+
354+
test('reports nothing when there are no external path dependencies',
355+
() async {
356+
final directory = Directory.systemTemp.createTempSync();
357+
File(path.join(directory.path, 'pubspec.yaml')).writeAsStringSync(
358+
'''
359+
name: example
360+
version: 0.1.0
361+
environment:
362+
sdk: ^2.17.0
363+
dependencies:
364+
mason: any
365+
dev_dependencies:
366+
test: any
367+
''',
368+
);
369+
await expectLater(
370+
reportExternalPathDependencies(context, directory),
371+
completes,
372+
);
373+
verifyNever(() => logger.err(any()));
374+
directory.delete(recursive: true).ignore();
375+
});
376+
377+
test('reports when there is a single external path dependency', () async {
378+
final directory = Directory.systemTemp.createTempSync();
379+
File(path.join(directory.path, 'pubspec.yaml')).writeAsStringSync(
380+
'''
381+
name: example
382+
version: 0.1.0
383+
environment:
384+
sdk: ^2.17.0
385+
dependencies:
386+
mason: any
387+
foo:
388+
path: ../../foo
389+
dev_dependencies:
390+
test: any
391+
''',
392+
);
393+
await expectLater(
394+
reportExternalPathDependencies(context, directory),
395+
completes,
396+
);
397+
verify(
398+
() => logger.err('All path dependencies must be within the project.'),
399+
).called(1);
400+
verify(
401+
() => logger.err('External path dependencies detected:'),
402+
).called(1);
403+
verify(
404+
() => logger.err(' \u{2022} foo from ../../foo'),
405+
).called(1);
406+
directory.delete(recursive: true).ignore();
407+
});
408+
409+
test('reports when there are multiple external path dependencies',
410+
() async {
411+
final directory = Directory.systemTemp.createTempSync();
412+
File(path.join(directory.path, 'pubspec.yaml')).writeAsStringSync(
413+
'''
414+
name: example
415+
version: 0.1.0
416+
environment:
417+
sdk: ^2.17.0
418+
dependencies:
419+
mason: any
420+
foo:
421+
path: ../../foo
422+
dev_dependencies:
423+
test: any
424+
bar:
425+
path: ../../bar
426+
''',
427+
);
428+
await expectLater(
429+
reportExternalPathDependencies(context, directory),
430+
completes,
431+
);
432+
verify(
433+
() => logger.err('All path dependencies must be within the project.'),
434+
).called(1);
435+
verify(
436+
() => logger.err('External path dependencies detected:'),
437+
).called(1);
438+
verify(
439+
() => logger.err(' \u{2022} foo from ../../foo'),
440+
).called(1);
441+
verify(
442+
() => logger.err(' \u{2022} bar from ../../bar'),
443+
).called(1);
444+
directory.delete(recursive: true).ignore();
445+
});
446+
});
447+
342448
group('ExitOverrides', () {
343449
group('runZoned', () {
344450
test('uses default exit when not specified', () {

0 commit comments

Comments
 (0)