@depup/angular-eslint__eslint-plugin 21.3.1-depup.0 → 22.1.0-depup.1
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 +6 -77
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -11
- package/dist/rules/consistent-component-styles.d.ts.map +1 -1
- package/dist/rules/contextual-lifecycle.d.ts.map +1 -1
- package/dist/rules/contextual-lifecycle.js +3 -0
- package/dist/rules/inject-at-top.d.ts +11 -0
- package/dist/rules/inject-at-top.d.ts.map +1 -0
- package/dist/rules/inject-at-top.js +76 -0
- package/dist/rules/no-async-lifecycle-method.d.ts.map +1 -1
- package/dist/rules/no-async-lifecycle-method.js +1 -0
- package/dist/rules/no-empty-lifecycle-method.d.ts.map +1 -1
- package/dist/rules/no-empty-lifecycle-method.js +1 -0
- package/dist/rules/no-implicit-take-until-destroyed.d.ts.map +1 -1
- package/dist/rules/no-implicit-take-until-destroyed.js +84 -1
- package/dist/rules/prefer-inject.d.ts.map +1 -1
- package/dist/rules/prefer-inject.js +1 -0
- package/dist/rules/prefer-on-push-component-change-detection.d.ts +1 -1
- package/dist/rules/prefer-on-push-component-change-detection.d.ts.map +1 -1
- package/dist/rules/prefer-on-push-component-change-detection.js +27 -41
- package/dist/rules/prefer-service-decorator.d.ts +11 -0
- package/dist/rules/prefer-service-decorator.d.ts.map +1 -0
- package/dist/rules/prefer-service-decorator.js +101 -0
- package/dist/rules/use-component-view-encapsulation.d.ts.map +1 -1
- package/package.json +9 -8
- package/dist/configs/README.md +0 -44
- package/dist/configs/all.json +0 -54
- package/dist/configs/recommended.json +0 -18
- package/dist/rules/no-conflicting-lifecycle.d.ts +0 -11
- package/dist/rules/no-conflicting-lifecycle.d.ts.map +0 -1
- package/dist/rules/no-conflicting-lifecycle.js +0 -74
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) @
|
|
17
|
-
| Processed | 2026-
|
|
16
|
+
| Original | [@angular-eslint/eslint-plugin](https://www.npmjs.com/package/@angular-eslint/eslint-plugin) @ 22.1.0 |
|
|
17
|
+
| Processed | 2026-07-21 |
|
|
18
18
|
| Smoke test | passed |
|
|
19
19
|
| Deps updated | 1 |
|
|
20
20
|
|
package/changes.json
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,78 +1,4 @@
|
|
|
1
1
|
declare const _default: {
|
|
2
|
-
configs: {
|
|
3
|
-
all: {
|
|
4
|
-
parser: string;
|
|
5
|
-
plugins: string[];
|
|
6
|
-
rules: {
|
|
7
|
-
"@angular-eslint/component-class-suffix": string;
|
|
8
|
-
"@angular-eslint/component-max-inline-declarations": string;
|
|
9
|
-
"@angular-eslint/component-selector": string;
|
|
10
|
-
"@angular-eslint/computed-must-return": string;
|
|
11
|
-
"@angular-eslint/consistent-component-styles": string;
|
|
12
|
-
"@angular-eslint/contextual-decorator": string;
|
|
13
|
-
"@angular-eslint/contextual-lifecycle": string;
|
|
14
|
-
"@angular-eslint/directive-class-suffix": string;
|
|
15
|
-
"@angular-eslint/directive-selector": string;
|
|
16
|
-
"@angular-eslint/no-async-lifecycle-method": string;
|
|
17
|
-
"@angular-eslint/no-attribute-decorator": string;
|
|
18
|
-
"@angular-eslint/no-developer-preview": string;
|
|
19
|
-
"@angular-eslint/no-duplicates-in-metadata-arrays": string;
|
|
20
|
-
"@angular-eslint/no-empty-lifecycle-method": string;
|
|
21
|
-
"@angular-eslint/no-experimental": string;
|
|
22
|
-
"@angular-eslint/no-forward-ref": string;
|
|
23
|
-
"@angular-eslint/no-implicit-take-until-destroyed": string;
|
|
24
|
-
"@angular-eslint/no-input-prefix": string;
|
|
25
|
-
"@angular-eslint/no-input-rename": string;
|
|
26
|
-
"@angular-eslint/no-inputs-metadata-property": string;
|
|
27
|
-
"@angular-eslint/no-lifecycle-call": string;
|
|
28
|
-
"@angular-eslint/no-output-native": string;
|
|
29
|
-
"@angular-eslint/no-output-on-prefix": string;
|
|
30
|
-
"@angular-eslint/no-output-rename": string;
|
|
31
|
-
"@angular-eslint/no-outputs-metadata-property": string;
|
|
32
|
-
"@angular-eslint/no-pipe-impure": string;
|
|
33
|
-
"@angular-eslint/no-queries-metadata-property": string;
|
|
34
|
-
"@angular-eslint/no-uncalled-signals": string;
|
|
35
|
-
"@angular-eslint/pipe-prefix": string;
|
|
36
|
-
"@angular-eslint/prefer-host-metadata-property": string;
|
|
37
|
-
"@angular-eslint/prefer-inject": string;
|
|
38
|
-
"@angular-eslint/prefer-on-push-component-change-detection": string;
|
|
39
|
-
"@angular-eslint/prefer-output-emitter-ref": string;
|
|
40
|
-
"@angular-eslint/prefer-output-readonly": string;
|
|
41
|
-
"@angular-eslint/prefer-signal-model": string;
|
|
42
|
-
"@angular-eslint/prefer-signals": string;
|
|
43
|
-
"@angular-eslint/prefer-standalone": string;
|
|
44
|
-
"@angular-eslint/relative-url-prefix": string;
|
|
45
|
-
"@angular-eslint/require-lifecycle-on-prototype": string;
|
|
46
|
-
"@angular-eslint/require-localize-metadata": string;
|
|
47
|
-
"@angular-eslint/runtime-localize": string;
|
|
48
|
-
"@angular-eslint/sort-keys-in-type-decorator": string;
|
|
49
|
-
"@angular-eslint/sort-lifecycle-methods": string;
|
|
50
|
-
"@angular-eslint/use-component-selector": string;
|
|
51
|
-
"@angular-eslint/use-component-view-encapsulation": string;
|
|
52
|
-
"@angular-eslint/use-injectable-provided-in": string;
|
|
53
|
-
"@angular-eslint/use-lifecycle-interface": string;
|
|
54
|
-
"@angular-eslint/use-pipe-transform-interface": string;
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
recommended: {
|
|
58
|
-
parser: string;
|
|
59
|
-
plugins: string[];
|
|
60
|
-
rules: {
|
|
61
|
-
"@angular-eslint/contextual-lifecycle": string;
|
|
62
|
-
"@angular-eslint/no-empty-lifecycle-method": string;
|
|
63
|
-
"@angular-eslint/no-input-rename": string;
|
|
64
|
-
"@angular-eslint/no-inputs-metadata-property": string;
|
|
65
|
-
"@angular-eslint/no-output-native": string;
|
|
66
|
-
"@angular-eslint/no-output-on-prefix": string;
|
|
67
|
-
"@angular-eslint/no-output-rename": string;
|
|
68
|
-
"@angular-eslint/no-outputs-metadata-property": string;
|
|
69
|
-
"@angular-eslint/prefer-inject": string;
|
|
70
|
-
"@angular-eslint/prefer-standalone": string;
|
|
71
|
-
"@angular-eslint/use-pipe-transform-interface": string;
|
|
72
|
-
"@angular-eslint/use-lifecycle-interface": string;
|
|
73
|
-
};
|
|
74
|
-
};
|
|
75
|
-
};
|
|
76
2
|
rules: {
|
|
77
3
|
"component-class-suffix": import("@typescript-eslint/utils/ts-eslint").RuleModule<"componentClassSuffix", import("./rules/component-class-suffix").Options, import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
78
4
|
name: string;
|
|
@@ -101,13 +27,13 @@ declare const _default: {
|
|
|
101
27
|
"directive-selector": import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/directive-selector").MessageIds, import("./rules/directive-selector").Options, import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
102
28
|
name: string;
|
|
103
29
|
};
|
|
104
|
-
"
|
|
30
|
+
"inject-at-top": import("@typescript-eslint/utils/ts-eslint").RuleModule<"injectAtTop", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
105
31
|
name: string;
|
|
106
32
|
};
|
|
107
|
-
"no-
|
|
33
|
+
"no-async-lifecycle-method": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noAsyncLifecycleMethod", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
108
34
|
name: string;
|
|
109
35
|
};
|
|
110
|
-
"no-
|
|
36
|
+
"no-attribute-decorator": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noAttributeDecorator", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
111
37
|
name: string;
|
|
112
38
|
};
|
|
113
39
|
"no-developer-preview": import("@typescript-eslint/utils/ts-eslint").RuleModule<"noDeveloperPreview", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
@@ -179,6 +105,9 @@ declare const _default: {
|
|
|
179
105
|
"prefer-output-readonly": import("@typescript-eslint/utils/ts-eslint").RuleModule<import("./rules/prefer-output-readonly").MessageIds, [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
180
106
|
name: string;
|
|
181
107
|
};
|
|
108
|
+
"prefer-service-decorator": import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferServiceDecorator", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
109
|
+
name: string;
|
|
110
|
+
};
|
|
182
111
|
"prefer-signal-model": import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferSignalModel", [], import("./utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
183
112
|
name: string;
|
|
184
113
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuJA,kBAsDE"}
|
package/dist/index.js
CHANGED
|
@@ -32,11 +32,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
const all_json_1 = __importDefault(require("./configs/all.json"));
|
|
39
|
-
const recommended_json_1 = __importDefault(require("./configs/recommended.json"));
|
|
40
35
|
const component_class_suffix_1 = __importStar(require("./rules/component-class-suffix"));
|
|
41
36
|
const component_max_inline_declarations_1 = __importStar(require("./rules/component-max-inline-declarations"));
|
|
42
37
|
const component_selector_1 = __importStar(require("./rules/component-selector"));
|
|
@@ -47,7 +42,6 @@ const directive_class_suffix_1 = __importStar(require("./rules/directive-class-s
|
|
|
47
42
|
const directive_selector_1 = __importStar(require("./rules/directive-selector"));
|
|
48
43
|
const no_async_lifecycle_method_1 = __importStar(require("./rules/no-async-lifecycle-method"));
|
|
49
44
|
const no_attribute_decorator_1 = __importStar(require("./rules/no-attribute-decorator"));
|
|
50
|
-
const no_conflicting_lifecycle_1 = __importStar(require("./rules/no-conflicting-lifecycle"));
|
|
51
45
|
const no_duplicates_in_metadata_arrays_1 = __importStar(require("./rules/no-duplicates-in-metadata-arrays"));
|
|
52
46
|
const no_empty_lifecycle_method_1 = __importStar(require("./rules/no-empty-lifecycle-method"));
|
|
53
47
|
const no_forward_ref_1 = __importStar(require("./rules/no-forward-ref"));
|
|
@@ -67,6 +61,7 @@ const prefer_host_metadata_property_1 = __importStar(require("./rules/prefer-hos
|
|
|
67
61
|
const prefer_on_push_component_change_detection_1 = __importStar(require("./rules/prefer-on-push-component-change-detection"));
|
|
68
62
|
const prefer_output_emitter_ref_1 = __importStar(require("./rules/prefer-output-emitter-ref"));
|
|
69
63
|
const prefer_output_readonly_1 = __importStar(require("./rules/prefer-output-readonly"));
|
|
64
|
+
const prefer_service_decorator_1 = __importStar(require("./rules/prefer-service-decorator"));
|
|
70
65
|
const prefer_signal_model_1 = __importStar(require("./rules/prefer-signal-model"));
|
|
71
66
|
const prefer_inject_1 = __importStar(require("./rules/prefer-inject"));
|
|
72
67
|
const prefer_signals_1 = __importStar(require("./rules/prefer-signals"));
|
|
@@ -86,11 +81,8 @@ const no_experimental_1 = __importStar(require("./rules/no-experimental"));
|
|
|
86
81
|
const no_developer_preview_1 = __importStar(require("./rules/no-developer-preview"));
|
|
87
82
|
const no_implicit_take_until_destroyed_1 = __importStar(require("./rules/no-implicit-take-until-destroyed"));
|
|
88
83
|
const computed_must_return_1 = __importStar(require("./rules/computed-must-return"));
|
|
84
|
+
const inject_at_top_1 = __importStar(require("./rules/inject-at-top"));
|
|
89
85
|
module.exports = {
|
|
90
|
-
configs: {
|
|
91
|
-
all: all_json_1.default,
|
|
92
|
-
recommended: recommended_json_1.default,
|
|
93
|
-
},
|
|
94
86
|
rules: {
|
|
95
87
|
[component_class_suffix_1.RULE_NAME]: component_class_suffix_1.default,
|
|
96
88
|
[component_max_inline_declarations_1.RULE_NAME]: component_max_inline_declarations_1.default,
|
|
@@ -101,9 +93,9 @@ module.exports = {
|
|
|
101
93
|
[contextual_lifecycle_1.RULE_NAME]: contextual_lifecycle_1.default,
|
|
102
94
|
[directive_class_suffix_1.RULE_NAME]: directive_class_suffix_1.default,
|
|
103
95
|
[directive_selector_1.RULE_NAME]: directive_selector_1.default,
|
|
96
|
+
[inject_at_top_1.RULE_NAME]: inject_at_top_1.default,
|
|
104
97
|
[no_async_lifecycle_method_1.RULE_NAME]: no_async_lifecycle_method_1.default,
|
|
105
98
|
[no_attribute_decorator_1.RULE_NAME]: no_attribute_decorator_1.default,
|
|
106
|
-
[no_conflicting_lifecycle_1.RULE_NAME]: no_conflicting_lifecycle_1.default,
|
|
107
99
|
[no_developer_preview_1.RULE_NAME]: no_developer_preview_1.default,
|
|
108
100
|
[no_duplicates_in_metadata_arrays_1.RULE_NAME]: no_duplicates_in_metadata_arrays_1.default,
|
|
109
101
|
[no_empty_lifecycle_method_1.RULE_NAME]: no_empty_lifecycle_method_1.default,
|
|
@@ -127,6 +119,7 @@ module.exports = {
|
|
|
127
119
|
[prefer_on_push_component_change_detection_1.RULE_NAME]: prefer_on_push_component_change_detection_1.default,
|
|
128
120
|
[prefer_output_emitter_ref_1.RULE_NAME]: prefer_output_emitter_ref_1.default,
|
|
129
121
|
[prefer_output_readonly_1.RULE_NAME]: prefer_output_readonly_1.default,
|
|
122
|
+
[prefer_service_decorator_1.RULE_NAME]: prefer_service_decorator_1.default,
|
|
130
123
|
[prefer_signal_model_1.RULE_NAME]: prefer_signal_model_1.default,
|
|
131
124
|
[prefer_signals_1.RULE_NAME]: prefer_signals_1.default,
|
|
132
125
|
[prefer_standalone_1.RULE_NAME]: prefer_standalone_1.default,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consistent-component-styles.d.ts","sourceRoot":"","sources":["../../src/rules/consistent-component-styles.ts"],"names":[],"mappings":"AAIA,KAAK,IAAI,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC/B,MAAM,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACnC,MAAM,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"consistent-component-styles.d.ts","sourceRoot":"","sources":["../../src/rules/consistent-component-styles.ts"],"names":[],"mappings":"AAIA,KAAK,IAAI,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC/B,MAAM,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACnC,MAAM,MAAM,UAAU,GACpB,gBAAgB,GAAG,iBAAiB,GAAG,aAAa,GAAG,cAAc,CAAC;AACxE,eAAO,MAAM,SAAS,gCAAgC,CAAC;;;;AAEvD,wBAsHG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contextual-lifecycle.d.ts","sourceRoot":"","sources":["../../src/rules/contextual-lifecycle.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAC/C,eAAO,MAAM,SAAS,yBAAyB,CAAC;;;;AAEhD,
|
|
1
|
+
{"version":3,"file":"contextual-lifecycle.d.ts","sourceRoot":"","sources":["../../src/rules/contextual-lifecycle.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,qBAAqB,CAAC;AAC/C,eAAO,MAAM,SAAS,yBAAyB,CAAC;;;;AAEhD,wBAiEG;AAEH,eAAO,MAAM,mBAAmB;;CAE/B,CAAC"}
|
|
@@ -53,6 +53,9 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
53
53
|
[utils_1.Selectors.PIPE_CLASS_DECORATOR](node) {
|
|
54
54
|
checkContext(node, utils_1.ASTUtils.AngularClassDecorators.Pipe);
|
|
55
55
|
},
|
|
56
|
+
[utils_1.Selectors.SERVICE_CLASS_DECORATOR](node) {
|
|
57
|
+
checkContext(node, utils_1.ASTUtils.AngularClassDecorators.Service);
|
|
58
|
+
},
|
|
56
59
|
};
|
|
57
60
|
},
|
|
58
61
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'injectAtTop';
|
|
3
|
+
export declare const RULE_NAME = "inject-at-top";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"injectAtTop", [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
9
|
+
rationale: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=inject-at-top.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inject-at-top.d.ts","sourceRoot":"","sources":["../../src/rules/inject-at-top.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AAEzB,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC;AACvC,eAAO,MAAM,SAAS,kBAAkB,CAAC;;;;AAEzC,wBAyFG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_DOCS_EXTENSION = exports.RULE_NAME = void 0;
|
|
4
|
+
const utils_1 = require("@angular-eslint/utils");
|
|
5
|
+
const utils_2 = require("@typescript-eslint/utils");
|
|
6
|
+
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
7
|
+
exports.RULE_NAME = 'inject-at-top';
|
|
8
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
9
|
+
name: exports.RULE_NAME,
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'problem',
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Requires inject() calls to be declared at the top of the class, before any other member',
|
|
14
|
+
},
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
injectAtTop: 'Move this inject() to the top of the class. Class fields are initialized in the order they are written, so anything declared above this line cannot safely use the service yet.',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
const angularDecoratorsPattern = (0, utils_1.toPattern)([
|
|
23
|
+
'Component',
|
|
24
|
+
'Directive',
|
|
25
|
+
'Injectable',
|
|
26
|
+
'Pipe',
|
|
27
|
+
'Service',
|
|
28
|
+
]);
|
|
29
|
+
function hasEagerInject(node) {
|
|
30
|
+
if (!node) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (node.type === utils_2.AST_NODE_TYPES.CallExpression &&
|
|
34
|
+
node.callee.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
35
|
+
node.callee.name === 'inject') {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const isFn = node.type === utils_2.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
39
|
+
node.type === utils_2.AST_NODE_TYPES.FunctionExpression;
|
|
40
|
+
const isIIFE = isFn &&
|
|
41
|
+
node.parent?.type === utils_2.AST_NODE_TYPES.CallExpression &&
|
|
42
|
+
node.parent.callee === node;
|
|
43
|
+
if (node.type === utils_2.AST_NODE_TYPES.ClassExpression || (isFn && !isIIFE)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return Object.values(node)
|
|
47
|
+
.flat()
|
|
48
|
+
.some((child) => child &&
|
|
49
|
+
typeof child === 'object' &&
|
|
50
|
+
'type' in child &&
|
|
51
|
+
child !== node.parent &&
|
|
52
|
+
hasEagerInject(child));
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
[`${utils_1.Selectors.decoratorDefinition(angularDecoratorsPattern)} > ClassBody`](node) {
|
|
56
|
+
let seenNonInject = false;
|
|
57
|
+
for (const member of node.body) {
|
|
58
|
+
if ('static' in member && member.static) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (member.type === utils_2.AST_NODE_TYPES.PropertyDefinition &&
|
|
62
|
+
hasEagerInject(member.value)) {
|
|
63
|
+
if (seenNonInject) {
|
|
64
|
+
context.report({ node: member, messageId: 'injectAtTop' });
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
seenNonInject = true;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
75
|
+
rationale: "Class fields are initialized in the order they are declared, so a field that calls inject() only creates its value when the initializer runs. Anything declared above that line which tries to read the injected service will see undefined, and the error message (usually something like 'Cannot read properties of undefined') points at the consumer, not at the field that was initialized out of order. TypeScript catches the obvious shape of this bug when a later-declared field is referenced directly from an earlier initializer, but it does not trace through a getter body or a method call, so the moment the read of this.someService happens behind that indirection the compiler lets it through and the failure only shows up at runtime. The scenario is deceptively easy to introduce: a getter that reads an injected service is defined below the inject() call, someone later adds a small convenience field above the getter that references it, and the class breaks at construction time even though nothing about the change looks suspicious. Keeping every inject() call at the very top of the class removes the ordering concern entirely, because every dependency is guaranteed to exist before any other field, getter, method, or constructor statement runs.",
|
|
76
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-async-lifecycle-method.d.ts","sourceRoot":"","sources":["../../src/rules/no-async-lifecycle-method.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,wBAAwB,CAAC;AAClD,eAAO,MAAM,SAAS,8BAA8B,CAAC;;;;AAErD,
|
|
1
|
+
{"version":3,"file":"no-async-lifecycle-method.d.ts","sourceRoot":"","sources":["../../src/rules/no-async-lifecycle-method.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,wBAAwB,CAAC;AAClD,eAAO,MAAM,SAAS,8BAA8B,CAAC;;;;AAErD,wBA0CG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-empty-lifecycle-method.d.ts","sourceRoot":"","sources":["../../src/rules/no-empty-lifecycle-method.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"no-empty-lifecycle-method.d.ts","sourceRoot":"","sources":["../../src/rules/no-empty-lifecycle-method.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GACpB,wBAAwB,GAAG,8BAA8B,CAAC;AAC5D,eAAO,MAAM,SAAS,8BAA8B,CAAC;;;;AAErD,wBAuFG;AAYH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-implicit-take-until-destroyed.d.ts","sourceRoot":"","sources":["../../src/rules/no-implicit-take-until-destroyed.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,8BAA8B,CAAC;AACxD,eAAO,MAAM,SAAS,qCAAqC,CAAC;;;;AAO5D,wBAgCG;
|
|
1
|
+
{"version":3,"file":"no-implicit-take-until-destroyed.d.ts","sourceRoot":"","sources":["../../src/rules/no-implicit-take-until-destroyed.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,8BAA8B,CAAC;AACxD,eAAO,MAAM,SAAS,qCAAqC,CAAC;;;;AAO5D,wBAgCG;AAwMH,eAAO,MAAM,mBAAmB;;CAE/B,CAAC"}
|
|
@@ -55,6 +55,17 @@ function isInInjectionContext(node) {
|
|
|
55
55
|
return true;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
+
if (isNonConstructorMethod(current)) {
|
|
59
|
+
const classDeclaration = utils_1.ASTUtils.getNearestNodeFrom(current, utils_1.ASTUtils.isClassDeclaration);
|
|
60
|
+
if (classDeclaration) {
|
|
61
|
+
const decorator = utils_1.ASTUtils.getAngularClassDecorator(classDeclaration);
|
|
62
|
+
if (decorator &&
|
|
63
|
+
decorator !== 'NgModule' &&
|
|
64
|
+
isMethodCalledFromInjectionContext(current, classDeclaration)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
58
69
|
current = current.parent;
|
|
59
70
|
}
|
|
60
71
|
return false;
|
|
@@ -62,6 +73,78 @@ function isInInjectionContext(node) {
|
|
|
62
73
|
function isConstructorMethod(node) {
|
|
63
74
|
return (node.type === utils_2.AST_NODE_TYPES.MethodDefinition && node.kind === 'constructor');
|
|
64
75
|
}
|
|
76
|
+
function isNonConstructorMethod(node) {
|
|
77
|
+
return (node.type === utils_2.AST_NODE_TYPES.MethodDefinition && node.kind !== 'constructor');
|
|
78
|
+
}
|
|
79
|
+
function getMethodName(node) {
|
|
80
|
+
if (node.key.type === utils_2.AST_NODE_TYPES.Identifier ||
|
|
81
|
+
node.key.type === utils_2.AST_NODE_TYPES.PrivateIdentifier) {
|
|
82
|
+
return node.key.name;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function isMethodCalledFromInjectionContext(method, classDeclaration) {
|
|
87
|
+
const methodName = getMethodName(method);
|
|
88
|
+
if (!methodName) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
const isPrivateField = method.key.type === utils_2.AST_NODE_TYPES.PrivateIdentifier;
|
|
92
|
+
for (const member of classDeclaration.body.body) {
|
|
93
|
+
if (!isConstructorMethod(member) &&
|
|
94
|
+
!utils_1.ASTUtils.isPropertyDefinition(member)) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (containsCallToMethod(member, methodName, isPrivateField)) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
function isASTNode(value) {
|
|
104
|
+
return (value !== null &&
|
|
105
|
+
typeof value === 'object' &&
|
|
106
|
+
'type' in value &&
|
|
107
|
+
typeof value.type === 'string');
|
|
108
|
+
}
|
|
109
|
+
function containsCallToMethod(node, methodName, isPrivateField) {
|
|
110
|
+
if (node.type === utils_2.AST_NODE_TYPES.CallExpression &&
|
|
111
|
+
node.callee.type === utils_2.AST_NODE_TYPES.MemberExpression) {
|
|
112
|
+
const { object, property } = node.callee;
|
|
113
|
+
if (object.type === utils_2.AST_NODE_TYPES.ThisExpression) {
|
|
114
|
+
if (isPrivateField &&
|
|
115
|
+
property.type === utils_2.AST_NODE_TYPES.PrivateIdentifier &&
|
|
116
|
+
property.name === methodName) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (!isPrivateField &&
|
|
120
|
+
property.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
121
|
+
property.name === methodName) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const key of Object.keys(node)) {
|
|
127
|
+
if (key === 'parent')
|
|
128
|
+
continue;
|
|
129
|
+
const child = node[key];
|
|
130
|
+
if (child && typeof child === 'object') {
|
|
131
|
+
if (Array.isArray(child)) {
|
|
132
|
+
for (const item of child) {
|
|
133
|
+
if (isASTNode(item) &&
|
|
134
|
+
containsCallToMethod(item, methodName, isPrivateField)) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else if (isASTNode(child)) {
|
|
140
|
+
if (containsCallToMethod(child, methodName, isPrivateField)) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
65
148
|
function isInFactoryFunction(node) {
|
|
66
149
|
if (node.type !== utils_2.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
67
150
|
node.type !== utils_2.AST_NODE_TYPES.FunctionExpression) {
|
|
@@ -79,5 +162,5 @@ function isInFactoryFunction(node) {
|
|
|
79
162
|
return false;
|
|
80
163
|
}
|
|
81
164
|
exports.RULE_DOCS_EXTENSION = {
|
|
82
|
-
rationale: `The \`takeUntilDestroyed()\` operator can automatically infer the current component's or directive's \`DestroyRef\` only when called within an injection context — specifically in constructors or field initializers of classes decorated with \`@Component\`, \`@Directive\`, \`@Injectable\`, or \`@
|
|
165
|
+
rationale: `The \`takeUntilDestroyed()\` operator can automatically infer the current component's or directive's \`DestroyRef\` only when called within an injection context — specifically in constructors or field initializers of classes decorated with \`@Component\`, \`@Directive\`, \`@Injectable\`, \`@Pipe\`, or \`@Service\`, or in factory functions (\`useFactory\` in providers or \`factory\` in InjectionTokens). When used in lifecycle methods like \`ngOnInit()\` or \`ngAfterViewInit()\`, in regular methods, in constructors of plain classes not managed by Angular's DI system, or in \`@NgModule\` classes (which don't support the \`ngOnDestroy\` lifecycle), the injection context is not available, and \`takeUntilDestroyed()\` will throw a runtime error: "NG0203: inject() must be called from an injection context." To fix this, inject \`DestroyRef\` using \`inject(DestroyRef)\` and pass it explicitly: \`takeUntilDestroyed(this.destroyRef)\`.`,
|
|
83
166
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-inject.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-inject.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AAEzB,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC;AACxC,eAAO,MAAM,SAAS,kBAAkB,CAAC;;;;AAEzC,
|
|
1
|
+
{"version":3,"file":"prefer-inject.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-inject.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AAEzB,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC;AACxC,eAAO,MAAM,SAAS,kBAAkB,CAAC;;;;AAEzC,wBAmGG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type Options = [];
|
|
2
|
-
export type MessageIds = 'preferOnPushComponentChangeDetection' | '
|
|
2
|
+
export type MessageIds = 'preferOnPushComponentChangeDetection' | 'suggestRemoveChangeDetection';
|
|
3
3
|
export declare const RULE_NAME = "prefer-on-push-component-change-detection";
|
|
4
4
|
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
5
5
|
name: string;
|
|
@@ -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":"AASA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,
|
|
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":"AASA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GACpB,sCAAsC,GAAG,8BAA8B,CAAC;AAC1E,eAAO,MAAM,SAAS,8CAA8C,CAAC;;;;AAKrE,wBAoEG;AAEH,eAAO,MAAM,mBAAmB;;CAE/B,CAAC"}
|
|
@@ -11,54 +11,48 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
11
11
|
meta: {
|
|
12
12
|
type: 'suggestion',
|
|
13
13
|
docs: {
|
|
14
|
-
description: `Ensures
|
|
14
|
+
description: `Ensures components do not opt out of the default \`${STRATEGY_ON_PUSH}\` change detection strategy`,
|
|
15
|
+
recommended: 'recommended',
|
|
15
16
|
},
|
|
16
17
|
hasSuggestions: true,
|
|
17
18
|
schema: [],
|
|
18
19
|
messages: {
|
|
19
|
-
preferOnPushComponentChangeDetection: `
|
|
20
|
-
|
|
20
|
+
preferOnPushComponentChangeDetection: `Components should not opt out of the default \`${STRATEGY_ON_PUSH}\` change detection strategy`,
|
|
21
|
+
suggestRemoveChangeDetection: `Remove \`${METADATA_PROPERTY_NAME}\` to use the default (\`${STRATEGY_ON_PUSH}\`)`,
|
|
21
22
|
},
|
|
22
23
|
},
|
|
23
24
|
defaultOptions: [],
|
|
24
25
|
create(context) {
|
|
26
|
+
const sourceCode = context.sourceCode;
|
|
25
27
|
const changeDetectionMetadataProperty = utils_1.Selectors.metadataProperty(METADATA_PROPERTY_NAME);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
/**
|
|
29
|
+
* As of Angular v22 OnPush is the default change detection strategy, so a
|
|
30
|
+
* component only needs to be reported when it explicitly opts out of it by
|
|
31
|
+
* setting a non-OnPush `ChangeDetectionStrategy` member (e.g. `Eager`, or
|
|
32
|
+
* the deprecated `Default`). Omitting `changeDetection` entirely, or setting
|
|
33
|
+
* it to `ChangeDetectionStrategy.OnPush`, is the desired default and is not
|
|
34
|
+
* reported.
|
|
35
|
+
*/
|
|
36
|
+
const onPushOptOutProperty = `${utils_1.Selectors.COMPONENT_CLASS_DECORATOR} > CallExpression > ObjectExpression > ${changeDetectionMetadataProperty}[value.object.name='ChangeDetectionStrategy'][value.property.name!='OnPush']`;
|
|
32
37
|
return {
|
|
33
|
-
[
|
|
38
|
+
[onPushOptOutProperty](node) {
|
|
39
|
+
const { value } = node;
|
|
40
|
+
// The selector guarantees a `ChangeDetectionStrategy.<member>` value;
|
|
41
|
+
// this narrows the type for the fixer.
|
|
42
|
+
if (!utils_1.ASTUtils.isMemberExpression(value)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
34
45
|
context.report({
|
|
35
|
-
node:
|
|
46
|
+
node: value.property,
|
|
36
47
|
messageId: 'preferOnPushComponentChangeDetection',
|
|
37
48
|
suggest: [
|
|
38
49
|
{
|
|
39
|
-
messageId: '
|
|
50
|
+
messageId: 'suggestRemoveChangeDetection',
|
|
40
51
|
fix: (fixer) => {
|
|
41
|
-
|
|
42
|
-
return [
|
|
43
|
-
utils_1.RuleFixes.getImportAddFix({
|
|
44
|
-
fixer,
|
|
45
|
-
importName: 'ChangeDetectionStrategy',
|
|
46
|
-
moduleName: '@angular/core',
|
|
47
|
-
node: node.parent.parent.parent.parent,
|
|
48
|
-
}),
|
|
49
|
-
utils_1.ASTUtils.isMemberExpression(node.value)
|
|
50
|
-
? fixer.replaceText(node.value.property, 'OnPush')
|
|
51
|
-
: fixer.replaceText(node.value, STRATEGY_ON_PUSH),
|
|
52
|
-
].filter(utils_1.isNotNullOrUndefined);
|
|
53
|
-
}
|
|
52
|
+
const importDeclarations = utils_1.ASTUtils.getImportDeclarations(node, '@angular/core') ?? [];
|
|
54
53
|
return [
|
|
55
|
-
utils_1.RuleFixes.
|
|
56
|
-
|
|
57
|
-
importName: 'ChangeDetectionStrategy',
|
|
58
|
-
moduleName: '@angular/core',
|
|
59
|
-
node: node.parent,
|
|
60
|
-
}),
|
|
61
|
-
utils_1.RuleFixes.getDecoratorPropertyAddFix(node, fixer, `${METADATA_PROPERTY_NAME}: ${STRATEGY_ON_PUSH}`),
|
|
54
|
+
utils_1.RuleFixes.getNodeToCommaRemoveFix(sourceCode, node, fixer),
|
|
55
|
+
utils_1.RuleFixes.getImportRemoveFix(sourceCode, importDeclarations, 'ChangeDetectionStrategy', fixer),
|
|
62
56
|
].filter(utils_1.isNotNullOrUndefined);
|
|
63
57
|
},
|
|
64
58
|
},
|
|
@@ -68,14 +62,6 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
68
62
|
};
|
|
69
63
|
},
|
|
70
64
|
});
|
|
71
|
-
function nodeToReport(node) {
|
|
72
|
-
if (!utils_1.ASTUtils.isProperty(node)) {
|
|
73
|
-
return node;
|
|
74
|
-
}
|
|
75
|
-
return utils_1.ASTUtils.isMemberExpression(node.value)
|
|
76
|
-
? node.value.property
|
|
77
|
-
: node.value;
|
|
78
|
-
}
|
|
79
65
|
exports.RULE_DOCS_EXTENSION = {
|
|
80
|
-
rationale: `
|
|
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. 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. The previous default, \`ChangeDetectionStrategy.Default\`, has been renamed to \`ChangeDetectionStrategy.Eager\`. 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\nBecause omitting \`${METADATA_PROPERTY_NAME}\` (or setting it to \`${STRATEGY_ON_PUSH}\`) already gives you OnPush, this rule does not require you to declare it. Instead it reports components that explicitly opt out of OnPush by setting \`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. The suggestion removes the \`${METADATA_PROPERTY_NAME}\` property entirely (along with the now-unused \`ChangeDetectionStrategy\` import), relying on the v22 default rather than setting \`${STRATEGY_ON_PUSH}\` explicitly. 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).`,
|
|
81
67
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'preferServiceDecorator';
|
|
3
|
+
export declare const RULE_NAME = "prefer-service-decorator";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferServiceDecorator", [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
9
|
+
rationale: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=prefer-service-decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-service-decorator.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-service-decorator.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AAEzB,MAAM,MAAM,UAAU,GAAG,wBAAwB,CAAC;AAElD,eAAO,MAAM,SAAS,6BAA6B,CAAC;;;;AAEpD,wBAwFG;AA0DH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_DOCS_EXTENSION = exports.RULE_NAME = void 0;
|
|
4
|
+
const utils_1 = require("@angular-eslint/utils");
|
|
5
|
+
const utils_2 = require("@typescript-eslint/utils");
|
|
6
|
+
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
7
|
+
exports.RULE_NAME = 'prefer-service-decorator';
|
|
8
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
9
|
+
name: exports.RULE_NAME,
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'suggestion',
|
|
12
|
+
docs: {
|
|
13
|
+
description: "Prefer the `@Service()` decorator over `@Injectable({ providedIn: 'root' })`",
|
|
14
|
+
},
|
|
15
|
+
fixable: 'code',
|
|
16
|
+
schema: [],
|
|
17
|
+
messages: {
|
|
18
|
+
preferServiceDecorator: "Use the `@Service()` decorator instead of `@Injectable({ providedIn: 'root' })`",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
return {
|
|
24
|
+
[utils_1.Selectors.INJECTABLE_CLASS_DECORATOR](node) {
|
|
25
|
+
const { expression } = node;
|
|
26
|
+
if (!utils_1.ASTUtils.isCallExpression(expression) ||
|
|
27
|
+
expression.arguments.length !== 1) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const [argument] = expression.arguments;
|
|
31
|
+
if (!utils_1.ASTUtils.isObjectExpression(argument)) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const migration = getMetadataMigration(argument);
|
|
35
|
+
if (!migration) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
context.report({
|
|
39
|
+
node,
|
|
40
|
+
messageId: 'preferServiceDecorator',
|
|
41
|
+
fix: (fixer) => {
|
|
42
|
+
const fixes = [
|
|
43
|
+
// `@Injectable(...)` => `@Service(...)`.
|
|
44
|
+
fixer.replaceText(expression.callee, 'Service'),
|
|
45
|
+
// Migrate the `Injectable` import to `Service`.
|
|
46
|
+
...utils_1.RuleFixes.getImportReplaceFix({
|
|
47
|
+
fixer,
|
|
48
|
+
fromName: 'Injectable',
|
|
49
|
+
moduleName: '@angular/core',
|
|
50
|
+
node,
|
|
51
|
+
sourceCode: context.sourceCode,
|
|
52
|
+
toName: 'Service',
|
|
53
|
+
}),
|
|
54
|
+
];
|
|
55
|
+
switch (migration.type) {
|
|
56
|
+
// Only `providedIn: 'root'`, so drop the whole metadata object.
|
|
57
|
+
case 'drop-metadata': {
|
|
58
|
+
fixes.push(fixer.remove(argument));
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
// Keep the factory, but rename `useFactory` => `factory` and drop
|
|
62
|
+
// `providedIn: 'root'` (which `@Service()` implies).
|
|
63
|
+
case 'rewrite-factory': {
|
|
64
|
+
fixes.push(fixer.replaceText(migration.useFactoryProperty.key, 'factory'), utils_1.RuleFixes.getNodeToCommaRemoveFix(context.sourceCode, migration.providedInProperty, fixer));
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return fixes.filter(utils_1.isNotNullOrUndefined);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
/**
|
|
76
|
+
* `@Service()` only models `providedIn: 'root'` (implicit) and accepts a
|
|
77
|
+
* `factory` (which maps from `useFactory`). Any other provider metadata
|
|
78
|
+
* (`useClass`, `useExisting`, `useValue`, `deps`, ...) has no equivalent, so we
|
|
79
|
+
* bail out and leave such `@Injectable`s untouched for now.
|
|
80
|
+
*/
|
|
81
|
+
function getMetadataMigration(argument) {
|
|
82
|
+
const properties = argument.properties.filter((property) => property.type === utils_2.AST_NODE_TYPES.Property);
|
|
83
|
+
const providedInProperty = properties.find((property) => utils_2.ASTUtils.getPropertyName(property) === 'providedIn');
|
|
84
|
+
const useFactoryProperty = properties.find((property) => utils_2.ASTUtils.getPropertyName(property) === 'useFactory');
|
|
85
|
+
// Bail out unless the only metadata is `providedIn: 'root'` plus an optional
|
|
86
|
+
// `useFactory`.
|
|
87
|
+
if (!providedInProperty ||
|
|
88
|
+
utils_2.ASTUtils.getStaticValue(providedInProperty.value)?.value !==
|
|
89
|
+
'root' ||
|
|
90
|
+
properties.length !==
|
|
91
|
+
[providedInProperty, useFactoryProperty].filter(utils_1.isNotNullOrUndefined)
|
|
92
|
+
.length) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
return useFactoryProperty
|
|
96
|
+
? { type: 'rewrite-factory', providedInProperty, useFactoryProperty }
|
|
97
|
+
: { type: 'drop-metadata' };
|
|
98
|
+
}
|
|
99
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
100
|
+
rationale: "Angular 22 introduced the `@Service()` decorator as a concise shorthand for `@Injectable({ providedIn: 'root' })`, the most common way to declare a tree-shakable, application-wide service. Using `@Service()` removes the boilerplate of the metadata object while expressing the exact same intent. Because `providedIn: 'root'` is exactly what `@Service()` implies, this migration is safe and behavior-preserving: the `providedIn: 'root'` entry is dropped, and a `useFactory` is preserved by renaming it to `@Service()`'s `factory` option. Services that rely on other provider metadata (`useClass`, `useExisting`, `useValue`, `deps`) or a different `providedIn` value (`'platform'`, `'any'`, a specific module) have no `@Service()` equivalent and are intentionally left untouched.",
|
|
101
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-component-view-encapsulation.d.ts","sourceRoot":"","sources":["../../src/rules/use-component-view-encapsulation.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"use-component-view-encapsulation.d.ts","sourceRoot":"","sources":["../../src/rules/use-component-view-encapsulation.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GACpB,+BAA+B,GAAG,oCAAoC,CAAC;AACzE,eAAO,MAAM,SAAS,qCAAqC,CAAC;;;;AAG5D,wBAwDG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@depup/angular-eslint__eslint-plugin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "22.1.0-depup.1",
|
|
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",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"url": "https://github.com/angular-eslint/angular-eslint.git",
|
|
11
11
|
"directory": "packages/eslint-plugin"
|
|
12
12
|
},
|
|
13
|
+
"bugs": "https://github.com/angular-eslint/angular-eslint/issues",
|
|
13
14
|
"files": [
|
|
14
15
|
"dist",
|
|
15
16
|
"!**/*.tsbuildinfo",
|
|
@@ -20,15 +21,15 @@
|
|
|
20
21
|
],
|
|
21
22
|
"dependencies": {
|
|
22
23
|
"ts-api-utils": "^2.5.0",
|
|
23
|
-
"@angular-eslint/bundled-angular-compiler": "
|
|
24
|
-
"@angular-eslint/utils": "
|
|
24
|
+
"@angular-eslint/bundled-angular-compiler": "22.1.0",
|
|
25
|
+
"@angular-eslint/utils": "22.1.0"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
|
-
"@angular-eslint/test-utils": "
|
|
28
|
+
"@angular-eslint/test-utils": "22.1.0"
|
|
28
29
|
},
|
|
29
30
|
"peerDependencies": {
|
|
30
|
-
"@typescript-eslint/utils": "^
|
|
31
|
-
"eslint": "^
|
|
31
|
+
"@typescript-eslint/utils": "^8.0.0",
|
|
32
|
+
"eslint": "^9.0.0 || ^10.0.0",
|
|
32
33
|
"typescript": "*"
|
|
33
34
|
},
|
|
34
35
|
"gitHead": "e2006e5e9c99e5a943d1a999e0efa5247d29ec24",
|
|
@@ -49,8 +50,8 @@
|
|
|
49
50
|
},
|
|
50
51
|
"depsUpdated": 1,
|
|
51
52
|
"originalPackage": "@angular-eslint/eslint-plugin",
|
|
52
|
-
"originalVersion": "
|
|
53
|
-
"processedAt": "2026-
|
|
53
|
+
"originalVersion": "22.1.0",
|
|
54
|
+
"processedAt": "2026-07-21T19:24:01.669Z",
|
|
54
55
|
"smokeTest": "passed"
|
|
55
56
|
}
|
|
56
57
|
}
|
package/dist/configs/README.md
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# Premade configs
|
|
2
|
-
|
|
3
|
-
These configs exist for your convenience. They contain configuration intended to save you time and effort when configuring your project by disabling rules known to conflict with this repository, or cause issues in Angular codebases.
|
|
4
|
-
|
|
5
|
-
## `all`
|
|
6
|
-
|
|
7
|
-
Quite simply, this enables all the possible rules from `@angular-eslint/eslint-plugin`. It is unlikely this will be applicable in real-world projects, but some folks find this useful to have as a reference.
|
|
8
|
-
|
|
9
|
-
## `recommended`
|
|
10
|
-
|
|
11
|
-
The recommended set is an **_opinionated_** set of Angular-specific rules that we think you should use because:
|
|
12
|
-
|
|
13
|
-
1. They help you adhere to Angular best practices.
|
|
14
|
-
2. They help catch probable issue vectors in your code.
|
|
15
|
-
|
|
16
|
-
That being said, it is not the only way to use `@angular-eslint/eslint-plugin`, nor is it the way that will necessarily work 100% for your project/company. It has been built based off of two main things:
|
|
17
|
-
|
|
18
|
-
1. Angular best practices collected and collated from places like:
|
|
19
|
-
- [Angular repo](https://github.com/angular/angular).
|
|
20
|
-
- [Angular documentation](https://angular.dev).
|
|
21
|
-
- Advice from the Angular Team at Google.
|
|
22
|
-
2. The combined state of community contributed rulesets at the time of creation.
|
|
23
|
-
|
|
24
|
-
It is strongly encouraged to combine the recommended Angular rules with the recommended configs from `typescript-eslint` (https://typescript-eslint.io/linting/configs/#recommended-configurations), and this is what our schematics will generate for you automatically.
|
|
25
|
-
|
|
26
|
-
We will not change/add new rules to the recommended set unless we release a major package version (i.e. it is seen as a breaking change).
|
|
27
|
-
|
|
28
|
-
### Altering the recommended set to suit your project
|
|
29
|
-
|
|
30
|
-
If you disagree with a rule (or it disagrees with your codebase), consider using your local config to change the rule config so it works for your project.
|
|
31
|
-
|
|
32
|
-
```cjson
|
|
33
|
-
{
|
|
34
|
-
"extends": ["plugin:@angular-eslint/recommended"],
|
|
35
|
-
"rules": {
|
|
36
|
-
// our project thinks using renaming inputs is ok
|
|
37
|
-
"@angular-eslint/no-input-rename": "off"
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Suggesting changes to the recommended set
|
|
43
|
-
|
|
44
|
-
If you feel _very_, **very**, **_very_** strongly that a specific rule should (or should not) be in the recommended ruleset, please feel free to file an issue along with a **detailed** argument explaining your reasoning. We expect to see you citing concrete evidence supporting why (or why not) a rule is considered best practice. **Please note that if your reasoning is along the lines of "it's what my project/company does", or "I don't like the rule", then we will likely close the request without discussion.**
|
package/dist/configs/all.json
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"parser": "@typescript-eslint/parser",
|
|
3
|
-
"plugins": ["@angular-eslint"],
|
|
4
|
-
"rules": {
|
|
5
|
-
"@angular-eslint/component-class-suffix": "error",
|
|
6
|
-
"@angular-eslint/component-max-inline-declarations": "error",
|
|
7
|
-
"@angular-eslint/component-selector": "error",
|
|
8
|
-
"@angular-eslint/computed-must-return": "error",
|
|
9
|
-
"@angular-eslint/consistent-component-styles": "error",
|
|
10
|
-
"@angular-eslint/contextual-decorator": "error",
|
|
11
|
-
"@angular-eslint/contextual-lifecycle": "error",
|
|
12
|
-
"@angular-eslint/directive-class-suffix": "error",
|
|
13
|
-
"@angular-eslint/directive-selector": "error",
|
|
14
|
-
"@angular-eslint/no-async-lifecycle-method": "error",
|
|
15
|
-
"@angular-eslint/no-attribute-decorator": "error",
|
|
16
|
-
"@angular-eslint/no-developer-preview": "error",
|
|
17
|
-
"@angular-eslint/no-duplicates-in-metadata-arrays": "error",
|
|
18
|
-
"@angular-eslint/no-empty-lifecycle-method": "error",
|
|
19
|
-
"@angular-eslint/no-experimental": "error",
|
|
20
|
-
"@angular-eslint/no-forward-ref": "error",
|
|
21
|
-
"@angular-eslint/no-implicit-take-until-destroyed": "error",
|
|
22
|
-
"@angular-eslint/no-input-prefix": "error",
|
|
23
|
-
"@angular-eslint/no-input-rename": "error",
|
|
24
|
-
"@angular-eslint/no-inputs-metadata-property": "error",
|
|
25
|
-
"@angular-eslint/no-lifecycle-call": "error",
|
|
26
|
-
"@angular-eslint/no-output-native": "error",
|
|
27
|
-
"@angular-eslint/no-output-on-prefix": "error",
|
|
28
|
-
"@angular-eslint/no-output-rename": "error",
|
|
29
|
-
"@angular-eslint/no-outputs-metadata-property": "error",
|
|
30
|
-
"@angular-eslint/no-pipe-impure": "error",
|
|
31
|
-
"@angular-eslint/no-queries-metadata-property": "error",
|
|
32
|
-
"@angular-eslint/no-uncalled-signals": "error",
|
|
33
|
-
"@angular-eslint/pipe-prefix": "error",
|
|
34
|
-
"@angular-eslint/prefer-host-metadata-property": "error",
|
|
35
|
-
"@angular-eslint/prefer-inject": "error",
|
|
36
|
-
"@angular-eslint/prefer-on-push-component-change-detection": "error",
|
|
37
|
-
"@angular-eslint/prefer-output-emitter-ref": "error",
|
|
38
|
-
"@angular-eslint/prefer-output-readonly": "error",
|
|
39
|
-
"@angular-eslint/prefer-signal-model": "error",
|
|
40
|
-
"@angular-eslint/prefer-signals": "error",
|
|
41
|
-
"@angular-eslint/prefer-standalone": "error",
|
|
42
|
-
"@angular-eslint/relative-url-prefix": "error",
|
|
43
|
-
"@angular-eslint/require-lifecycle-on-prototype": "error",
|
|
44
|
-
"@angular-eslint/require-localize-metadata": "error",
|
|
45
|
-
"@angular-eslint/runtime-localize": "error",
|
|
46
|
-
"@angular-eslint/sort-keys-in-type-decorator": "error",
|
|
47
|
-
"@angular-eslint/sort-lifecycle-methods": "error",
|
|
48
|
-
"@angular-eslint/use-component-selector": "error",
|
|
49
|
-
"@angular-eslint/use-component-view-encapsulation": "error",
|
|
50
|
-
"@angular-eslint/use-injectable-provided-in": "error",
|
|
51
|
-
"@angular-eslint/use-lifecycle-interface": "error",
|
|
52
|
-
"@angular-eslint/use-pipe-transform-interface": "error"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"parser": "@typescript-eslint/parser",
|
|
3
|
-
"plugins": ["@angular-eslint"],
|
|
4
|
-
"rules": {
|
|
5
|
-
"@angular-eslint/contextual-lifecycle": "error",
|
|
6
|
-
"@angular-eslint/no-empty-lifecycle-method": "error",
|
|
7
|
-
"@angular-eslint/no-input-rename": "error",
|
|
8
|
-
"@angular-eslint/no-inputs-metadata-property": "error",
|
|
9
|
-
"@angular-eslint/no-output-native": "error",
|
|
10
|
-
"@angular-eslint/no-output-on-prefix": "error",
|
|
11
|
-
"@angular-eslint/no-output-rename": "error",
|
|
12
|
-
"@angular-eslint/no-outputs-metadata-property": "error",
|
|
13
|
-
"@angular-eslint/prefer-inject": "error",
|
|
14
|
-
"@angular-eslint/prefer-standalone": "error",
|
|
15
|
-
"@angular-eslint/use-pipe-transform-interface": "error",
|
|
16
|
-
"@angular-eslint/use-lifecycle-interface": "warn"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export type Options = [];
|
|
2
|
-
export type MessageIds = 'noConflictingLifecycleInterface' | 'noConflictingLifecycleMethod';
|
|
3
|
-
export declare const RULE_NAME = "no-conflicting-lifecycle";
|
|
4
|
-
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
5
|
-
name: string;
|
|
6
|
-
};
|
|
7
|
-
export default _default;
|
|
8
|
-
export declare const RULE_DOCS_EXTENSION: {
|
|
9
|
-
rationale: string;
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=no-conflicting-lifecycle.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"no-conflicting-lifecycle.d.ts","sourceRoot":"","sources":["../../src/rules/no-conflicting-lifecycle.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAClB,iCAAiC,GACjC,8BAA8B,CAAC;AACnC,eAAO,MAAM,SAAS,6BAA6B,CAAC;;;;AAWpD,wBA8EG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RULE_DOCS_EXTENSION = exports.RULE_NAME = void 0;
|
|
4
|
-
const utils_1 = require("@angular-eslint/utils");
|
|
5
|
-
const utils_2 = require("@typescript-eslint/utils");
|
|
6
|
-
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
7
|
-
exports.RULE_NAME = 'no-conflicting-lifecycle';
|
|
8
|
-
const LIFECYCLE_INTERFACES = [
|
|
9
|
-
utils_1.ASTUtils.AngularLifecycleInterfaces.DoCheck,
|
|
10
|
-
utils_1.ASTUtils.AngularLifecycleInterfaces.OnChanges,
|
|
11
|
-
];
|
|
12
|
-
const LIFECYCLE_METHODS = [
|
|
13
|
-
utils_1.ASTUtils.AngularLifecycleMethods.ngDoCheck,
|
|
14
|
-
utils_1.ASTUtils.AngularLifecycleMethods.ngOnChanges,
|
|
15
|
-
];
|
|
16
|
-
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
17
|
-
name: exports.RULE_NAME,
|
|
18
|
-
meta: {
|
|
19
|
-
type: 'suggestion',
|
|
20
|
-
deprecated: true,
|
|
21
|
-
docs: {
|
|
22
|
-
description: 'Ensures that directives do not implement conflicting lifecycle interfaces.',
|
|
23
|
-
},
|
|
24
|
-
schema: [],
|
|
25
|
-
messages: {
|
|
26
|
-
noConflictingLifecycleInterface: `Implementing ${utils_1.ASTUtils.AngularLifecycleInterfaces.DoCheck} and ${utils_1.ASTUtils.AngularLifecycleInterfaces.OnChanges} in a class is not recommended`,
|
|
27
|
-
noConflictingLifecycleMethod: `Declaring ${utils_1.ASTUtils.AngularLifecycleMethods.ngDoCheck} and ${utils_1.ASTUtils.AngularLifecycleMethods.ngOnChanges} method in a class is not recommended`,
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
defaultOptions: [],
|
|
31
|
-
create(context) {
|
|
32
|
-
const validateInterfaces = (node) => {
|
|
33
|
-
const declaredAngularLifecycleInterfaces = utils_1.ASTUtils.getDeclaredAngularLifecycleInterfaces(node);
|
|
34
|
-
const hasInterfaceConflictingLifecycle = LIFECYCLE_INTERFACES.every((lifecycleInterface) => declaredAngularLifecycleInterfaces.includes(lifecycleInterface));
|
|
35
|
-
if (!hasInterfaceConflictingLifecycle)
|
|
36
|
-
return;
|
|
37
|
-
const declaredInterfaces = utils_1.ASTUtils.getInterfaces(node);
|
|
38
|
-
const declaredAngularLifecycleInterfacesNodes = declaredInterfaces.filter((node) => {
|
|
39
|
-
const interfaceName = utils_1.ASTUtils.getInterfaceName(node);
|
|
40
|
-
return (interfaceName && utils_1.ASTUtils.isAngularLifecycleInterface(interfaceName));
|
|
41
|
-
});
|
|
42
|
-
for (const interFaceNode of declaredAngularLifecycleInterfacesNodes) {
|
|
43
|
-
context.report({
|
|
44
|
-
node: interFaceNode,
|
|
45
|
-
messageId: 'noConflictingLifecycleInterface',
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
const validateMethods = (node) => {
|
|
50
|
-
const declaredAngularLifecycleMethods = utils_1.ASTUtils.getDeclaredAngularLifecycleMethods(node);
|
|
51
|
-
const hasMethodConflictingLifecycle = LIFECYCLE_METHODS.every((lifecycleMethod) => declaredAngularLifecycleMethods.includes(lifecycleMethod));
|
|
52
|
-
if (!hasMethodConflictingLifecycle)
|
|
53
|
-
return;
|
|
54
|
-
const declaredMethods = utils_1.ASTUtils.getDeclaredMethods(node);
|
|
55
|
-
const declaredAngularLifecycleMethodNodes = declaredMethods.filter((node) => utils_2.ASTUtils.isIdentifier(node.key) &&
|
|
56
|
-
utils_1.ASTUtils.isAngularLifecycleMethod(node.key.name));
|
|
57
|
-
for (const method of declaredAngularLifecycleMethodNodes) {
|
|
58
|
-
context.report({
|
|
59
|
-
node: method,
|
|
60
|
-
messageId: 'noConflictingLifecycleMethod',
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
return {
|
|
65
|
-
ClassDeclaration(node) {
|
|
66
|
-
validateInterfaces(node);
|
|
67
|
-
validateMethods(node);
|
|
68
|
-
},
|
|
69
|
-
};
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
exports.RULE_DOCS_EXTENSION = {
|
|
73
|
-
rationale: 'This rule was created with the intent to prevent potential issues when both DoCheck and OnChanges lifecycle hooks are implemented together, as they both deal with change detection and could create confusing or duplicated logic. However, the rule has proven to be overly broad in practice. It triggers whenever a component or directive implements both hooks, regardless of whether they actually conflict or track the same data. In reality, there are legitimate use cases where both hooks can coexist without issues—for instance, using OnChanges to respond to specific input changes while using DoCheck for custom change detection logic on different data. Because the rule cannot accurately determine whether the hooks are actually conflicting (i.e., checking the same variables), it produces false positives and has been removed from the recommended configuration. While the rule was well-intentioned, it should not be relied upon in practice.',
|
|
74
|
-
};
|