@angular-eslint/eslint-plugin 19.0.0-alpha.4 → 19.0.0-alpha.6

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/README.md CHANGED
@@ -66,7 +66,7 @@ Please see https://github.com/angular-eslint/angular-eslint for full usage instr
66
66
  | [`pipe-prefix`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/pipe-prefix.md) | Enforce consistent prefix for pipes. | | | |
67
67
  | [`prefer-on-push-component-change-detection`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-on-push-component-change-detection.md) | Ensures component's `changeDetection` is set to `ChangeDetectionStrategy.OnPush` | | | :bulb: |
68
68
  | [`prefer-output-readonly`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-output-readonly.md) | Prefer to declare `@Output`, `OutputEmitterRef` and `OutputRef` as `readonly` since they are not supposed to be reassigned | | | :bulb: |
69
- | [`prefer-standalone`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-standalone.md) | Ensures component, directive and pipe `standalone` property is set to `true` in the component decorator | | :wrench: | |
69
+ | [`prefer-standalone`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-standalone.md) | Ensures Components, Directives and Pipes do not opt out of standalone | :white_check_mark: | :wrench: | |
70
70
  | [`relative-url-prefix`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/relative-url-prefix.md) | The ./ and ../ prefix is standard syntax for relative URLs; don't depend on Angular's current ability to do without that prefix. See more at https://angular.dev/style-guide#style-05-04 | | | |
71
71
  | [`require-localize-metadata`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/require-localize-metadata.md) | Ensures that $localize tagged messages contain helpful metadata to aid with translations. | | | |
72
72
  | [`runtime-localize`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/runtime-localize.md) | Ensures that $localize tagged messages can use runtime-loaded translations. | | | |
@@ -12,6 +12,7 @@
12
12
  "@angular-eslint/no-output-on-prefix": "error",
13
13
  "@angular-eslint/no-output-rename": "error",
14
14
  "@angular-eslint/no-outputs-metadata-property": "error",
15
+ "@angular-eslint/prefer-standalone": "error",
15
16
  "@angular-eslint/use-pipe-transform-interface": "error",
16
17
  "@angular-eslint/use-lifecycle-interface": "warn"
17
18
  }
