Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f03d7ee

Browse files
authored
feat(eslint-plugin): add rule prefer-output-emitter-ref (#2324)
1 parent 57401f8 commit f03d7ee

File tree

8 files changed

+274
-0
lines changed

8 files changed

+274
-0
lines changed

packages/angular-eslint/src/configs/ts-all.ts

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export default (
4242
'@angular-eslint/no-queries-metadata-property': 'error',
4343
'@angular-eslint/pipe-prefix': 'error',
4444
'@angular-eslint/prefer-on-push-component-change-detection': 'error',
45+
'@angular-eslint/prefer-output-emitter-ref': 'error',
4546
'@angular-eslint/prefer-output-readonly': 'error',
4647
'@angular-eslint/prefer-signals': 'error',
4748
'@angular-eslint/prefer-standalone': 'error',

packages/eslint-plugin/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Please see https://github.com/angular-eslint/angular-eslint for full usage instr
6666
| [`no-queries-metadata-property`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/no-queries-metadata-property.md) | Disallows usage of the `queries` metadata property. See more at https://angular.dev/style-guide#style-05-12. | | | |
6767
| [`pipe-prefix`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/pipe-prefix.md) | Enforce consistent prefix for pipes. | | | |
6868
| [`prefer-on-push-component-change-detection`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-on-push-component-change-detection.md) | Ensures component's `changeDetection` is set to `ChangeDetectionStrategy.OnPush` | | | :bulb: |
69+
| [`prefer-output-emitter-ref`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-output-emitter-ref.md) | Use `OutputEmitterRef` instead of `@Output()` | | | |
6970
| [`prefer-output-readonly`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-output-readonly.md) | Prefer to declare `@Output`, `OutputEmitterRef` and `OutputRef` as `readonly` since they are not supposed to be reassigned | | | :bulb: |
7071
| [`prefer-signals`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-signals.md) | Use readonly signals instead of `@Input()`, `@ViewChild()` and other legacy decorators | | :wrench: | |
7172
| [`prefer-standalone`](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-standalone.md) | Ensures Components, Directives and Pipes do not opt out of standalone | :white_check_mark: | :wrench: | |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<!--
2+
3+
DO NOT EDIT.
4+
5+
This markdown file was autogenerated using a mixture of the following files as the source of truth for its data:
6+
- ../../src/rules/prefer-output-emitter-ref.ts
7+
- ../../tests/rules/prefer-output-emitter-ref/cases.ts
8+
9+
In order to update this file, it is therefore those files which need to be updated, as well as potentially the generator script:
10+
- ../../../../tools/scripts/generate-rule-docs.ts
11+
12+
-->
13+
14+
<br>
15+
16+
# `@angular-eslint/prefer-output-emitter-ref`
17+
18+
Use `OutputEmitterRef` instead of `@Output()`
19+
20+
- Type: suggestion
21+
22+
<br>
23+
24+
## Rule Options
25+
26+
The rule does not have any configuration options.
27+
28+
<br>
29+
30+
## Usage Examples
31+
32+
> The following examples are generated automatically from the actual unit tests within the plugin, so you can be assured that their behavior is accurate based on the current commit.
33+
34+
<br>
35+
36+
<details>
37+
<summary>❌ - Toggle examples of <strong>incorrect</strong> code for this rule</summary>
38+
39+
<br>
40+
41+
#### Default Config
42+
43+
```json
44+
{
45+
"rules": {
46+
"@angular-eslint/prefer-output-emitter-ref": [
47+
"error"
48+
]
49+
}
50+
}
51+
```
52+
53+
<br>
54+
55+
#### ❌ Invalid Code
56+
57+
```ts
58+
class Test {
59+
@Output()
60+
~~~~~~~~~
61+
readonly change: EventEmitter<number>;
62+
}
63+
```
64+
65+
</details>
66+
67+
<br>
68+
69+
---
70+
71+
<br>
72+
73+
<details>
74+
<summary>✅ - Toggle examples of <strong>correct</strong> code for this rule</summary>
75+
76+
<br>
77+
78+
#### Default Config
79+
80+
```json
81+
{
82+
"rules": {
83+
"@angular-eslint/prefer-output-emitter-ref": [
84+
"error"
85+
]
86+
}
87+
}
88+
```
89+
90+
<br>
91+
92+
#### ✅ Valid Code
93+
94+
```ts
95+
class Test {
96+
change = outputValue();
97+
}
98+
```
99+
100+
<br>
101+
102+
---
103+
104+
<br>
105+
106+
#### Default Config
107+
108+
```json
109+
{
110+
"rules": {
111+
"@angular-eslint/prefer-output-emitter-ref": [
112+
"error"
113+
]
114+
}
115+
}
116+
```
117+
118+
<br>
119+
120+
#### ✅ Valid Code
121+
122+
```ts
123+
class Test {
124+
change = new EventEmitter();
125+
}
126+
```
127+
128+
<br>
129+
130+
---
131+
132+
<br>
133+
134+
#### Default Config
135+
136+
```json
137+
{
138+
"rules": {
139+
"@angular-eslint/prefer-output-emitter-ref": [
140+
"error"
141+
]
142+
}
143+
}
144+
```
145+
146+
<br>
147+
148+
#### ✅ Valid Code
149+
150+
```ts
151+
class Test {
152+
readonly change = output();
153+
}
154+
```
155+
156+
<br>
157+
158+
---
159+
160+
<br>
161+
162+
#### Default Config
163+
164+
```json
165+
{
166+
"rules": {
167+
"@angular-eslint/prefer-output-emitter-ref": [
168+
"error"
169+
]
170+
}
171+
}
172+
```
173+
174+
<br>
175+
176+
#### ✅ Valid Code
177+
178+
```ts
179+
class Test {
180+
readonly change = output<string>();
181+
}
182+
```
183+
184+
</details>
185+
186+
<br>

packages/eslint-plugin/src/configs/all.json

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"@angular-eslint/no-queries-metadata-property": "error",
2929
"@angular-eslint/pipe-prefix": "error",
3030
"@angular-eslint/prefer-on-push-component-change-detection": "error",
31+
"@angular-eslint/prefer-output-emitter-ref": "error",
3132
"@angular-eslint/prefer-output-readonly": "error",
3233
"@angular-eslint/prefer-signals": "error",
3334
"@angular-eslint/prefer-standalone": "error",

packages/eslint-plugin/src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ import pipePrefix, {
7676
import preferOnPushComponentChangeDetection, {
7777
RULE_NAME as preferOnPushComponentChangeDetectionRuleName,
7878
} from './rules/prefer-on-push-component-change-detection';
79+
import preferOutputEmitterRef, {
80+
RULE_NAME as preferOutputEmitterRefRuleName,
81+
} from './rules/prefer-output-emitter-ref';
7982
import preferOutputReadonly, {
8083
RULE_NAME as preferOutputReadonlyRuleName,
8184
} from './rules/prefer-output-readonly';
@@ -154,6 +157,7 @@ export = {
154157
preferOnPushComponentChangeDetection,
155158
[preferSignalsRuleName]: preferSignals,
156159
[preferStandaloneRuleName]: preferStandalone,
160+
[preferOutputEmitterRefRuleName]: preferOutputEmitterRef,
157161
[preferOutputReadonlyRuleName]: preferOutputReadonly,
158162
[relativeUrlPrefixRuleName]: relativeUrlPrefix,
159163
[requireLifecycleOnPrototypeRuleName]: requireLifecycleOnPrototype,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Selectors } from '@angular-eslint/utils';
2+
import { createESLintRule } from '../utils/create-eslint-rule';
3+
4+
type Options = [];
5+
6+
export type MessageIds = 'preferOutputEmitterRef';
7+
export const RULE_NAME = 'prefer-output-emitter-ref';
8+
9+
export default createESLintRule<Options, MessageIds>({
10+
name: RULE_NAME,
11+
meta: {
12+
type: 'suggestion',
13+
docs: {
14+
description: 'Use `OutputEmitterRef` instead of `@Output()`',
15+
},
16+
schema: [],
17+
messages: {
18+
preferOutputEmitterRef:
19+
'Use `OutputEmitterRef` via `output()` for Component and Directive outputs rather than the legacy `@Output()` decorator',
20+
},
21+
},
22+
defaultOptions: [],
23+
create(context) {
24+
return {
25+
[Selectors.OUTPUT_DECORATOR]: (node) => {
26+
context.report({ node, messageId: 'preferOutputEmitterRef' });
27+
},
28+
};
29+
},
30+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { convertAnnotatedSourceToFailureCase } from '@angular-eslint/test-utils';
2+
import type { MessageIds } from '../../../src/rules/prefer-output-emitter-ref';
3+
4+
const messageIdPreferOutputEmitterRef: MessageIds = 'preferOutputEmitterRef';
5+
6+
export const valid = [
7+
`
8+
class Test {
9+
change = outputValue();
10+
}
11+
`,
12+
`
13+
class Test {
14+
change = new EventEmitter();
15+
}
16+
`,
17+
`
18+
class Test {
19+
readonly change = output();
20+
}
21+
`,
22+
`
23+
class Test {
24+
readonly change = output<string>();
25+
}
26+
`,
27+
];
28+
29+
export const invalid = [
30+
convertAnnotatedSourceToFailureCase({
31+
description: 'should fail when an @Output is used',
32+
annotatedSource: `
33+
class Test {
34+
@Output()
35+
~~~~~~~~~
36+
readonly change: EventEmitter<number>;
37+
}
38+
`,
39+
messageId: messageIdPreferOutputEmitterRef,
40+
}),
41+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { RuleTester } from '@angular-eslint/test-utils';
2+
import rule, { RULE_NAME } from '../../../src/rules/prefer-output-emitter-ref';
3+
import { invalid, valid } from './cases';
4+
5+
const ruleTester = new RuleTester();
6+
7+
ruleTester.run(RULE_NAME, rule, {
8+
valid,
9+
invalid,
10+
});

0 commit comments

Comments
 (0)