Skip to content
This repository has been archived by the owner on Jul 16, 2023. It is now read-only.

Commit

Permalink
Rule implementaion
Browse files Browse the repository at this point in the history
  • Loading branch information
incendial committed Aug 15, 2020
1 parent e979bee commit fe5487c
Show file tree
Hide file tree
Showing 6 changed files with 413 additions and 1 deletion.
6 changes: 5 additions & 1 deletion CHANGELOG.md
@@ -1,8 +1,12 @@
# Changelog

# Unreleased

- Add static code diagnostics component-annotation-arguments-ordering

# 1.8.1

- Fix static code diagnostics member-ordering and prefer-condifional-expression
- Fix static code diagnostics member-ordering and prefer-conditional-expression

# 1.8.0

Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -115,4 +115,5 @@ If you want command line tool to check rules, you should add configuration to yo
### Angular specific
* [avoid-preserve-whitespace-false](https://github.com/wrike/dart-code-metrics/blob/master/doc/rules/avoid_preserve_whitespace_false.md)
* [component-annotation-arguments-ordering](https://github.com/wrike/dart-code-metrics/blob/master/doc/rules/component_annotation_arguments_ordering.md)   ![Configurable](https://img.shields.io/badge/-configurable-informational)
* [prefer-on-push-cd-strategy](https://github.com/wrike/dart-code-metrics/blob/master/doc/rules/prefer_on_push_cd_strategy.md)
34 changes: 34 additions & 0 deletions doc/rules/component_annotation_arguments_ordering.md
@@ -0,0 +1,34 @@
# Component annotation arguments ordering

![Configurable](https://img.shields.io/badge/-configurable-informational)

## Rule id

component-annotation-arguments-ordering

## Description

Enforces Angular `@Component` annotation arguments ordering.

The value for `order` may be an array consisting of the following strings (default order listed):

- selector
- templates
- styles
- directives
- pipes
- providers
- encapsulation
- visibility
- exports
- change_detection

## Config example

```yaml
component-annotation-arguments-ordering:
order:
- selector
- templates
- change_detection
```
233 changes: 233 additions & 0 deletions lib/src/rules/component_annotation_arguments_ordering.dart
@@ -0,0 +1,233 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:dart_code_metrics/src/models/code_issue.dart';
import 'package:dart_code_metrics/src/models/code_issue_severity.dart';
import 'package:meta/meta.dart';

import 'base_rule.dart';
import 'rule_utils.dart';

class ComponentAnnotationArgumentsOrderingRule extends BaseRule {
static const ruleId = 'component-annotation-arguments-ordering';
static const _documentationUrl = 'https://git.io/JJ5HC';

static const _warningMessage = 'should be before';

final List<_ArgumentGroup> _groupsOrder;

ComponentAnnotationArgumentsOrderingRule(
{Map<String, Object> config = const {}})
: _groupsOrder = _parseOrder(config),
super(
id: ruleId,
documentation: Uri.parse(_documentationUrl),
severity:
CodeIssueSeverity.fromJson(config['severity'] as String) ??
CodeIssueSeverity.style);

@override
Iterable<CodeIssue> check(
CompilationUnit unit,
Uri sourceUrl,
String sourceContent,
) {
final _visitor = _Visitor(_groupsOrder);

final argumentsInfo = [
for (final entry in unit.childEntities)
if (entry is ClassDeclaration) ...entry.accept(_visitor),
];

return argumentsInfo.where((info) => info.argumentOrder.isWrong).map(
(info) => createIssue(
this,
'Arguments group ${info.argumentOrder.argumentGroup.name} $_warningMessage ${info.argumentOrder.previousArgumentGroup.name}',
null,
null,
sourceUrl,
sourceContent,
unit.lineInfo,
info.argument),
);
}

static List<_ArgumentGroup> _parseOrder(Map<String, Object> config) {
final order = config.containsKey('order') && config['order'] is Iterable
? List<String>.from(config['order'] as Iterable)
: <String>[];

return order.isEmpty
? _ArgumentGroup._groupsOrder
: order
.map(_ArgumentGroup.parseGroupName)
.where((group) => group != null)
.toList();
}
}

class _Visitor extends SimpleAstVisitor<List<_ArgumentInfo>> {
final List<_ArgumentGroup> _groupsOrder;

_Visitor(this._groupsOrder);

@override
List<_ArgumentInfo> visitClassDeclaration(ClassDeclaration node) {
final componentAnnotation =
node.metadata.firstWhere(_isComponentAnnotation, orElse: () => null);

return componentAnnotation != null
? _visitAnnotation(componentAnnotation)
: [];
}

List<_ArgumentInfo> _visitAnnotation(Annotation node) {
final _argumentsInfo = <_ArgumentInfo>[];

final arguments = node.arguments.arguments.whereType<NamedExpression>();

for (final argument in arguments) {
final name = argument.name.label.name;
final group = _ArgumentGroup.parseArgumentName(name);

if (group != null && _groupsOrder.contains(group)) {
_argumentsInfo.add(_ArgumentInfo(
argument: argument,
argumentOrder: _getOrder(group, name, _argumentsInfo),
));
}
}

return _argumentsInfo;
}

_ArgumentOrder _getOrder(
_ArgumentGroup group,
String argumentName,
Iterable<_ArgumentInfo> argumentsInfo,
) {
if (argumentsInfo.isNotEmpty) {
final lastArgumentOrder = argumentsInfo.last.argumentOrder;
final hasSameGroup = lastArgumentOrder.argumentGroup == group;

final previousArgumentGroup = hasSameGroup
? lastArgumentOrder.previousArgumentGroup
: lastArgumentOrder.argumentGroup;

return _ArgumentOrder(
argumentGroup: group,
previousArgumentGroup: previousArgumentGroup,
isWrong: (hasSameGroup && lastArgumentOrder.isWrong) ||
_isCurrentGroupBefore(lastArgumentOrder.argumentGroup, group),
);
}

return _ArgumentOrder(isWrong: false, argumentGroup: group);
}

bool _isCurrentGroupBefore(
_ArgumentGroup lastArgumentGroup,
_ArgumentGroup argumentGroup,
) =>
_groupsOrder.indexOf(lastArgumentGroup) >
_groupsOrder.indexOf(argumentGroup);

bool _isComponentAnnotation(Annotation node) =>
node.name.name == 'Component' && node.atSign.type.lexeme == '@';
}

@immutable
class _ArgumentGroup {
final String name;
final Iterable<String> arguments;

static const selector = _ArgumentGroup._(
'selector',
['selector'],
);
static const templates = _ArgumentGroup._(
'templates',
['template', 'templateUrl'],
);
static const styles = _ArgumentGroup._(
'styles',
['styles', 'styleUrls'],
);
static const directives = _ArgumentGroup._(
'directives',
['directives', 'directiveTypes'],
);
static const pipes = _ArgumentGroup._(
'pipes',
['pipes'],
);
static const providers = _ArgumentGroup._(
'providers',
['providers', 'viewProviders'],
);
static const encapsulation = _ArgumentGroup._(
'encapsulation',
['encapsulation'],
);
static const visibility = _ArgumentGroup._(
'visibility',
['visibility'],
);
static const exports = _ArgumentGroup._(
'exports',
['exports', 'exportAs'],
);
static const changeDetection = _ArgumentGroup._(
'change_detection',
['changeDetection'],
);

static const _groupsOrder = [
selector,
templates,
styles,
directives,
pipes,
providers,
encapsulation,
visibility,
exports,
changeDetection,
];

const _ArgumentGroup._(this.name, this.arguments);

static _ArgumentGroup parseGroupName(String name) => _groupsOrder.firstWhere(
(group) => group.name == name,
orElse: () => null,
);

static _ArgumentGroup parseArgumentName(String name) =>
_groupsOrder.firstWhere(
(group) => group.arguments.contains(name),
orElse: () => null,
);
}

@immutable
class _ArgumentInfo {
final NamedExpression argument;
final _ArgumentOrder argumentOrder;

const _ArgumentInfo({
this.argument,
this.argumentOrder,
});
}

@immutable
class _ArgumentOrder {
final bool isWrong;
final _ArgumentGroup argumentGroup;
final _ArgumentGroup previousArgumentGroup;

const _ArgumentOrder({
this.isWrong,
this.argumentGroup,
this.previousArgumentGroup,
});
}
3 changes: 3 additions & 0 deletions lib/src/rules_factory.dart
@@ -1,6 +1,7 @@
import 'rules/avoid_preserve_whitespace_false.dart';
import 'rules/base_rule.dart';
import 'rules/binary_expression_operand_order_rule.dart';
import 'rules/component_annotation_arguments_ordering.dart';
import 'rules/double_literal_format_rule.dart';
import 'rules/member_ordering.dart';
import 'rules/newline_before_return.dart';
Expand All @@ -18,6 +19,8 @@ final _implementedRules = <String, BaseRule Function(Map<String, Object>)>{
AvoidPreserveWhitespaceFalseRule(config: config),
BinaryExpressionOperandOrderRule.ruleId: (config) =>
BinaryExpressionOperandOrderRule(config: config),
ComponentAnnotationArgumentsOrderingRule.ruleId: (config) =>
ComponentAnnotationArgumentsOrderingRule(config: config),
DoubleLiteralFormatRule.ruleId: (config) =>
DoubleLiteralFormatRule(config: config),
MemberOrderingRule.ruleId: (config) => MemberOrderingRule(config: config),
Expand Down

0 comments on commit fe5487c

Please sign in to comment.