Skip to content

Commit c82a9da

Browse files
committed
feat:format import
1 parent 99aebd6 commit c82a9da

9 files changed

Lines changed: 299 additions & 175 deletions
Lines changed: 61 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
11
import 'dart:io';
22

33
import 'package:args/args.dart';
4-
import 'package:path/path.dart' as p;
54

6-
import 'package:flutterguard_cli/src/config_loader.dart';
7-
import 'package:flutterguard_cli/src/file_collector.dart';
85
import 'package:flutterguard_cli/src/report_generator.dart';
9-
import 'package:flutterguard_cli/src/rules/circular_dependency.dart';
10-
import 'package:flutterguard_cli/src/rules/large_units.dart';
11-
import 'package:flutterguard_cli/src/rules/layer_violation.dart';
12-
import 'package:flutterguard_cli/src/rules/lifecycle_resource.dart';
13-
import 'package:flutterguard_cli/src/rules/missing_const_constructor.dart';
14-
import 'package:flutterguard_cli/src/rules/module_violation.dart';
15-
import 'package:flutterguard_cli/src/static_issue.dart';
6+
import 'package:flutterguard_cli/src/scanner.dart';
167

178
const _version = '0.1.0';
189

1910
void main(List<String> args) {
20-
if (args.contains('--version') || args.contains('-V')) {
21-
stdout.writeln('flutterguard $_version');
22-
exit(0);
23-
}
24-
2511
final scanParser = ArgParser()
2612
..addOption('path',
2713
abbr: 'p', defaultsTo: '.', help: 'Project path to scan')
@@ -46,23 +32,39 @@ void main(List<String> args) {
4632
defaultsTo: 'none',
4733
allowed: ['none', 'high', 'medium', 'low'],
4834
help: 'Fail threshold for CI gate')
49-
..addOption('min-score', help: 'Minimum score threshold (0-100)');
35+
..addOption('min-score', help: 'Minimum score threshold (0-100)')
36+
..addFlag('help', abbr: 'h', help: 'Show scan usage', negatable: false);
5037

5138
final parser = ArgParser()
5239
..addCommand('scan', scanParser)
53-
..addFlag('help', abbr: 'h', help: 'Show usage', negatable: false);
40+
..addFlag('help', abbr: 'h', help: 'Show usage', negatable: false)
41+
..addFlag('version', abbr: 'V', help: 'Show version', negatable: false);
5442

5543
try {
5644
final results = parser.parse(args);
5745

46+
if (results['version'] == true) {
47+
stdout.writeln('flutterguard $_version');
48+
exit(0);
49+
}
50+
5851
if (results['help'] == true || args.isEmpty || args.first == 'help') {
5952
_printUsage(parser);
6053
exit(0);
6154
}
6255

6356
final command = results.command;
64-
if (command?.name == 'scan') {
65-
_handleScan(command!);
57+
if (command == null) {
58+
_printUsage(parser);
59+
exit(0);
60+
}
61+
62+
if (command.name == 'scan') {
63+
if (command['help'] == true) {
64+
_printScanUsage(scanParser);
65+
exit(0);
66+
}
67+
_handleScan(command);
6668
} else {
6769
_printUsage(parser);
6870
exit(0);
@@ -76,106 +78,63 @@ void main(List<String> args) {
7678
}
7779

7880
void _handleScan(ArgResults args) {
79-
final projectPath = p.normalize(p.absolute(args['path'] as String));
80-
final configPath = args['config'] as String;
81-
final outputDir = args['output'] as String;
8281
final format = args['format'] as String;
8382
final verbose = args['verbose'] as bool;
8483
final failOn = args['fail-on'] as String;
8584
final minScoreStr = args['min-score'] as String?;
85+
final minScore = _parseMinScore(minScoreStr);
8686

87-
if (!Directory(projectPath).existsSync()) {
88-
stderr.writeln('Error: Project path "$projectPath" does not exist.');
87+
late final ScanResult result;
88+
try {
89+
result = FlutterGuardScanner.scan(
90+
projectPath: args['path'] as String,
91+
configPath: args['config'] as String,
92+
outputDir: args['output'] as String,
93+
writeJson: format == 'json',
94+
);
95+
} on ScanException catch (e) {
96+
stderr.writeln('Error: ${e.message}');
97+
exit(2);
98+
} on FormatException catch (e) {
99+
stderr.writeln('Error: ${e.message}');
89100
exit(2);
90101
}
91102

92-
final config = ScanConfig.fromFile(p.join(projectPath, configPath));
93-
94-
final files = FileCollector.collect(projectPath, config);
95-
96-
if (files.isEmpty) {
103+
if (result.files.isEmpty) {
97104
stderr.writeln('No Dart files found. Check your include/exclude patterns.');
98105
exit(0);
99106
}
100107

101-
final allIssues = <StaticIssue>[];
102-
103-
allIssues.addAll(LargeUnitsRule(
104-
largeFileConfig: config.rules.largeFile,
105-
largeClassConfig: config.rules.largeClass,
106-
largeBuildMethodConfig: config.rules.largeBuildMethod,
107-
).analyze(files));
108-
allIssues.addAll(
109-
LifecycleResourceRule(config.rules.lifecycleResource).analyze(files));
110-
if (config.architecture.layerViolationEnabled) {
111-
allIssues.addAll(LayerViolationRule(
112-
config.architecture.layers,
113-
projectPath: projectPath,
114-
).analyze(files));
115-
}
116-
if (config.architecture.moduleViolationEnabled) {
117-
allIssues.addAll(ModuleViolationRule(
118-
config.architecture.modules,
119-
projectPath: projectPath,
120-
).analyze(files));
121-
}
122-
allIssues.addAll(MissingConstConstructorRule(
123-
config.rules.missingConstConstructor,
124-
).analyze(files));
125-
allIssues.addAll(CircularDependencyRule(
126-
enabled: config.architecture.detectCycles,
127-
projectPath: projectPath,
128-
).analyze(files));
129-
130-
allIssues.sort((a, b) {
131-
final levelOrder = {
132-
RiskLevel.high: 0,
133-
RiskLevel.medium: 1,
134-
RiskLevel.low: 2,
135-
};
136-
final cmp = levelOrder[a.level]!.compareTo(levelOrder[b.level]!);
137-
if (cmp != 0) return cmp;
138-
return a.file.compareTo(b.file);
139-
});
140-
141-
final reportDir =
142-
p.isAbsolute(outputDir) ? outputDir : p.join(projectPath, outputDir);
143-
Directory(reportDir).createSync(recursive: true);
144-
145-
if (format == 'json') {
146-
final json = ReportGenerator.generateJson(
147-
projectPath: projectPath,
148-
issues: allIssues,
149-
);
150-
File(p.join(reportDir, 'report.json')).writeAsStringSync(json);
151-
}
152-
153108
final stdoutOutput = ReportGenerator.generateStdout(
154-
projectPath: projectPath,
155-
issues: allIssues,
109+
projectPath: result.projectPath,
110+
issues: result.issues,
111+
scannedFileCount: result.files.length,
156112
verbose: verbose,
157113
);
158114
stdout.writeln(stdoutOutput);
159115

160116
if (failOn != 'none') {
161-
if (ReportGenerator.shouldFail(allIssues, failOn)) {
117+
if (ReportGenerator.shouldFail(result.issues, failOn)) {
162118
stderr
163119
.writeln('CI gate failed: Issues found at or above "$failOn" level.');
164120
exit(1);
165121
}
166122
}
167123

168-
if (minScoreStr != null) {
169-
final minScore = int.tryParse(minScoreStr);
170-
if (minScore != null) {
171-
final score = ReportGenerator.calculateScore(allIssues);
172-
if (score < minScore) {
173-
stderr.writeln(
174-
'CI gate failed: Score $score is below minimum $minScore.');
175-
exit(1);
176-
}
177-
}
124+
if (minScore != null && result.score < minScore) {
125+
stderr.writeln(
126+
'CI gate failed: Score ${result.score} is below minimum $minScore.');
127+
exit(1);
128+
}
129+
}
130+
131+
int? _parseMinScore(String? value) {
132+
if (value == null) return null;
133+
final score = int.tryParse(value);
134+
if (score == null || score < 0 || score > 100) {
135+
throw const FormatException('Expected --min-score to be an integer 0-100.');
178136
}
137+
return score;
179138
}
180139

181140
void _printUsage(ArgParser parser) {
@@ -206,3 +165,11 @@ void _printUsage(ArgParser parser) {
206165
stdout.writeln(' flutterguard scan -p ./my_flutter_app');
207166
stdout.writeln(' flutterguard scan -p . --format json --fail-on high');
208167
}
168+
169+
void _printScanUsage(ArgParser scanParser) {
170+
stdout.writeln('FlutterGuard — scan command');
171+
stdout.writeln();
172+
stdout.writeln('Usage: flutterguard scan [options]');
173+
stdout.writeln();
174+
stdout.writeln(scanParser.usage);
175+
}

packages/flutterguard_cli/lib/flutterguard_cli.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ export 'src/rules/layer_violation.dart';
1111
export 'src/rules/lifecycle_resource.dart';
1212
export 'src/rules/missing_const_constructor.dart';
1313
export 'src/rules/module_violation.dart';
14+
export 'src/scanner.dart';
15+
export 'src/source_utils.dart';
1416
export 'src/static_issue.dart';

0 commit comments

Comments
 (0)