package/dist/index.d.ts CHANGED
@@ -57,6 +57,7 @@ declare const _default: {
57
57
  "@angular-eslint/no-output-on-prefix": string;
58
58
  "@angular-eslint/no-output-rename": string;
59
59
  "@angular-eslint/no-outputs-metadata-property": string;
60
+ "@angular-eslint/prefer-standalone": string;
60
61
  "@angular-eslint/use-pipe-transform-interface": string;
61
62
  "@angular-eslint/use-lifecycle-interface": string;
62
63
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmHA,kBA6CE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmHA,kBA6CE"}
@@ -1 +1 @@
1
- {"version":3,"file":"no-input-rename.d.ts","sourceRoot":"","sources":["../../src/rules/no-input-rename.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,OAAO,GAAG,CAAC;IAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AACtE,MAAM,MAAM,UAAU,GAClB,eAAe,GACf,wBAAwB,GACxB,yCAAyC,CAAC;AAC9C,eAAO,MAAM,SAAS,oBAAoB,CAAC;;AAG3C,wBAkPG"}
1
+ {"version":3,"file":"no-input-rename.d.ts","sourceRoot":"","sources":["../../src/rules/no-input-rename.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,OAAO,GAAG,CAAC;IAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AACtE,MAAM,MAAM,UAAU,GAClB,eAAe,GACf,wBAAwB,GACxB,yCAAyC,CAAC;AAC9C,eAAO,MAAM,SAAS,oBAAoB,CAAC;;AAG3C,wBAgQG"}
@@ -58,14 +58,6 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
58
58
  !utils_2.ASTUtils.isIdentifier(propertyOrMethodDefinition.key)) {
59
59
  return;
60
60
  }
61
- let isAliasMetadataProperty = false;
62
- if (node.parent && utils_1.ASTUtils.isProperty(node.parent)) {
63
- if (utils_1.ASTUtils.getRawText(node.parent.key) !== 'alias') {
64
- // We're within an Input decorator metadata object, but it is not the alias property
65
- return;
66
- }
67
- isAliasMetadataProperty = true;
68
- }
69
61
  const aliasName = utils_1.ASTUtils.getRawText(node);
70
62
  const propertyName = utils_1.ASTUtils.getRawText(propertyOrMethodDefinition.key);
71
63
  if (allowedNames.includes(aliasName) ||
@@ -73,28 +65,65 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
73
65
  propertyName === (0, utils_1.kebabToCamelCase)(aliasName))) {
74
66
  return;
75
67
  }
76
- const inputCallExpression = utils_1.ASTUtils.getNearestNodeFrom(node, utils_1.ASTUtils.isCallExpression);
77
- if (inputCallExpression &&
78
- utils_2.ASTUtils.isIdentifier(inputCallExpression.callee) &&
79
- inputCallExpression.callee.name === 'Input' &&
80
- utils_1.ASTUtils.isObjectExpression(inputCallExpression.arguments?.[0])) {
81
- const [firstArg] = inputCallExpression.arguments;
82
- const aliasProperty = firstArg.properties.find((property) => utils_1.ASTUtils.isProperty(property) &&
83
- utils_1.ASTUtils.getRawText(property.key) === 'alias');
84
- if (!aliasProperty) {
85
- return;
68
+ // The alias is either a string in the `@Input()` decorator function,
69
+ // or a string on an `alias` property that is in an object expression
70
+ // that is in the `@Input()` decorator or the `input()` function or the
71
+ // `input.required()` function. If it's on the `alias` property, then we
72
+ // want to remove that whole property rather than just the string literal.
73
+ const stringToRemove = utils_1.ASTUtils.isTemplateElement(node)
74
+ ? node.parent
75
+ : node;
76
+ let rangeToRemove = stringToRemove.range;
77
+ if (utils_1.ASTUtils.isProperty(stringToRemove.parent)) {
78
+ const property = stringToRemove.parent;
79
+ rangeToRemove = property.range;
80
+ if (utils_1.ASTUtils.isObjectExpression(property.parent)) {
81
+ const objectExpression = property.parent;
82
+ if (objectExpression.properties.length === 1) {
83
+ // The property is the only property in the
84
+ // object, so we can remove the whole object.
85
+ rangeToRemove = objectExpression.range;
86
+ // If the object is in an `input()` function, then
87
+ // the object will be the second argument. The first
88
+ // argument will be the default value. We need to
89
+ // remove the comma after the default value.
90
+ const tokenBefore = context.sourceCode.getTokenBefore(objectExpression);
91
+ if (tokenBefore && utils_2.ASTUtils.isCommaToken(tokenBefore)) {
92
+ rangeToRemove = [tokenBefore.range[0], rangeToRemove[1]];
93
+ }
94
+ }
95
+ else {
96
+ // There are other properties in the object, so we
97
+ // can only remove the property. How we remove it
98
+ // will depend on where the property is in the object.
99
+ const propertyIndex = objectExpression.properties.indexOf(property);
100
+ if (propertyIndex < objectExpression.properties.length - 1) {
101
+ // The property is not the last one, so we can
102
+ // remove everything up to the next property
103
+ // which will remove the comma after it.
104
+ rangeToRemove = [
105
+ property.range[0],
106
+ objectExpression.properties[propertyIndex + 1].range[0],
107
+ ];
108
+ }
109
+ else {
110
+ // The property is the last one. If the object has a
111
+ // trailing comma, then we want to keep the trailing comma.
112
+ // The simplest way to do that is to remove the property
113
+ // and the comma that precedes it.
114
+ const tokenBefore = context.sourceCode.getTokenBefore(property);
115
+ if (tokenBefore && utils_2.ASTUtils.isCommaToken(tokenBefore)) {
116
+ rangeToRemove = [tokenBefore.range[0], rangeToRemove[1]];
117
+ }
118
+ }
119
+ }
86
120
  }
87
121
  }
88
122
  if (aliasName === propertyName) {
89
123
  context.report({
90
124
  node,
91
125
  messageId: 'noInputRename',
92
- fix: (fixer) => {
93
- if (node.parent && isAliasMetadataProperty) {
94
- return fixer.remove(node.parent);
95
- }
96
- return fixer.remove(node);
97
- },
126
+ fix: (fixer) => fixer.removeRange(rangeToRemove),
98
127
  });
99
128
  }
100
129
  else if (!isAliasNameAllowed(selectors, propertyName, aliasName, selectorDirectiveName)) {
@@ -104,17 +133,12 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
104
133
  suggest: [
105
134
  {
106
135
  messageId: 'suggestRemoveAliasName',
107
- fix: (fixer) => {
108
- if (node.parent && isAliasMetadataProperty) {
109
- return fixer.remove(node.parent);
110
- }
111
- return fixer.remove(node);
112
- },
136
+ fix: (fixer) => fixer.removeRange(rangeToRemove),
113
137
  },
114
138
  {
115
139
  messageId: 'suggestReplaceOriginalNameWithAliasName',
116
140
  fix: (fixer) => [
117
- fixer.remove(node),
141
+ fixer.removeRange(rangeToRemove),
118
142
  fixer.replaceText(propertyOrMethodDefinition.key, aliasName.includes('-') ? `'${aliasName}'` : aliasName),
119
143
  ],
120
144
  },
@@ -1 +1 @@
1
- {"version":3,"file":"no-output-rename.d.ts","sourceRoot":"","sources":["../../src/rules/no-output-rename.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAClB,gBAAgB,GAChB,wBAAwB,GACxB,yCAAyC,CAAC;AAC9C,eAAO,MAAM,SAAS,qBAAqB,CAAC;;AAG5C,wBAkJG"}
1
+ {"version":3,"file":"no-output-rename.d.ts","sourceRoot":"","sources":["../../src/rules/no-output-rename.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAClB,gBAAgB,GAChB,wBAAwB,GACxB,yCAAyC,CAAC;AAC9C,eAAO,MAAM,SAAS,qBAAqB,CAAC;;AAG5C,wBAgMG"}
@@ -38,11 +38,56 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
38
38
  }
39
39
  const aliasName = utils_1.ASTUtils.getRawText(node);
40
40
  const propertyName = utils_1.ASTUtils.getRawText(propertyOrMethodDefinition.key);
41
+ // The alias is either a string in the `@Output()` decorator function,
42
+ // or a string on an `alias` property that is in an object expression
43
+ // that is in the `output()` function. If it's the latter, then we want
44
+ // to remove that whole property rather than just the string literal.
45
+ const stringToRemove = utils_1.ASTUtils.isTemplateElement(node)
46
+ ? node.parent
47
+ : node;
48
+ let rangeToRemove = stringToRemove.range;
49
+ if (utils_1.ASTUtils.isProperty(stringToRemove.parent)) {
50
+ const property = stringToRemove.parent;
51
+ rangeToRemove = property.range;
52
+ if (utils_1.ASTUtils.isObjectExpression(property.parent)) {
53
+ const objectExpression = property.parent;
54
+ if (objectExpression.properties.length === 1) {
55
+ // The property is the only property in the
56
+ // object, so we can remove the whole object.
57
+ rangeToRemove = objectExpression.range;
58
+ }
59
+ else {
60
+ // There are other properties in the object, so we
61
+ // can only remove the property. How we remove it
62
+ // will depend on where the property is in the object.
63
+ const propertyIndex = objectExpression.properties.indexOf(property);
64
+ if (propertyIndex < objectExpression.properties.length - 1) {
65
+ // The property is not the last one, so we can
66
+ // remove everything up to the next property
67
+ // which will remove the comma after it.
68
+ rangeToRemove = [
69
+ property.range[0],
70
+ objectExpression.properties[propertyIndex + 1].range[0],
71
+ ];
72
+ }
73
+ else {
74
+ // The property is the last one. If the object has a
75
+ // trailing comma, then we want to keep the trailing comma.
76
+ // The simplest way to do that is to remove the property
77
+ // and the comma that precedes it.
78
+ const tokenBefore = context.sourceCode.getTokenBefore(property);
79
+ if (tokenBefore && utils_2.ASTUtils.isCommaToken(tokenBefore)) {
80
+ rangeToRemove = [tokenBefore.range[0], rangeToRemove[1]];
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
41
86
  if (aliasName === propertyName) {
42
87
  context.report({
43
88
  node,
44
89
  messageId: 'noOutputRename',
45
- fix: (fixer) => fixer.remove(node),
90
+ fix: (fixer) => fixer.removeRange(rangeToRemove),
46
91
  });
47
92
  }
48
93
  else if (!isAliasNameAllowed(selectors, propertyName, aliasName)) {
@@ -52,12 +97,12 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
52
97
  suggest: [
53
98
  {
54
99
  messageId: 'suggestRemoveAliasName',
55
- fix: (fixer) => fixer.remove(node),
100
+ fix: (fixer) => fixer.removeRange(rangeToRemove),
56
101
  },
57
102
  {
58
103
  messageId: 'suggestReplaceOriginalNameWithAliasName',
59
104
  fix: (fixer) => [
60
- fixer.remove(node),
105
+ fixer.removeRange(rangeToRemove),
61
106
  fixer.replaceText(propertyOrMethodDefinition.key, aliasName.includes('-') ? `'${aliasName}'` : aliasName),
62
107
  ],
63
108
  },
@@ -1 +1 @@
1
- {"version":3,"file":"prefer-standalone.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-standalone.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AAEzB,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAC5C,eAAO,MAAM,SAAS,sBAAsB,CAAC;;AAI7C,wBAiEG"}
1
+ {"version":3,"file":"prefer-standalone.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-standalone.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AAEzB,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAC5C,eAAO,MAAM,SAAS,sBAAsB,CAAC;;AAE7C,wBA6DG"}
@@ -4,25 +4,25 @@ exports.RULE_NAME = void 0;
4
4
  const utils_1 = require("@angular-eslint/utils");
5
5
  const create_eslint_rule_1 = require("../utils/create-eslint-rule");
6
6
  exports.RULE_NAME = 'prefer-standalone';
7
- const METADATA_PROPERTY_NAME = 'standalone';
8
- const IS_STANDALONE = 'true';
9
7
  exports.default = (0, create_eslint_rule_1.createESLintRule)({
10
8
  name: exports.RULE_NAME,
11
9
  meta: {
12
10
  type: 'suggestion',
13
11
  docs: {
14
- description: `Ensures component, directive and pipe \`${METADATA_PROPERTY_NAME}\` property is set to \`${IS_STANDALONE}\` in the component decorator`,
12
+ description: `Ensures Components, Directives and Pipes do not opt out of standalone`,
13
+ recommended: 'recommended',
15
14
  },
16
15
  fixable: 'code',
17
16
  schema: [],
18
17
  messages: {
19
- preferStandalone: `The {{type}} \`${METADATA_PROPERTY_NAME}\` property should be set to \`${IS_STANDALONE}\``,
18
+ preferStandalone: `Components, Directives and Pipes should not opt out of standalone`,
20
19
  },
21
20
  },
22
21
  defaultOptions: [],
23
22
  create(context) {
24
23
  const standaloneRuleFactory = (type) => (node) => {
25
- const standalone = utils_1.ASTUtils.getDecoratorPropertyValue(node, METADATA_PROPERTY_NAME);
24
+ const standalone = utils_1.ASTUtils.getDecoratorPropertyValue(node, 'standalone');
25
+ // Leave the standalone property alone if it was set to true or not present
26
26
  if (!standalone ||
27
27
  (utils_1.ASTUtils.isLiteral(standalone) && standalone.value === true)) {
28
28
  return;
@@ -35,14 +35,15 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
35
35
  messageId: 'preferStandalone',
36
36
  data: { type },
37
37
  fix: (fixer) => {
38
- if (standalone &&
39
- utils_1.ASTUtils.isLiteral(standalone) &&
40
- standalone.value !== true) {
41
- return [fixer.replaceText(standalone, IS_STANDALONE)].filter(utils_1.isNotNullOrUndefined);
38
+ // Remove the standalone property altogether if it was set to false
39
+ const tokenAfter = context.sourceCode.getTokenAfter(standalone.parent);
40
+ // Remove the trailing comma, if present
41
+ const removeStart = standalone.parent.range[0];
42
+ let removeEnd = standalone.parent.range[1];
43
+ if (tokenAfter && tokenAfter.value === ',') {
44
+ removeEnd = tokenAfter.range[1];
42
45
  }
43
- return [
44
- utils_1.RuleFixes.getDecoratorPropertyAddFix(node, fixer, `${METADATA_PROPERTY_NAME}: ${IS_STANDALONE}`),
45
- ].filter(utils_1.isNotNullOrUndefined);
46
+ return fixer.removeRange([removeStart, removeEnd]);
46
47
  },
47
48
  });
48
49
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular-eslint/eslint-plugin",
3
- "version": "19.0.0-alpha.4",
3
+ "version": "19.0.0-alpha.6",
4
4
  "description": "ESLint plugin for Angular applications, following https://angular.dev/style-guide",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -18,11 +18,11 @@
18
18
  "LICENSE"
19
19
  ],
20
20
  "dependencies": {
21
- "@angular-eslint/bundled-angular-compiler": "19.0.0-alpha.4",
22
- "@angular-eslint/utils": "19.0.0-alpha.4"
21
+ "@angular-eslint/bundled-angular-compiler": "19.0.0-alpha.6",
22
+ "@angular-eslint/utils": "19.0.0-alpha.6"
23
23
  },
24
24
  "devDependencies": {
25
- "@angular-eslint/test-utils": "19.0.0-alpha.4"
25
+ "@angular-eslint/test-utils": "19.0.0-alpha.6"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@typescript-eslint/utils": "^7.11.0 || ^8.0.0",