@depup/angular-eslint__eslint-plugin 21.3.1-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/LICENSE +21 -0
- package/README.md +31 -0
- package/changes.json +10 -0
- package/dist/configs/README.md +44 -0
- package/dist/configs/all.json +54 -0
- package/dist/configs/recommended.json +18 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +145 -0
- package/dist/rules/component-class-suffix.d.ts +15 -0
- package/dist/rules/component-class-suffix.d.ts.map +1 -0
- package/dist/rules/component-class-suffix.js +56 -0
- package/dist/rules/component-max-inline-declarations.d.ts +17 -0
- package/dist/rules/component-max-inline-declarations.d.ts.map +1 -0
- package/dist/rules/component-max-inline-declarations.js +107 -0
- package/dist/rules/component-selector.d.ts +14 -0
- package/dist/rules/component-selector.d.ts.map +1 -0
- package/dist/rules/component-selector.js +218 -0
- package/dist/rules/computed-must-return.d.ts +11 -0
- package/dist/rules/computed-must-return.d.ts.map +1 -0
- package/dist/rules/computed-must-return.js +62 -0
- package/dist/rules/consistent-component-styles.d.ts +12 -0
- package/dist/rules/consistent-component-styles.d.ts.map +1 -0
- package/dist/rules/consistent-component-styles.js +97 -0
- package/dist/rules/contextual-decorator.d.ts +12 -0
- package/dist/rules/contextual-decorator.d.ts.map +1 -0
- package/dist/rules/contextual-decorator.js +61 -0
- package/dist/rules/contextual-lifecycle.d.ts +11 -0
- package/dist/rules/contextual-lifecycle.d.ts.map +1 -0
- package/dist/rules/contextual-lifecycle.js +61 -0
- package/dist/rules/directive-class-suffix.d.ts +13 -0
- package/dist/rules/directive-class-suffix.d.ts.map +1 -0
- package/dist/rules/directive-class-suffix.js +60 -0
- package/dist/rules/directive-selector.d.ts +14 -0
- package/dist/rules/directive-selector.d.ts.map +1 -0
- package/dist/rules/directive-selector.js +156 -0
- package/dist/rules/no-async-lifecycle-method.d.ts +11 -0
- package/dist/rules/no-async-lifecycle-method.d.ts.map +1 -0
- package/dist/rules/no-async-lifecycle-method.js +45 -0
- package/dist/rules/no-attribute-decorator.d.ts +11 -0
- package/dist/rules/no-attribute-decorator.d.ts.map +1 -0
- package/dist/rules/no-attribute-decorator.js +32 -0
- package/dist/rules/no-conflicting-lifecycle.d.ts +11 -0
- package/dist/rules/no-conflicting-lifecycle.d.ts.map +1 -0
- package/dist/rules/no-conflicting-lifecycle.js +74 -0
- package/dist/rules/no-developer-preview.d.ts +11 -0
- package/dist/rules/no-developer-preview.d.ts.map +1 -0
- package/dist/rules/no-developer-preview.js +45 -0
- package/dist/rules/no-duplicates-in-metadata-arrays.d.ts +11 -0
- package/dist/rules/no-duplicates-in-metadata-arrays.d.ts.map +1 -0
- package/dist/rules/no-duplicates-in-metadata-arrays.js +67 -0
- package/dist/rules/no-empty-lifecycle-method.d.ts +11 -0
- package/dist/rules/no-empty-lifecycle-method.d.ts.map +1 -0
- package/dist/rules/no-empty-lifecycle-method.js +75 -0
- package/dist/rules/no-experimental.d.ts +11 -0
- package/dist/rules/no-experimental.d.ts.map +1 -0
- package/dist/rules/no-experimental.js +45 -0
- package/dist/rules/no-forward-ref.d.ts +12 -0
- package/dist/rules/no-forward-ref.d.ts.map +1 -0
- package/dist/rules/no-forward-ref.js +33 -0
- package/dist/rules/no-implicit-take-until-destroyed.d.ts +11 -0
- package/dist/rules/no-implicit-take-until-destroyed.d.ts.map +1 -0
- package/dist/rules/no-implicit-take-until-destroyed.js +83 -0
- package/dist/rules/no-input-prefix.d.ts +13 -0
- package/dist/rules/no-input-prefix.d.ts.map +1 -0
- package/dist/rules/no-input-prefix.js +118 -0
- package/dist/rules/no-input-rename.d.ts +13 -0
- package/dist/rules/no-input-rename.d.ts.map +1 -0
- package/dist/rules/no-input-rename.js +213 -0
- package/dist/rules/no-inputs-metadata-property.d.ts +11 -0
- package/dist/rules/no-inputs-metadata-property.d.ts.map +1 -0
- package/dist/rules/no-inputs-metadata-property.js +53 -0
- package/dist/rules/no-lifecycle-call.d.ts +11 -0
- package/dist/rules/no-lifecycle-call.d.ts.map +1 -0
- package/dist/rules/no-lifecycle-call.js +49 -0
- package/dist/rules/no-output-native.d.ts +11 -0
- package/dist/rules/no-output-native.d.ts.map +1 -0
- package/dist/rules/no-output-native.js +47 -0
- package/dist/rules/no-output-on-prefix.d.ts +11 -0
- package/dist/rules/no-output-on-prefix.d.ts.map +1 -0
- package/dist/rules/no-output-on-prefix.js +48 -0
- package/dist/rules/no-output-rename.d.ts +11 -0
- package/dist/rules/no-output-rename.d.ts.map +1 -0
- package/dist/rules/no-output-rename.js +172 -0
- package/dist/rules/no-outputs-metadata-property.d.ts +11 -0
- package/dist/rules/no-outputs-metadata-property.d.ts.map +1 -0
- package/dist/rules/no-outputs-metadata-property.js +53 -0
- package/dist/rules/no-pipe-impure.d.ts +11 -0
- package/dist/rules/no-pipe-impure.d.ts.map +1 -0
- package/dist/rules/no-pipe-impure.js +42 -0
- package/dist/rules/no-queries-metadata-property.d.ts +11 -0
- package/dist/rules/no-queries-metadata-property.d.ts.map +1 -0
- package/dist/rules/no-queries-metadata-property.js +38 -0
- package/dist/rules/no-uncalled-signals.d.ts +12 -0
- package/dist/rules/no-uncalled-signals.d.ts.map +1 -0
- package/dist/rules/no-uncalled-signals.js +92 -0
- package/dist/rules/pipe-prefix.d.ts +15 -0
- package/dist/rules/pipe-prefix.d.ts.map +1 -0
- package/dist/rules/pipe-prefix.js +92 -0
- package/dist/rules/prefer-host-metadata-property.d.ts +10 -0
- package/dist/rules/prefer-host-metadata-property.d.ts.map +1 -0
- package/dist/rules/prefer-host-metadata-property.js +40 -0
- package/dist/rules/prefer-inject.d.ts +11 -0
- package/dist/rules/prefer-inject.d.ts.map +1 -0
- package/dist/rules/prefer-inject.js +86 -0
- package/dist/rules/prefer-on-push-component-change-detection.d.ts +11 -0
- package/dist/rules/prefer-on-push-component-change-detection.d.ts.map +1 -0
- package/dist/rules/prefer-on-push-component-change-detection.js +81 -0
- package/dist/rules/prefer-output-emitter-ref.d.ts +10 -0
- package/dist/rules/prefer-output-emitter-ref.d.ts.map +1 -0
- package/dist/rules/prefer-output-emitter-ref.js +30 -0
- package/dist/rules/prefer-output-readonly.d.ts +11 -0
- package/dist/rules/prefer-output-readonly.d.ts.map +1 -0
- package/dist/rules/prefer-output-readonly.js +51 -0
- package/dist/rules/prefer-signal-model.d.ts +10 -0
- package/dist/rules/prefer-signal-model.d.ts.map +1 -0
- package/dist/rules/prefer-signal-model.js +71 -0
- package/dist/rules/prefer-signals.d.ts +20 -0
- package/dist/rules/prefer-signals.d.ts.map +1 -0
- package/dist/rules/prefer-signals.js +174 -0
- package/dist/rules/prefer-standalone.d.ts +11 -0
- package/dist/rules/prefer-standalone.d.ts.map +1 -0
- package/dist/rules/prefer-standalone.js +66 -0
- package/dist/rules/relative-url-prefix.d.ts +11 -0
- package/dist/rules/relative-url-prefix.d.ts.map +1 -0
- package/dist/rules/relative-url-prefix.js +59 -0
- package/dist/rules/require-lifecycle-on-prototype.d.ts +11 -0
- package/dist/rules/require-lifecycle-on-prototype.d.ts.map +1 -0
- package/dist/rules/require-lifecycle-on-prototype.js +72 -0
- package/dist/rules/require-localize-metadata.d.ts +17 -0
- package/dist/rules/require-localize-metadata.d.ts.map +1 -0
- package/dist/rules/require-localize-metadata.js +123 -0
- package/dist/rules/runtime-localize.d.ts +11 -0
- package/dist/rules/runtime-localize.d.ts.map +1 -0
- package/dist/rules/runtime-localize.js +45 -0
- package/dist/rules/sort-keys-in-type-decorator.d.ts +16 -0
- package/dist/rules/sort-keys-in-type-decorator.d.ts.map +1 -0
- package/dist/rules/sort-keys-in-type-decorator.js +191 -0
- package/dist/rules/sort-lifecycle-methods.d.ts +11 -0
- package/dist/rules/sort-lifecycle-methods.d.ts.map +1 -0
- package/dist/rules/sort-lifecycle-methods.js +45 -0
- package/dist/rules/use-component-selector.d.ts +11 -0
- package/dist/rules/use-component-selector.d.ts.map +1 -0
- package/dist/rules/use-component-selector.js +41 -0
- package/dist/rules/use-component-view-encapsulation.d.ts +11 -0
- package/dist/rules/use-component-view-encapsulation.d.ts.map +1 -0
- package/dist/rules/use-component-view-encapsulation.js +49 -0
- package/dist/rules/use-injectable-provided-in.d.ts +13 -0
- package/dist/rules/use-injectable-provided-in.d.ts.map +1 -0
- package/dist/rules/use-injectable-provided-in.js +63 -0
- package/dist/rules/use-lifecycle-interface.d.ts +11 -0
- package/dist/rules/use-lifecycle-interface.d.ts.map +1 -0
- package/dist/rules/use-lifecycle-interface.js +65 -0
- package/dist/rules/use-pipe-transform-interface.d.ts +11 -0
- package/dist/rules/use-pipe-transform-interface.d.ts.map +1 -0
- package/dist/rules/use-pipe-transform-interface.js +49 -0
- package/dist/utils/create-eslint-rule.d.ts +8 -0
- package/dist/utils/create-eslint-rule.d.ts.map +1 -0
- package/dist/utils/create-eslint-rule.js +28 -0
- package/dist/utils/jsdoc.d.ts +11 -0
- package/dist/utils/jsdoc.d.ts.map +1 -0
- package/dist/utils/jsdoc.js +174 -0
- package/dist/utils/signals.d.ts +2 -0
- package/dist/utils/signals.d.ts.map +1 -0
- package/dist/utils/signals.js +10 -0
- package/package.json +56 -0
|
@@ -0,0 +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,wBAkGG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
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-inject';
|
|
8
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
9
|
+
name: exports.RULE_NAME,
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'suggestion',
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Prefer using the inject() function over constructor parameter injection',
|
|
14
|
+
recommended: 'recommended',
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
messages: {
|
|
18
|
+
preferInject: "Prefer using the inject() function over constructor parameter injection. Use Angular's migration schematic to automatically refactor: ng generate @angular/core:inject",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
const angularDecoratorsPattern = (0, utils_1.toPattern)([
|
|
24
|
+
'Component',
|
|
25
|
+
'Directive',
|
|
26
|
+
'Injectable',
|
|
27
|
+
'Pipe',
|
|
28
|
+
]);
|
|
29
|
+
function shouldReportParameter(param) {
|
|
30
|
+
let actualParam = param;
|
|
31
|
+
let hasModifier = false;
|
|
32
|
+
if (param.type === utils_2.AST_NODE_TYPES.TSParameterProperty) {
|
|
33
|
+
actualParam = param.parameter;
|
|
34
|
+
hasModifier = true;
|
|
35
|
+
}
|
|
36
|
+
const decorators = (param.type === utils_2.AST_NODE_TYPES.TSParameterProperty
|
|
37
|
+
? param.parameter
|
|
38
|
+
: param).decorators;
|
|
39
|
+
if (decorators?.some((d) => {
|
|
40
|
+
const name = utils_1.ASTUtils.getDecoratorName(d);
|
|
41
|
+
return (name === 'Inject' ||
|
|
42
|
+
name === 'Optional' ||
|
|
43
|
+
name === 'Self' ||
|
|
44
|
+
name === 'SkipSelf' ||
|
|
45
|
+
name === 'Host');
|
|
46
|
+
})) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
if (hasModifier) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
const typeAnnotation = actualParam.typeAnnotation;
|
|
53
|
+
if (typeAnnotation) {
|
|
54
|
+
switch (typeAnnotation.typeAnnotation.type) {
|
|
55
|
+
case utils_2.AST_NODE_TYPES.TSStringKeyword:
|
|
56
|
+
case utils_2.AST_NODE_TYPES.TSNumberKeyword:
|
|
57
|
+
case utils_2.AST_NODE_TYPES.TSBooleanKeyword:
|
|
58
|
+
case utils_2.AST_NODE_TYPES.TSBigIntKeyword:
|
|
59
|
+
case utils_2.AST_NODE_TYPES.TSSymbolKeyword:
|
|
60
|
+
case utils_2.AST_NODE_TYPES.TSAnyKeyword:
|
|
61
|
+
case utils_2.AST_NODE_TYPES.TSUnknownKeyword:
|
|
62
|
+
return false;
|
|
63
|
+
default:
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
[`${utils_1.Selectors.decoratorDefinition(angularDecoratorsPattern)} > ClassBody > MethodDefinition[kind="constructor"]`](node) {
|
|
71
|
+
const params = node.value.params ?? [];
|
|
72
|
+
if (params.length === 0) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
for (const param of params) {
|
|
76
|
+
if (shouldReportParameter(param)) {
|
|
77
|
+
context.report({ node: param, messageId: 'preferInject' });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
85
|
+
rationale: "The inject() function is Angular's modern dependency injection API that offers several advantages over constructor-based injection. First, it enables dependency injection outside of constructors, allowing you to use DI in functions, factories, and even at the class field level. This makes code more flexible and composable. Second, inject() is more concise and reduces boilerplate - you don't need constructor parameter properties or manual field assignments. Third, inject() works naturally with modern TypeScript features and tree-shaking. Fourth, it's required for using many modern Angular features like functional guards, interceptors, and the new signal-based APIs. Angular provides an automated migration schematic (ng generate @angular/core:inject) to convert constructor injection to inject(), making adoption straightforward. This is the recommended approach for all new Angular code.",
|
|
86
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'preferOnPushComponentChangeDetection' | 'suggestAddChangeDetectionOnPush';
|
|
3
|
+
export declare const RULE_NAME = "prefer-on-push-component-change-detection";
|
|
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=prefer-on-push-component-change-detection.d.ts.map
|
|
@@ -0,0 +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,GAClB,sCAAsC,GACtC,iCAAiC,CAAC;AACtC,eAAO,MAAM,SAAS,8CAA8C,CAAC;;;;AAKrE,wBAuEG;AAYH,eAAO,MAAM,mBAAmB;;CAE/B,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
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 create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
6
|
+
exports.RULE_NAME = 'prefer-on-push-component-change-detection';
|
|
7
|
+
const METADATA_PROPERTY_NAME = 'changeDetection';
|
|
8
|
+
const STRATEGY_ON_PUSH = 'ChangeDetectionStrategy.OnPush';
|
|
9
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
10
|
+
name: exports.RULE_NAME,
|
|
11
|
+
meta: {
|
|
12
|
+
type: 'suggestion',
|
|
13
|
+
docs: {
|
|
14
|
+
description: `Ensures component's \`${METADATA_PROPERTY_NAME}\` is set to \`${STRATEGY_ON_PUSH}\``,
|
|
15
|
+
},
|
|
16
|
+
hasSuggestions: true,
|
|
17
|
+
schema: [],
|
|
18
|
+
messages: {
|
|
19
|
+
preferOnPushComponentChangeDetection: `The component's \`${METADATA_PROPERTY_NAME}\` value should be set to \`${STRATEGY_ON_PUSH}\``,
|
|
20
|
+
suggestAddChangeDetectionOnPush: `Add \`${STRATEGY_ON_PUSH}\``,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultOptions: [],
|
|
24
|
+
create(context) {
|
|
25
|
+
const changeDetectionMetadataProperty = utils_1.Selectors.metadataProperty(METADATA_PROPERTY_NAME);
|
|
26
|
+
const withoutChangeDetectionDecorator = `${utils_1.Selectors.COMPONENT_CLASS_DECORATOR}:matches([expression.arguments.length=0], [expression.arguments.0.type='ObjectExpression']:not(:has(${changeDetectionMetadataProperty})))`;
|
|
27
|
+
const nonChangeDetectionOnPushProperty = `${utils_1.Selectors.COMPONENT_CLASS_DECORATOR} > CallExpression > ObjectExpression > ${changeDetectionMetadataProperty}:matches([value.type='Identifier'][value.name='undefined'], [value.object.name='ChangeDetectionStrategy'][value.property.name!='OnPush'])`;
|
|
28
|
+
const selectors = [
|
|
29
|
+
withoutChangeDetectionDecorator,
|
|
30
|
+
nonChangeDetectionOnPushProperty,
|
|
31
|
+
].join(',');
|
|
32
|
+
return {
|
|
33
|
+
[selectors](node) {
|
|
34
|
+
context.report({
|
|
35
|
+
node: nodeToReport(node),
|
|
36
|
+
messageId: 'preferOnPushComponentChangeDetection',
|
|
37
|
+
suggest: [
|
|
38
|
+
{
|
|
39
|
+
messageId: 'suggestAddChangeDetectionOnPush',
|
|
40
|
+
fix: (fixer) => {
|
|
41
|
+
if (utils_1.ASTUtils.isProperty(node)) {
|
|
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
|
+
}
|
|
54
|
+
return [
|
|
55
|
+
utils_1.RuleFixes.getImportAddFix({
|
|
56
|
+
fixer,
|
|
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}`),
|
|
62
|
+
].filter(utils_1.isNotNullOrUndefined);
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
});
|
|
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
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
80
|
+
rationale: `By default, Angular's change detection checks every component on every change detection cycle, which can involve thousands of checks per second in a large application. OnPush change detection strategy is a performance optimization that tells Angular to only check a component when: (1) its input properties receive new references, (2) an event originates from the component or its children, or (3) change detection is manually triggered. This dramatically reduces the number of change detection runs, improving application performance. OnPush pairs well with immutable data patterns and Angular signals, and is considered a best practice for most components. However, you must be careful to use immutable data patterns (creating new object references when data changes) for OnPush to work correctly.`,
|
|
81
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type MessageIds = 'preferOutputEmitterRef';
|
|
2
|
+
export declare const RULE_NAME = "prefer-output-emitter-ref";
|
|
3
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferOutputEmitterRef", [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
export default _default;
|
|
7
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
8
|
+
rationale: string;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=prefer-output-emitter-ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-output-emitter-ref.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-output-emitter-ref.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,UAAU,GAAG,wBAAwB,CAAC;AAClD,eAAO,MAAM,SAAS,8BAA8B,CAAC;;;;AAErD,wBAqBG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
6
|
+
exports.RULE_NAME = 'prefer-output-emitter-ref';
|
|
7
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
8
|
+
name: exports.RULE_NAME,
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'suggestion',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Use `OutputEmitterRef` instead of `@Output()`',
|
|
13
|
+
},
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: {
|
|
16
|
+
preferOutputEmitterRef: 'Use `OutputEmitterRef` via `output()` for Component and Directive outputs rather than the legacy `@Output()` decorator',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultOptions: [],
|
|
20
|
+
create(context) {
|
|
21
|
+
return {
|
|
22
|
+
[utils_1.Selectors.OUTPUT_DECORATOR]: (node) => {
|
|
23
|
+
context.report({ node, messageId: 'preferOutputEmitterRef' });
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
29
|
+
rationale: "The output() function is Angular's modern API for component outputs, intended to be used instead of the @Output() decorator (which still exists for backwards compatibility). OutputEmitterRef (returned by output()) provides better type safety, integrates seamlessly with Angular's signal ecosystem, and offers a more functional programming approach. Unlike @Output() which uses EventEmitter, output() is specifically designed for component outputs and has a cleaner, more predictable API, that resembles other function based and signals APIs that have been added to the framework in recent versions.",
|
|
30
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'preferOutputReadonly' | 'suggestAddReadonlyModifier';
|
|
3
|
+
export declare const RULE_NAME = "prefer-output-readonly";
|
|
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=prefer-output-readonly.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-output-readonly.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-output-readonly.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,sBAAsB,GAAG,4BAA4B,CAAC;AAC/E,eAAO,MAAM,SAAS,2BAA2B,CAAC;;;;AAElD,wBAkDG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
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 create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
6
|
+
exports.RULE_NAME = 'prefer-output-readonly';
|
|
7
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
8
|
+
name: exports.RULE_NAME,
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'suggestion',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Prefer to declare `@Output`, `OutputEmitterRef` and `OutputRef` as `readonly` since they are not supposed to be reassigned',
|
|
13
|
+
},
|
|
14
|
+
hasSuggestions: true,
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
preferOutputReadonly: 'Prefer to declare `{{type}}` as `readonly` since they are not supposed to be reassigned',
|
|
18
|
+
suggestAddReadonlyModifier: 'Add `readonly` modifier',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
return {
|
|
24
|
+
[`PropertyDefinition:not([readonly=true]) > ${utils_1.Selectors.OUTPUT_DECORATOR}`]({ parent: { key }, }) {
|
|
25
|
+
report(key, '@Output');
|
|
26
|
+
},
|
|
27
|
+
[`PropertyDefinition:not([readonly=true]):matches([typeAnnotation.typeAnnotation.typeName.name=OutputEmitterRef], [value.callee.name=output])`]({ key, }) {
|
|
28
|
+
report(key, 'OutputEmitterRef');
|
|
29
|
+
},
|
|
30
|
+
[`PropertyDefinition:not([readonly=true]):matches([typeAnnotation.typeAnnotation.typeName.name=OutputRef], [value.callee.name=outputFromObservable])`]({ key, }) {
|
|
31
|
+
report(key, 'OutputRef');
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
function report(key, type) {
|
|
35
|
+
context.report({
|
|
36
|
+
node: key,
|
|
37
|
+
messageId: 'preferOutputReadonly',
|
|
38
|
+
data: { type },
|
|
39
|
+
suggest: [
|
|
40
|
+
{
|
|
41
|
+
messageId: 'suggestAddReadonlyModifier',
|
|
42
|
+
fix: (fixer) => fixer.insertTextBefore(key, 'readonly '),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
50
|
+
rationale: "Output properties should never be reassigned after initialization because they are meant to be stable references that parent components can bind to. Reassigning an output would break those bindings. For @Output() with EventEmitter, marking the property as 'readonly' makes this constraint explicit and leverages TypeScript's type system to prevent accidental reassignment. The correct pattern is '@Output() readonly userClick = new EventEmitter<User>()', not '@Output() userClick: EventEmitter<User>' where the emitter could be reassigned later. Note that the modern output() function (introduced in Angular v17.3) returns an OutputEmitterRef which is inherently immutable, so this rule is less relevant for those outputs but remains important for traditional EventEmitter-based outputs.",
|
|
51
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type MessageIds = 'preferSignalModel';
|
|
2
|
+
export declare const RULE_NAME = "prefer-signal-model";
|
|
3
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferSignalModel", [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
export default _default;
|
|
7
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
8
|
+
rationale: string;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=prefer-signal-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-signal-model.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-signal-model.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,UAAU,GAAG,mBAAmB,CAAC;AAC7C,eAAO,MAAM,SAAS,wBAAwB,CAAC;;;;AAE/C,wBA0EG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
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 create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
6
|
+
exports.RULE_NAME = 'prefer-signal-model';
|
|
7
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
8
|
+
name: exports.RULE_NAME,
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'suggestion',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Use `model` instead of `input` and `output` for two-way bindings',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
preferSignalModel: 'Use `model` for two-way bindings instead of `input()` and `output()`',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
const inputs = new Map();
|
|
23
|
+
const outputs = new Map();
|
|
24
|
+
return {
|
|
25
|
+
"PropertyDefinition > CallExpression[callee.name='input']"(node) {
|
|
26
|
+
const propertyDef = node.parent;
|
|
27
|
+
const propertyName = utils_1.ASTUtils.getPropertyDefinitionName(propertyDef);
|
|
28
|
+
inputs.set(propertyName, propertyDef);
|
|
29
|
+
},
|
|
30
|
+
"PropertyDefinition > CallExpression[callee.name='output']"(node) {
|
|
31
|
+
const propertyDef = node.parent;
|
|
32
|
+
const propertyName = utils_1.ASTUtils.getPropertyDefinitionName(propertyDef);
|
|
33
|
+
outputs.set(propertyName, propertyDef);
|
|
34
|
+
},
|
|
35
|
+
'ClassDeclaration:exit'() {
|
|
36
|
+
for (const [inputName, inputProperty] of inputs) {
|
|
37
|
+
const outputName = `${inputName}Change`;
|
|
38
|
+
const outputProperty = outputs.get(outputName);
|
|
39
|
+
if (outputProperty) {
|
|
40
|
+
// Report on the input property
|
|
41
|
+
context.report({
|
|
42
|
+
node: inputProperty,
|
|
43
|
+
messageId: 'preferSignalModel',
|
|
44
|
+
fix: (fixer) => {
|
|
45
|
+
const sourceCode = context.sourceCode;
|
|
46
|
+
const inputText = sourceCode.getText(inputProperty);
|
|
47
|
+
const fixedInputText = inputText.replace(/\binput\b/, 'model');
|
|
48
|
+
return [
|
|
49
|
+
utils_1.RuleFixes.getImportAddFix({
|
|
50
|
+
fixer,
|
|
51
|
+
importName: 'model',
|
|
52
|
+
moduleName: '@angular/core',
|
|
53
|
+
node: inputProperty,
|
|
54
|
+
}),
|
|
55
|
+
fixer.replaceText(inputProperty, fixedInputText),
|
|
56
|
+
fixer.remove(outputProperty),
|
|
57
|
+
].filter(utils_1.isNotNullOrUndefined);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Clear the maps for the next class
|
|
63
|
+
inputs.clear();
|
|
64
|
+
outputs.clear();
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
70
|
+
rationale: "The model() function is Angular's modern API for two-way bindings, combining both input and output into a single signal. When you have an input property paired with an output property that follows the naming pattern of `propertyChange` (e.g., `enabled` input with `enabledChange` output), this is the traditional pattern for two-way binding. The model() function provides a cleaner, more concise way to express this pattern with better type safety and integration with Angular's signal ecosystem. It eliminates the boilerplate of managing separate input and output properties while maintaining the same two-way binding functionality.",
|
|
71
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
type Options = [
|
|
3
|
+
{
|
|
4
|
+
preferReadonlySignalProperties: boolean;
|
|
5
|
+
preferInputSignals: boolean;
|
|
6
|
+
preferQuerySignals: boolean;
|
|
7
|
+
useTypeChecking: boolean;
|
|
8
|
+
additionalSignalCreationFunctions: string[];
|
|
9
|
+
}
|
|
10
|
+
];
|
|
11
|
+
export type MessageIds = 'preferInputSignals' | 'preferQuerySignals' | 'preferReadonlySignalProperties';
|
|
12
|
+
export declare const RULE_NAME = "prefer-signals";
|
|
13
|
+
declare const _default: ESLintUtils.RuleModule<MessageIds, Options, import("../utils/create-eslint-rule").RuleDocs, ESLintUtils.RuleListener> & {
|
|
14
|
+
name: string;
|
|
15
|
+
};
|
|
16
|
+
export default _default;
|
|
17
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
18
|
+
rationale: string;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=prefer-signals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-signals.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-signals.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAIvE,KAAK,OAAO,GAAG;IACb;QACE,8BAA8B,EAAE,OAAO,CAAC;QACxC,kBAAkB,EAAE,OAAO,CAAC;QAC5B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,eAAe,EAAE,OAAO,CAAC;QACzB,iCAAiC,EAAE,MAAM,EAAE,CAAC;KAC7C;CACF,CAAC;AAuBF,MAAM,MAAM,UAAU,GAClB,oBAAoB,GACpB,oBAAoB,GACpB,gCAAgC,CAAC;AACrC,eAAO,MAAM,SAAS,mBAAmB,CAAC;;;;AAE1C,wBAsLG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,174 @@
|
|
|
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
|
+
const signals_1 = require("../utils/signals");
|
|
8
|
+
const DEFAULT_OPTIONS = {
|
|
9
|
+
preferReadonlySignalProperties: true,
|
|
10
|
+
preferInputSignals: true,
|
|
11
|
+
preferQuerySignals: true,
|
|
12
|
+
useTypeChecking: false,
|
|
13
|
+
additionalSignalCreationFunctions: [],
|
|
14
|
+
};
|
|
15
|
+
const KNOWN_SIGNAL_CREATION_FUNCTIONS = new Set([
|
|
16
|
+
'computed',
|
|
17
|
+
'contentChild',
|
|
18
|
+
'contentChildren',
|
|
19
|
+
'input',
|
|
20
|
+
'linkedSignal',
|
|
21
|
+
'model',
|
|
22
|
+
'signal',
|
|
23
|
+
'toSignal',
|
|
24
|
+
'viewChild',
|
|
25
|
+
'viewChildren',
|
|
26
|
+
]);
|
|
27
|
+
exports.RULE_NAME = 'prefer-signals';
|
|
28
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
29
|
+
name: exports.RULE_NAME,
|
|
30
|
+
meta: {
|
|
31
|
+
type: 'suggestion',
|
|
32
|
+
docs: {
|
|
33
|
+
description: 'Use readonly signals instead of `@Input()`, `@ViewChild()` and other legacy decorators',
|
|
34
|
+
},
|
|
35
|
+
fixable: 'code',
|
|
36
|
+
schema: [
|
|
37
|
+
{
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
preferReadonlySignalProperties: {
|
|
41
|
+
type: 'boolean',
|
|
42
|
+
default: DEFAULT_OPTIONS.preferReadonlySignalProperties,
|
|
43
|
+
},
|
|
44
|
+
preferInputSignals: {
|
|
45
|
+
type: 'boolean',
|
|
46
|
+
default: DEFAULT_OPTIONS.preferInputSignals,
|
|
47
|
+
},
|
|
48
|
+
preferQuerySignals: {
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
default: DEFAULT_OPTIONS.preferQuerySignals,
|
|
51
|
+
},
|
|
52
|
+
useTypeChecking: {
|
|
53
|
+
type: 'boolean',
|
|
54
|
+
default: DEFAULT_OPTIONS.useTypeChecking,
|
|
55
|
+
},
|
|
56
|
+
additionalSignalCreationFunctions: {
|
|
57
|
+
type: 'array',
|
|
58
|
+
items: { type: 'string' },
|
|
59
|
+
default: DEFAULT_OPTIONS.additionalSignalCreationFunctions,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
additionalProperties: false,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
messages: {
|
|
66
|
+
preferInputSignals: 'Use `InputSignal`s (e.g. via `input()`) for Component input properties rather than the legacy `@Input()` decorator',
|
|
67
|
+
preferQuerySignals: 'Use the `{{function}}` function instead of the `{{decorator}}` decorator',
|
|
68
|
+
preferReadonlySignalProperties: 'Properties declared using signals should be marked as `readonly` since they should not be reassigned',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
defaultOptions: [{ ...DEFAULT_OPTIONS }],
|
|
72
|
+
create(context, [{ preferReadonlySignalProperties = DEFAULT_OPTIONS.preferReadonlySignalProperties, preferInputSignals = DEFAULT_OPTIONS.preferInputSignals, preferQuerySignals = DEFAULT_OPTIONS.preferQuerySignals, additionalSignalCreationFunctions = DEFAULT_OPTIONS.additionalSignalCreationFunctions, useTypeChecking = DEFAULT_OPTIONS.useTypeChecking, },]) {
|
|
73
|
+
let services;
|
|
74
|
+
const listener = {};
|
|
75
|
+
if (preferReadonlySignalProperties) {
|
|
76
|
+
listener[`PropertyDefinition:not([readonly=true])`] = (node) => {
|
|
77
|
+
let shouldBeReadonly = false;
|
|
78
|
+
if (node.typeAnnotation) {
|
|
79
|
+
// Use the type annotation to determine
|
|
80
|
+
// whether the property is a signal.
|
|
81
|
+
if (node.typeAnnotation.typeAnnotation.type ===
|
|
82
|
+
utils_2.AST_NODE_TYPES.TSTypeReference) {
|
|
83
|
+
const type = node.typeAnnotation.typeAnnotation;
|
|
84
|
+
if (type.typeArguments &&
|
|
85
|
+
type.typeName.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
86
|
+
signals_1.KNOWN_SIGNAL_TYPES.has(type.typeName.name)) {
|
|
87
|
+
shouldBeReadonly = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// There is no type annotation, so try to
|
|
93
|
+
// use the value assigned to the property
|
|
94
|
+
// to determine whether it would be a signal.
|
|
95
|
+
let value = node.value;
|
|
96
|
+
if (value?.type === utils_2.AST_NODE_TYPES.CallExpression) {
|
|
97
|
+
const callee = value.callee;
|
|
98
|
+
// A `WritableSignal` can be turned into a `Signal` using
|
|
99
|
+
// the `.asReadonly()` method. If that method is being
|
|
100
|
+
// called, then we need to look at the object that the method
|
|
101
|
+
// is called on to determine if it's being called on a `Signal`.
|
|
102
|
+
if (callee.type === utils_2.AST_NODE_TYPES.MemberExpression) {
|
|
103
|
+
if (callee.property.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
104
|
+
callee.property.name === 'asReadonly') {
|
|
105
|
+
value = callee.object;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (value?.type === utils_2.AST_NODE_TYPES.CallExpression) {
|
|
110
|
+
let callee = value.callee;
|
|
111
|
+
// Some signal-creating functions have a `.required`
|
|
112
|
+
// member. For example, `input.required()`.
|
|
113
|
+
if (callee.type === utils_2.AST_NODE_TYPES.MemberExpression) {
|
|
114
|
+
if (callee.property.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
115
|
+
callee.property.name !== 'required') {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
callee = callee.object;
|
|
119
|
+
}
|
|
120
|
+
if (callee.type === utils_2.AST_NODE_TYPES.Identifier &&
|
|
121
|
+
(KNOWN_SIGNAL_CREATION_FUNCTIONS.has(callee.name) ||
|
|
122
|
+
additionalSignalCreationFunctions.includes(callee.name))) {
|
|
123
|
+
shouldBeReadonly = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (!shouldBeReadonly && useTypeChecking && node.value) {
|
|
127
|
+
services ??= utils_2.ESLintUtils.getParserServices(context);
|
|
128
|
+
const name = services
|
|
129
|
+
.getTypeAtLocation(node.value)
|
|
130
|
+
.getSymbol()?.name;
|
|
131
|
+
shouldBeReadonly =
|
|
132
|
+
name !== undefined && signals_1.KNOWN_SIGNAL_TYPES.has(name);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (shouldBeReadonly) {
|
|
136
|
+
context.report({
|
|
137
|
+
node: node.key,
|
|
138
|
+
messageId: 'preferReadonlySignalProperties',
|
|
139
|
+
fix: (fixer) => fixer.insertTextBefore(node.key, 'readonly '),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (preferInputSignals) {
|
|
145
|
+
listener[utils_1.Selectors.INPUT_DECORATOR] = (node) => {
|
|
146
|
+
context.report({
|
|
147
|
+
node,
|
|
148
|
+
messageId: 'preferInputSignals',
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (preferQuerySignals) {
|
|
153
|
+
listener['Decorator[expression.callee.name=/^(ContentChild|ContentChildren|ViewChild|ViewChildren)$/]'] = (node) => {
|
|
154
|
+
if (node.expression.type === utils_2.AST_NODE_TYPES.CallExpression &&
|
|
155
|
+
node.expression.callee.type === utils_2.AST_NODE_TYPES.Identifier) {
|
|
156
|
+
const decoratorName = node.expression.callee.name;
|
|
157
|
+
context.report({
|
|
158
|
+
node,
|
|
159
|
+
messageId: 'preferQuerySignals',
|
|
160
|
+
data: {
|
|
161
|
+
function: decoratorName.slice(0, 1).toLowerCase() +
|
|
162
|
+
decoratorName.slice(1),
|
|
163
|
+
decorator: decoratorName,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return listener;
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
173
|
+
rationale: "Angular signals represent the future of reactivity in Angular, offering fine-grained change detection, better performance, and improved developer experience. Signal-based APIs like input(), viewChild(), and contentChild() provide type-safe, reactive properties that integrate seamlessly with computed values and effects. Unlike decorator-based APIs (@Input(), @ViewChild(), etc.), signals enable more granular tracking of dependencies and updates, allowing Angular to optimize change detection. Signal properties should be marked readonly because signals themselves are stable references - you read their value by calling them, you don't reassign the signal. This prevents bugs where developers might accidentally reassign a signal instead of updating its value. Using signals throughout your components creates a consistent, reactive programming model that makes data flow explicit and easier to understand.",
|
|
174
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'preferStandalone' | 'removeStandaloneFalse';
|
|
3
|
+
export declare const RULE_NAME = "prefer-standalone";
|
|
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=prefer-standalone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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,GAAG,uBAAuB,CAAC;AACtE,eAAO,MAAM,SAAS,sBAAsB,CAAC;;;;AAK7C,wBAkEG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|