Skip to content
This repository was archived by the owner on Jul 16, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Changelog
# 1.2.0
- Allow to analyze multiple directories

## 1.1.5
- Tweak console reporter
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ metrics lib

### Full usage:
```
Usage: metrics [options...] <directory>
Usage: metrics [options...] <directories>
-h, --help Print this usage information.
-r, --reporter=<console> The format of the output of the analysis
[console (default), json, html, codeclimate]
Expand Down
170 changes: 101 additions & 69 deletions bin/metrics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,73 +5,104 @@ import 'package:dart_code_metrics/metrics_analyzer.dart';
import 'package:dart_code_metrics/reporters.dart';
import 'package:glob/glob.dart';

void main(List<String> args) {
const helpFlagName = 'help';
const reporterOptionName = 'reporter';
const cyclomaticComplexityThreshold = 'cyclomatic-complexity';
const linesOfCodeThreshold = 'lines-of-code';
const verboseName = 'verbose';
const ignoredFilesName = 'ignore-files';
const rootFolderName = 'root-folder';

var showUsage = false;

final parser = ArgParser()
..addFlag(helpFlagName,
abbr: 'h', help: 'Print this usage information.', negatable: false)
..addOption(reporterOptionName,
abbr: 'r',
help: 'The format of the output of the analysis',
valueHelp: 'console',
allowed: ['console', 'json', 'html', 'codeclimate'],
defaultsTo: 'console')
..addOption(cyclomaticComplexityThreshold,
help: 'Cyclomatic complexity threshold',
valueHelp: '20',
defaultsTo: '20')
..addOption(linesOfCodeThreshold,
help: 'Lines of code threshold', valueHelp: '50', defaultsTo: '50')
..addOption(rootFolderName,
help: 'Root folder',
valueHelp: './',
defaultsTo: Directory.current.path)
..addOption(ignoredFilesName,
help: 'Filepaths in Glob syntax to be ignored',
valueHelp: '{/**.g.dart,/**.template.dart}',
defaultsTo: '{/**.g.dart,/**.template.dart}')
..addFlag(verboseName, negatable: false);

ArgResults arguments;
const _usageHeader = 'Usage: metrics [options...] <directories>';
const _helpFlagName = 'help';
const _reporterOptionName = 'reporter';
const _cyclomaticComplexityThreshold = 'cyclomatic-complexity';
const _linesOfCodeThreshold = 'lines-of-code';
const _verboseName = 'verbose';
const _ignoredFilesName = 'ignore-files';
const _rootFolderName = 'root-folder';

final _parser = ArgParser()
..addFlag(_helpFlagName,
abbr: 'h', help: 'Print this usage information.', negatable: false)
..addOption(_reporterOptionName,
abbr: 'r',
help: 'The format of the output of the analysis',
valueHelp: 'console',
allowed: ['console', 'json', 'html', 'codeclimate'],
defaultsTo: 'console')
..addOption(_cyclomaticComplexityThreshold,
help: 'Cyclomatic complexity threshold',
valueHelp: '20',
defaultsTo: '20', callback: (String i) {
if (int.tryParse(i) == null) print('$_cyclomaticComplexityThreshold:');
})
..addOption(_linesOfCodeThreshold,
help: 'Lines of code threshold',
valueHelp: '50',
defaultsTo: '50', callback: (String i) {
if (int.tryParse(i) == null) print('$_linesOfCodeThreshold:');
})
..addOption(_rootFolderName,
help: 'Root folder', valueHelp: './', defaultsTo: Directory.current.path)
..addOption(_ignoredFilesName,
help: 'Filepaths in Glob syntax to be ignored',
valueHelp: '{/**.g.dart,/**.template.dart}',
defaultsTo: '{/**.g.dart,/**.template.dart}')
..addFlag(_verboseName, negatable: false);

void main(List<String> args) {
try {
arguments = parser.parse(args);
} on FormatException catch (_) {
showUsage = true;
}
final arguments = _parser.parse(args);

if (arguments == null ||
arguments[helpFlagName] as bool ||
arguments.rest.length != 1) {
showUsage = true;
} else if (!Directory(arguments.rest.single).existsSync()) {
print("Can't find directory ${arguments.rest.single}");
showUsage = true;
}
if (arguments[_helpFlagName] as bool) {
_showUsageAndExit(0);
}

if (arguments.rest.isEmpty) {
throw _InvalidArgumentException(
'Invalid number of directories. At least one must be specified');
}

arguments.rest.forEach((p) {
if (!Directory(p).existsSync()) {
throw _InvalidArgumentException(
"$p doesn't exist or isn't a directory");
}
});

if (showUsage) {
print('Usage: metrics [options...] <directory>');
print(parser.usage);
return;
// TODO: check that directories to analyze are all children of root folder

_runAnalysis(
arguments[_rootFolderName] as String,
arguments.rest,
arguments[_ignoredFilesName] as String,
int.parse(arguments[_cyclomaticComplexityThreshold] as String),
int.parse(arguments[_linesOfCodeThreshold] as String),
arguments[_reporterOptionName] as String,
arguments[_verboseName] as bool);
} on FormatException catch (e) {
print('${e.message}\n');
_showUsageAndExit(1);
} on _InvalidArgumentException catch (e) {
print('${e.message}\n');
_showUsageAndExit(1);
}
}

void _showUsageAndExit(int exitCode) {
print(_usageHeader);
print(_parser.usage);
exit(exitCode);
}

final rootFolder = arguments[rootFolderName] as String;
var dartFilePaths = Glob('${arguments.rest.single}**.dart')
.listSync(root: rootFolder, followLinks: false)
.whereType<File>()
.map((entity) => entity.path);
void _runAnalysis(
String rootFolder,
Iterable<String> analysisDirectories,
String ignoreFilesPattern,
int cyclomaticComplexityThreshold,
int linesOfCodeThreshold,
String reporterType,
bool verbose) {
var dartFilePaths = analysisDirectories.expand((directory) =>
Glob('$directory**.dart')
.listSync(root: rootFolder, followLinks: false)
.whereType<File>()
.map((entity) => entity.path));

final ignoreFilesPattern = arguments[ignoredFilesName] as Object;
if (ignoreFilesPattern is String && ignoreFilesPattern.isNotEmpty) {
if (ignoreFilesPattern.isNotEmpty) {
final ignoreFilesGlob = Glob(ignoreFilesPattern);
dartFilePaths =
dartFilePaths.where((path) => !ignoreFilesGlob.matches(path));
Expand All @@ -84,17 +115,14 @@ void main(List<String> args) {
..run();

final config = Config(
cyclomaticComplexityWarningLevel:
int.parse(arguments[cyclomaticComplexityThreshold] as String),
linesOfCodeWarningLevel:
int.parse(arguments[linesOfCodeThreshold] as String));
cyclomaticComplexityWarningLevel: cyclomaticComplexityThreshold,
linesOfCodeWarningLevel: linesOfCodeThreshold);

Reporter reporter;

switch (arguments[reporterOptionName] as String) {
switch (reporterType) {
case 'console':
reporter = ConsoleReporter(
reportConfig: config, reportAll: arguments[verboseName] as bool);
reporter = ConsoleReporter(reportConfig: config, reportAll: verbose);
break;
case 'json':
reporter = JsonReporter(reportConfig: config);
Expand All @@ -106,9 +134,13 @@ void main(List<String> args) {
reporter = CodeClimateReporter(reportConfig: config);
break;
default:
throw ArgumentError.value(
arguments[reporterOptionName], reporterOptionName);
throw ArgumentError.value(reporterType, 'reporter');
}

reporter.report(runner.results()).forEach(print);
}

class _InvalidArgumentException implements Exception {
final String message;
_InvalidArgumentException(this.message);
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: dart_code_metrics
description: |
Command line tool which helps to improve code quality.
Reports cyclomatic complexity and method length in plain console, JSON, HTML and Codeclimate formats
version: 1.1.5
version: 1.2.0
homepage: https://github.com/wrike/dart-code-metrics

environment:
Expand Down