From f2711ef64f1b13fdb4d3b5deff9842f8f3ec018c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 8 Sep 2026 19:11:44 +0000 Subject: [PATCH 1/6] FEDX-7265: treat hook/ as public-facing for dependency validation Co-authored-by: Dustin Pauze --- CHANGELOG.md | 2 + README.md | 2 +- lib/src/dependency_validator.dart | 6 +-- test/executable_test.dart | 74 ++++++++++++++++++++++++++++++- 4 files changed, 78 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55b5bbd..744b8d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Unreleased +- Treat `hook/` as a public-facing directory when validating dependencies. Dependencies imported in hook scripts run at install time and must be regular dependencies, not dev_dependencies. + # 5.0.6 - Allow up to analyzer 13 diff --git a/README.md b/README.md index dc4a9e1..7c762d6 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ used even if it isn't imported. [dart-build]: https://github.com/dart-lang/build - Missing: When a dependency is used in the package but not declared in the `pubspec.yaml` -- Under-promoted: When a dependency is used within `lib/` but only declared as a dev_dependency. +- Under-promoted: When a dependency is used within `lib/`, `bin/`, or `hook/` but only declared as a dev_dependency. - Over-promoted: When a dependency is only used outside `lib/` but declared as a dependency. - Unused: When a dependency is not used in the package but declared in the `pubspec.yaml`. diff --git a/lib/src/dependency_validator.dart b/lib/src/dependency_validator.dart index 7eb7d9e..472c536 100644 --- a/lib/src/dependency_validator.dart +++ b/lib/src/dependency_validator.dart @@ -105,7 +105,7 @@ Future checkPackage({required String root}) async { '${bulletItems(devDeps)}\n', ); - final publicDirs = ['$root/bin/', '$root/lib/']; + final publicDirs = ['$root/bin/', '$root/hook/', '$root/lib/']; logger.fine("Excluding: $excludes"); final publicDartFiles = [ for (final dir in publicDirs) ...listDartFilesIn(dir, excludes), @@ -236,7 +236,7 @@ Future checkPackage({required String root}) async { if (missingDependencies.isNotEmpty) { log( Level.WARNING, - 'These packages are used in lib/ but are not dependencies:', + 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', missingDependencies, ); result = false; @@ -294,7 +294,7 @@ Future checkPackage({required String root}) async { if (underPromotedDependencies.isNotEmpty) { log( Level.WARNING, - 'These packages are used in lib/ and should be promoted to actual dependencies:', + 'These packages are used in lib/, bin/, or hook/ and should be promoted to actual dependencies:', underPromotedDependencies, ); result = false; diff --git a/test/executable_test.dart b/test/executable_test.dart index 71f9b47..59d49af 100644 --- a/test/executable_test.dart +++ b/test/executable_test.dart @@ -49,7 +49,7 @@ void main() { expect(result.exitCode, 1); expect( result.stderr, - contains('These packages are used in lib/ but are not dependencies:'), + contains('These packages are used in lib/, bin/, or hook/ but are not dependencies:'), ); expect(result.stderr, contains('yaml')); expect(result.stderr, contains('some_scss_package')); @@ -171,7 +171,7 @@ void main() { expect( result.stderr, contains( - 'These packages are used in lib/ and should be promoted to actual dependencies:', + 'These packages are used in lib/, bin/, or hook/ and should be promoted to actual dependencies:', ), ); expect(result.stderr, contains('logging')); @@ -201,6 +201,76 @@ void main() { ); }); + group('fails when hook scripts use dev_dependencies', () { + final devDependencies = {"yaml": hostedAny}; + final config = DepValidatorConfig(ignore: ['yaml']); + + final project = [ + d.dir('hook', [ + d.file('post_install.dart', 'import "package:yaml/yaml.dart";'), + ]), + ]; + + test('', () async { + result = await checkProject( + project: project, + devDependencies: devDependencies, + ); + expect(result.exitCode, 1); + expect( + result.stderr, + contains( + 'These packages are used in lib/, bin/, or hook/ and should be promoted to actual dependencies:', + ), + ); + expect(result.stderr, contains('yaml')); + }); + + test('except when they are ignored', () async { + result = await checkProject( + project: project, + devDependencies: devDependencies, + config: config, + ); + expect(result.exitCode, 0); + }); + }); + + group('passes when hook scripts use regular dependencies', () { + test('', () async { + result = await checkProject( + dependencies: {"yaml": hostedAny}, + project: [ + d.dir('hook', [ + d.file('post_install.dart', 'import "package:yaml/yaml.dart";'), + ]), + ], + ); + expect(result.exitCode, 0); + expect(result.stdout, contains('No dependency issues found!')); + }); + }); + + group('fails when hook scripts use undeclared dependencies', () { + final project = [ + d.dir('hook', [ + d.file('post_install.dart', 'import "package:yaml/yaml.dart";'), + ]), + ]; + + test('', () async { + result = await checkProject(project: project); + expect(result.exitCode, 1); + expect( + result.stderr, + contains( + 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', + ), + ); + expect(result.stderr, contains('yaml')); + }); + }); + group('fails when there are unused packages', () { final devDependencies = {'yaml': hostedAny}; From 147706b344e65b3b4e223d0c150adc3e43ce9d1d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 8 Sep 2026 19:20:57 +0000 Subject: [PATCH 2/6] FEDX-7265: address PR review feedback for hook/ public dir changes - Reformat long contains() assertion in executable_test.dart - Align missing-dev and over-promoted warning messages with lib/, bin/, hook/ - Mark CHANGELOG entry as Breaking Change with resolution path - Fix hook timing (build/link time) and update README over-promoted bullet Co-authored-by: Dustin Pauze --- CHANGELOG.md | 4 +++- README.md | 2 +- lib/src/dependency_validator.dart | 4 ++-- test/executable_test.dart | 6 ++++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 744b8d3..6db9f05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # Unreleased -- Treat `hook/` as a public-facing directory when validating dependencies. Dependencies imported in hook scripts run at install time and must be regular dependencies, not dev_dependencies. +- **Breaking Change:** Treat `hook/` as a public-facing directory when validating dependencies. Dependencies imported in hook scripts run at build/link time (dart build, flutter build) and must be regular dependencies, not dev_dependencies. +This is enabled by default, and will break the execution of dependency_validator if it occurs within the codebase. +Resolution is to either move the dependency to `dependencies`, or `ignore`/`exclude` it. # 5.0.6 diff --git a/README.md b/README.md index 7c762d6..64052c3 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ used even if it isn't imported. - Missing: When a dependency is used in the package but not declared in the `pubspec.yaml` - Under-promoted: When a dependency is used within `lib/`, `bin/`, or `hook/` but only declared as a dev_dependency. -- Over-promoted: When a dependency is only used outside `lib/` but declared as a dependency. +- Over-promoted: When a dependency is only used outside `lib/`, `bin/`, and `hook/` but declared as a dependency. - Unused: When a dependency is not used in the package but declared in the `pubspec.yaml`. ## Configuration diff --git a/lib/src/dependency_validator.dart b/lib/src/dependency_validator.dart index 472c536..c03ec84 100644 --- a/lib/src/dependency_validator.dart +++ b/lib/src/dependency_validator.dart @@ -258,7 +258,7 @@ Future checkPackage({required String root}) async { if (missingDevDependencies.isNotEmpty) { log( Level.WARNING, - 'These packages are used outside lib/ but are not dev_dependencies:', + 'These packages are used outside lib/, bin/, and hook/ but are not dev_dependencies:', missingDevDependencies, ); result = false; @@ -278,7 +278,7 @@ Future checkPackage({required String root}) async { if (overPromotedDependencies.isNotEmpty) { log( Level.WARNING, - 'These packages are only used outside lib/ and should be downgraded to dev_dependencies:', + 'These packages are only used outside lib/, bin/, and hook/ and should be downgraded to dev_dependencies:', overPromotedDependencies, ); result = false; diff --git a/test/executable_test.dart b/test/executable_test.dart index 59d49af..cc4c4ea 100644 --- a/test/executable_test.dart +++ b/test/executable_test.dart @@ -49,7 +49,9 @@ void main() { expect(result.exitCode, 1); expect( result.stderr, - contains('These packages are used in lib/, bin/, or hook/ but are not dependencies:'), + contains( + 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', + ), ); expect(result.stderr, contains('yaml')); expect(result.stderr, contains('some_scss_package')); @@ -120,7 +122,7 @@ void main() { expect( result.stderr, contains( - 'These packages are only used outside lib/ and should be downgraded to dev_dependencies:', + 'These packages are only used outside lib/, bin/, and hook/ and should be downgraded to dev_dependencies:', ), ); expect(result.stderr, contains('path')); From 9414a46e6d8a9716c8a8f5d8aed84d2de70196d8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 8 Sep 2026 19:42:34 +0000 Subject: [PATCH 3/6] FEDX-7265: address round 2 PR review feedback for hook/ support - Derive publicDirsDescription from publicDirNames constant - Update all warning messages including executables check - Add hook/ over-promotion exemption test and deprecated ignore variant - Collapse single-test hook groups into named tests - Bump version to 6.0.0 and document hook/ in README Co-authored-by: Dustin Pauze --- CHANGELOG.md | 3 +- README.md | 2 + lib/src/constants.dart | 10 ++++ lib/src/dependency_validator.dart | 12 ++--- pubspec.yaml | 2 +- test/executable_test.dart | 77 ++++++++++++++++++++++--------- 6 files changed, 75 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6db9f05..65cc463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,4 @@ -# Unreleased - +# 6.0.0 - **Breaking Change:** Treat `hook/` as a public-facing directory when validating dependencies. Dependencies imported in hook scripts run at build/link time (dart build, flutter build) and must be regular dependencies, not dev_dependencies. This is enabled by default, and will break the execution of dependency_validator if it occurs within the codebase. diff --git a/README.md b/README.md index 64052c3..7b8a1bc 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ used even if it isn't imported. - Over-promoted: When a dependency is only used outside `lib/`, `bin/`, and `hook/` but declared as a dependency. - Unused: When a dependency is not used in the package but declared in the `pubspec.yaml`. +Hook scripts in `hook/` run at build/link time (for example, `dart build` or `flutter build`), so their imports must be regular `dependencies`. Use `ignore` or `exclude` in `dart_dependency_validator.yaml` if a hook-only dependency should not be validated. + ## Configuration There may be packages that are intentionally depended on but not used, or there diff --git a/lib/src/constants.dart b/lib/src/constants.dart index a7e7d29..a31bbaa 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -14,6 +14,16 @@ final RegExp importLessPackageRegex = RegExp( r'@import\s+(?:\(.*\)\s+)?"(?:packages\/|package:\/\/)([a-zA-Z1-9_-]+)\/', ); +/// Directory names treated as public-facing for dependency validation. +const publicDirNames = ['lib', 'bin', 'hook']; + +/// Human-readable list of [publicDirNames], e.g. `lib/, bin/, or hook/`. +String publicDirsDescription({String conjunction = 'or'}) { + final dirs = [for (final name in publicDirNames) '$name/']; + if (dirs.length == 1) return dirs.first; + return '${dirs.sublist(0, dirs.length - 1).join(', ')}, $conjunction ${dirs.last}'; +} + /// String key in pubspec.yaml for the dependencies map. const String dependenciesKey = 'dependencies'; diff --git a/lib/src/dependency_validator.dart b/lib/src/dependency_validator.dart index c03ec84..5900e11 100644 --- a/lib/src/dependency_validator.dart +++ b/lib/src/dependency_validator.dart @@ -105,7 +105,7 @@ Future checkPackage({required String root}) async { '${bulletItems(devDeps)}\n', ); - final publicDirs = ['$root/bin/', '$root/hook/', '$root/lib/']; + final publicDirs = [for (final dir in publicDirNames) '$root/$dir/']; logger.fine("Excluding: $excludes"); final publicDartFiles = [ for (final dir in publicDirs) ...listDartFilesIn(dir, excludes), @@ -236,7 +236,7 @@ Future checkPackage({required String root}) async { if (missingDependencies.isNotEmpty) { log( Level.WARNING, - 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', + 'These packages are used in ${publicDirsDescription()} but are not dependencies:', missingDependencies, ); result = false; @@ -258,7 +258,7 @@ Future checkPackage({required String root}) async { if (missingDevDependencies.isNotEmpty) { log( Level.WARNING, - 'These packages are used outside lib/, bin/, and hook/ but are not dev_dependencies:', + 'These packages are used outside ${publicDirsDescription(conjunction: 'and')} but are not dev_dependencies:', missingDevDependencies, ); result = false; @@ -278,7 +278,7 @@ Future checkPackage({required String root}) async { if (overPromotedDependencies.isNotEmpty) { log( Level.WARNING, - 'These packages are only used outside lib/, bin/, and hook/ and should be downgraded to dev_dependencies:', + 'These packages are only used outside ${publicDirsDescription(conjunction: 'and')} and should be downgraded to dev_dependencies:', overPromotedDependencies, ); result = false; @@ -294,7 +294,7 @@ Future checkPackage({required String root}) async { if (underPromotedDependencies.isNotEmpty) { log( Level.WARNING, - 'These packages are used in lib/, bin/, or hook/ and should be promoted to actual dependencies:', + 'These packages are used in ${publicDirsDescription()} and should be promoted to actual dependencies:', underPromotedDependencies, ); result = false; @@ -376,7 +376,7 @@ Future checkPackage({required String root}) async { if (nonDevPackagesWithExecutables.isNotEmpty) { logIntersection( Level.WARNING, - 'The following packages contain executables, and are only used outside of lib/. These should be downgraded to dev_dependencies:', + 'The following packages contain executables, and are only used outside of ${publicDirsDescription(conjunction: 'and')}. These should be downgraded to dev_dependencies:', unusedDependencies, nonDevPackagesWithExecutables, ); diff --git a/pubspec.yaml b/pubspec.yaml index 3420e1a..8ae0806 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dependency_validator -version: 5.0.6 +version: 6.0.0 description: Checks for missing, under-promoted, over-promoted, and unused dependencies. homepage: https://github.com/Workiva/dependency_validator diff --git a/test/executable_test.dart b/test/executable_test.dart index cc4c4ea..deffc35 100644 --- a/test/executable_test.dart +++ b/test/executable_test.dart @@ -15,6 +15,7 @@ @TestOn('vm') import 'dart:io'; +import 'package:dependency_validator/src/constants.dart'; import 'package:dependency_validator/src/pubspec_config.dart'; import 'package:io/io.dart'; import 'package:test/test.dart'; @@ -50,7 +51,7 @@ void main() { expect( result.stderr, contains( - 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', + 'These packages are used in ${publicDirsDescription()} but are not dependencies:', ), ); expect(result.stderr, contains('yaml')); @@ -122,7 +123,7 @@ void main() { expect( result.stderr, contains( - 'These packages are only used outside lib/, bin/, and hook/ and should be downgraded to dev_dependencies:', + 'These packages are only used outside ${publicDirsDescription(conjunction: 'and')} and should be downgraded to dev_dependencies:', ), ); expect(result.stderr, contains('path')); @@ -173,7 +174,7 @@ void main() { expect( result.stderr, contains( - 'These packages are used in lib/, bin/, or hook/ and should be promoted to actual dependencies:', + 'These packages are used in ${publicDirsDescription()} and should be promoted to actual dependencies:', ), ); expect(result.stderr, contains('logging')); @@ -222,7 +223,7 @@ void main() { expect( result.stderr, contains( - 'These packages are used in lib/, bin/, or hook/ and should be promoted to actual dependencies:', + 'These packages are used in ${publicDirsDescription()} and should be promoted to actual dependencies:', ), ); expect(result.stderr, contains('yaml')); @@ -236,10 +237,37 @@ void main() { ); expect(result.exitCode, 0); }); + + test( + 'except when they are ignored (deprecated pubspec method)', + () async { + result = await checkProject( + project: project, + devDependencies: devDependencies, + config: config, + embedConfigInPubspec: true, + ); + expect(result.exitCode, 0); + }, + ); }); - group('passes when hook scripts use regular dependencies', () { - test('', () async { + test('passes when hook scripts use regular dependencies', () async { + result = await checkProject( + dependencies: {"yaml": hostedAny}, + project: [ + d.dir('hook', [ + d.file('post_install.dart', 'import "package:yaml/yaml.dart";'), + ]), + ], + ); + expect(result.exitCode, 0); + expect(result.stdout, contains('No dependency issues found!')); + }); + + test( + 'passes when hook-only dependency is not flagged as over-promoted', + () async { result = await checkProject( dependencies: {"yaml": hostedAny}, project: [ @@ -249,28 +277,33 @@ void main() { ], ); expect(result.exitCode, 0); - expect(result.stdout, contains('No dependency issues found!')); - }); - }); + expect( + result.stderr, + isNot( + contains( + 'These packages are only used outside ${publicDirsDescription(conjunction: 'and')} and should be downgraded to dev_dependencies:', + ), + ), + ); + }, + ); - group('fails when hook scripts use undeclared dependencies', () { + test('fails when hook scripts use undeclared dependencies', () async { final project = [ d.dir('hook', [ d.file('post_install.dart', 'import "package:yaml/yaml.dart";'), ]), ]; - test('', () async { - result = await checkProject(project: project); - expect(result.exitCode, 1); - expect( - result.stderr, - contains( - 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', - ), - ); - expect(result.stderr, contains('yaml')); - }); + result = await checkProject(project: project); + expect(result.exitCode, 1); + expect( + result.stderr, + contains( + 'These packages are used in ${publicDirsDescription()} but are not dependencies:', + ), + ); + expect(result.stderr, contains('yaml')); }); group('fails when there are unused packages', () { @@ -414,7 +447,7 @@ void main() { expect( result.stderr, contains( - 'The following packages contain executables, and are only used outside of lib/. These should be downgraded to dev_dependencies', + 'The following packages contain executables, and are only used outside of ${publicDirsDescription(conjunction: 'and')}. These should be downgraded to dev_dependencies', ), ); }, From 87e8db1a0ce27accb32616d20569055cf828d84e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 8 Sep 2026 20:00:30 +0000 Subject: [PATCH 4/6] FEDX-7265: pin message literals in tests and use hook/build.dart fixtures Co-authored-by: Dustin Pauze --- test/executable_test.dart | 23 +++++++++++------------ test/utils_test.dart | 13 +++++++++++++ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/test/executable_test.dart b/test/executable_test.dart index deffc35..c57e452 100644 --- a/test/executable_test.dart +++ b/test/executable_test.dart @@ -15,7 +15,6 @@ @TestOn('vm') import 'dart:io'; -import 'package:dependency_validator/src/constants.dart'; import 'package:dependency_validator/src/pubspec_config.dart'; import 'package:io/io.dart'; import 'package:test/test.dart'; @@ -51,7 +50,7 @@ void main() { expect( result.stderr, contains( - 'These packages are used in ${publicDirsDescription()} but are not dependencies:', + 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', ), ); expect(result.stderr, contains('yaml')); @@ -123,7 +122,7 @@ void main() { expect( result.stderr, contains( - 'These packages are only used outside ${publicDirsDescription(conjunction: 'and')} and should be downgraded to dev_dependencies:', + 'These packages are only used outside lib/, bin/, and hook/ and should be downgraded to dev_dependencies:', ), ); expect(result.stderr, contains('path')); @@ -174,7 +173,7 @@ void main() { expect( result.stderr, contains( - 'These packages are used in ${publicDirsDescription()} and should be promoted to actual dependencies:', + 'These packages are used in lib/, bin/, or hook/ and should be promoted to actual dependencies:', ), ); expect(result.stderr, contains('logging')); @@ -210,7 +209,7 @@ void main() { final project = [ d.dir('hook', [ - d.file('post_install.dart', 'import "package:yaml/yaml.dart";'), + d.file('build.dart', 'import "package:yaml/yaml.dart";'), ]), ]; @@ -223,7 +222,7 @@ void main() { expect( result.stderr, contains( - 'These packages are used in ${publicDirsDescription()} and should be promoted to actual dependencies:', + 'These packages are used in lib/, bin/, or hook/ and should be promoted to actual dependencies:', ), ); expect(result.stderr, contains('yaml')); @@ -257,7 +256,7 @@ void main() { dependencies: {"yaml": hostedAny}, project: [ d.dir('hook', [ - d.file('post_install.dart', 'import "package:yaml/yaml.dart";'), + d.file('build.dart', 'import "package:yaml/yaml.dart";'), ]), ], ); @@ -272,7 +271,7 @@ void main() { dependencies: {"yaml": hostedAny}, project: [ d.dir('hook', [ - d.file('post_install.dart', 'import "package:yaml/yaml.dart";'), + d.file('build.dart', 'import "package:yaml/yaml.dart";'), ]), ], ); @@ -281,7 +280,7 @@ void main() { result.stderr, isNot( contains( - 'These packages are only used outside ${publicDirsDescription(conjunction: 'and')} and should be downgraded to dev_dependencies:', + 'These packages are only used outside lib/, bin/, and hook/ and should be downgraded to dev_dependencies:', ), ), ); @@ -291,7 +290,7 @@ void main() { test('fails when hook scripts use undeclared dependencies', () async { final project = [ d.dir('hook', [ - d.file('post_install.dart', 'import "package:yaml/yaml.dart";'), + d.file('build.dart', 'import "package:yaml/yaml.dart";'), ]), ]; @@ -300,7 +299,7 @@ void main() { expect( result.stderr, contains( - 'These packages are used in ${publicDirsDescription()} but are not dependencies:', + 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', ), ); expect(result.stderr, contains('yaml')); @@ -447,7 +446,7 @@ void main() { expect( result.stderr, contains( - 'The following packages contain executables, and are only used outside of ${publicDirsDescription(conjunction: 'and')}. These should be downgraded to dev_dependencies', + 'The following packages contain executables, and are only used outside of lib/, bin/, and hook/. These should be downgraded to dev_dependencies', ), ); }, diff --git a/test/utils_test.dart b/test/utils_test.dart index 4c7407c..985f321 100644 --- a/test/utils_test.dart +++ b/test/utils_test.dart @@ -21,6 +21,19 @@ import 'package:dependency_validator/src/constants.dart'; import 'package:dependency_validator/src/utils.dart'; void main() { + group('publicDirsDescription', () { + test('default conjunction', () { + expect(publicDirsDescription(), 'lib/, bin/, or hook/'); + }); + + test('and conjunction', () { + expect( + publicDirsDescription(conjunction: 'and'), + 'lib/, bin/, and hook/', + ); + }); + }); + group('getAnalysisOptionsIncludePackage', () { test('no analysis_options.yaml', () { expect(getAnalysisOptionsIncludePackage(path: d.sandbox), isNull); From 367575163e4268a7c1ac933549db478547af46b2 Mon Sep 17 00:00:00 2001 From: Dustin Pauze Date: Wed, 9 Sep 2026 09:40:34 -0400 Subject: [PATCH 5/6] FEDX-7265: add hook exclude and link.dart test coverage --- README.md | 2 +- test/executable_test.dart | 81 ++++++++++++++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7b8a1bc..7d39572 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ used even if it isn't imported. - Over-promoted: When a dependency is only used outside `lib/`, `bin/`, and `hook/` but declared as a dependency. - Unused: When a dependency is not used in the package but declared in the `pubspec.yaml`. -Hook scripts in `hook/` run at build/link time (for example, `dart build` or `flutter build`), so their imports must be regular `dependencies`. Use `ignore` or `exclude` in `dart_dependency_validator.yaml` if a hook-only dependency should not be validated. +Hook scripts in `hook/` (for example, `build.dart` and `link.dart`) run at build/link time (`dart build`, `flutter build`), so their imports must be regular `dependencies`. To opt out, use `ignore` to suppress warnings for a specific package name (for example, a dev_dependency used only in hooks), or `exclude: ["hook/**"]` to skip scanning the hook directory entirely (which also skips missing-dependency checks in hook files). ## Configuration diff --git a/test/executable_test.dart b/test/executable_test.dart index c57e452..13936b5 100644 --- a/test/executable_test.dart +++ b/test/executable_test.dart @@ -251,6 +251,29 @@ void main() { ); }); + group('fails when hook link scripts use dev_dependencies', () { + final devDependencies = {"yaml": hostedAny}; + + test('', () async { + result = await checkProject( + devDependencies: devDependencies, + project: [ + d.dir('hook', [ + d.file('link.dart', 'import "package:yaml/yaml.dart";'), + ]), + ], + ); + expect(result.exitCode, 1); + expect( + result.stderr, + contains( + 'These packages are used in lib/, bin/, or hook/ and should be promoted to actual dependencies:', + ), + ); + expect(result.stderr, contains('yaml')); + }); + }); + test('passes when hook scripts use regular dependencies', () async { result = await checkProject( dependencies: {"yaml": hostedAny}, @@ -264,6 +287,19 @@ void main() { expect(result.stdout, contains('No dependency issues found!')); }); + test('passes when hook link scripts use regular dependencies', () async { + result = await checkProject( + dependencies: {"yaml": hostedAny}, + project: [ + d.dir('hook', [ + d.file('link.dart', 'import "package:yaml/yaml.dart";'), + ]), + ], + ); + expect(result.exitCode, 0); + expect(result.stdout, contains('No dependency issues found!')); + }); + test( 'passes when hook-only dependency is not flagged as over-promoted', () async { @@ -287,22 +323,49 @@ void main() { }, ); - test('fails when hook scripts use undeclared dependencies', () async { + group('fails when hook scripts use undeclared dependencies', () { final project = [ d.dir('hook', [ d.file('build.dart', 'import "package:yaml/yaml.dart";'), ]), ]; + final excludeHook = DepValidatorConfig(exclude: ['hook/**']); - result = await checkProject(project: project); - expect(result.exitCode, 1); - expect( - result.stderr, - contains( - 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', - ), + test('', () async { + result = await checkProject(project: project); + expect(result.exitCode, 1); + expect( + result.stderr, + contains( + 'These packages are used in lib/, bin/, or hook/ but are not dependencies:', + ), + ); + expect(result.stderr, contains('yaml')); + }); + + test('except when hook is excluded', () async { + result = await checkProject(project: project, config: excludeHook); + expect(result.exitCode, 0); + expect(result.stderr, isEmpty); + }); + + test( + 'except when hook is excluded (deprecated pubspec method)', + () async { + result = await checkProject( + project: project, + config: excludeHook, + embedConfigInPubspec: true, + ); + expect(result.exitCode, 0); + expect( + result.stderr, + contains( + 'Configuring dependency_validator in pubspec.yaml is deprecated', + ), + ); + }, ); - expect(result.stderr, contains('yaml')); }); group('fails when there are unused packages', () { From 95c21e5fd42542ed59fc256a72da6c7e0c452a22 Mon Sep 17 00:00:00 2001 From: Dustin Pauze Date: Wed, 9 Sep 2026 09:53:06 -0400 Subject: [PATCH 6/6] FEDX-7265: revert version change --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 8ae0806..3420e1a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dependency_validator -version: 6.0.0 +version: 5.0.6 description: Checks for missing, under-promoted, over-promoted, and unused dependencies. homepage: https://github.com/Workiva/dependency_validator