Skip to content

Commit 6e2bce4

Browse files
committed
feat(onpush): handle signals syntax
1 parent bce9675 commit 6e2bce4

File tree

5 files changed

+136
-5
lines changed

5 files changed

+136
-5
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ jobs:
55
runs-on: ubuntu-latest
66
strategy:
77
matrix:
8-
node: [ '14', '16', '18' ]
8+
node: [ '18', '20', '22', '24' ]
99
name: Node ${{ matrix.node }} sample
1010
steps:
1111
- uses: actions/checkout@v2

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@monade/eslint-plugin-angular",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "A couple of quality-of-life rules for eslint + angular",
55
"main": "src/index.js",
66
"scripts": {

src/rules/strategy-onpush.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { getDecoratorName } = require('../utils');
1+
const { getDecoratorName, isAngularSignal, hasDecorator } = require('../utils');
22

33
/** @type {import('eslint').Rule.RuleModule} */
44
module.exports = {
@@ -40,6 +40,33 @@ module.exports = {
4040
}
4141
}
4242

43+
const hasInject = node.body.body.find((e) => {
44+
return e.type === "PropertyDefinition" && isAngularSignal(e, "inject");
45+
});
46+
47+
if (hasInject) {
48+
return;
49+
}
50+
51+
const hasIOProperty = node.body.body.find((e) => {
52+
if (e.type !== "PropertyDefinition") {
53+
return false;
54+
}
55+
if (isAngularSignal(e, "input") ||
56+
isAngularSignal(e, "output") ||
57+
hasDecorator(e, "Input") ||
58+
hasDecorator(e, "Output")
59+
) {
60+
return false;
61+
}
62+
63+
return true;
64+
});
65+
66+
if (hasIOProperty) {
67+
return;
68+
}
69+
4370
const decoratorParams =
4471
componentDecorator?.expression?.arguments?.[0]?.properties?.map(
4572
(e) => e.value

src/utils.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
1+
function getDecoratorName(decorator) {
2+
return decorator?.expression?.callee?.name || decorator?.expression?.name;
3+
}
4+
15
module.exports = {
2-
getDecoratorName(decorator) {
3-
return decorator?.expression?.callee?.name || decorator?.expression?.name;
6+
getDecoratorName,
7+
/** @param {import('estree').PropertyDefinition} node */
8+
/** @param {string} name */
9+
isAngularSignal(node, name) {
10+
if (node.value?.type !== "CallExpression") {
11+
return false;
12+
}
13+
14+
if (node.value.callee?.name !== name) {
15+
return false;
16+
}
17+
18+
return true;
19+
},
20+
/** @param {import('estree').PropertyDefinition} node */
21+
/** @param {string} name */
22+
hasDecorator(node, name) {
23+
return node.decorators?.some((decorator) => {
24+
return getDecoratorName(decorator) === name;
25+
});
426
}
527
}

test/strategy-onpush.test.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,52 @@ ruleTester.run("strategy-onpush", strategyOnPush, {
2828
}
2929
`,
3030
},
31+
{
32+
name: "Injected dependency, no strategy onpush",
33+
code: `
34+
@Component({
35+
selector: 'app-root',
36+
})
37+
public class AppComponent {
38+
someService = inject(Service);
39+
}
40+
`,
41+
},
42+
{
43+
name: "Input, no property",
44+
code: `
45+
@Component({
46+
selector: 'app-root',
47+
changeDetection: ChangeDetectionStrategy.OnPush,
48+
})
49+
public class AppComponent {
50+
@Input() someInput: string;
51+
}
52+
`,
53+
},
54+
{
55+
name: "Input signal, no property",
56+
code: `
57+
@Component({
58+
selector: 'app-root',
59+
changeDetection: ChangeDetectionStrategy.OnPush,
60+
})
61+
public class AppComponent {
62+
someInput = input("some");
63+
}
64+
`,
65+
},
66+
{
67+
name: "property that is not an input nor an output",
68+
code: `
69+
@Component({
70+
selector: 'app-root',
71+
})
72+
public class AppComponent {
73+
someInput = signal();
74+
}
75+
`,
76+
}
3177
],
3278
invalid: [
3379
{
@@ -57,5 +103,41 @@ public class AppComponent {}`,
57103
},
58104
],
59105
},
106+
{
107+
name: "property that is not an input nor an output",
108+
code: `
109+
@Component({
110+
selector: 'app-root',
111+
})
112+
public class AppComponent {
113+
someInput = input('');
114+
}`,
115+
output: `
116+
@Component({
117+
selector: 'app-root',
118+
changeDetection: ChangeDetectionStrategy.OnPush,
119+
})
120+
public class AppComponent {
121+
someInput = input('');
122+
}`,
123+
errors: [
124+
{
125+
messageId: "strategyOnPush",
126+
suggestions: [
127+
{
128+
messageId: 'strategyOnPushSuggestion',
129+
output: `
130+
@Component({
131+
selector: 'app-root',
132+
changeDetection: ChangeDetectionStrategy.OnPush,
133+
})
134+
public class AppComponent {
135+
someInput = input('');
136+
}`,
137+
},
138+
],
139+
},
140+
],
141+
}
60142
],
61143
});

0 commit comments

Comments
 (0)