Skip to content

Commit 48860fb

Browse files
committed
Improved yaml parser and added the analysis_options loader
1 parent 6275b4c commit 48860fb

3 files changed

Lines changed: 87 additions & 99 deletions

File tree

lib/main.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class SolidLintsPlugin extends Plugin {
2121

2222
@override
2323
void register(PluginRegistry registry) {
24+
// final directory = Directory.current;
25+
// final rules = analysisLoader.loadRules(directory.path);
26+
2427
registry.registerLintRule(
2528
AvoidGlobalStateRule(),
2629
);
Lines changed: 54 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,70 @@
1-
import 'dart:io';
1+
import 'dart:collection';
2+
3+
import 'package:analyzer/file_system/file_system.dart';
4+
import 'package:analyzer/file_system/physical_file_system.dart';
5+
import 'package:path/path.dart' as p;
6+
import 'package:solid_lints/src/common/parameter_parser/lint_options.dart';
27
import 'package:yaml/yaml.dart';
38

9+
/// A global instance of [AnalysisOptionsLoader] for use across the plugin.
10+
final analysisLoader = AnalysisOptionsLoader();
11+
412
/// Loads and parses analysis options from a Dart project's YAML file.
513
class AnalysisOptionsLoader {
6-
final Map<String, dynamic> _cache = {};
14+
/// Asynchronously loads analysis options from the specified [rootPath].
15+
Map<String, LintOptions> loadRules(String rootPath) {
16+
final file = PhysicalResourceProvider.INSTANCE.getFile(
17+
p.join(rootPath, 'analysis_options.yaml'),
18+
);
719

8-
/// Loads analysis options from a YAML file at the given [yamlPath].
9-
Map<String, dynamic> loadAnalysisOptions(String yamlPath) {
10-
if (_cache.containsKey(yamlPath)) {
11-
return _cache[yamlPath] as Map<String, dynamic>;
12-
}
20+
final rules = _getRules(file);
21+
return rules;
22+
}
1323

14-
final file = File(yamlPath);
15-
if (!file.existsSync()) {
16-
_cache[yamlPath] = {};
24+
Map<String, LintOptions> _getRules(File? analysisOptionsFile) {
25+
if (analysisOptionsFile == null || !analysisOptionsFile.exists) {
1726
return {};
1827
}
1928

29+
final optionsString = analysisOptionsFile.readAsStringSync();
30+
Object? yaml;
2031
try {
21-
final content = file.readAsStringSync();
22-
final yamlMap = loadYaml(content);
23-
final parsedYaml = _convertYaml(yamlMap);
24-
final result =
25-
parsedYaml is Map<String, dynamic> ? parsedYaml : <String, dynamic>{};
26-
27-
_cache[yamlPath] = result;
28-
return result;
29-
} on YamlException {
30-
_cache[yamlPath] = {};
32+
yaml = loadYaml(optionsString) as Object?;
33+
} catch (err) {
3134
return {};
3235
}
33-
}
34-
35-
/// Extracts custom lint rules from the provided YAML map.
36-
Map<String, dynamic> extractLintRules(
37-
Map<String, dynamic> yaml, {
38-
String lintName = 'custom_lint',
39-
}) {
40-
final customLint = yaml[lintName];
41-
42-
if (customLint is! Map) return {};
43-
44-
final rules = customLint['rules'];
45-
46-
if (rules is! List) return {};
47-
48-
final result = <String, dynamic>{};
49-
50-
for (final item in rules) {
51-
final rule = _extractRuleEntry(item);
52-
if (rule == null) continue;
53-
54-
result[rule.$1] = rule.$2;
55-
}
56-
57-
return result;
58-
}
59-
60-
dynamic _convertYaml(dynamic yaml) {
61-
if (yaml is YamlMap) {
62-
return _yamlMapToDartMap(yaml);
63-
}
64-
65-
if (yaml is YamlList) {
66-
return yaml.map(_convertRuleItem).toList();
67-
}
68-
69-
return yaml;
70-
}
71-
72-
dynamic _convertRuleItem(dynamic item) {
73-
if (item is! YamlMap) return item;
74-
75-
final map = _yamlMapToDartMap(item);
76-
77-
final keys = map.keys.toList();
78-
79-
if (keys.length >= 2 && map[keys.first] == null) {
80-
final ruleName = keys.first;
81-
final config = Map<String, dynamic>.from(map)..remove(ruleName);
82-
83-
return {ruleName: config.isEmpty ? null : config};
84-
}
85-
86-
return map;
87-
}
88-
89-
Map<String, dynamic> _yamlMapToDartMap(YamlMap yamlMap) {
90-
return Map<String, dynamic>.fromEntries(
91-
yamlMap.entries.map(
92-
(e) => MapEntry(e.key.toString(), _convertYaml(e.value)),
93-
),
94-
);
95-
}
96-
97-
(String, Map<String, dynamic>)? _extractRuleEntry(dynamic item) {
98-
if (item is String) return (item, {});
99-
if (item is! Map || item.isEmpty) return null;
100-
101-
final entry = item.entries.first;
102-
final ruleName = entry.key.toString();
103-
final config = entry.value;
104-
105-
if (config is Map<String, dynamic>) {
106-
return (ruleName, config);
107-
}
108-
109-
if (config is Map) {
110-
return (ruleName, Map<String, dynamic>.from(config));
36+
if (yaml is! Map) return {};
37+
38+
final rules = <String, LintOptions>{};
39+
final pluginsYaml = yaml['plugins'] as Object?;
40+
41+
if (pluginsYaml is Map) {
42+
final solidLint = pluginsYaml['solid_lints'];
43+
if (solidLint is Map) {
44+
final diagnostics = solidLint['diagnostics'];
45+
46+
if (diagnostics is Map) {
47+
for (final diag in diagnostics.entries) {
48+
final ruleName = diag.key as String;
49+
final value = diag.value;
50+
51+
if (value is bool) {
52+
rules[ruleName] = LintOptions.empty(enabled: value);
53+
} else if (value is Map) {
54+
final map = Map<String, Object?>.from(value);
55+
56+
final enabled = map.remove('enabled') as bool? ?? true;
57+
58+
rules[ruleName] = LintOptions.fromYaml(
59+
map,
60+
enabled: enabled,
61+
);
62+
}
63+
}
64+
}
65+
}
11166
}
11267

113-
return (ruleName, {});
68+
return UnmodifiableMapView(rules);
11469
}
11570
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:analyzer/analysis_rule/analysis_rule.dart';
2+
import 'package:collection/collection.dart';
3+
4+
/// Option information for a specific [AnalysisRule].
5+
class LintOptions {
6+
/// Creates a [LintOptions] from YAML.
7+
const LintOptions.fromYaml(Map<String, Object?> yaml, {required this.enabled})
8+
: json = yaml;
9+
10+
/// Options with no [json]
11+
const LintOptions.empty({required this.enabled}) : json = const {};
12+
13+
/// Whether the configuration enables/disables the lint rule.
14+
final bool enabled;
15+
16+
/// Extra configurations for a [AnalysisRule].
17+
final Map<String, Object?> json;
18+
19+
@override
20+
bool operator ==(Object other) =>
21+
other is LintOptions &&
22+
other.enabled == enabled &&
23+
const MapEquality<String, Object?>().equals(other.json, json);
24+
25+
@override
26+
int get hashCode => Object.hash(
27+
enabled,
28+
const MapEquality<String, Object?>().hash(json),
29+
);
30+
}

0 commit comments

Comments
 (0)