diff --git a/.vscode/settings.json b/.vscode/settings.json index 5b46f25aba..e9ef71f485 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -30,6 +30,7 @@ "mocktail", "negatable", "nullsafety", + "posix", "pubignore", "pubspec", "semver", diff --git a/CHANGELOG.md b/CHANGELOG.md index 108b6a48f9..a8751c3e82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * test: added test case in [`prefer-const-border-radius`](https://dartcodemetrics.dev/docs/rules/flutter/prefer-const-border-radius) rule. * chore: restrict `analyzer` version to `>=2.4.0 <4.2.0`. * fix: improve context root included files calculation. +* fix: resolve package with imported analysis options. ## 4.15.2 diff --git a/dart_dependency_validator.yaml b/dart_dependency_validator.yaml index 8c6549f2e6..f42f9e1cef 100644 --- a/dart_dependency_validator.yaml +++ b/dart_dependency_validator.yaml @@ -1,3 +1,4 @@ ignore: - analyzer - intl + - test_lints diff --git a/lib/src/analyzers/lint_analyzer/lint_analyzer.dart b/lib/src/analyzers/lint_analyzer/lint_analyzer.dart index 0383b3e906..b286d060cc 100644 --- a/lib/src/analyzers/lint_analyzer/lint_analyzer.dart +++ b/lib/src/analyzers/lint_analyzer/lint_analyzer.dart @@ -81,8 +81,8 @@ class LintAnalyzer { final analyzerResult = []; for (final context in collection.contexts) { - final analysisOptions = await analysisOptionsFromContext(context) ?? - await analysisOptionsFromFilePath(rootFolder); + final analysisOptions = analysisOptionsFromContext(context) ?? + analysisOptionsFromFilePath(rootFolder, context); final excludesRootFolder = analysisOptions.folderPath ?? rootFolder; 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 610560b1f0..ac2687d441 100644 --- a/lib/src/analyzers/unused_code_analyzer/unused_code_analyzer.dart +++ b/lib/src/analyzers/unused_code_analyzer/unused_code_analyzer.dart @@ -55,7 +55,7 @@ class UnusedCodeAnalyzer { for (final context in collection.contexts) { final unusedCodeAnalysisConfig = - await _getAnalysisConfig(context, rootFolder, config); + _getAnalysisConfig(context, rootFolder, config); final filePaths = getFilePaths( folders, @@ -98,13 +98,13 @@ class UnusedCodeAnalyzer { return _getReports(codeUsages, publicCode, rootFolder); } - Future _getAnalysisConfig( + UnusedCodeAnalysisConfig _getAnalysisConfig( AnalysisContext context, String rootFolder, UnusedCodeConfig config, - ) async { - final analysisOptions = await analysisOptionsFromContext(context) ?? - await analysisOptionsFromFilePath(rootFolder); + ) { + final analysisOptions = analysisOptionsFromContext(context) ?? + analysisOptionsFromFilePath(rootFolder, context); final contextConfig = ConfigBuilder.getUnusedCodeConfigFromOption(analysisOptions) diff --git a/lib/src/analyzers/unused_files_analyzer/unused_files_analyzer.dart b/lib/src/analyzers/unused_files_analyzer/unused_files_analyzer.dart index 5b666bb118..6a694a3622 100644 --- a/lib/src/analyzers/unused_files_analyzer/unused_files_analyzer.dart +++ b/lib/src/analyzers/unused_files_analyzer/unused_files_analyzer.dart @@ -47,7 +47,7 @@ class UnusedFilesAnalyzer { for (final context in collection.contexts) { final unusedFilesAnalysisConfig = - await _getAnalysisConfig(context, rootFolder, config); + _getAnalysisConfig(context, rootFolder, config); final filePaths = getFilePaths( folders, @@ -92,13 +92,13 @@ class UnusedFilesAnalyzer { } } - Future _getAnalysisConfig( + UnusedFilesAnalysisConfig _getAnalysisConfig( AnalysisContext context, String rootFolder, UnusedFilesConfig config, - ) async { - final analysisOptions = await analysisOptionsFromContext(context) ?? - await analysisOptionsFromFilePath(rootFolder); + ) { + final analysisOptions = analysisOptionsFromContext(context) ?? + analysisOptionsFromFilePath(rootFolder, context); final contextConfig = ConfigBuilder.getUnusedFilesConfigFromOption(analysisOptions) diff --git a/lib/src/analyzers/unused_l10n_analyzer/unused_l10n_analyzer.dart b/lib/src/analyzers/unused_l10n_analyzer/unused_l10n_analyzer.dart index 44ab327a09..4361b00c91 100644 --- a/lib/src/analyzers/unused_l10n_analyzer/unused_l10n_analyzer.dart +++ b/lib/src/analyzers/unused_l10n_analyzer/unused_l10n_analyzer.dart @@ -51,8 +51,8 @@ class UnusedL10nAnalyzer { final localizationUsages = >{}; for (final context in collection.contexts) { - final analysisOptions = await analysisOptionsFromContext(context) ?? - await analysisOptionsFromFilePath(rootFolder); + final analysisOptions = analysisOptionsFromContext(context) ?? + analysisOptionsFromFilePath(rootFolder, context); final contextConfig = ConfigBuilder.getUnusedL10nConfigFromOption(analysisOptions) diff --git a/lib/src/config_builder/models/analysis_options.dart b/lib/src/config_builder/models/analysis_options.dart index 1aeaaa3f6c..e6fd9f15e9 100644 --- a/lib/src/config_builder/models/analysis_options.dart +++ b/lib/src/config_builder/models/analysis_options.dart @@ -1,7 +1,7 @@ import 'dart:io'; -import 'dart:isolate'; import 'package:analyzer/dart/analysis/analysis_context.dart'; +import 'package:analyzer/dart/analysis/uri_converter.dart'; import 'package:path/path.dart' as p; import 'package:yaml/yaml.dart'; @@ -109,28 +109,43 @@ class AnalysisOptions { } } -Future analysisOptionsFromContext( +AnalysisOptions? analysisOptionsFromContext( AnalysisContext context, -) async { +) { final optionsFilePath = context.contextRoot.optionsFile?.path; return optionsFilePath == null ? null - : analysisOptionsFromFile(File(optionsFilePath)); + : analysisOptionsFromFile(File(optionsFilePath), context); } -Future analysisOptionsFromFilePath(String path) { +AnalysisOptions analysisOptionsFromFilePath( + String path, + AnalysisContext context, +) { final analysisOptionsFile = File(p.absolute(path, _analysisOptionsFileName)); - return analysisOptionsFromFile(analysisOptionsFile); + return analysisOptionsFromFile(analysisOptionsFile, context); } -Future analysisOptionsFromFile(File? options) async => +AnalysisOptions analysisOptionsFromFile( + File? options, + AnalysisContext context, +) => options != null && options.existsSync() - ? AnalysisOptions(options.path, await _loadConfigFromYamlFile(options)) + ? AnalysisOptions( + options.path, + _loadConfigFromYamlFile( + options, + context.currentSession.uriConverter, + ), + ) : const AnalysisOptions(null, {}); -Future> _loadConfigFromYamlFile(File options) async { +Map _loadConfigFromYamlFile( + File options, + UriConverter converter, +) { try { final node = options.existsSync() ? loadYamlNode(options.readAsStringSync()) @@ -141,12 +156,15 @@ Future> _loadConfigFromYamlFile(File options) async { final includeNode = optionsNode['include']; if (includeNode is String) { - final resolvedUri = includeNode.startsWith('package:') - ? await Isolate.resolvePackageUri(Uri.parse(includeNode)) - : Uri.file(p.absolute(p.dirname(options.path), includeNode)); + final packageImport = includeNode.startsWith('package:'); + + final resolvedUri = packageImport + ? converter.uriToPath(Uri.parse(includeNode)) + : p.absolute(p.dirname(options.path), includeNode); + if (resolvedUri != null) { final resolvedYamlMap = - await _loadConfigFromYamlFile(File.fromUri(resolvedUri)); + _loadConfigFromYamlFile(File(resolvedUri), converter); optionsNode = mergeMaps(defaults: resolvedYamlMap, overrides: optionsNode); } diff --git a/pubspec.yaml b/pubspec.yaml index d104815542..185c49b705 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,5 +32,9 @@ dev_dependencies: mocktail: ^0.3.0 test: ^1.16.8 + # internal package, used only in tests data + test_lints: + path: ./test/resources/test_lints + executables: metrics: diff --git a/test/resources/analysis_options_with_import.yaml b/test/resources/analysis_options_with_import.yaml new file mode 100644 index 0000000000..737c5f4c44 --- /dev/null +++ b/test/resources/analysis_options_with_import.yaml @@ -0,0 +1 @@ +include: package:test_lints/analysis_options.yaml diff --git a/test/resources/test_lints/lib/analysis_options.1.0.0.yaml b/test/resources/test_lints/lib/analysis_options.1.0.0.yaml new file mode 100644 index 0000000000..b44e070196 --- /dev/null +++ b/test/resources/test_lints/lib/analysis_options.1.0.0.yaml @@ -0,0 +1,3 @@ +analyzer: + plugins: + - dart_code_metrics diff --git a/test/resources/test_lints/lib/analysis_options.yaml b/test/resources/test_lints/lib/analysis_options.yaml new file mode 100644 index 0000000000..e8b23a1c6c --- /dev/null +++ b/test/resources/test_lints/lib/analysis_options.yaml @@ -0,0 +1 @@ +include: package:test_lints/analysis_options.1.0.0.yaml diff --git a/test/resources/test_lints/pubspec.yaml b/test/resources/test_lints/pubspec.yaml new file mode 100644 index 0000000000..b0539a10ec --- /dev/null +++ b/test/resources/test_lints/pubspec.yaml @@ -0,0 +1,6 @@ +name: test_lints +description: Lints for Dart and Flutter based on software industry standards and best practices. +version: 1.0.0 + +environment: + sdk: ">=2.14.0 <3.0.0" diff --git a/test/src/config_builder/models/analysis_options_test.dart b/test/src/config_builder/models/analysis_options_test.dart index 890d9fbaa3..30872c7976 100644 --- a/test/src/config_builder/models/analysis_options_test.dart +++ b/test/src/config_builder/models/analysis_options_test.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:dart_code_metrics/src/config_builder/models/analysis_options.dart'; +import 'package:dart_code_metrics/src/utils/analyzer_utils.dart'; import 'package:test/test.dart'; const _options = { @@ -28,23 +29,35 @@ const _options = { }; void main() { + final collection = createAnalysisContextCollection( + ['test'], + Directory.current.path, + null, + ); + group('analysisOptionsFromFile constructs AnalysisOptions from', () { - test('null', () async { - final options = await analysisOptionsFromFile(null); + test('null', () { + final options = analysisOptionsFromFile(null, collection.contexts.first); expect(options.options, isEmpty); }); - test('invalid file', () async { - final options = await analysisOptionsFromFile(File('unavailable.yaml')); + test('invalid file', () { + final options = analysisOptionsFromFile( + File('unavailable.yaml'), + collection.contexts.first, + ); expect(options.options, isEmpty); }); - test('yaml file', () async { + test('yaml file', () { const yamlFilePath = './test/resources/analysis_options_pkg.yaml'; - final options = await analysisOptionsFromFile(File(yamlFilePath)); + final options = analysisOptionsFromFile( + File(yamlFilePath), + collection.contexts.first, + ); expect(options.options, contains('linter')); expect(options.options['linter'], contains('rules')); @@ -97,6 +110,21 @@ void main() { ), ); }); + + test('valid file with single import', () { + const yamlFilePath = './test/resources/analysis_options_with_import.yaml'; + + final options = analysisOptionsFromFile( + File(yamlFilePath), + collection.contexts.first, + ); + + expect(options.options['analyzer'], isNotEmpty); + expect( + (options.options['analyzer'] as Map)['plugins'], + equals(['dart_code_metrics']), + ); + }); }); group('AnalysisOptions', () {