eslint-plugin-absolute 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,153 +0,0 @@
1
- import { TSESLint, TSESTree } from "@typescript-eslint/utils";
2
-
3
- type Options = [number?];
4
- type MessageIds = "tooDeep";
5
-
6
- export const maxDepthExtended: TSESLint.RuleModule<MessageIds, Options> = {
7
- create(context) {
8
- const [option] = context.options;
9
- const maxDepth = typeof option === "number" ? option : 1;
10
- const functionStack: number[] = [];
11
-
12
- // Helper to get ancestors of a node by walking its parent chain.
13
- const getAncestors = (node: TSESTree.Node) => {
14
- const ancestors: TSESTree.Node[] = [];
15
- let current: TSESTree.Node | null | undefined = node.parent;
16
- while (current) {
17
- ancestors.push(current);
18
- current = current.parent;
19
- }
20
- return ancestors;
21
- };
22
-
23
- // Check if a block only contains a single early exit: return or throw.
24
- const isEarlyExitBlock = (node: TSESTree.BlockStatement) => {
25
- if (node.body.length !== 1) {
26
- return false;
27
- }
28
- const [first] = node.body;
29
- if (!first) {
30
- return false;
31
- }
32
- return (
33
- first.type === "ReturnStatement" ||
34
- first.type === "ThrowStatement"
35
- );
36
- };
37
-
38
- const isFunctionBody = (node: TSESTree.BlockStatement) => {
39
- const ancestors = getAncestors(node);
40
- const [parent] = ancestors;
41
- return (
42
- parent &&
43
- (parent.type === "FunctionDeclaration" ||
44
- parent.type === "FunctionExpression" ||
45
- parent.type === "ArrowFunctionExpression") &&
46
- node === parent.body
47
- );
48
- };
49
-
50
- const incrementCurrentDepth = () => {
51
- if (functionStack.length === 0) {
52
- return null;
53
- }
54
- const index = functionStack.length - 1;
55
- const currentDepth = functionStack[index];
56
- if (typeof currentDepth !== "number") {
57
- return null;
58
- }
59
- const nextDepth = currentDepth + 1;
60
- functionStack[index] = nextDepth;
61
- return nextDepth;
62
- };
63
-
64
- const decrementCurrentDepth = () => {
65
- if (functionStack.length === 0) {
66
- return;
67
- }
68
- const index = functionStack.length - 1;
69
- const currentDepth = functionStack[index];
70
- if (typeof currentDepth !== "number") {
71
- return;
72
- }
73
- functionStack[index] = currentDepth - 1;
74
- };
75
-
76
- // Report if the current depth exceeds the allowed maximum.
77
- const checkDepth = (node: TSESTree.BlockStatement, depth: number) => {
78
- if (depth > maxDepth) {
79
- context.report({
80
- data: { depth, maxDepth },
81
- messageId: "tooDeep",
82
- node
83
- });
84
- }
85
- };
86
-
87
- return {
88
- ArrowFunctionExpression() {
89
- functionStack.push(0);
90
- },
91
- "ArrowFunctionExpression:exit"() {
92
- functionStack.pop();
93
- },
94
- BlockStatement(node: TSESTree.BlockStatement) {
95
- // Do not count if this block is the body of a function.
96
- if (isFunctionBody(node)) {
97
- return;
98
- }
99
-
100
- // Skip blocks that only have an early exit.
101
- if (isEarlyExitBlock(node)) {
102
- return;
103
- }
104
-
105
- const depth = incrementCurrentDepth();
106
- if (depth !== null) {
107
- checkDepth(node, depth);
108
- }
109
- },
110
- "BlockStatement:exit"(node: TSESTree.BlockStatement) {
111
- if (isFunctionBody(node)) {
112
- return;
113
- }
114
-
115
- if (isEarlyExitBlock(node)) {
116
- return;
117
- }
118
-
119
- decrementCurrentDepth();
120
- },
121
- FunctionDeclaration() {
122
- functionStack.push(0);
123
- },
124
- "FunctionDeclaration:exit"() {
125
- functionStack.pop();
126
- },
127
- FunctionExpression() {
128
- functionStack.push(0);
129
- },
130
- "FunctionExpression:exit"() {
131
- functionStack.pop();
132
- }
133
- };
134
- },
135
- defaultOptions: [1],
136
- meta: {
137
- docs: {
138
- description:
139
- "disallow too many nested blocks except when the block only contains an early exit (return or throw)"
140
- },
141
- messages: {
142
- tooDeep:
143
- "Blocks are nested too deeply ({{depth}}). Maximum allowed is {{maxDepth}} or an early exit."
144
- },
145
- schema: [
146
- {
147
- // Accepts a single number as the maximum allowed depth.
148
- type: "number"
149
- }
150
- ],
151
- type: "suggestion"
152
- }
153
- };
@@ -1,59 +0,0 @@
1
- import { TSESLint, TSESTree } from "@typescript-eslint/utils";
2
-
3
- type Options = [number];
4
- type MessageIds = "tooDeeplyNested";
5
-
6
- const isJSXAncestor = (node: TSESTree.Node) =>
7
- node.type === "JSXElement" || node.type === "JSXFragment";
8
-
9
- export const maxJSXNesting: TSESLint.RuleModule<MessageIds, Options> = {
10
- create(context) {
11
- const [option] = context.options;
12
- const maxAllowed = typeof option === "number" ? option : 1;
13
-
14
- /**
15
- * Calculates the JSX nesting level for the given node by traversing the node.parent chain.
16
- */
17
- const getJSXNestingLevel = (node: TSESTree.Node) => {
18
- let level = 1; // count the current node as level 1
19
- let current: TSESTree.Node | null | undefined = node.parent;
20
- while (current) {
21
- level += isJSXAncestor(current) ? 1 : 0;
22
- current = current.parent;
23
- }
24
- return level;
25
- };
26
-
27
- return {
28
- JSXElement(node: TSESTree.JSXElement) {
29
- const level = getJSXNestingLevel(node);
30
- if (level > maxAllowed) {
31
- context.report({
32
- data: { level, maxAllowed },
33
- messageId: "tooDeeplyNested",
34
- node
35
- });
36
- }
37
- }
38
- };
39
- },
40
- defaultOptions: [1],
41
- meta: {
42
- docs: {
43
- description:
44
- "Warn when JSX elements are nested too deeply, suggesting refactoring into a separate component."
45
- },
46
- messages: {
47
- tooDeeplyNested:
48
- "JSX element is nested too deeply ({{level}} levels, allowed is {{maxAllowed}} levels). Consider refactoring into a separate component."
49
- },
50
- // The rule accepts a single numeric option (minimum 1)
51
- schema: [
52
- {
53
- minimum: 1,
54
- type: "number"
55
- }
56
- ],
57
- type: "suggestion"
58
- }
59
- };
@@ -1,360 +0,0 @@
1
- import { TSESLint, TSESTree } from "@typescript-eslint/utils";
2
-
3
- type MinVarLengthOption = {
4
- minLength?: number;
5
- allowedVars?: string[];
6
- };
7
-
8
- type Options = [MinVarLengthOption?];
9
- type MessageIds = "variableNameTooShort";
10
-
11
- /**
12
- * Recursively extract identifier names from a pattern (for destructuring)
13
- */
14
- const extractIdentifiersFromPattern = (
15
- pattern: TSESTree.Node | null,
16
- identifiers: string[] = []
17
- ) => {
18
- if (!pattern) return identifiers;
19
- switch (pattern.type) {
20
- case "Identifier":
21
- identifiers.push(pattern.name);
22
- break;
23
- case "ObjectPattern":
24
- pattern.properties.forEach((prop) => {
25
- if (prop.type === "Property") {
26
- extractIdentifiersFromPattern(prop.value, identifiers);
27
- } else if (prop.type === "RestElement") {
28
- extractIdentifiersFromPattern(prop.argument, identifiers);
29
- }
30
- });
31
- break;
32
- case "ArrayPattern":
33
- pattern.elements
34
- .filter(
35
- (element): element is TSESTree.DestructuringPattern =>
36
- element !== null
37
- )
38
- .forEach((element) => {
39
- extractIdentifiersFromPattern(element, identifiers);
40
- });
41
- break;
42
- case "AssignmentPattern":
43
- extractIdentifiersFromPattern(pattern.left, identifiers);
44
- break;
45
- default:
46
- break;
47
- }
48
- return identifiers;
49
- };
50
-
51
- const getDeclaratorNames = (declarations: TSESTree.VariableDeclarator[]) =>
52
- declarations
53
- .filter(
54
- (
55
- decl
56
- ): decl is TSESTree.VariableDeclarator & {
57
- id: TSESTree.Identifier;
58
- } => decl.id.type === "Identifier"
59
- )
60
- .map((decl) => decl.id.name);
61
-
62
- const collectParamNames = (params: TSESTree.Parameter[]) => {
63
- const names: string[] = [];
64
- params.forEach((param) => {
65
- extractIdentifiersFromPattern(param, names);
66
- });
67
- return names;
68
- };
69
-
70
- export const minVarLength: TSESLint.RuleModule<MessageIds, Options> = {
71
- create(context) {
72
- const { sourceCode } = context;
73
- const [options] = context.options;
74
- const configuredMinLength =
75
- options && typeof options.minLength === "number"
76
- ? options.minLength
77
- : 1;
78
- const configuredAllowedVars =
79
- options && Array.isArray(options.allowedVars)
80
- ? options.allowedVars
81
- : [];
82
-
83
- const minLength = configuredMinLength;
84
- const allowedVars = configuredAllowedVars;
85
-
86
- // Helper: walk up the node.parent chain to get ancestors.
87
- const getAncestors = (node: TSESTree.Node) => {
88
- const ancestors: TSESTree.Node[] = [];
89
- let current: TSESTree.Node | null | undefined = node.parent;
90
- while (current) {
91
- ancestors.push(current);
92
- current = current.parent;
93
- }
94
- return ancestors;
95
- };
96
-
97
- // Helper: retrieve the scope for a given node using the scopeManager.
98
- const getScope = (node: TSESTree.Node) => {
99
- const { scopeManager } = sourceCode;
100
- if (!scopeManager) {
101
- return null;
102
- }
103
- const acquired = scopeManager.acquire(node);
104
- if (acquired) {
105
- return acquired;
106
- }
107
- return scopeManager.globalScope ?? null;
108
- };
109
-
110
- // Fallback: get declared variable names in the nearest BlockStatement.
111
- const getVariablesInNearestBlock = (node: TSESTree.Node) => {
112
- let current: TSESTree.Node | null | undefined = node.parent;
113
- while (current && current.type !== "BlockStatement") {
114
- current = current.parent;
115
- }
116
- if (
117
- !current ||
118
- current.type !== "BlockStatement" ||
119
- !Array.isArray(current.body)
120
- ) {
121
- return [];
122
- }
123
-
124
- const varDeclarations = current.body.filter(
125
- (stmt): stmt is TSESTree.VariableDeclaration =>
126
- stmt.type === "VariableDeclaration"
127
- );
128
- return varDeclarations.flatMap((stmt) =>
129
- getDeclaratorNames(stmt.declarations)
130
- );
131
- };
132
-
133
- const isLongerMatchInScope = (shortName: string, varName: string) =>
134
- varName.length >= minLength &&
135
- varName.length > shortName.length &&
136
- varName.startsWith(shortName);
137
-
138
- const checkScopeVariables = (
139
- shortName: string,
140
- node: TSESTree.Identifier
141
- ) => {
142
- const startingScope = getScope(node);
143
- let outer =
144
- startingScope && startingScope.upper
145
- ? startingScope.upper
146
- : null;
147
- while (outer) {
148
- if (
149
- outer.variables.some((variable) =>
150
- isLongerMatchInScope(shortName, variable.name)
151
- )
152
- ) {
153
- return true;
154
- }
155
- outer = outer.upper;
156
- }
157
- return false;
158
- };
159
-
160
- const checkBlockVariables = (
161
- shortName: string,
162
- node: TSESTree.Identifier
163
- ) => {
164
- const blockVars = getVariablesInNearestBlock(node);
165
- return blockVars.some((varName) =>
166
- isLongerMatchInScope(shortName, varName)
167
- );
168
- };
169
-
170
- const checkAncestorDeclarators = (
171
- shortName: string,
172
- node: TSESTree.Identifier
173
- ) => {
174
- const ancestors = getAncestors(node);
175
- return ancestors.some(
176
- (anc) =>
177
- anc.type === "VariableDeclarator" &&
178
- anc.id &&
179
- anc.id.type === "Identifier" &&
180
- isLongerMatchInScope(shortName, anc.id.name)
181
- );
182
- };
183
-
184
- const checkFunctionAncestor = (
185
- shortName: string,
186
- anc:
187
- | TSESTree.FunctionDeclaration
188
- | TSESTree.FunctionExpression
189
- | TSESTree.ArrowFunctionExpression
190
- ) => {
191
- const names = collectParamNames(anc.params);
192
- return names.some((paramName) =>
193
- isLongerMatchInScope(shortName, paramName)
194
- );
195
- };
196
-
197
- const checkCatchAncestor = (
198
- shortName: string,
199
- anc: TSESTree.CatchClause
200
- ) => {
201
- if (!anc.param) {
202
- return false;
203
- }
204
- const names = extractIdentifiersFromPattern(anc.param, []);
205
- return names.some((paramName) =>
206
- isLongerMatchInScope(shortName, paramName)
207
- );
208
- };
209
-
210
- const checkAncestorParams = (
211
- shortName: string,
212
- node: TSESTree.Identifier
213
- ) => {
214
- const ancestors = getAncestors(node);
215
- return ancestors.some((anc) => {
216
- if (
217
- anc.type === "FunctionDeclaration" ||
218
- anc.type === "FunctionExpression" ||
219
- anc.type === "ArrowFunctionExpression"
220
- ) {
221
- return checkFunctionAncestor(shortName, anc);
222
- }
223
- if (anc.type === "CatchClause") {
224
- return checkCatchAncestor(shortName, anc);
225
- }
226
- return false;
227
- });
228
- };
229
-
230
- /**
231
- * Checks if there is an outer variable whose name is longer than the current short name
232
- * and starts with the same characters.
233
- */
234
- const hasOuterCorrespondingIdentifier = (
235
- shortName: string,
236
- node: TSESTree.Identifier
237
- ) =>
238
- checkScopeVariables(shortName, node) ||
239
- checkBlockVariables(shortName, node) ||
240
- checkAncestorDeclarators(shortName, node) ||
241
- checkAncestorParams(shortName, node);
242
-
243
- /**
244
- * Checks an Identifier node. If its name is shorter than minLength (and not in the allowed list)
245
- * and no outer variable with a longer name starting with the short name is found, it reports an error.
246
- */
247
- const checkIdentifier = (node: TSESTree.Identifier) => {
248
- const { name } = node;
249
- if (name.length >= minLength) {
250
- return;
251
- }
252
-
253
- // If the name is in the allowed list, skip.
254
- if (allowedVars.includes(name)) {
255
- return;
256
- }
257
- if (!hasOuterCorrespondingIdentifier(name, node)) {
258
- context.report({
259
- data: { minLength, name },
260
- messageId: "variableNameTooShort",
261
- node
262
- });
263
- }
264
- };
265
-
266
- /**
267
- * Recursively checks a pattern node for identifiers.
268
- */
269
- const checkPattern = (pattern: TSESTree.Node | null) => {
270
- if (!pattern) return;
271
- switch (pattern.type) {
272
- case "Identifier":
273
- checkIdentifier(pattern);
274
- break;
275
- case "ObjectPattern":
276
- pattern.properties.forEach((prop) => {
277
- if (prop.type === "Property") {
278
- checkPattern(prop.value);
279
- } else if (prop.type === "RestElement") {
280
- checkPattern(prop.argument);
281
- }
282
- });
283
- break;
284
- case "ArrayPattern":
285
- pattern.elements
286
- .filter(
287
- (
288
- element
289
- ): element is TSESTree.DestructuringPattern =>
290
- element !== null
291
- )
292
- .forEach((element) => {
293
- checkPattern(element);
294
- });
295
- break;
296
- case "AssignmentPattern":
297
- checkPattern(pattern.left);
298
- break;
299
- default:
300
- break;
301
- }
302
- };
303
-
304
- return {
305
- CatchClause(node: TSESTree.CatchClause) {
306
- if (node.param) {
307
- checkPattern(node.param);
308
- }
309
- },
310
- "FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(
311
- node:
312
- | TSESTree.FunctionDeclaration
313
- | TSESTree.FunctionExpression
314
- | TSESTree.ArrowFunctionExpression
315
- ) {
316
- node.params.forEach((param) => {
317
- checkPattern(param);
318
- });
319
- },
320
- VariableDeclarator(node: TSESTree.VariableDeclarator) {
321
- if (node.id) {
322
- checkPattern(node.id);
323
- }
324
- }
325
- };
326
- },
327
- defaultOptions: [{}],
328
- meta: {
329
- docs: {
330
- description:
331
- "Disallow variable names shorter than the configured minimum length unless an outer variable with a longer name starting with the same characters exists. You can exempt specific variable names using the allowedVars option."
332
- },
333
- messages: {
334
- variableNameTooShort:
335
- "Variable '{{name}}' is too short. Minimum allowed length is {{minLength}} characters unless an outer variable with a longer name starting with '{{name}}' exists."
336
- },
337
- schema: [
338
- {
339
- additionalProperties: false,
340
- properties: {
341
- allowedVars: {
342
- default: [],
343
- items: {
344
- minLength: 1,
345
- type: "string"
346
- // Note: The maxLength for each string should be at most the configured minLength.
347
- },
348
- type: "array"
349
- },
350
- minLength: {
351
- default: 1,
352
- type: "number"
353
- }
354
- },
355
- type: "object"
356
- }
357
- ],
358
- type: "problem"
359
- }
360
- };