@angular-eslint/eslint-plugin-template 21.3.2-alpha.1 → 21.4.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.
@@ -1,6 +1,9 @@
1
- export type Options = [{
2
- maxComplexity: number;
3
- }];
1
+ export type Options = [
2
+ {
3
+ maxComplexity?: number;
4
+ variant?: 'classic' | 'modified';
5
+ }
6
+ ];
4
7
  export type MessageIds = 'cyclomaticComplexity';
5
8
  export declare const RULE_NAME = "cyclomatic-complexity";
6
9
  declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"cyclomaticComplexity", Options, import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
@@ -1 +1 @@
1
- {"version":3,"file":"cyclomatic-complexity.d.ts","sourceRoot":"","sources":["../../src/rules/cyclomatic-complexity.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,OAAO,GAAG,CAAC;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAChD,eAAO,MAAM,SAAS,0BAA0B,CAAC;;;;AAIjD,wBAiEG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
1
+ {"version":3,"file":"cyclomatic-complexity.d.ts","sourceRoot":"","sources":["../../src/rules/cyclomatic-complexity.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,OAAO,GAAG;IACpB;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,CAAA;KAAE;CAC7D,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAChD,eAAO,MAAM,SAAS,0BAA0B,CAAC;;;;AAOjD,wBA8GG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
@@ -5,7 +5,10 @@ const bundled_angular_compiler_1 = require("@angular-eslint/bundled-angular-comp
5
5
  const utils_1 = require("@angular-eslint/utils");
6
6
  const create_eslint_rule_1 = require("../utils/create-eslint-rule");
7
7
  exports.RULE_NAME = 'cyclomatic-complexity';
8
- const DEFAULT_MAX_COMPLEXITY = 5;
8
+ const DEFAULT_OPTIONS = {
9
+ maxComplexity: 5,
10
+ variant: 'classic',
11
+ };
9
12
  exports.default = (0, create_eslint_rule_1.createESLintRule)({
10
13
  name: exports.RULE_NAME,
11
14
  meta: {
@@ -21,6 +24,10 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
21
24
  type: 'number',
22
25
  minimum: 1,
23
26
  },
27
+ variant: {
28
+ type: 'string',
29
+ enum: ['classic', 'modified'],
30
+ },
24
31
  },
25
32
  additionalProperties: false,
26
33
  },
@@ -29,8 +36,13 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
29
36
  cyclomaticComplexity: 'The cyclomatic complexity {{totalComplexity}} exceeds the defined limit {{maxComplexity}}',
30
37
  },
31
38
  },
32
- defaultOptions: [{ maxComplexity: DEFAULT_MAX_COMPLEXITY }],
33
- create(context, [{ maxComplexity }]) {
39
+ defaultOptions: [
40
+ {
41
+ maxComplexity: DEFAULT_OPTIONS.maxComplexity,
42
+ variant: DEFAULT_OPTIONS.variant,
43
+ },
44
+ ],
45
+ create(context, [{ maxComplexity = DEFAULT_OPTIONS.maxComplexity, variant = DEFAULT_OPTIONS.variant, },]) {
34
46
  let totalComplexity = 0;
35
47
  const parserServices = (0, utils_1.getTemplateParserServices)(context);
36
48
  function increment(node) {
@@ -44,19 +56,37 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
44
56
  data: { maxComplexity, totalComplexity },
45
57
  });
46
58
  }
59
+ const isModified = variant === 'modified';
60
+ // Checks *ngIf, *ngFor, and *ngSwitchCase ('classic' variant) or *ngSwitch ('modified' variant)
61
+ function isLegacyStructuralDirective(node) {
62
+ const legacyStructuralDirectiveRegex = isModified
63
+ ? /^(ngForOf|ngIf|ngSwitch)$/
64
+ : /^(ngForOf|ngIf|ngSwitchCase)$/;
65
+ return (node instanceof bundled_angular_compiler_1.TmplAstBoundAttribute &&
66
+ legacyStructuralDirectiveRegex.test(node.name));
67
+ }
68
+ // Checks *ngSwitchDefault ('classic' variant)
69
+ function isLegacySwitchDefault(node) {
70
+ return (!isModified &&
71
+ node instanceof bundled_angular_compiler_1.TmplAstTextAttribute &&
72
+ node.name === 'ngSwitchDefault');
73
+ }
74
+ // Checks @if and @for
75
+ function isControlFlowBlock(node) {
76
+ return (node instanceof bundled_angular_compiler_1.TmplAstIfBlock || node instanceof bundled_angular_compiler_1.TmplAstForLoopBlock);
77
+ }
78
+ // Checks @switch ('modified' variant) or @switchCase ('classic' variant)
79
+ function isSwitchComplexity(node) {
80
+ return isModified
81
+ ? node instanceof bundled_angular_compiler_1.TmplAstSwitchBlock
82
+ : node instanceof bundled_angular_compiler_1.TmplAstSwitchBlockCase;
83
+ }
47
84
  return {
48
85
  '*': (node) => {
49
- if (node instanceof bundled_angular_compiler_1.TmplAstBoundAttribute &&
50
- /^(ngForOf|ngIf|ngSwitchCase)$/.test(node.name)) {
51
- increment(node);
52
- }
53
- else if (node instanceof bundled_angular_compiler_1.TmplAstTextAttribute &&
54
- node.name === 'ngSwitchDefault') {
55
- increment(node);
56
- }
57
- else if (node instanceof bundled_angular_compiler_1.TmplAstIfBlock ||
58
- node instanceof bundled_angular_compiler_1.TmplAstForLoopBlock ||
59
- node instanceof bundled_angular_compiler_1.TmplAstSwitchBlockCase) {
86
+ if (isLegacyStructuralDirective(node) ||
87
+ isLegacySwitchDefault(node) ||
88
+ isControlFlowBlock(node) ||
89
+ isSwitchComplexity(node)) {
60
90
  increment(node);
61
91
  }
62
92
  },
@@ -1 +1 @@
1
- {"version":3,"file":"eqeqeq.d.ts","sourceRoot":"","sources":["../../src/rules/eqeqeq.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQzD,MAAM,MAAM,OAAO,GAAG,CAAC;IAAE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACpE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,uBAAuB,CAAC;AAC5D,eAAO,MAAM,SAAS,WAAW,CAAC;;;;AAGlC,wBA4EG;AA6EH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
1
+ {"version":3,"file":"eqeqeq.d.ts","sourceRoot":"","sources":["../../src/rules/eqeqeq.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQzD,MAAM,MAAM,OAAO,GAAG,CAAC;IAAE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AACpE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,uBAAuB,CAAC;AAC5D,eAAO,MAAM,SAAS,WAAW,CAAC;;;;AAGlC,wBA4EG;AA8EH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
@@ -78,9 +78,10 @@ function getSpanLength({ span: { start, end } }) {
78
78
  return end - start;
79
79
  }
80
80
  const getFix = ({ node, right, end, sourceCode, fixer, }) => {
81
- const { source, ast } = (0, get_nearest_node_from_1.getNearestNodeFrom)(node, isASTWithSource);
82
- if (!source)
81
+ const result = (0, get_nearest_node_from_1.getNearestNodeFrom)(node, isASTWithSource);
82
+ if (!result?.source)
83
83
  return null;
84
+ const { source, ast } = result;
84
85
  let startOffset = 0;
85
86
  while (!isInterpolation(ast) && isLeadingTriviaChar(source[startOffset])) {
86
87
  startOffset++;
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-template-literal.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-template-literal.ts"],"names":[],"mappings":"AAeA,QAAA,MAAM,SAAS,0BAA0B,CAAC;AAE1C,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,OAAO,SAAS,CAAC;AAC1C,eAAO,MAAM,SAAS,4BAA4B,CAAC;;;;AA8FnD,wBA+IG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
1
+ {"version":3,"file":"prefer-template-literal.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-template-literal.ts"],"names":[],"mappings":"AAeA,QAAA,MAAM,SAAS,0BAA0B,CAAC;AAE1C,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,OAAO,SAAS,CAAC;AAC1C,eAAO,MAAM,SAAS,4BAA4B,CAAC;;;;AA8FnD,wBAiKG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
@@ -90,6 +90,21 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
90
90
  },
91
91
  defaultOptions: [],
92
92
  create(context) {
93
+ // When the template is defined inline in a component, the template will
94
+ // usually be defined as a template string, which means if template
95
+ // literals were to be used within the template, the backticks would
96
+ // need to be escaped. Escaping them causes errors when we parse the
97
+ // template. What this ultimately means is that template literals cannot
98
+ // be used in inline templates, so we'll just skip this entire template
99
+ // if it looks like it is an inline template.
100
+ //
101
+ // See https://github.com/angular-eslint/angular-eslint/issues/2906
102
+ //
103
+ // This pattern needs to match what is used in
104
+ // `packages/eslint-plugin-template/src/processors.ts`.
105
+ if (/inline-template-[^/\\]+-\d+\.component\.html$/.test(context.filename)) {
106
+ return {};
107
+ }
93
108
  (0, utils_1.ensureTemplateParser)(context);
94
109
  const { sourceCode } = context;
95
110
  return {
@@ -185,5 +200,5 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
185
200
  },
186
201
  });
187
202
  exports.RULE_DOCS_EXTENSION = {
188
- rationale: 'Template literals (backticks with ${} syntax) are more modern, readable, and maintainable than string concatenation with the + operator. String concatenation like "Hello " + name + "!" is harder to read and error-prone (easy to forget spaces or quotes) compared to the template literal `Hello ${name}!`. Template literals make string composition clearer, especially with multiple expressions. This is a widely accepted JavaScript/TypeScript best practice that should be followed in Angular templates for consistency. Angular templates have supported template literal syntax since v19.2. Using template literals throughout your codebase creates a consistent style and makes complex string building much more readable.',
203
+ rationale: 'Template literals (backticks with ${} syntax) are more modern, readable, and maintainable than string concatenation with the + operator. String concatenation like "Hello " + name + "!" is harder to read and error-prone (easy to forget spaces or quotes) compared to the template literal `Hello ${name}!`. Template literals make string composition clearer, especially with multiple expressions. This is a widely accepted JavaScript/TypeScript best practice that should be followed in Angular templates for consistency. Angular templates have supported template literal syntax since v19.2. Using template literals throughout your codebase creates a consistent style and makes complex string building much more readable.\n\nℹ This rule is not checked in inline templates due to the common use of backticks to define the inline template.',
189
204
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular-eslint/eslint-plugin-template",
3
- "version": "21.3.2-alpha.1",
3
+ "version": "21.4.0",
4
4
  "description": "ESLint plugin for Angular Templates",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -20,19 +20,19 @@
20
20
  "dependencies": {
21
21
  "aria-query": "5.3.2",
22
22
  "axobject-query": "4.1.0",
23
- "@angular-eslint/utils": "21.3.2-alpha.1",
24
- "@angular-eslint/bundled-angular-compiler": "21.3.2-alpha.1"
23
+ "@angular-eslint/bundled-angular-compiler": "21.4.0",
24
+ "@angular-eslint/utils": "21.4.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/aria-query": "5.0.4",
28
- "@angular-eslint/test-utils": "21.3.2-alpha.1"
28
+ "@angular-eslint/test-utils": "21.4.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "@typescript-eslint/types": "^7.11.0 || ^8.0.0",
32
32
  "@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
33
33
  "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
34
34
  "typescript": "*",
35
- "@angular-eslint/template-parser": "21.3.2-alpha.1"
35
+ "@angular-eslint/template-parser": "21.4.0"
36
36
  },
37
37
  "gitHead": "e2006e5e9c99e5a943d1a999e0efa5247d29ec24"
38
38
  }