From 8b714dcf984526aae27e884125134bfdb80e0e04 Mon Sep 17 00:00:00 2001 From: Dmitry Zhifarsky Date: Mon, 31 Jan 2022 21:08:27 +0300 Subject: [PATCH] feat: add experimental monorepo support for check-unused-code (#659) * feat: add experimental monorepo support for check-unused-code * fix: analyze all not analyzed files if in monorepo mode * fix: fix how context folders are resolved * chore: update changelog * chore: fix test and update the docs * docs: add info about limitations * docs: add no-congratulate flag --- CHANGELOG.md | 1 + .../unused_code_analyzer.dart | 14 +++++++++----- .../unused_code_config.dart | 10 +++++++++- lib/src/cli/cli_runner.dart | 14 +++++++------- .../cli/commands/check_unused_code_command.dart | 17 +++++++++++++++-- lib/src/cli/models/flag_names.dart | 1 + lib/src/config_builder/config_builder.dart | 7 ++++--- .../unused_code_analyzer_test.dart | 1 + .../check_unused_code_command_test.dart | 3 +++ website/docs/cli/check-unused-code.md | 14 +++++++++++++- 10 files changed, 63 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecbe1c7ebd..8b873ef6c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * fix: make `check-unused-l10n` also cover supertype member calls. * fix: cyclomatic complexity calculation for functions with internal lambdas. * chore: restrict `analyzer` version to `>=2.4.0 <3.3.0`. +* feat: support monorepos for `check-unused-code` command. ## 4.10.0-dev.2 diff --git a/lib/src/analyzers/unused_code_analyzer/unused_code_analyzer.dart b/lib/src/analyzers/unused_code_analyzer/unused_code_analyzer.dart index 3cf036e4e7..dcf4254edf 100644 --- a/lib/src/analyzers/unused_code_analyzer/unused_code_analyzer.dart +++ b/lib/src/analyzers/unused_code_analyzer/unused_code_analyzer.dart @@ -89,7 +89,9 @@ class UnusedCodeAnalyzer { } } - codeUsages.exports.forEach(publicCode.remove); + if (!config.isMonorepo) { + codeUsages.exports.forEach(publicCode.remove); + } return _getReports(codeUsages, publicCode, rootFolder); } @@ -115,10 +117,12 @@ class UnusedCodeAnalyzer { String rootFolder, Iterable excludes, ) { - final contextFolders = folders - .where((path) => normalize(join(rootFolder, path)) - .startsWith(context.contextRoot.root.path)) - .toList(); + final contextFolders = folders.where((path) { + final newPath = normalize(join(rootFolder, path)); + + return newPath == context.contextRoot.root.path || + context.contextRoot.root.path.startsWith('$newPath/'); + }).toList(); return extractDartFilesFromFolders(contextFolders, rootFolder, excludes); } diff --git a/lib/src/analyzers/unused_code_analyzer/unused_code_config.dart b/lib/src/analyzers/unused_code_analyzer/unused_code_config.dart index ef7489db33..1e0ad8194d 100644 --- a/lib/src/analyzers/unused_code_analyzer/unused_code_config.dart +++ b/lib/src/analyzers/unused_code_analyzer/unused_code_config.dart @@ -4,10 +4,12 @@ import '../../config_builder/models/analysis_options.dart'; class UnusedCodeConfig { final Iterable excludePatterns; final Iterable analyzerExcludePatterns; + final bool isMonorepo; const UnusedCodeConfig({ required this.excludePatterns, required this.analyzerExcludePatterns, + required this.isMonorepo, }); /// Creates the config from analysis [options]. @@ -16,13 +18,18 @@ class UnusedCodeConfig { excludePatterns: const [], analyzerExcludePatterns: options.readIterableOfString(['analyzer', 'exclude']), + isMonorepo: false, ); /// Creates the config from cli args. - factory UnusedCodeConfig.fromArgs(Iterable excludePatterns) => + factory UnusedCodeConfig.fromArgs( + Iterable excludePatterns, { + required bool isMonorepo, + }) => UnusedCodeConfig( excludePatterns: excludePatterns, analyzerExcludePatterns: const [], + isMonorepo: isMonorepo, ); /// Merges two configs into a single one. @@ -35,5 +42,6 @@ class UnusedCodeConfig { ...analyzerExcludePatterns, ...overrides.analyzerExcludePatterns, }, + isMonorepo: isMonorepo || overrides.isMonorepo, ); } diff --git a/lib/src/cli/cli_runner.dart b/lib/src/cli/cli_runner.dart index 8a542a98b3..cb6ee4625a 100644 --- a/lib/src/cli/cli_runner.dart +++ b/lib/src/cli/cli_runner.dart @@ -37,16 +37,16 @@ class CliRunner extends CommandRunner { /// Main entry point for running a command @override Future run(Iterable args) async { - final results = parse(args); - final showVersion = results[FlagNames.version] as bool; + try { + final results = parse(args); + final showVersion = results[FlagNames.version] as bool; - if (showVersion) { - _output.writeln('Dart Code Metrics version: $packageVersion'); + if (showVersion) { + _output.writeln('Dart Code Metrics version: $packageVersion'); - return; - } + return; + } - try { await super.run(_addDefaultCommand(args)); } on UsageException catch (e) { _output diff --git a/lib/src/cli/commands/check_unused_code_command.dart b/lib/src/cli/commands/check_unused_code_command.dart index 7d3efe526a..2c275a11ef 100644 --- a/lib/src/cli/commands/check_unused_code_command.dart +++ b/lib/src/cli/commands/check_unused_code_command.dart @@ -31,10 +31,13 @@ class CheckUnusedCodeCommand extends BaseCommand { final folders = argResults.rest; final excludePath = argResults[FlagNames.exclude] as String; final reporterName = argResults[FlagNames.reporter] as String; - + final isMonorepo = argResults[FlagNames.isMonorepo] as bool; final noCongratulate = argResults[FlagNames.noCongratulate] as bool; - final config = ConfigBuilder.getUnusedCodeConfigFromArgs([excludePath]); + final config = ConfigBuilder.getUnusedCodeConfigFromArgs( + [excludePath], + isMonorepo: isMonorepo, + ); final unusedCodeResult = await _analyzer.runCliAnalysis( folders, @@ -63,6 +66,7 @@ class CheckUnusedCodeCommand extends BaseCommand { void _addFlags() { _usesReporterOption(); addCommonFlags(); + _usesIsMonorepoOption(); _usesExitOption(); } @@ -82,6 +86,15 @@ class CheckUnusedCodeCommand extends BaseCommand { ); } + void _usesIsMonorepoOption() { + argParser + ..addSeparator('') + ..addFlag( + FlagNames.isMonorepo, + help: 'Treats all exported code as unused by default.', + ); + } + void _usesExitOption() { argParser ..addSeparator('') diff --git a/lib/src/cli/models/flag_names.dart b/lib/src/cli/models/flag_names.dart index 598356f48d..95a42d14a8 100644 --- a/lib/src/cli/models/flag_names.dart +++ b/lib/src/cli/models/flag_names.dart @@ -11,6 +11,7 @@ class FlagNames { static const reporter = 'reporter'; static const rootFolder = 'root-folder'; static const sdkPath = 'sdk-path'; + static const isMonorepo = 'monorepo'; static const version = 'version'; static const consoleReporter = ConsoleReporter.id; diff --git a/lib/src/config_builder/config_builder.dart b/lib/src/config_builder/config_builder.dart index e84a1b1e1a..1f4899e067 100644 --- a/lib/src/config_builder/config_builder.dart +++ b/lib/src/config_builder/config_builder.dart @@ -92,9 +92,10 @@ class ConfigBuilder { /// Creates a raw unused code config from given [excludePatterns]. static UnusedCodeConfig getUnusedCodeConfigFromArgs( - Iterable excludePatterns, - ) => - UnusedCodeConfig.fromArgs(excludePatterns); + Iterable excludePatterns, { + required bool isMonorepo, + }) => + UnusedCodeConfig.fromArgs(excludePatterns, isMonorepo: isMonorepo); /// Creates a raw unused code config from given [options]. static UnusedCodeConfig getUnusedCodeConfigFromOption( diff --git a/test/src/analyzers/unused_code_analyzer/unused_code_analyzer_test.dart b/test/src/analyzers/unused_code_analyzer/unused_code_analyzer_test.dart index b5b3cd9db7..4ca7648dbc 100644 --- a/test/src/analyzers/unused_code_analyzer/unused_code_analyzer_test.dart +++ b/test/src/analyzers/unused_code_analyzer/unused_code_analyzer_test.dart @@ -151,4 +151,5 @@ UnusedCodeConfig _createConfig({ UnusedCodeConfig( excludePatterns: const [], analyzerExcludePatterns: analyzerExcludePatterns, + isMonorepo: false, ); diff --git a/test/src/cli/commands/check_unused_code_command_test.dart b/test/src/cli/commands/check_unused_code_command_test.dart index e34faa13cf..7c0625203b 100644 --- a/test/src/cli/commands/check_unused_code_command_test.dart +++ b/test/src/cli/commands/check_unused_code_command_test.dart @@ -22,6 +22,9 @@ const _usage = 'Check unused code in *.dart files.\n' " --no-congratulate Don't show output even when there are no issues.\n" '\n' '\n' + ' --[no-]monorepo Treats all exported code as unused by default.\n' + '\n' + '\n' ' --[no-]fatal-unused Treat find unused file as fatal.\n' '\n' 'Run "metrics help" to see global options.'; diff --git a/website/docs/cli/check-unused-code.md b/website/docs/cli/check-unused-code.md index 67f9a26797..77ae70f781 100644 --- a/website/docs/cli/check-unused-code.md +++ b/website/docs/cli/check-unused-code.md @@ -2,6 +2,8 @@ Checks unused classes, functions, top level variables, extensions, enums, mixins and type aliases. +**Note:** current implementation doesn't check for particular class methods usage. Also, it treats code, that is imported with not named conditional imports as unused. This will be fixed in the future releases. + To execute the command, run ```sh @@ -31,9 +33,19 @@ Usage: metrics check-unused-code [arguments] (defaults to "{/**.g.dart,/**.template.dart}") - --[no-]fatal-unused Treat find unused l10n as fatal. + --no-congratulate Don't show output even when there are no issues. + + + --[no-]monorepo Treats all exported code as unused by default. + + + --[no-]fatal-unused Treat find unused file as fatal. ``` +## Monorepo support + +By default the command treats all code that exported from the package as used. To disable this behavior use `--monorepo` flag. This might be useful when all the packages in your repository only unused inside this repository and not published to pub. + ## Output example {#output-example} ### Console {#console}