UNPKG

5.65 kBPlain TextView Raw
1import * as ts from 'typescript';
2import * as Lint from 'tslint';
3
4import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker';
5import { ExtendedMetadata } from './utils/ExtendedMetadata';
6
7export const FAILURE_MIN_STRING: string = 'Too short; difficult to understand its purpose without context';
8export const FAILURE_MAX_STRING: string = 'Too long; difficult to read and potentially less maintainable';
9
10const OPTION_MIN_STRING: string = 'min';
11const OPTION_MAX_STRING: string = 'max';
12const OPTION_EXCEPTIONS_STRING: string = 'exceptions';
13
14/**
15 * Implementation of the id-length rule.
16 */
17export class Rule extends Lint.Rules.AbstractRule {
18 public static metadata: ExtendedMetadata = {
19 ruleName: 'id-length',
20 type: 'maintainability',
21 description: 'This rule enforces a minimum and/or maximum identifier length convention.',
22 options: {
23 definitions: {
24 'minimum-length': {
25 type: 'integer',
26 minimum: 1,
27 default: 2,
28 },
29 'maximum-length': {
30 type: 'integer',
31 minimum: 1,
32 },
33 exceptions: {
34 type: 'array',
35 items: {
36 type: 'string',
37 },
38 minLength: 0,
39 uniqueItems: true,
40 },
41 },
42 type: 'array',
43 items: {
44 type: 'array',
45 items: {
46 oneOf: [
47 {
48 title: 'Only the minimum length',
49 $ref: '#/definitions/minimum-length',
50 },
51 {
52 title: 'Only the exceptions array',
53 $ref: '#/definitions/exceptions',
54 },
55 {
56 title: 'Configuration object',
57 type: 'object',
58 properties: {
59 min: { $ref: '#/definitions/minimum-length' },
60 max: { $ref: '#/definitions/maximum-length' },
61 exceptions: { $ref: '#/definitions/exceptions' },
62 },
63 additionalProperties: false,
64 minProperties: 1,
65 },
66 ],
67 },
68 minItems: 1,
69 maxItems: 1,
70 },
71 },
72 optionsDescription: `
73 One of the following combinations can be provided:
74 * Minimum desired length.
75 * An array of exceptions.
76 * Minimum desired length and an exceptions array.
77 * A configuration object containing at least one of the following properties:
78 * \`"${OPTION_MIN_STRING}"\`
79 * \`"${OPTION_MAX_STRING}"\`
80 * \`"${OPTION_EXCEPTIONS_STRING}"\`
81 `,
82 optionExamples: <any>[
83 [true],
84 [true, 2],
85 [true, ['x', 'y', 'f', 'c']],
86 [true, 2, ['x', 'y', 'f', 'c']],
87 [
88 true,
89 {
90 min: 2,
91 max: 10,
92 exceptions: ['x', 'y', 'f', 'c'],
93 },
94 ],
95 ],
96 typescriptOnly: true,
97 issueClass: 'Non-SDL',
98 issueType: 'Warning',
99 severity: 'Important',
100 level: 'Opportunity for Excellence',
101 group: 'Correctness',
102 recommendation: 'true,',
103 commonWeaknessEnumeration: '',
104 };
105
106 public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
107 return this.applyWithWalker(new IdLengthRuleWalker(sourceFile, this.getOptions()));
108 }
109}
110
111class IdLengthRuleWalker extends ErrorTolerantWalker {
112 private min: number = 2;
113 private max: number = Infinity;
114 private exceptions: string[] = [];
115
116 constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) {
117 super(sourceFile, options);
118 this.parseOptions();
119 }
120
121 protected visitIdentifier(node: ts.Identifier): void {
122 this.checkAndReport(node);
123 super.visitIdentifier(node);
124 }
125
126 private checkAndReport(node: ts.Identifier): void {
127 const { text } = node;
128 if (this.exceptions.indexOf(text) === -1) {
129 if (text.length < this.min) {
130 return this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_MIN_STRING + ': ' + text);
131 }
132 if (text.length > this.max) {
133 return this.addFailureAt(node.getStart(), node.getWidth(), FAILURE_MAX_STRING + ': ' + text);
134 }
135 }
136 }
137
138 private parseOptions(): void {
139 this.getOptions().forEach((opt: any) => {
140 if (typeof opt === 'boolean') {
141 return;
142 }
143 if (typeof opt === 'number') {
144 this.min = opt;
145 return;
146 }
147 if (Array.isArray(opt)) {
148 this.exceptions = opt;
149 return;
150 }
151 if (typeof opt === 'object') {
152 this.min = typeof opt[OPTION_MIN_STRING] === 'number' ? opt[OPTION_MIN_STRING] : this.min;
153 this.max = typeof opt[OPTION_MAX_STRING] === 'number' ? opt[OPTION_MAX_STRING] : this.max;
154 this.exceptions = opt[OPTION_EXCEPTIONS_STRING] || this.exceptions;
155 return;
156 }
157 });
158 }
159}