eslint-plugin-absolute 0.8.0 → 0.9.0
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.
- package/dist/index.js +99 -41
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,26 +1,83 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// src/rules/
|
|
2
|
+
// src/rules/angular-one-feature-per-file.ts
|
|
3
3
|
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
4
|
+
var FEATURE_DECORATOR_NAMES = new Set([
|
|
5
|
+
"Component",
|
|
6
|
+
"Directive",
|
|
7
|
+
"Injectable",
|
|
8
|
+
"NgModule",
|
|
9
|
+
"Pipe"
|
|
10
|
+
]);
|
|
11
|
+
var getDecoratorName = (decorator) => {
|
|
12
|
+
const { expression } = decorator;
|
|
13
|
+
if (expression.type === AST_NODE_TYPES.CallExpression && expression.callee.type === AST_NODE_TYPES.Identifier) {
|
|
14
|
+
return expression.callee.name;
|
|
15
|
+
}
|
|
16
|
+
if (expression.type === AST_NODE_TYPES.Identifier) {
|
|
17
|
+
return expression.name;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
};
|
|
21
|
+
var isFeatureClass = (node) => {
|
|
22
|
+
if (!node.decorators || node.decorators.length === 0)
|
|
23
|
+
return false;
|
|
24
|
+
return node.decorators.some((decorator) => {
|
|
25
|
+
const name = getDecoratorName(decorator);
|
|
26
|
+
return name !== null && FEATURE_DECORATOR_NAMES.has(name);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
var angularOneFeaturePerFile = {
|
|
30
|
+
create(context) {
|
|
31
|
+
const seenFeatures = [];
|
|
32
|
+
return {
|
|
33
|
+
ClassDeclaration(node) {
|
|
34
|
+
if (!isFeatureClass(node))
|
|
35
|
+
return;
|
|
36
|
+
seenFeatures.push(node);
|
|
37
|
+
if (seenFeatures.length === 1)
|
|
38
|
+
return;
|
|
39
|
+
context.report({
|
|
40
|
+
messageId: "multiFeature",
|
|
41
|
+
node: node.id ?? node
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
defaultOptions: [],
|
|
47
|
+
meta: {
|
|
48
|
+
docs: {
|
|
49
|
+
description: 'Disallow defining more than one Angular feature class (@Component, @Directive, @Pipe, @Injectable, @NgModule) per file. Mirrors the Angular Style Guide\'s Single Responsibility / Rule of One. Test and Storybook files legitimately define stub/host classes alongside the subject under test \u2014 disable this rule for those files via an ESLint override (e.g., `{ files: ["**/*.spec.ts", "**/*.stories.ts"], rules: { "absolute/angular-one-feature-per-file": "off" } }`).'
|
|
50
|
+
},
|
|
51
|
+
messages: {
|
|
52
|
+
multiFeature: "Only one Angular feature class is allowed per file. Move this class into its own file (Angular Style Guide: Single Responsibility / Rule of One)."
|
|
53
|
+
},
|
|
54
|
+
schema: [],
|
|
55
|
+
type: "problem"
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/rules/no-nested-jsx-return.ts
|
|
60
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES2 } from "@typescript-eslint/utils";
|
|
4
61
|
var noNestedJSXReturn = {
|
|
5
62
|
create(context) {
|
|
6
|
-
const isJSX = (node) => node !== null && node !== undefined && (node.type ===
|
|
63
|
+
const isJSX = (node) => node !== null && node !== undefined && (node.type === AST_NODE_TYPES2.JSXElement || node.type === AST_NODE_TYPES2.JSXFragment);
|
|
7
64
|
const getLeftmostJSXIdentifier = (name) => {
|
|
8
65
|
let current = name;
|
|
9
|
-
while (current.type ===
|
|
66
|
+
while (current.type === AST_NODE_TYPES2.JSXMemberExpression) {
|
|
10
67
|
current = current.object;
|
|
11
68
|
}
|
|
12
|
-
if (current.type ===
|
|
69
|
+
if (current.type === AST_NODE_TYPES2.JSXIdentifier) {
|
|
13
70
|
return current;
|
|
14
71
|
}
|
|
15
72
|
return null;
|
|
16
73
|
};
|
|
17
74
|
const isJSXComponentElement = (node) => {
|
|
18
|
-
if (!node || node.type !==
|
|
75
|
+
if (!node || node.type !== AST_NODE_TYPES2.JSXElement) {
|
|
19
76
|
return false;
|
|
20
77
|
}
|
|
21
78
|
const opening = node.openingElement;
|
|
22
79
|
const nameNode = opening.name;
|
|
23
|
-
if (nameNode.type ===
|
|
80
|
+
if (nameNode.type === AST_NODE_TYPES2.JSXIdentifier) {
|
|
24
81
|
return /^[A-Z]/.test(nameNode.name);
|
|
25
82
|
}
|
|
26
83
|
const leftmost = getLeftmostJSXIdentifier(nameNode);
|
|
@@ -31,7 +88,7 @@ var noNestedJSXReturn = {
|
|
|
31
88
|
};
|
|
32
89
|
const hasNoMeaningfulChildren = (children) => {
|
|
33
90
|
const filtered = children.filter((child) => {
|
|
34
|
-
if (child.type ===
|
|
91
|
+
if (child.type === AST_NODE_TYPES2.JSXText) {
|
|
35
92
|
return child.value.trim() !== "";
|
|
36
93
|
}
|
|
37
94
|
return true;
|
|
@@ -42,7 +99,7 @@ var noNestedJSXReturn = {
|
|
|
42
99
|
if (!isJSX(node))
|
|
43
100
|
return false;
|
|
44
101
|
const children = node.children.filter((child2) => {
|
|
45
|
-
if (child2.type ===
|
|
102
|
+
if (child2.type === AST_NODE_TYPES2.JSXText) {
|
|
46
103
|
return child2.value.trim() !== "";
|
|
47
104
|
}
|
|
48
105
|
return true;
|
|
@@ -57,7 +114,7 @@ var noNestedJSXReturn = {
|
|
|
57
114
|
if (!child) {
|
|
58
115
|
return false;
|
|
59
116
|
}
|
|
60
|
-
if (child.type ===
|
|
117
|
+
if (child.type === AST_NODE_TYPES2.JSXElement || child.type === AST_NODE_TYPES2.JSXFragment) {
|
|
61
118
|
return hasNoMeaningfulChildren(child.children);
|
|
62
119
|
}
|
|
63
120
|
return true;
|
|
@@ -2209,7 +2266,7 @@ var sortExports = {
|
|
|
2209
2266
|
};
|
|
2210
2267
|
|
|
2211
2268
|
// src/rules/localize-react-props.ts
|
|
2212
|
-
import { AST_NODE_TYPES as
|
|
2269
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES3 } from "@typescript-eslint/utils";
|
|
2213
2270
|
var localizeReactProps = {
|
|
2214
2271
|
create(context) {
|
|
2215
2272
|
const candidateVariables = [];
|
|
@@ -2221,20 +2278,20 @@ var localizeReactProps = {
|
|
|
2221
2278
|
};
|
|
2222
2279
|
const getRightmostJSXIdentifier = (name) => {
|
|
2223
2280
|
let current = name;
|
|
2224
|
-
while (current.type ===
|
|
2281
|
+
while (current.type === AST_NODE_TYPES3.JSXMemberExpression) {
|
|
2225
2282
|
current = current.property;
|
|
2226
2283
|
}
|
|
2227
|
-
if (current.type ===
|
|
2284
|
+
if (current.type === AST_NODE_TYPES3.JSXIdentifier) {
|
|
2228
2285
|
return current;
|
|
2229
2286
|
}
|
|
2230
2287
|
return null;
|
|
2231
2288
|
};
|
|
2232
2289
|
const getLeftmostJSXIdentifier = (name) => {
|
|
2233
2290
|
let current = name;
|
|
2234
|
-
while (current.type ===
|
|
2291
|
+
while (current.type === AST_NODE_TYPES3.JSXMemberExpression) {
|
|
2235
2292
|
current = current.object;
|
|
2236
2293
|
}
|
|
2237
|
-
if (current.type ===
|
|
2294
|
+
if (current.type === AST_NODE_TYPES3.JSXIdentifier) {
|
|
2238
2295
|
return current;
|
|
2239
2296
|
}
|
|
2240
2297
|
return null;
|
|
@@ -2244,7 +2301,7 @@ var localizeReactProps = {
|
|
|
2244
2301
|
return "";
|
|
2245
2302
|
}
|
|
2246
2303
|
const nameNode = jsxElement.openingElement.name;
|
|
2247
|
-
if (nameNode.type ===
|
|
2304
|
+
if (nameNode.type === AST_NODE_TYPES3.JSXIdentifier) {
|
|
2248
2305
|
return nameNode.name;
|
|
2249
2306
|
}
|
|
2250
2307
|
const rightmost = getRightmostJSXIdentifier(nameNode);
|
|
@@ -2253,12 +2310,12 @@ var localizeReactProps = {
|
|
|
2253
2310
|
}
|
|
2254
2311
|
return "";
|
|
2255
2312
|
};
|
|
2256
|
-
const isUseStateCall = (node) => node !== null && node.type ===
|
|
2257
|
-
const isHookCall = (node) => node !== null && node.type ===
|
|
2313
|
+
const isUseStateCall = (node) => node !== null && node.type === AST_NODE_TYPES3.CallExpression && node.callee !== null && (node.callee.type === AST_NODE_TYPES3.Identifier && node.callee.name === "useState" || node.callee.type === AST_NODE_TYPES3.MemberExpression && node.callee.property !== null && node.callee.property.type === AST_NODE_TYPES3.Identifier && node.callee.property.name === "useState");
|
|
2314
|
+
const isHookCall = (node) => node !== null && node.type === AST_NODE_TYPES3.CallExpression && node.callee !== null && node.callee.type === AST_NODE_TYPES3.Identifier && /^use[A-Z]/.test(node.callee.name) && node.callee.name !== "useState";
|
|
2258
2315
|
const getJSXAncestor = (node) => {
|
|
2259
2316
|
let current = node.parent;
|
|
2260
2317
|
while (current) {
|
|
2261
|
-
if (current.type ===
|
|
2318
|
+
if (current.type === AST_NODE_TYPES3.JSXElement) {
|
|
2262
2319
|
return current;
|
|
2263
2320
|
}
|
|
2264
2321
|
current = current.parent;
|
|
@@ -2267,14 +2324,14 @@ var localizeReactProps = {
|
|
|
2267
2324
|
};
|
|
2268
2325
|
const getTagNameFromOpening = (openingElement) => {
|
|
2269
2326
|
const nameNode = openingElement.name;
|
|
2270
|
-
if (nameNode.type ===
|
|
2327
|
+
if (nameNode.type === AST_NODE_TYPES3.JSXIdentifier) {
|
|
2271
2328
|
return nameNode.name;
|
|
2272
2329
|
}
|
|
2273
2330
|
const rightmost = getRightmostJSXIdentifier(nameNode);
|
|
2274
2331
|
return rightmost ? rightmost.name : null;
|
|
2275
2332
|
};
|
|
2276
2333
|
const isProviderOrContext = (tagName) => tagName.endsWith("Provider") || tagName.endsWith("Context");
|
|
2277
|
-
const isValueAttributeOnProvider = (node) => node.type ===
|
|
2334
|
+
const isValueAttributeOnProvider = (node) => node.type === AST_NODE_TYPES3.JSXAttribute && node.name && node.name.type === AST_NODE_TYPES3.JSXIdentifier && node.name.name === "value" && node.parent && node.parent.type === AST_NODE_TYPES3.JSXOpeningElement && (() => {
|
|
2278
2335
|
const tagName = getTagNameFromOpening(node.parent);
|
|
2279
2336
|
return tagName !== null && isProviderOrContext(tagName);
|
|
2280
2337
|
})();
|
|
@@ -2293,7 +2350,7 @@ var localizeReactProps = {
|
|
|
2293
2350
|
return false;
|
|
2294
2351
|
}
|
|
2295
2352
|
const nameNode = jsxElement.openingElement.name;
|
|
2296
|
-
if (nameNode.type ===
|
|
2353
|
+
if (nameNode.type === AST_NODE_TYPES3.JSXIdentifier) {
|
|
2297
2354
|
return /^[A-Z]/.test(nameNode.name);
|
|
2298
2355
|
}
|
|
2299
2356
|
const leftmost = getLeftmostJSXIdentifier(nameNode);
|
|
@@ -2302,7 +2359,7 @@ var localizeReactProps = {
|
|
|
2302
2359
|
const getComponentFunction = (node) => {
|
|
2303
2360
|
let current = node;
|
|
2304
2361
|
while (current) {
|
|
2305
|
-
if (current.type ===
|
|
2362
|
+
if (current.type === AST_NODE_TYPES3.FunctionDeclaration || current.type === AST_NODE_TYPES3.FunctionExpression || current.type === AST_NODE_TYPES3.ArrowFunctionExpression) {
|
|
2306
2363
|
return current;
|
|
2307
2364
|
}
|
|
2308
2365
|
current = current.parent;
|
|
@@ -2374,11 +2431,11 @@ var localizeReactProps = {
|
|
|
2374
2431
|
return false;
|
|
2375
2432
|
};
|
|
2376
2433
|
const processUseStateDeclarator = (node) => {
|
|
2377
|
-
if (!node.init || !isUseStateCall(node.init) || node.id.type !==
|
|
2434
|
+
if (!node.init || !isUseStateCall(node.init) || node.id.type !== AST_NODE_TYPES3.ArrayPattern || node.id.elements.length < 2) {
|
|
2378
2435
|
return false;
|
|
2379
2436
|
}
|
|
2380
2437
|
const [stateElem, setterElem] = node.id.elements;
|
|
2381
|
-
if (!stateElem || stateElem.type !==
|
|
2438
|
+
if (!stateElem || stateElem.type !== AST_NODE_TYPES3.Identifier || !setterElem || setterElem.type !== AST_NODE_TYPES3.Identifier) {
|
|
2382
2439
|
return false;
|
|
2383
2440
|
}
|
|
2384
2441
|
const stateVarName = stateElem.name;
|
|
@@ -2402,7 +2459,7 @@ var localizeReactProps = {
|
|
|
2402
2459
|
return true;
|
|
2403
2460
|
};
|
|
2404
2461
|
const processGeneralVariable = (node, componentFunction) => {
|
|
2405
|
-
if (!node.id || node.id.type !==
|
|
2462
|
+
if (!node.id || node.id.type !== AST_NODE_TYPES3.Identifier) {
|
|
2406
2463
|
return;
|
|
2407
2464
|
}
|
|
2408
2465
|
const varName = node.id.name;
|
|
@@ -2454,7 +2511,7 @@ var localizeReactProps = {
|
|
|
2454
2511
|
const componentFunction = getComponentFunction(node);
|
|
2455
2512
|
if (!componentFunction || !componentFunction.body)
|
|
2456
2513
|
return;
|
|
2457
|
-
if (node.init && node.id && node.id.type ===
|
|
2514
|
+
if (node.init && node.id && node.id.type === AST_NODE_TYPES3.Identifier && node.init.type === AST_NODE_TYPES3.CallExpression && isHookCall(node.init)) {
|
|
2458
2515
|
const hookSet = getHookSet(componentFunction);
|
|
2459
2516
|
hookSet.add(node.id.name);
|
|
2460
2517
|
}
|
|
@@ -3445,29 +3502,29 @@ var noInlineObjectTypes = {
|
|
|
3445
3502
|
};
|
|
3446
3503
|
|
|
3447
3504
|
// src/rules/no-nondeterministic-render.ts
|
|
3448
|
-
import { AST_NODE_TYPES as
|
|
3505
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES4 } from "@typescript-eslint/utils";
|
|
3449
3506
|
var BANNED_TEMPLATE_PATTERN = /\bMath\.random\s*\(|\bDate\.now\s*\(|\bnew\s+Date\s*\(\s*\)|\bcrypto\.randomUUID\s*\(|\bperformance\.now\s*\(/;
|
|
3450
|
-
var isIdentifier2 = (node, name) => node?.type ===
|
|
3451
|
-
var isStaticMemberCall = (node, objectName, propertyName) => node.callee.type ===
|
|
3507
|
+
var isIdentifier2 = (node, name) => node?.type === AST_NODE_TYPES4.Identifier && node.name === name;
|
|
3508
|
+
var isStaticMemberCall = (node, objectName, propertyName) => node.callee.type === AST_NODE_TYPES4.MemberExpression && !node.callee.computed && isIdentifier2(node.callee.object, objectName) && isIdentifier2(node.callee.property, propertyName);
|
|
3452
3509
|
var getPropertyName2 = (node) => {
|
|
3453
3510
|
const { key } = node;
|
|
3454
|
-
if (key.type ===
|
|
3511
|
+
if (key.type === AST_NODE_TYPES4.Identifier)
|
|
3455
3512
|
return key.name;
|
|
3456
|
-
if (key.type ===
|
|
3513
|
+
if (key.type === AST_NODE_TYPES4.Literal && typeof key.value === "string") {
|
|
3457
3514
|
return key.value;
|
|
3458
3515
|
}
|
|
3459
3516
|
return null;
|
|
3460
3517
|
};
|
|
3461
3518
|
var isComponentDecorator = (decorator) => {
|
|
3462
3519
|
const { expression } = decorator;
|
|
3463
|
-
return expression.type ===
|
|
3520
|
+
return expression.type === AST_NODE_TYPES4.CallExpression && isIdentifier2(expression.callee, "Component");
|
|
3464
3521
|
};
|
|
3465
|
-
var isAngularComponentClass = (node) => node.type ===
|
|
3522
|
+
var isAngularComponentClass = (node) => node.type === AST_NODE_TYPES4.ClassDeclaration && (node.decorators ?? []).some(isComponentDecorator);
|
|
3466
3523
|
var getTemplateText = (node) => {
|
|
3467
|
-
if (node.type ===
|
|
3524
|
+
if (node.type === AST_NODE_TYPES4.Literal && typeof node.value === "string") {
|
|
3468
3525
|
return node.value;
|
|
3469
3526
|
}
|
|
3470
|
-
if (node.type ===
|
|
3527
|
+
if (node.type === AST_NODE_TYPES4.TemplateLiteral) {
|
|
3471
3528
|
return node.quasis.map((quasi) => quasi.value.cooked ?? "").join("");
|
|
3472
3529
|
}
|
|
3473
3530
|
return null;
|
|
@@ -3484,10 +3541,10 @@ var getEnclosingAngularComponentClass = (node) => {
|
|
|
3484
3541
|
var getEnclosingPropertyDefinition = (node) => {
|
|
3485
3542
|
let current = node.parent;
|
|
3486
3543
|
while (current) {
|
|
3487
|
-
if (current.type ===
|
|
3544
|
+
if (current.type === AST_NODE_TYPES4.PropertyDefinition) {
|
|
3488
3545
|
return current;
|
|
3489
3546
|
}
|
|
3490
|
-
if (current.type ===
|
|
3547
|
+
if (current.type === AST_NODE_TYPES4.MethodDefinition || current.type === AST_NODE_TYPES4.FunctionDeclaration || current.type === AST_NODE_TYPES4.FunctionExpression || current.type === AST_NODE_TYPES4.ArrowFunctionExpression) {
|
|
3491
3548
|
return null;
|
|
3492
3549
|
}
|
|
3493
3550
|
current = current.parent;
|
|
@@ -3625,15 +3682,15 @@ var noRedundantTypeAnnotation = {
|
|
|
3625
3682
|
};
|
|
3626
3683
|
|
|
3627
3684
|
// src/rules/no-unnecessary-div.ts
|
|
3628
|
-
import { AST_NODE_TYPES as
|
|
3685
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES5 } from "@typescript-eslint/utils";
|
|
3629
3686
|
var noUnnecessaryDiv = {
|
|
3630
3687
|
create(context) {
|
|
3631
3688
|
const isDivElement = (node) => {
|
|
3632
3689
|
const nameNode = node.openingElement.name;
|
|
3633
|
-
return nameNode.type ===
|
|
3690
|
+
return nameNode.type === AST_NODE_TYPES5.JSXIdentifier && nameNode.name === "div";
|
|
3634
3691
|
};
|
|
3635
3692
|
const isMeaningfulChild = (child) => {
|
|
3636
|
-
if (child.type ===
|
|
3693
|
+
if (child.type === AST_NODE_TYPES5.JSXText) {
|
|
3637
3694
|
return child.value.trim() !== "";
|
|
3638
3695
|
}
|
|
3639
3696
|
return true;
|
|
@@ -3652,7 +3709,7 @@ var noUnnecessaryDiv = {
|
|
|
3652
3709
|
if (!onlyChild) {
|
|
3653
3710
|
return;
|
|
3654
3711
|
}
|
|
3655
|
-
if (onlyChild.type ===
|
|
3712
|
+
if (onlyChild.type === AST_NODE_TYPES5.JSXElement) {
|
|
3656
3713
|
context.report({
|
|
3657
3714
|
messageId: "unnecessaryDivWrapper",
|
|
3658
3715
|
node
|
|
@@ -3780,6 +3837,7 @@ var preferInlineExports = {
|
|
|
3780
3837
|
// src/index.ts
|
|
3781
3838
|
var src_default = {
|
|
3782
3839
|
rules: {
|
|
3840
|
+
"angular-one-feature-per-file": angularOneFeaturePerFile,
|
|
3783
3841
|
"explicit-object-types": explicitObjectTypes,
|
|
3784
3842
|
"inline-style-limit": inlineStyleLimit,
|
|
3785
3843
|
"localize-react-props": localizeReactProps,
|
package/package.json
CHANGED