-
Notifications
You must be signed in to change notification settings - Fork 235
Expand file tree
/
Copy pathcomponentSelectorRule.ts
More file actions
119 lines (109 loc) · 4.67 KB
/
componentSelectorRule.ts
File metadata and controls
119 lines (109 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { IRuleMetadata } from 'tslint/lib';
import { arrayify, dedent } from 'tslint/lib/utils';
import {
OPTION_STYLE_CAMEL_CASE,
OPTION_STYLE_KEBAB_CASE,
OPTION_TYPE_ATTRIBUTE,
OPTION_TYPE_ELEMENT,
SelectorPropertyBase,
SelectorStyle,
SelectorType,
} from './selectorPropertyBase';
import { isNotNullOrUndefined } from './util/isNotNullOrUndefined';
const STYLE_GUIDE_PREFIX_LINK = 'https://angular.io/guide/styleguide#style-02-07';
const STYLE_GUIDE_STYLE_LINK = 'https://angular.io/guide/styleguide#style-05-02';
const STYLE_GUIDE_TYPE_LINK = 'https://angular.io/guide/styleguide#style-05-03';
export class Rule extends SelectorPropertyBase {
static readonly metadata: IRuleMetadata = {
description: 'Component selectors should follow given naming rules.',
descriptionDetails: dedent`
See more at ${STYLE_GUIDE_PREFIX_LINK}, ${STYLE_GUIDE_STYLE_LINK}
and ${STYLE_GUIDE_TYPE_LINK}.
`,
optionExamples: [
[true, OPTION_TYPE_ELEMENT, 'my-prefix', OPTION_STYLE_KEBAB_CASE],
[true, OPTION_TYPE_ELEMENT, ['ng', 'ngx'], OPTION_STYLE_KEBAB_CASE],
[true, OPTION_TYPE_ATTRIBUTE, 'myPrefix', OPTION_STYLE_CAMEL_CASE],
[true, [OPTION_TYPE_ELEMENT, OPTION_TYPE_ATTRIBUTE], 'myPrefix', OPTION_STYLE_CAMEL_CASE],
],
options: {
items: [
{
enum: [OPTION_TYPE_ATTRIBUTE, OPTION_TYPE_ELEMENT],
},
{
oneOf: [
{
items: {
type: 'string',
},
type: 'array',
},
{
type: 'string',
},
],
},
{
enum: [OPTION_STYLE_CAMEL_CASE, OPTION_STYLE_KEBAB_CASE],
},
],
maxLength: 3,
minLength: 3,
type: 'array',
},
optionsDescription: dedent`
Options accept three obligatory items as an array:
1. \`${OPTION_TYPE_ELEMENT}\` or \`${OPTION_TYPE_ATTRIBUTE}\` forces components to be used as either elements, attributes, or both (not recommended)
2. A single prefix (string) or array of prefixes (strings) which have to be used in component selectors.
3. \`${OPTION_STYLE_KEBAB_CASE}\` or \`${OPTION_STYLE_CAMEL_CASE}\` allows you to pick a case.
`,
rationale: dedent`
* Consistent conventions make it easy to quickly identify and reference assets of different types.
* Makes it easier to promote and share the component in other apps.
* Components are easy to identify in the DOM.
* Keeps the element names consistent with the specification for Custom Elements.
* Components have templates containing HTML and optional Angular template syntax.
* They display content. Developers place components on the page as they would native HTML elements and WebComponents.
* It is easier to recognize that a symbol is a component by looking at the template's HTML.
`,
ruleName: 'component-selector',
type: 'style',
typescriptOnly: true,
};
handleType = 'Component';
getPrefixFailure(prefixes: ReadonlyArray<string>): string {
const prefixStr = prefixes.length === 1 ? '' : ' one of the prefixes:';
return `The selector should be prefixed by${prefixStr} "${prefixes.join(', ')}" (${STYLE_GUIDE_PREFIX_LINK})`;
}
getStyleFailure(style: SelectorStyle): string {
const styleStr = style === OPTION_STYLE_KEBAB_CASE ? `${OPTION_STYLE_KEBAB_CASE}d and include a dash` : `${OPTION_STYLE_CAMEL_CASE}d`;
return `The selector should be ${styleStr} (${STYLE_GUIDE_STYLE_LINK})`;
}
getTypeFailure(types: ReadonlyArray<SelectorType>): string {
return `The selector should be used as an ${types.join(' or ')} (${STYLE_GUIDE_TYPE_LINK})`;
}
isEnabled(): boolean {
const {
metadata: {
options: {
items: {
[0]: { enum: enumTypes },
[2]: { enum: enumStyles },
},
maxLength,
minLength,
},
},
} = Rule;
const { length: argumentsLength } = this.ruleArguments;
const typeArgument = arrayify<SelectorType>(this.ruleArguments[0]);
const prefixArgument = arrayify<string>(this.ruleArguments[1]).filter(isNotNullOrUndefined);
const styleArgument = this.ruleArguments[2] as SelectorStyle;
const argumentsLengthInRange = argumentsLength >= minLength && argumentsLength <= maxLength;
const isTypeArgumentValid = typeArgument.length > 0 && typeArgument.every((argument) => enumTypes.indexOf(argument) !== -1);
const isPrefixArgumentValid = prefixArgument.length > 0;
const isStyleArgumentValid = enumStyles.indexOf(styleArgument) !== -1;
return super.isEnabled() && argumentsLengthInRange && isTypeArgumentValid && isPrefixArgumentValid && isStyleArgumentValid;
}
}