@depup/angular-eslint__eslint-plugin 22.1.0-depup.1 → 22.2.0-depup.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/README.md +2 -2
- package/changes.json +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/rules/no-duplicates-in-metadata-arrays.d.ts.map +1 -1
- package/dist/rules/no-duplicates-in-metadata-arrays.js +10 -5
- package/dist/rules/prefer-on-push-component-change-detection.d.ts +6 -3
- package/dist/rules/prefer-on-push-component-change-detection.d.ts.map +1 -1
- package/dist/rules/prefer-on-push-component-change-detection.js +50 -20
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ npm install @depup/angular-eslint__eslint-plugin
|
|
|
13
13
|
|
|
14
14
|
| Field | Value |
|
|
15
15
|
|-------|-------|
|
|
16
|
-
| Original | [@angular-eslint/eslint-plugin](https://www.npmjs.com/package/@angular-eslint/eslint-plugin) @ 22.
|
|
17
|
-
| Processed | 2026-
|
|
16
|
+
| Original | [@angular-eslint/eslint-plugin](https://www.npmjs.com/package/@angular-eslint/eslint-plugin) @ 22.2.0 |
|
|
17
|
+
| Processed | 2026-08-30 |
|
|
18
18
|
| Smoke test | passed |
|
|
19
19
|
| Deps updated | 1 |
|
|
20
20
|
|
package/changes.json
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -96,7 +96,7 @@ declare const _default: {
|
|
|
96
96
|
"prefer-inject": import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferInject", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
97
97
|
name: string;
|
|
98
98
|
};
|
|
99
|
-
"prefer-on-push-component-change-detection": import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/prefer-on-push-component-change-detection").MessageIds,
|
|
99
|
+
"prefer-on-push-component-change-detection": import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/prefer-on-push-component-change-detection").MessageIds, import("./rules/prefer-on-push-component-change-detection").Options, import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
100
100
|
name: string;
|
|
101
101
|
};
|
|
102
102
|
"prefer-output-emitter-ref": import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferOutputEmitterRef", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-duplicates-in-metadata-arrays.d.ts","sourceRoot":"","sources":["../../src/rules/no-duplicates-in-metadata-arrays.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,8BAA8B,CAAC;AACxD,eAAO,MAAM,SAAS,qCAAqC,CAAC;;;;AAE5D,
|
|
1
|
+
{"version":3,"file":"no-duplicates-in-metadata-arrays.d.ts","sourceRoot":"","sources":["../../src/rules/no-duplicates-in-metadata-arrays.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,8BAA8B,CAAC;AACxD,eAAO,MAAM,SAAS,qCAAqC,CAAC;;;;AAE5D,wBA+CG;AA2BH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -30,6 +30,9 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
30
30
|
// https://angular.dev/api/core/Component
|
|
31
31
|
`${utils_1.Selectors.COMPONENT_CLASS_DECORATOR} Property[key.name=${(0, utils_1.toPattern)([
|
|
32
32
|
'imports',
|
|
33
|
+
'providers',
|
|
34
|
+
'viewProviders',
|
|
35
|
+
'styleUrls',
|
|
33
36
|
])}] > ArrayExpression`,
|
|
34
37
|
// https://angular.dev/api/core/Directive
|
|
35
38
|
`${utils_1.Selectors.DIRECTIVE_CLASS_DECORATOR} Property[key.name=${(0, utils_1.toPattern)([
|
|
@@ -49,15 +52,17 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
49
52
|
},
|
|
50
53
|
});
|
|
51
54
|
function getDuplicateItems(elements) {
|
|
52
|
-
const items = elements.filter(utils_2.ASTUtils.isIdentifier)
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
+
const items = elements.filter((element) => utils_2.ASTUtils.isIdentifier(element) ||
|
|
56
|
+
(element?.type === 'Literal' && typeof element.value === 'string'));
|
|
57
|
+
const uniqueItemKeys = new Set();
|
|
58
|
+
const duplicateItems = new Array();
|
|
55
59
|
items.forEach((item) => {
|
|
56
|
-
|
|
60
|
+
const key = item.type === 'Identifier' ? item.name : item.value;
|
|
61
|
+
if (uniqueItemKeys.has(key)) {
|
|
57
62
|
duplicateItems.push(item);
|
|
58
63
|
}
|
|
59
64
|
else {
|
|
60
|
-
|
|
65
|
+
uniqueItemKeys.add(key);
|
|
61
66
|
}
|
|
62
67
|
});
|
|
63
68
|
return duplicateItems;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
export type
|
|
1
|
+
import type { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
export type Options = [{
|
|
3
|
+
readonly allowExplicitOnPush: boolean;
|
|
4
|
+
}];
|
|
5
|
+
export type MessageIds = 'preferOnPushComponentChangeDetection' | 'suggestRemoveChangeDetection' | 'redundantOnPushComponentChangeDetection';
|
|
3
6
|
export declare const RULE_NAME = "prefer-on-push-component-change-detection";
|
|
4
|
-
declare const _default:
|
|
7
|
+
declare const _default: TSESLint.RuleModule<MessageIds, Options, import("../utils/create-eslint-rule").RuleDocs, TSESLint.RuleListener> & {
|
|
5
8
|
name: string;
|
|
6
9
|
};
|
|
7
10
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-on-push-component-change-detection.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-on-push-component-change-detection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prefer-on-push-component-change-detection.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-on-push-component-change-detection.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAY,MAAM,0BAA0B,CAAC;AAGnE,MAAM,MAAM,OAAO,GAAG,CAAC;IAAE,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAClE,MAAM,MAAM,UAAU,GAClB,sCAAsC,GACtC,8BAA8B,GAC9B,yCAAyC,CAAC;AAC9C,eAAO,MAAM,SAAS,8CAA8C,CAAC;;;;AAMrE,wBAoGG;AAEH,eAAO,MAAM,mBAAmB;;CAQ/B,CAAC"}
|
|
@@ -6,6 +6,7 @@ const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
|
6
6
|
exports.RULE_NAME = 'prefer-on-push-component-change-detection';
|
|
7
7
|
const METADATA_PROPERTY_NAME = 'changeDetection';
|
|
8
8
|
const STRATEGY_ON_PUSH = 'ChangeDetectionStrategy.OnPush';
|
|
9
|
+
const DEFAULT_OPTIONS = { allowExplicitOnPush: true };
|
|
9
10
|
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
10
11
|
name: exports.RULE_NAME,
|
|
11
12
|
meta: {
|
|
@@ -14,26 +15,41 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
14
15
|
description: `Ensures components do not opt out of the default \`${STRATEGY_ON_PUSH}\` change detection strategy`,
|
|
15
16
|
recommended: 'recommended',
|
|
16
17
|
},
|
|
18
|
+
fixable: 'code',
|
|
17
19
|
hasSuggestions: true,
|
|
18
|
-
schema: [
|
|
20
|
+
schema: [
|
|
21
|
+
{
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
allowExplicitOnPush: {
|
|
25
|
+
type: 'boolean',
|
|
26
|
+
default: DEFAULT_OPTIONS.allowExplicitOnPush,
|
|
27
|
+
description: `Whether to allow a component to set \`${METADATA_PROPERTY_NAME}: ${STRATEGY_ON_PUSH}\` explicitly even though it is now the default.`,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
additionalProperties: false,
|
|
31
|
+
},
|
|
32
|
+
],
|
|
19
33
|
messages: {
|
|
20
34
|
preferOnPushComponentChangeDetection: `Components should not opt out of the default \`${STRATEGY_ON_PUSH}\` change detection strategy`,
|
|
21
35
|
suggestRemoveChangeDetection: `Remove \`${METADATA_PROPERTY_NAME}\` to use the default (\`${STRATEGY_ON_PUSH}\`)`,
|
|
36
|
+
redundantOnPushComponentChangeDetection: `\`${METADATA_PROPERTY_NAME}: ${STRATEGY_ON_PUSH}\` is redundant because \`${STRATEGY_ON_PUSH}\` is the default change detection strategy`,
|
|
22
37
|
},
|
|
23
38
|
},
|
|
24
|
-
defaultOptions: [],
|
|
25
|
-
create(context) {
|
|
39
|
+
defaultOptions: [DEFAULT_OPTIONS],
|
|
40
|
+
create(context, [{ allowExplicitOnPush }]) {
|
|
26
41
|
const sourceCode = context.sourceCode;
|
|
27
42
|
const changeDetectionMetadataProperty = utils_1.Selectors.metadataProperty(METADATA_PROPERTY_NAME);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
const changeDetectionStrategyProperty = `${utils_1.Selectors.COMPONENT_CLASS_DECORATOR} > CallExpression > ObjectExpression > ${changeDetectionMetadataProperty}[value.object.name='ChangeDetectionStrategy']`;
|
|
44
|
+
const onPushOptOutProperty = `${changeDetectionStrategyProperty}[value.property.name!='OnPush']`;
|
|
45
|
+
const redundantOnPushProperty = `${changeDetectionStrategyProperty}[value.property.name='OnPush']`;
|
|
46
|
+
function removeChangeDetectionProperty(node, fixer) {
|
|
47
|
+
const importDeclarations = utils_1.ASTUtils.getImportDeclarations(node, '@angular/core') ?? [];
|
|
48
|
+
return [
|
|
49
|
+
utils_1.RuleFixes.getNodeToCommaRemoveFix(sourceCode, node, fixer),
|
|
50
|
+
utils_1.RuleFixes.getImportRemoveFix(sourceCode, importDeclarations, 'ChangeDetectionStrategy', fixer),
|
|
51
|
+
].filter(utils_1.isNotNullOrUndefined);
|
|
52
|
+
}
|
|
37
53
|
return {
|
|
38
54
|
[onPushOptOutProperty](node) {
|
|
39
55
|
const { value } = node;
|
|
@@ -48,20 +64,34 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
48
64
|
suggest: [
|
|
49
65
|
{
|
|
50
66
|
messageId: 'suggestRemoveChangeDetection',
|
|
51
|
-
fix: (fixer) =>
|
|
52
|
-
const importDeclarations = utils_1.ASTUtils.getImportDeclarations(node, '@angular/core') ?? [];
|
|
53
|
-
return [
|
|
54
|
-
utils_1.RuleFixes.getNodeToCommaRemoveFix(sourceCode, node, fixer),
|
|
55
|
-
utils_1.RuleFixes.getImportRemoveFix(sourceCode, importDeclarations, 'ChangeDetectionStrategy', fixer),
|
|
56
|
-
].filter(utils_1.isNotNullOrUndefined);
|
|
57
|
-
},
|
|
67
|
+
fix: (fixer) => removeChangeDetectionProperty(node, fixer),
|
|
58
68
|
},
|
|
59
69
|
],
|
|
60
70
|
});
|
|
61
71
|
},
|
|
72
|
+
[redundantOnPushProperty](node) {
|
|
73
|
+
if (allowExplicitOnPush) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const { value } = node;
|
|
77
|
+
if (!utils_1.ASTUtils.isMemberExpression(value)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
context.report({
|
|
81
|
+
node: value.property,
|
|
82
|
+
messageId: 'redundantOnPushComponentChangeDetection',
|
|
83
|
+
fix: (fixer) => removeChangeDetectionProperty(node, fixer),
|
|
84
|
+
});
|
|
85
|
+
},
|
|
62
86
|
};
|
|
63
87
|
},
|
|
64
88
|
});
|
|
65
89
|
exports.RULE_DOCS_EXTENSION = {
|
|
66
|
-
rationale: `As of Angular v22, \`${STRATEGY_ON_PUSH}\` is the default change detection strategy: a component that does not specify \`${METADATA_PROPERTY_NAME}\` is checked using OnPush.
|
|
90
|
+
rationale: `As of Angular v22, \`${STRATEGY_ON_PUSH}\` is the default change detection strategy: a component that does not specify \`${METADATA_PROPERTY_NAME}\` is checked using OnPush. \
|
|
91
|
+
This brings new code in line with zoneless being the default and with Angular's goal of performance by default, and means it is no longer necessary to set \`${STRATEGY_ON_PUSH}\` explicitly. \
|
|
92
|
+
The previous default, \`ChangeDetectionStrategy.Default\`, has been renamed to \`ChangeDetectionStrategy.Eager\`. \
|
|
93
|
+
When you run \`ng update\`, the v22 migration adds an explicit \`ChangeDetectionStrategy.Eager\` to existing components that relied on the old implicit default, so that they keep behaving as before.\n\n\
|
|
94
|
+
By default the rule reports components that explicitly opt out of OnPush by setting \`${METADATA_PROPERTY_NAME}\` to \`ChangeDetectionStrategy.Eager\` (or the deprecated \`ChangeDetectionStrategy.Default\`) — including the components the migration marked as \`Eager\` — so you can review them and adopt OnPush where it is safe to do so. \
|
|
95
|
+
Because omitting \`${METADATA_PROPERTY_NAME}\` already gives you OnPush, declaring \`${STRATEGY_ON_PUSH}\` explicitly is redundant; set \`allowExplicitOnPush\` to \`false\` to have the rule flag and autofix that too. \
|
|
96
|
+
Note that switching a component from eager checking to OnPush can change its runtime behaviour, so apply the suggestion deliberately and make sure the component uses immutable data patterns (creating new object references when data changes).`,
|
|
67
97
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@depup/angular-eslint__eslint-plugin",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.2.0-depup.0",
|
|
4
4
|
"description": "ESLint plugin for Angular applications, following https://angular.dev/style-guide (with updated dependencies)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"ts-api-utils": "^2.5.0",
|
|
24
|
-
"@angular-eslint/bundled-angular-compiler": "22.
|
|
25
|
-
"@angular-eslint/utils": "22.
|
|
24
|
+
"@angular-eslint/bundled-angular-compiler": "22.2.0",
|
|
25
|
+
"@angular-eslint/utils": "22.2.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@angular-eslint/test-utils": "22.
|
|
28
|
+
"@angular-eslint/test-utils": "22.2.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@typescript-eslint/utils": "^8.0.0",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
},
|
|
51
51
|
"depsUpdated": 1,
|
|
52
52
|
"originalPackage": "@angular-eslint/eslint-plugin",
|
|
53
|
-
"originalVersion": "22.
|
|
54
|
-
"processedAt": "2026-
|
|
53
|
+
"originalVersion": "22.2.0",
|
|
54
|
+
"processedAt": "2026-08-30T16:08:13.663Z",
|
|
55
55
|
"smokeTest": "passed"
|
|
56
56
|
}
|
|
57
57
|
}
|