Skip to content

Commit c1f4fc5

Browse files
feat(eslint-plugin): split required typechecking configs (#5002)
1 parent 9ae8856 commit c1f4fc5

File tree

56 files changed

+606
-282
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+606
-282
lines changed

modules/eslint-plugin/schematics/ng-add/schema.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
"default": "all",
1111
"enum": [
1212
"all",
13+
"allTypedChecked",
1314
"component-store",
1415
"effects",
16+
"effectsTypedChecked",
1517
"operators",
1618
"signals",
19+
"signalsTypedChecked",
1720
"store"
1821
],
1922
"x-prompt": {
@@ -24,6 +27,10 @@
2427
"value": "all",
2528
"label": "all"
2629
},
30+
{
31+
"value": "allTypedChecked",
32+
"label": "allTypedChecked"
33+
},
2734
{
2835
"value": "component-store",
2936
"label": "component-store"
@@ -32,6 +39,10 @@
3239
"value": "effects",
3340
"label": "effects"
3441
},
42+
{
43+
"value": "effectsTypedChecked",
44+
"label": "effectsTypedChecked"
45+
},
3546
{
3647
"value": "operators",
3748
"label": "operators"
@@ -40,6 +51,10 @@
4051
"value": "signals",
4152
"label": "signals"
4253
},
54+
{
55+
"value": "signalsTypedChecked",
56+
"label": "signalsTypedChecked"
57+
},
4358
{
4459
"value": "store",
4560
"label": "store"

modules/eslint-plugin/scripts/generate-config.ts

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,52 @@ import { NgRxRule } from '../src/rule-creator';
99
const RULE_MODULE = '@ngrx';
1010
const CONFIG_DIRECTORY = './modules/eslint-plugin/src/configs/';
1111

12-
writeConfig('all', (_rule) => true);
13-
writeConfig('store', (rule) => rule.meta.docs?.ngrxModule === 'store');
14-
writeConfig('effects', (rule) => rule.meta.docs?.ngrxModule === 'effects');
12+
const isModule = (rule: NgRxRule, moduleName: string) =>
13+
rule.meta.docs?.ngrxModule === moduleName;
14+
const isTypeChecked = (rule: NgRxRule) =>
15+
rule.meta.docs?.requiresTypeChecking === true;
16+
17+
writeConfig('all', (rule) => !isTypeChecked(rule));
18+
writeConfig('allTypeChecked', (_rule) => true);
19+
20+
writeConfig(
21+
'store',
22+
(rule) => isModule(rule, 'store') && !isTypeChecked(rule)
23+
);
24+
25+
writeConfig(
26+
'effects',
27+
(rule) => isModule(rule, 'effects') && !isTypeChecked(rule)
28+
);
29+
writeConfig('effectsTypeChecked', (rule) => isModule(rule, 'effects'));
30+
1531
writeConfig(
1632
'component-store',
17-
(rule) => rule.meta.docs?.ngrxModule === 'component-store'
33+
(rule) => isModule(rule, 'component-store') && !isTypeChecked(rule)
1834
);
35+
1936
writeConfig(
2037
'operators',
21-
(rule) => rule.meta.docs?.ngrxModule === 'operators'
38+
(rule) => isModule(rule, 'operators') && !isTypeChecked(rule)
39+
);
40+
41+
writeConfig(
42+
'signals',
43+
(rule) => isModule(rule, 'signals') && !isTypeChecked(rule)
2244
);
23-
writeConfig('signals', (rule) => rule.meta.docs?.ngrxModule === 'signals');
45+
writeConfig('signalsTypeChecked', (rule) => isModule(rule, 'signals'));
2446

2547
async function writeConfig(
2648
configName:
2749
| 'all'
50+
| 'allTypeChecked'
2851
| 'store'
2952
| 'effects'
53+
| 'effectsTypeChecked'
3054
| 'component-store'
3155
| 'operators'
32-
| 'signals',
56+
| 'signals'
57+
| 'signalsTypeChecked',
3358
predicate: (rule: NgRxRule) => boolean
3459
) {
3560
const rulesForConfig = Object.entries(rulesForGenerate).filter(
@@ -42,14 +67,6 @@ import { NgRxRule } from '../src/rule-creator';
4267
},
4368
{}
4469
);
45-
const requireParserOptions: null | Record<string, string | number> =
46-
rulesForConfig.some(([_, rule]) => rule.meta.docs?.requiresTypeChecking)
47-
? {
48-
ecmaVersion: 2020,
49-
sourceType: 'module',
50-
project: './tsconfig.json',
51-
}
52-
: null;
5370

5471
const tsCode = `
5572
/**
@@ -67,7 +84,6 @@ import { NgRxRule } from '../src/rule-creator';
6784
name: 'ngrx/base',
6885
languageOptions: {
6986
parser,
70-
sourceType: 'module',
7187
},
7288
plugins: {
7389
'@ngrx': plugin,
@@ -77,15 +93,6 @@ import { NgRxRule } from '../src/rule-creator';
7793
name: 'ngrx/${configName}',
7894
languageOptions: {
7995
parser,
80-
${
81-
requireParserOptions
82-
? `parserOptions: ${JSON.stringify(
83-
requireParserOptions,
84-
null,
85-
2
86-
)},`
87-
: ''
88-
}
8996
},
9097
rules: ${JSON.stringify(configRules, null, 2)}
9198
},
@@ -104,13 +111,6 @@ import { NgRxRule } from '../src/rule-creator';
104111
plugins: ['@ngrx'],
105112
rules: configRules,
106113
};
107-
if (requireParserOptions) {
108-
jsonConfig.parserOptions = {
109-
ecmaVersion: 2020,
110-
sourceType: 'module',
111-
project: './tsconfig.json',
112-
};
113-
}
114114
const jsonConfigFormatted = await format(
115115
JSON.stringify(jsonConfig, null, 2),
116116
{

modules/eslint-plugin/spec/exported-rules.spec.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,23 @@ describe('ESLint V8', () => {
2222
});
2323
test('exports all configurations', () => {
2424
const configs = getAllConfigs();
25-
expect(configs.length).toBe(6);
25+
expect(configs.length).toBe(9);
2626
});
2727
});
2828

2929
describe('ESLint V9', () => {
3030
test('exports all rules ', () => {
3131
const rules = getAllRules();
32-
expect(Object.keys((configs.all[1] as any).rules).length).toBe(
32+
expect(Object.keys((configs.allTypeChecked[1] as any).rules).length).toBe(
3333
rules.length
3434
);
3535
});
36+
test('there is a difference between typed checke rules ', () => {
37+
expect(
38+
Object.keys((configs.allTypeChecked[1] as any).rules).length
39+
).toBeGreaterThan(Object.keys((configs.all[1] as any).rules).length);
40+
});
3641
test('exports all configurations', () => {
37-
expect(Object.keys(configs).length).toBe(6);
42+
expect(Object.keys(configs).length).toBe(9);
3843
});
3944
});

modules/eslint-plugin/spec/rules/component-store/avoid-combining-component-store-selectors.spec.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class Ok {
4949
constructor(customStore: ComponentStore<MoviesState>) {
5050
const movies = customStore.select((state) => state.movies);
5151
const selectedId = this.customStore.select((state) => state.selectedId);
52-
52+
5353
this.movie$ = this.customStore.select(
5454
this.movies$,
5555
this.selectedId$,
@@ -208,7 +208,11 @@ class NotOk {
208208
}`),
209209
];
210210

