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.
Files changed (2) hide show
  1. package/dist/index.js +99 -41
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,26 +1,83 @@
1
1
  // @bun
2
- // src/rules/no-nested-jsx-return.ts
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 === AST_NODE_TYPES.JSXElement || node.type === AST_NODE_TYPES.JSXFragment);
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 === AST_NODE_TYPES.JSXMemberExpression) {
66
+ while (current.type === AST_NODE_TYPES2.JSXMemberExpression) {
10
67
  current = current.object;
11
68
  }
12
- if (current.type === AST_NODE_TYPES.JSXIdentifier) {
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 !== AST_NODE_TYPES.JSXElement) {
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 === AST_NODE_TYPES.JSXIdentifier) {
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 === AST_NODE_TYPES.JSXText) {
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 === AST_NODE_TYPES.JSXText) {
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 === AST_NODE_TYPES.JSXElement || child.type === AST_NODE_TYPES.JSXFragment) {
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 AST_NODE_TYPES2 } from "@typescript-eslint/utils";
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 === AST_NODE_TYPES2.JSXMemberExpression) {
2281
+ while (current.type === AST_NODE_TYPES3.JSXMemberExpression) {
2225
2282
  current = current.property;
2226
2283
  }
2227
- if (current.type === AST_NODE_TYPES2.JSXIdentifier) {
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 === AST_NODE_TYPES2.JSXMemberExpression) {
2291
+ while (current.type === AST_NODE_TYPES3.JSXMemberExpression) {
2235
2292
  current = current.object;
2236
2293
  }
2237
- if (current.type === AST_NODE_TYPES2.JSXIdentifier) {
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 === AST_NODE_TYPES2.JSXIdentifier) {
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 === AST_NODE_TYPES2.CallExpression && node.callee !== null && (node.callee.type === AST_NODE_TYPES2.Identifier && node.callee.name === "useState" || node.callee.type === AST_NODE_TYPES2.MemberExpression && node.callee.property !== null && node.callee.property.type === AST_NODE_TYPES2.Identifier && node.callee.property.name === "useState");
2257
- const isHookCall = (node) => node !== null && node.type === AST_NODE_TYPES2.CallExpression && node.callee !== null && node.callee.type === AST_NODE_TYPES2.Identifier && /^use[A-Z]/.test(node.callee.name) && node.callee.name !== "useState";
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 === AST_NODE_TYPES2.JSXElement) {
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 === AST_NODE_TYPES2.JSXIdentifier) {
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 === AST_NODE_TYPES2.JSXAttribute && node.name && node.name.type === AST_NODE_TYPES2.JSXIdentifier && node.name.name === "value" && node.parent && node.parent.type === AST_NODE_TYPES2.JSXOpeningElement && (() => {
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 === AST_NODE_TYPES2.JSXIdentifier) {
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 === AST_NODE_TYPES2.FunctionDeclaration || current.type === AST_NODE_TYPES2.FunctionExpression || current.type === AST_NODE_TYPES2.ArrowFunctionExpression) {
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 !== AST_NODE_TYPES2.ArrayPattern || node.id.elements.length < 2) {
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 !== AST_NODE_TYPES2.Identifier || !setterElem || setterElem.type !== AST_NODE_TYPES2.Identifier) {
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 !== AST_NODE_TYPES2.Identifier) {
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 === AST_NODE_TYPES2.Identifier && node.init.type === AST_NODE_TYPES2.CallExpression && isHookCall(node.init)) {
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 AST_NODE_TYPES3 } from "@typescript-eslint/utils";
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 === AST_NODE_TYPES3.Identifier && node.name === name;
3451
- var isStaticMemberCall = (node, objectName, propertyName) => node.callee.type === AST_NODE_TYPES3.MemberExpression && !node.callee.computed && isIdentifier2(node.callee.object, objectName) && isIdentifier2(node.callee.property, propertyName);
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 === AST_NODE_TYPES3.Identifier)
3511
+ if (key.type === AST_NODE_TYPES4.Identifier)
3455
3512
  return key.name;
3456
- if (key.type === AST_NODE_TYPES3.Literal && typeof key.value === "string") {
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 === AST_NODE_TYPES3.CallExpression && isIdentifier2(expression.callee, "Component");
3520
+ return expression.type === AST_NODE_TYPES4.CallExpression && isIdentifier2(expression.callee, "Component");
3464
3521
  };
3465
- var isAngularComponentClass = (node) => node.type === AST_NODE_TYPES3.ClassDeclaration && (node.decorators ?? []).some(isComponentDecorator);
3522
+ var isAngularComponentClass = (node) => node.type === AST_NODE_TYPES4.ClassDeclaration && (node.decorators ?? []).some(isComponentDecorator);
3466
3523
  var getTemplateText = (node) => {
3467
- if (node.type === AST_NODE_TYPES3.Literal && typeof node.value === "string") {
3524
+ if (node.type === AST_NODE_TYPES4.Literal && typeof node.value === "string") {
3468
3525
  return node.value;
3469
3526
  }
3470
- if (node.type === AST_NODE_TYPES3.TemplateLiteral) {
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 === AST_NODE_TYPES3.PropertyDefinition) {
3544
+ if (current.type === AST_NODE_TYPES4.PropertyDefinition) {
3488
3545
  return current;
3489
3546
  }
3490
- if (current.type === AST_NODE_TYPES3.MethodDefinition || current.type === AST_NODE_TYPES3.FunctionDeclaration || current.type === AST_NODE_TYPES3.FunctionExpression || current.type === AST_NODE_TYPES3.ArrowFunctionExpression) {
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 AST_NODE_TYPES4 } from "@typescript-eslint/utils";
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 === AST_NODE_TYPES4.JSXIdentifier && nameNode.name === "div";
3690
+ return nameNode.type === AST_NODE_TYPES5.JSXIdentifier && nameNode.name === "div";
3634
3691
  };
3635
3692
  const isMeaningfulChild = (child) => {
3636
- if (child.type === AST_NODE_TYPES4.JSXText) {
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 === AST_NODE_TYPES4.JSXElement) {
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
@@ -40,5 +40,5 @@
40
40
  "typecheck": "bun run tsc --noEmit"
41
41
  },
42
42
  "type": "module",
43
- "version": "0.8.0"
43
+ "version": "0.9.0"
44
44
  }