211-
ruleTester().run(path.parse(__filename).name, rule, {
212-
valid: [...validConstructor(), ...validInject()],
213-
invalid: [...invalidConstructor(), ...invalidInject()],
214-
});
211+
ruleTester(rule.meta.docs?.requiresTypeChecking).run(
212+
path.parse(__filename).name,
213+
rule,
214+
{
215+
valid: [...validConstructor(), ...validInject()],
216+
invalid: [...invalidConstructor(), ...invalidInject()],
217+
}
218+
);

modules/eslint-plugin/spec/rules/component-store/avoid-mapping-component-store-selectors.spec.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class Ok {
4343
constructor(private customStore: ComponentStore<MoviesState>) {
4444
const movies = customStore.select((state) => state.movies);
4545
this.firstMovie$ = customStore.select(movies, (movies) => movies[0]);
46-
46+
4747
this.movies$ = this.customStore.select((state) => state.movies);
4848
this.firstMovie$ = this.customStore.select(this.movies$, (movies) => movies[0]);
4949
}
@@ -87,7 +87,7 @@ import { ComponentStore } from '@ngrx/component-store'
8787
8888
class NotOk {
8989
readonly movies$ = this.customStore.select((state) => state.movies).pipe(
90-
filter(Boolean),
90+
filter(Boolean),
9191
map((movies) => movies)
9292
~~~~~~~~~~~~~~~~~~~~~~~ [${messageId}]
9393
)
@@ -133,7 +133,11 @@ const invalidInject: () => InvalidTestCase<MessageIds, Options>[] = () => [
133133
}`),
134134
];
135135

136-
ruleTester().run(path.parse(__filename).name, rule, {
137-
valid: [...validConstructor(), ...validInject()],
138-
invalid: [...invalidConstructor(), ...invalidInject()],
139-
});
136+
ruleTester(rule.meta.docs?.requiresTypeChecking).run(
137+
path.parse(__filename).name,
138+
rule,
139+
{
140+
valid: [...validConstructor(), ...validInject()],
141+
invalid: [...invalidConstructor(), ...invalidInject()],
142+
}
143+
);

modules/eslint-plugin/spec/rules/component-store/require-super-ondestroy.spec.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,11 @@ class BooksStore extends ComponentStore<BooksState> implements OnDestroy {
115115
}`),
116116
];
117117

118-
ruleTester().run(path.parse(__filename).name, rule, {
119-
valid: valid(),
120-
invalid: invalid(),
121-
});
118+
ruleTester(rule.meta.docs?.requiresTypeChecking).run(
119+
path.parse(__filename).name,
120+
rule,
121+
{
122+
valid: valid(),
123+
invalid: invalid(),
124+
}
125+
);

modules/eslint-plugin/spec/rules/component-store/updater-explicit-return-type.spec.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,11 @@ class NotOk6 {
212212
}`),
213213
];
214214

215-
ruleTester().run(path.parse(__filename).name, rule, {
216-
valid: [...validConstructor(), ...validInject()],
217-
invalid: [...invalidConstructor(), ...invalidInject()],
218-
});
215+
ruleTester(rule.meta.docs?.requiresTypeChecking).run(
216+
path.parse(__filename).name,
217+
rule,
218+
{
219+
valid: [...validConstructor(), ...validInject()],
220+
invalid: [...invalidConstructor(), ...invalidInject()],
221+
}
222+
);

modules/eslint-plugin/spec/rules/effects/avoid-cyclic-effects.spec.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,11 @@ class Effect {
455455
}`),
456456
];
457457

458-
ruleTester().run(path.parse(__filename).name, rule, {
459-
valid: [...validConstructor(), ...validInject()],
460-
invalid: [...invalidConstructor(), ...invalidInject()],
461-
});
458+
ruleTester(rule.meta.docs?.requiresTypeChecking).run(
459+
path.parse(__filename).name,
460+
rule,
461+
{
462+
valid: [...validConstructor(), ...validInject()],
463+
invalid: [...invalidConstructor(), ...invalidInject()],
464+
}
465+
);

modules/eslint-plugin/spec/rules/effects/no-dispatch-in-effects.spec.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,27 @@ type Options = ESLintUtils.InferOptionsTypeFromRule<typeof rule>;
1616
const validConstructor: () => (string | ValidTestCase<Options>)[] = () => [
1717
`
1818
import { Store } from '@ngrx/store'
19-
19+
2020
class Ok {
2121
readonly effect = somethingOutside();
2222
}`,
2323
`
2424
import { Store } from '@ngrx/store'
25-
25+
2626
class Ok1 {
2727
effect = createEffect(() => this.actions.pipe(
2828
ofType('PING'),
2929
tap(() => ({ type: 'PONG' }))
3030
))
31-
31+
3232
constructor(private actions: Actions, private store: Store) {}
3333
}`,
3434
`
3535
import { Store } from '@ngrx/store'
36-
36+
3737
class Ok2 {
3838
readonly effect: CreateEffectMetadata
39-
39+
4040
constructor(private actions: Actions, private store$: Store) {
4141
this.effect = createEffect(
4242
() => ({ scheduler = asyncScheduler } = {}) =>
@@ -591,7 +591,11 @@ class NotOk7 {
591591
),
592592
];
593593

594-
ruleTester().run(path.parse(__filename).name, rule, {
595-
valid: [...validConstructor(), ...validInject()],
596-
invalid: [...invalidConstructor(), ...invalidInject()],
597-
});
594+
ruleTester(rule.meta.docs?.requiresTypeChecking).run(
595+
path.parse(__filename).name,
596+
rule,
597+
{
598+
valid: [...validConstructor(), ...validInject()],
599+
invalid: [...invalidConstructor(), ...invalidInject()],
600+
}
601+
);

modules/eslint-plugin/spec/rules/effects/no-effects-in-providers.spec.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,11 @@ export class AppModule {}`,
191191
),
192192
];
193193

194-
ruleTester().run(path.parse(__filename).name, rule, {
195-
valid: valid(),
196-
invalid: invalid(),
197-
});
194+
ruleTester(rule.meta.docs?.requiresTypeChecking).run(
195+
path.parse(__filename).name,
196+
rule,
197+
{
198+
valid: valid(),
199+
invalid: invalid(),
200+
}
201+
);

0 commit comments

Comments
 (0)