@taiga-ui/eslint-plugin-experience-next 0.472.0 → 0.474.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/README.md +102 -4
- package/index.d.ts +3 -0
- package/index.esm.js +462 -158
- package/package.json +1 -1
- package/rules/prefer-combined-if-control-flow.d.ts +5 -0
- package/rules/utils/angular-signals.d.ts +1 -0
package/README.md
CHANGED
|
@@ -54,12 +54,13 @@ export default [
|
|
|
54
54
|
| no-playwright-empty-fill | Enforce `clear()` over `fill('')` in Playwright tests | ✅ | 🔧 | |
|
|
55
55
|
| no-project-as-in-ng-template | `ngProjectAs` has no effect inside `<ng-template>` or dynamic outlets | ✅ | | |
|
|
56
56
|
| no-redundant-type-annotation | Disallow redundant type annotations when the type is already inferred from the initializer | ✅ | 🔧 | |
|
|
57
|
-
| no-side-effects-in-computed | Disallow
|
|
57
|
+
| no-side-effects-in-computed | Disallow side effects and effectful helper calls inside Angular `computed()` callbacks | ✅ | | |
|
|
58
58
|
| no-signal-reads-after-await-in-reactive-context | Disallow bare signal reads after `await` inside reactive callbacks | ✅ | | |
|
|
59
59
|
| no-string-literal-concat | Disallow string literal concatenation; merge adjacent literals into one | ✅ | 🔧 | |
|
|
60
60
|
| no-untracked-outside-reactive-context | Disallow `untracked()` outside reactive callbacks, except explicit post-`await` snapshots | ✅ | 🔧 | |
|
|
61
61
|
| no-useless-untracked | Disallow provably useless `untracked()` wrappers in reactive callbacks | ✅ | 🔧 | |
|
|
62
62
|
| object-single-line | Enforce single-line formatting for single-property objects when it fits `printWidth` | ✅ | 🔧 | |
|
|
63
|
+
| prefer-combined-if-control-flow | Combine consecutive `if` statements that use the same `return`, `break`, `continue`, or `throw` | ✅ | 🔧 | |
|
|
63
64
|
| prefer-deep-imports | Allow deep imports of Taiga UI packages | | 🔧 | |
|
|
64
65
|
| prefer-multi-arg-push | Combine consecutive `.push()` calls on the same array into a single multi-argument call | ✅ | 🔧 | |
|
|
65
66
|
| prefer-untracked-incidental-signal-reads | Wrap likely-incidental signal reads with `untracked()` in reactive callbacks | ✅ | 🔧 | |
|
|
@@ -680,8 +681,9 @@ const doubled = computed(() => {
|
|
|
680
681
|
<sup>`✅ Recommended`</sup>
|
|
681
682
|
|
|
682
683
|
`computed()` should only derive a value from its inputs. This rule reports observable side effects inside Angular
|
|
683
|
-
`computed()` callbacks, including signal writes (`.set()`, `.update()`, `.mutate()`),
|
|
684
|
-
`++/--`, `delete`,
|
|
684
|
+
`computed()` callbacks, including signal writes (`.set()`, `.update()`, `.mutate()`), `effect()`, `inject()`,
|
|
685
|
+
assignments to captured state, `++/--`, `delete`, property mutations on objects that were not created inside the
|
|
686
|
+
computation itself, and calls to local helper functions or methods when their bodies perform those operations.
|
|
685
687
|
|
|
686
688
|
```ts
|
|
687
689
|
// ❌ error
|
|
@@ -690,8 +692,12 @@ import {computed, signal} from '@angular/core';
|
|
|
690
692
|
const source = signal(0);
|
|
691
693
|
const target = signal(0);
|
|
692
694
|
|
|
693
|
-
|
|
695
|
+
function syncTarget(): void {
|
|
694
696
|
target.set(source() + 1);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const derived = computed(() => {
|
|
700
|
+
syncTarget();
|
|
695
701
|
return target();
|
|
696
702
|
});
|
|
697
703
|
```
|
|
@@ -863,6 +869,98 @@ const x = {foo: bar};
|
|
|
863
869
|
|
|
864
870
|
---
|
|
865
871
|
|
|
872
|
+
## prefer-combined-if-control-flow
|
|
873
|
+
|
|
874
|
+
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
875
|
+
|
|
876
|
+
Combine consecutive `if` statements when they have no `else` branch and use the same `return`, `break`, `continue`, or
|
|
877
|
+
`throw` statement. The autofix merges their conditions with `||`, while intentionally skipping cases with intervening
|
|
878
|
+
code or comments that should remain a separate control-flow boundary.
|
|
879
|
+
|
|
880
|
+
```ts
|
|
881
|
+
// ❌ error
|
|
882
|
+
while (true) {
|
|
883
|
+
if (a) continue;
|
|
884
|
+
if (b && c) continue;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// ✅ after autofix
|
|
888
|
+
while (true) {
|
|
889
|
+
if (a || (b && c)) continue;
|
|
890
|
+
}
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
```ts
|
|
894
|
+
// ❌ error
|
|
895
|
+
if (a || b) {
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
if (c) {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// ✅ after autofix
|
|
904
|
+
if (a || b || c) {
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
```ts
|
|
910
|
+
// ❌ error
|
|
911
|
+
if (isInvalid) return result;
|
|
912
|
+
|
|
913
|
+
if (isLegacy && shouldStop) return result;
|
|
914
|
+
|
|
915
|
+
// ✅ after autofix
|
|
916
|
+
if (isInvalid || (isLegacy && shouldStop)) return result;
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
```ts
|
|
920
|
+
// ❌ error
|
|
921
|
+
while (true) {
|
|
922
|
+
if (isDone) break;
|
|
923
|
+
if (hasError) break;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// ✅ after autofix
|
|
927
|
+
while (true) {
|
|
928
|
+
if (isDone || hasError) break;
|
|
929
|
+
}
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
```ts
|
|
933
|
+
// ❌ error
|
|
934
|
+
if (isFatal) throw error;
|
|
935
|
+
|
|
936
|
+
if (isExpired && shouldAbort) throw error;
|
|
937
|
+
|
|
938
|
+
// ✅ after autofix
|
|
939
|
+
if (isFatal || (isExpired && shouldAbort)) throw error;
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
```ts
|
|
943
|
+
// not changed — different control flow
|
|
944
|
+
while (true) {
|
|
945
|
+
if (isDone) continue;
|
|
946
|
+
if (hasError) break;
|
|
947
|
+
}
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
```ts
|
|
951
|
+
// not changed — comment keeps branches separate
|
|
952
|
+
if (a) {
|
|
953
|
+
return value;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// explain why this branch exists
|
|
957
|
+
if (b) {
|
|
958
|
+
return value;
|
|
959
|
+
}
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
---
|
|
963
|
+
|
|
866
964
|
## prefer-deep-imports
|
|
867
965
|
|
|
868
966
|
<sup>`Taiga-specific`</sup> <sup>`Fixable`</sup>
|
package/index.d.ts
CHANGED
|
@@ -81,6 +81,9 @@ declare const plugin: {
|
|
|
81
81
|
}], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
82
82
|
name: string;
|
|
83
83
|
};
|
|
84
|
+
'prefer-combined-if-control-flow': import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferCombinedIfControlFlow", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
85
|
+
name: string;
|
|
86
|
+
};
|
|
84
87
|
'prefer-deep-imports': import("@typescript-eslint/utils/ts-eslint").RuleModule<"prefer-deep-imports", [{
|
|
85
88
|
importFilter: string[] | string;
|
|
86
89
|
strict?: boolean;
|
package/index.esm.js
CHANGED
|
@@ -914,6 +914,7 @@ var recommended = defineConfig([
|
|
|
914
914
|
'@taiga-ui/experience-next/no-untracked-outside-reactive-context': 'error',
|
|
915
915
|
'@taiga-ui/experience-next/no-useless-untracked': 'error',
|
|
916
916
|
'@taiga-ui/experience-next/object-single-line': ['error', { printWidth: 90 }],
|
|
917
|
+
'@taiga-ui/experience-next/prefer-combined-if-control-flow': 'error',
|
|
917
918
|
'@taiga-ui/experience-next/prefer-multi-arg-push': 'error',
|
|
918
919
|
'@taiga-ui/experience-next/prefer-untracked-incidental-signal-reads': 'error',
|
|
919
920
|
'@taiga-ui/experience-next/prefer-untracked-signal-getter': 'error',
|
|
@@ -1335,8 +1336,8 @@ function intersect(a, b) {
|
|
|
1335
1336
|
return a.some((type) => origin.has(type));
|
|
1336
1337
|
}
|
|
1337
1338
|
|
|
1338
|
-
const createRule$
|
|
1339
|
-
var classPropertyNaming = createRule$
|
|
1339
|
+
const createRule$i = ESLintUtils.RuleCreator((name) => name);
|
|
1340
|
+
var classPropertyNaming = createRule$i({
|
|
1340
1341
|
create(context, [configs]) {
|
|
1341
1342
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
1342
1343
|
const typeChecker = parserServices.program.getTypeChecker();
|
|
@@ -1505,9 +1506,9 @@ function isExternalPureTuple(typeChecker, type) {
|
|
|
1505
1506
|
return typeArgs.every((item) => isClassType(item));
|
|
1506
1507
|
}
|
|
1507
1508
|
|
|
1508
|
-
const createRule$
|
|
1509
|
+
const createRule$h = ESLintUtils.RuleCreator((name) => name);
|
|
1509
1510
|
const MESSAGE_ID$7 = 'spreadArrays';
|
|
1510
|
-
var flatExports = createRule$
|
|
1511
|
+
var flatExports = createRule$h({
|
|
1511
1512
|
create(context) {
|
|
1512
1513
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
1513
1514
|
const typeChecker = parserServices.program.getTypeChecker();
|
|
@@ -1667,10 +1668,7 @@ function getDecoratorMetadata(decorator, allowedNames) {
|
|
|
1667
1668
|
return null;
|
|
1668
1669
|
}
|
|
1669
1670
|
const callee = expr.callee;
|
|
1670
|
-
if (callee.type !== AST_NODE_TYPES$1.Identifier) {
|
|
1671
|
-
return null;
|
|
1672
|
-
}
|
|
1673
|
-
if (!allowedNames.has(callee.name)) {
|
|
1671
|
+
if (callee.type !== AST_NODE_TYPES$1.Identifier || !allowedNames.has(callee.name)) {
|
|
1674
1672
|
return null;
|
|
1675
1673
|
}
|
|
1676
1674
|
const arg = expr.arguments[0];
|
|
@@ -1746,8 +1744,8 @@ const PRESETS = {
|
|
|
1746
1744
|
$VUE: ['$CLASS', '$ID', '$VUE_ATTRIBUTE'],
|
|
1747
1745
|
$VUE_ATTRIBUTE: /^v-/,
|
|
1748
1746
|
};
|
|
1749
|
-
const createRule$
|
|
1750
|
-
const rule$
|
|
1747
|
+
const createRule$g = ESLintUtils.RuleCreator((name) => name);
|
|
1748
|
+
const rule$j = createRule$g({
|
|
1751
1749
|
create(context, [options]) {
|
|
1752
1750
|
const sourceCode = context.sourceCode;
|
|
1753
1751
|
const settings = {
|
|
@@ -1842,10 +1840,8 @@ function getHostObject(metadata) {
|
|
|
1842
1840
|
if (property.type !== AST_NODE_TYPES$1.Property ||
|
|
1843
1841
|
property.kind !== 'init' ||
|
|
1844
1842
|
property.computed ||
|
|
1845
|
-
property.method
|
|
1846
|
-
|
|
1847
|
-
}
|
|
1848
|
-
if (getStaticPropertyName(property.key) !== 'host') {
|
|
1843
|
+
property.method ||
|
|
1844
|
+
getStaticPropertyName(property.key) !== 'host') {
|
|
1849
1845
|
continue;
|
|
1850
1846
|
}
|
|
1851
1847
|
return property.value.type === AST_NODE_TYPES$1.ObjectExpression
|
|
@@ -2025,7 +2021,7 @@ const config$2 = {
|
|
|
2025
2021
|
const MESSAGE_ID$5 = 'invalid-injection-token-description';
|
|
2026
2022
|
const ERROR_MESSAGE$3 = "InjectionToken's description should contain token's name";
|
|
2027
2023
|
const NG_DEV_MODE = 'ngDevMode';
|
|
2028
|
-
const createRule$
|
|
2024
|
+
const createRule$f = ESLintUtils.RuleCreator((name) => name);
|
|
2029
2025
|
function getVariableName(node) {
|
|
2030
2026
|
if (node.parent.type !== AST_NODE_TYPES$1.VariableDeclarator) {
|
|
2031
2027
|
return undefined;
|
|
@@ -2096,7 +2092,7 @@ function getNgDevModeDeclarationFix(program, fixer) {
|
|
|
2096
2092
|
}
|
|
2097
2093
|
return fixer.insertTextBeforeRange([0, 0], 'declare const ngDevMode: boolean;\n');
|
|
2098
2094
|
}
|
|
2099
|
-
const rule$
|
|
2095
|
+
const rule$i = createRule$f({
|
|
2100
2096
|
create(context) {
|
|
2101
2097
|
const { sourceCode } = context;
|
|
2102
2098
|
const program = sourceCode.ast;
|
|
@@ -2164,8 +2160,8 @@ const DEFAULT_OPTIONS = {
|
|
|
2164
2160
|
importDeclaration: '^@taiga-ui*',
|
|
2165
2161
|
projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
|
|
2166
2162
|
};
|
|
2167
|
-
const createRule$
|
|
2168
|
-
const rule$
|
|
2163
|
+
const createRule$e = ESLintUtils.RuleCreator((name) => name);
|
|
2164
|
+
const rule$h = createRule$e({
|
|
2169
2165
|
create(context) {
|
|
2170
2166
|
const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
|
|
2171
2167
|
const hasNonCodeExtension = (source) => {
|
|
@@ -2252,13 +2248,13 @@ const rule$g = createRule$d({
|
|
|
2252
2248
|
name: 'no-deep-imports',
|
|
2253
2249
|
});
|
|
2254
2250
|
|
|
2255
|
-
const createRule$
|
|
2251
|
+
const createRule$d = ESLintUtils.RuleCreator((name) => name);
|
|
2256
2252
|
const resolveCacheByOptions = new WeakMap();
|
|
2257
2253
|
const nearestFileUpCache = new Map();
|
|
2258
2254
|
const markerCache = new Map();
|
|
2259
2255
|
const indexFileCache = new Map();
|
|
2260
2256
|
const indexExportsCache = new Map();
|
|
2261
|
-
var noDeepImportsToIndexedPackages = createRule$
|
|
2257
|
+
var noDeepImportsToIndexedPackages = createRule$d({
|
|
2262
2258
|
create(context) {
|
|
2263
2259
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
2264
2260
|
const program = parserServices.program;
|
|
@@ -2364,13 +2360,9 @@ var noDeepImportsToIndexedPackages = createRule$c({
|
|
|
2364
2360
|
return {
|
|
2365
2361
|
ImportDeclaration(node) {
|
|
2366
2362
|
const importSpecifier = node.source.value;
|
|
2367
|
-
if (typeof importSpecifier !== 'string'
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
if (!importSpecifier.includes('/')) {
|
|
2371
|
-
return;
|
|
2372
|
-
}
|
|
2373
|
-
if (!isExternalModuleSpecifier(importSpecifier)) {
|
|
2363
|
+
if (typeof importSpecifier !== 'string' ||
|
|
2364
|
+
!importSpecifier.includes('/') ||
|
|
2365
|
+
!isExternalModuleSpecifier(importSpecifier)) {
|
|
2374
2366
|
return;
|
|
2375
2367
|
}
|
|
2376
2368
|
const packageRootSpecifier = getPackageRootSpecifier(importSpecifier);
|
|
@@ -2438,10 +2430,8 @@ function getPackageRootSpecifier(importSpecifier) {
|
|
|
2438
2430
|
return pathParts[0] ?? importSpecifier;
|
|
2439
2431
|
}
|
|
2440
2432
|
function getSubpath(importSpecifier, packageRootSpecifier) {
|
|
2441
|
-
if (importSpecifier === packageRootSpecifier
|
|
2442
|
-
|
|
2443
|
-
}
|
|
2444
|
-
if (!importSpecifier.startsWith(`${packageRootSpecifier}/`)) {
|
|
2433
|
+
if (importSpecifier === packageRootSpecifier ||
|
|
2434
|
+
!importSpecifier.startsWith(`${packageRootSpecifier}/`)) {
|
|
2445
2435
|
return null;
|
|
2446
2436
|
}
|
|
2447
2437
|
return importSpecifier.slice(packageRootSpecifier.length + 1);
|
|
@@ -2516,10 +2506,8 @@ function getOrderedChildren(node) {
|
|
|
2516
2506
|
];
|
|
2517
2507
|
return children.filter((child) => child !== undefined && child !== null);
|
|
2518
2508
|
}
|
|
2519
|
-
if (node.type === AST_NODE_TYPES.BlockStatement
|
|
2520
|
-
|
|
2521
|
-
}
|
|
2522
|
-
if (node.type === AST_NODE_TYPES.Program) {
|
|
2509
|
+
if (node.type === AST_NODE_TYPES.BlockStatement ||
|
|
2510
|
+
node.type === AST_NODE_TYPES.Program) {
|
|
2523
2511
|
return node.body;
|
|
2524
2512
|
}
|
|
2525
2513
|
if (node.type === AST_NODE_TYPES.IfStatement) {
|
|
@@ -2580,10 +2568,7 @@ function getOrderedChildren(node) {
|
|
|
2580
2568
|
function walkSynchronousAst(root, visitor) {
|
|
2581
2569
|
traverse(root, true);
|
|
2582
2570
|
function traverse(node, isRoot = false) {
|
|
2583
|
-
if (visitor(node) === false) {
|
|
2584
|
-
return false;
|
|
2585
|
-
}
|
|
2586
|
-
if (!isRoot && isFunctionLike$1(node)) {
|
|
2571
|
+
if (visitor(node) === false || (!isRoot && isFunctionLike$1(node))) {
|
|
2587
2572
|
return false;
|
|
2588
2573
|
}
|
|
2589
2574
|
if (node.type === AST_NODE_TYPES.AwaitExpression) {
|
|
@@ -2733,6 +2718,9 @@ function appendObjectPropertyReactiveScopes(scopes, call, object, labels) {
|
|
|
2733
2718
|
function isAngularEffectCall(node, program) {
|
|
2734
2719
|
return isAngularCoreCall(node, program, 'effect');
|
|
2735
2720
|
}
|
|
2721
|
+
function isAngularInjectCall(node, program) {
|
|
2722
|
+
return isAngularCoreCall(node, program, 'inject');
|
|
2723
|
+
}
|
|
2736
2724
|
function isAngularUntrackedCall(node, program) {
|
|
2737
2725
|
return isAngularCoreCall(node, program, 'untracked');
|
|
2738
2726
|
}
|
|
@@ -2864,10 +2852,8 @@ function isWritableSignalWrite(node, checker, esTreeNodeToTSNodeMap) {
|
|
|
2864
2852
|
return false;
|
|
2865
2853
|
}
|
|
2866
2854
|
const { object, property } = node.callee;
|
|
2867
|
-
if (property.type !== AST_NODE_TYPES.Identifier
|
|
2868
|
-
|
|
2869
|
-
}
|
|
2870
|
-
if (!SIGNAL_WRITE_METHODS.has(property.name)) {
|
|
2855
|
+
if (property.type !== AST_NODE_TYPES.Identifier ||
|
|
2856
|
+
!SIGNAL_WRITE_METHODS.has(property.name)) {
|
|
2871
2857
|
return false;
|
|
2872
2858
|
}
|
|
2873
2859
|
return isSignalType(object, checker, esTreeNodeToTSNodeMap);
|
|
@@ -2941,10 +2927,8 @@ const createUntrackedRule = ESLintUtils.RuleCreator((name) => `${UNTRACKED_RULES
|
|
|
2941
2927
|
function collectReadsInsideUntracked(root, checker, esTreeNodeToTSNodeMap, program) {
|
|
2942
2928
|
const reads = [];
|
|
2943
2929
|
walkSynchronousAst(root, (node) => {
|
|
2944
|
-
if (node.type !== AST_NODE_TYPES.CallExpression
|
|
2945
|
-
|
|
2946
|
-
}
|
|
2947
|
-
if (!isAngularUntrackedCall(node, program)) {
|
|
2930
|
+
if (node.type !== AST_NODE_TYPES.CallExpression ||
|
|
2931
|
+
!isAngularUntrackedCall(node, program)) {
|
|
2948
2932
|
return;
|
|
2949
2933
|
}
|
|
2950
2934
|
const [arg] = node.arguments;
|
|
@@ -2963,7 +2947,7 @@ function collectReadsInsideUntracked(root, checker, esTreeNodeToTSNodeMap, progr
|
|
|
2963
2947
|
});
|
|
2964
2948
|
return reads;
|
|
2965
2949
|
}
|
|
2966
|
-
const rule$
|
|
2950
|
+
const rule$g = createUntrackedRule({
|
|
2967
2951
|
create(context) {
|
|
2968
2952
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
2969
2953
|
const checker = parserServices.program.getTypeChecker();
|
|
@@ -3049,15 +3033,15 @@ const config$1 = {
|
|
|
3049
3033
|
},
|
|
3050
3034
|
};
|
|
3051
3035
|
|
|
3052
|
-
const createRule$
|
|
3053
|
-
const rule$
|
|
3036
|
+
const createRule$c = ESLintUtils.RuleCreator((name) => name);
|
|
3037
|
+
const rule$f = createRule$c({
|
|
3054
3038
|
create(context) {
|
|
3055
3039
|
const checkImplicitPublic = (node) => {
|
|
3056
3040
|
const classRef = getClass(node);
|
|
3057
|
-
if (!classRef ||
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3041
|
+
if (!classRef ||
|
|
3042
|
+
node.kind === 'constructor' ||
|
|
3043
|
+
!!node?.accessibility ||
|
|
3044
|
+
node.key?.type === AST_NODE_TYPES.PrivateIdentifier) {
|
|
3061
3045
|
return;
|
|
3062
3046
|
}
|
|
3063
3047
|
const name = node?.key?.name ||
|
|
@@ -3121,9 +3105,9 @@ function getClass(node) {
|
|
|
3121
3105
|
return getClass(node.parent);
|
|
3122
3106
|
}
|
|
3123
3107
|
|
|
3124
|
-
const createRule$
|
|
3108
|
+
const createRule$b = ESLintUtils.RuleCreator((name) => name);
|
|
3125
3109
|
const LEGACY_PEER_DEPS_PATTERN = /^legacy-peer-deps\s*=\s*true$/i;
|
|
3126
|
-
const rule$
|
|
3110
|
+
const rule$e = createRule$b({
|
|
3127
3111
|
create(context) {
|
|
3128
3112
|
return {
|
|
3129
3113
|
Program(node) {
|
|
@@ -3161,8 +3145,8 @@ const rule$d = createRule$a({
|
|
|
3161
3145
|
name: 'no-legacy-peer-deps',
|
|
3162
3146
|
});
|
|
3163
3147
|
|
|
3164
|
-
const createRule$
|
|
3165
|
-
const rule$
|
|
3148
|
+
const createRule$a = ESLintUtils.RuleCreator((name) => name);
|
|
3149
|
+
const rule$d = createRule$a({
|
|
3166
3150
|
create(context) {
|
|
3167
3151
|
const services = ESLintUtils.getParserServices(context);
|
|
3168
3152
|
const checker = services.program.getTypeChecker();
|
|
@@ -3327,7 +3311,7 @@ const config = {
|
|
|
3327
3311
|
},
|
|
3328
3312
|
};
|
|
3329
3313
|
|
|
3330
|
-
const createRule$
|
|
3314
|
+
const createRule$9 = ESLintUtils.RuleCreator((name) => name);
|
|
3331
3315
|
function collectArrayExpressions(node) {
|
|
3332
3316
|
const result = [];
|
|
3333
3317
|
if (node.type === AST_NODE_TYPES.ArrayExpression) {
|
|
@@ -3353,7 +3337,7 @@ function collectArrayExpressions(node) {
|
|
|
3353
3337
|
}
|
|
3354
3338
|
return result;
|
|
3355
3339
|
}
|
|
3356
|
-
const rule$
|
|
3340
|
+
const rule$c = createRule$9({
|
|
3357
3341
|
create(context) {
|
|
3358
3342
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
3359
3343
|
const typeChecker = parserServices.program.getTypeChecker();
|
|
@@ -3481,7 +3465,12 @@ function unwrapExpression(expression) {
|
|
|
3481
3465
|
return current;
|
|
3482
3466
|
}
|
|
3483
3467
|
|
|
3484
|
-
const createRule$
|
|
3468
|
+
const createRule$8 = ESLintUtils.RuleCreator((name) => name);
|
|
3469
|
+
function isFunctionLikeScope(node) {
|
|
3470
|
+
return (node?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
3471
|
+
node?.type === AST_NODE_TYPES.FunctionDeclaration ||
|
|
3472
|
+
node?.type === AST_NODE_TYPES.FunctionExpression);
|
|
3473
|
+
}
|
|
3485
3474
|
function isReactiveCallback(node) {
|
|
3486
3475
|
return (node?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
3487
3476
|
node?.type === AST_NODE_TYPES.FunctionExpression);
|
|
@@ -3526,7 +3515,7 @@ function getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap) {
|
|
|
3526
3515
|
}
|
|
3527
3516
|
return checker.getSymbolAtLocation(tsNode) ?? null;
|
|
3528
3517
|
}
|
|
3529
|
-
function isLocalIdentifier(node, context) {
|
|
3518
|
+
function isLocalIdentifier(node, context, localScopes) {
|
|
3530
3519
|
const symbol = getSymbolAtNode(node, context.checker, context.esTreeNodeToTSNodeMap);
|
|
3531
3520
|
if (!symbol) {
|
|
3532
3521
|
return false;
|
|
@@ -3534,10 +3523,24 @@ function isLocalIdentifier(node, context) {
|
|
|
3534
3523
|
return (symbol.declarations ?? []).some((declaration) => {
|
|
3535
3524
|
const estreeDeclaration = context.tsNodeToESTreeNodeMap.get(declaration);
|
|
3536
3525
|
return (!!estreeDeclaration &&
|
|
3537
|
-
|
|
3526
|
+
isDeclaredInsideLocalScope(estreeDeclaration, localScopes));
|
|
3527
|
+
});
|
|
3528
|
+
}
|
|
3529
|
+
function isDeclaredInsideLocalScope(node, localScopes) {
|
|
3530
|
+
return localScopes.some((scope) => isNodeInsideFunctionScope(node, scope));
|
|
3531
|
+
}
|
|
3532
|
+
function isNodeInsideFunctionScope(node, scope) {
|
|
3533
|
+
let found = false;
|
|
3534
|
+
walkSynchronousAst(scope, (inner) => {
|
|
3535
|
+
if (inner !== node) {
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
found = true;
|
|
3539
|
+
return false;
|
|
3538
3540
|
});
|
|
3541
|
+
return found;
|
|
3539
3542
|
}
|
|
3540
|
-
function isLocallyCreatedExpression(node, context) {
|
|
3543
|
+
function isLocallyCreatedExpression(node, context, localScopes) {
|
|
3541
3544
|
const expression = unwrapExpression(node);
|
|
3542
3545
|
switch (expression.type) {
|
|
3543
3546
|
case AST_NODE_TYPES.ArrayExpression:
|
|
@@ -3545,65 +3548,227 @@ function isLocallyCreatedExpression(node, context) {
|
|
|
3545
3548
|
case AST_NODE_TYPES.ObjectExpression:
|
|
3546
3549
|
return true;
|
|
3547
3550
|
case AST_NODE_TYPES.Identifier:
|
|
3548
|
-
return isLocalIdentifier(expression, context);
|
|
3551
|
+
return isLocalIdentifier(expression, context, localScopes);
|
|
3549
3552
|
case AST_NODE_TYPES.MemberExpression:
|
|
3550
|
-
return isLocallyCreatedExpression(expression.object, context);
|
|
3553
|
+
return isLocallyCreatedExpression(expression.object, context, localScopes);
|
|
3551
3554
|
default:
|
|
3552
3555
|
return false;
|
|
3553
3556
|
}
|
|
3554
3557
|
}
|
|
3555
|
-
function hasObservableMutationTarget(node, context) {
|
|
3558
|
+
function hasObservableMutationTarget(node, context, localScopes) {
|
|
3556
3559
|
return collectMutationTargets(node).some((target) => {
|
|
3557
3560
|
if (target.type === AST_NODE_TYPES.Identifier) {
|
|
3558
|
-
return !isLocalIdentifier(target, context);
|
|
3561
|
+
return !isLocalIdentifier(target, context, localScopes);
|
|
3559
3562
|
}
|
|
3560
|
-
return !isLocallyCreatedExpression(target.object, context);
|
|
3563
|
+
return !isLocallyCreatedExpression(target.object, context, localScopes);
|
|
3561
3564
|
});
|
|
3562
3565
|
}
|
|
3563
|
-
function reportSideEffect(node,
|
|
3566
|
+
function reportSideEffect(node, context, report) {
|
|
3564
3567
|
const key = String(node.range);
|
|
3565
|
-
if (
|
|
3568
|
+
if (context.reported.has(key)) {
|
|
3566
3569
|
return;
|
|
3567
3570
|
}
|
|
3568
|
-
|
|
3571
|
+
context.reported.add(key);
|
|
3569
3572
|
report(node);
|
|
3570
3573
|
}
|
|
3571
|
-
function
|
|
3574
|
+
function isDirectAngularSideEffectCall(node, context) {
|
|
3575
|
+
return (isWritableSignalWrite(node, context.checker, context.esTreeNodeToTSNodeMap) ||
|
|
3576
|
+
isAngularEffectCall(node, context.program) ||
|
|
3577
|
+
isAngularInjectCall(node, context.program));
|
|
3578
|
+
}
|
|
3579
|
+
function isInspectableFunctionContainer(node) {
|
|
3580
|
+
return (!!node &&
|
|
3581
|
+
(isFunctionLikeScope(node) ||
|
|
3582
|
+
node.type === AST_NODE_TYPES.MethodDefinition ||
|
|
3583
|
+
node.type === AST_NODE_TYPES.Property ||
|
|
3584
|
+
node.type === AST_NODE_TYPES.PropertyDefinition ||
|
|
3585
|
+
node.type === AST_NODE_TYPES.VariableDeclarator));
|
|
3586
|
+
}
|
|
3587
|
+
function resolveFunctionLikeFromContainer(node, context, seenSymbols = new Set()) {
|
|
3588
|
+
if (isFunctionLikeScope(node)) {
|
|
3589
|
+
return [node];
|
|
3590
|
+
}
|
|
3591
|
+
if ((node.type === AST_NODE_TYPES.MethodDefinition &&
|
|
3592
|
+
isFunctionLikeScope(node.value)) ||
|
|
3593
|
+
(node.type === AST_NODE_TYPES.Property && isFunctionLikeScope(node.value))) {
|
|
3594
|
+
return [node.value];
|
|
3595
|
+
}
|
|
3596
|
+
if (node.type === AST_NODE_TYPES.Property &&
|
|
3597
|
+
node.value.type === AST_NODE_TYPES.Identifier) {
|
|
3598
|
+
return resolveFunctionLikeFromIdentifier(node.value, context, seenSymbols);
|
|
3599
|
+
}
|
|
3600
|
+
if (node.type === AST_NODE_TYPES.PropertyDefinition &&
|
|
3601
|
+
node.value &&
|
|
3602
|
+
isFunctionLikeScope(node.value)) {
|
|
3603
|
+
return [node.value];
|
|
3604
|
+
}
|
|
3605
|
+
if (node.type === AST_NODE_TYPES.PropertyDefinition &&
|
|
3606
|
+
node.value?.type === AST_NODE_TYPES.Identifier) {
|
|
3607
|
+
return resolveFunctionLikeFromIdentifier(node.value, context, seenSymbols);
|
|
3608
|
+
}
|
|
3609
|
+
if (node.type === AST_NODE_TYPES.VariableDeclarator) {
|
|
3610
|
+
const { init } = node;
|
|
3611
|
+
if (init && isFunctionLikeScope(init)) {
|
|
3612
|
+
return [init];
|
|
3613
|
+
}
|
|
3614
|
+
if (init?.type === AST_NODE_TYPES.Identifier) {
|
|
3615
|
+
return resolveFunctionLikeFromIdentifier(init, context, seenSymbols);
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
return [];
|
|
3619
|
+
}
|
|
3620
|
+
function resolveFunctionLikeFromIdentifier(node, context, seenSymbols = new Set()) {
|
|
3621
|
+
const symbol = getSymbolAtNode(node, context.checker, context.esTreeNodeToTSNodeMap);
|
|
3622
|
+
if (!symbol) {
|
|
3623
|
+
return [];
|
|
3624
|
+
}
|
|
3625
|
+
const symbolId = `${symbol.name}:${symbol.declarations?.[0]?.pos ?? -1}`;
|
|
3626
|
+
if (seenSymbols.has(symbolId)) {
|
|
3627
|
+
return [];
|
|
3628
|
+
}
|
|
3629
|
+
seenSymbols.add(symbolId);
|
|
3630
|
+
return (symbol.declarations ?? []).flatMap((declaration) => {
|
|
3631
|
+
const estreeDeclaration = context.tsNodeToESTreeNodeMap.get(declaration);
|
|
3632
|
+
if (!estreeDeclaration || !isInspectableFunctionContainer(estreeDeclaration)) {
|
|
3633
|
+
return [];
|
|
3634
|
+
}
|
|
3635
|
+
return resolveFunctionLikeFromContainer(estreeDeclaration, context, seenSymbols);
|
|
3636
|
+
});
|
|
3637
|
+
}
|
|
3638
|
+
function resolveCalledFunctions(node, context) {
|
|
3639
|
+
const resolved = new Map();
|
|
3640
|
+
const tsNode = context.esTreeNodeToTSNodeMap.get(node);
|
|
3641
|
+
const signature = tsNode ? context.checker.getResolvedSignature(tsNode) : undefined;
|
|
3642
|
+
const declarations = new Set();
|
|
3643
|
+
if (signature?.declaration) {
|
|
3644
|
+
declarations.add(signature.declaration);
|
|
3645
|
+
}
|
|
3646
|
+
const callee = unwrapExpression(node.callee);
|
|
3647
|
+
if (callee.type === AST_NODE_TYPES.Identifier ||
|
|
3648
|
+
callee.type === AST_NODE_TYPES.MemberExpression) {
|
|
3649
|
+
const symbol = getSymbolAtNode(callee, context.checker, context.esTreeNodeToTSNodeMap);
|
|
3650
|
+
for (const declaration of symbol?.declarations ?? []) {
|
|
3651
|
+
declarations.add(declaration);
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
for (const declaration of declarations) {
|
|
3655
|
+
const estreeDeclaration = context.tsNodeToESTreeNodeMap.get(declaration);
|
|
3656
|
+
if (!estreeDeclaration || !isInspectableFunctionContainer(estreeDeclaration)) {
|
|
3657
|
+
continue;
|
|
3658
|
+
}
|
|
3659
|
+
for (const fn of resolveFunctionLikeFromContainer(estreeDeclaration, context)) {
|
|
3660
|
+
resolved.set(String(fn.range), fn);
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
return [...resolved.values()];
|
|
3664
|
+
}
|
|
3665
|
+
function functionHasObservableSideEffects(root, context, localScopes, visitedFunctions) {
|
|
3666
|
+
let hasSideEffect = false;
|
|
3572
3667
|
walkSynchronousAst(root, (node) => {
|
|
3573
3668
|
if (node.type === AST_NODE_TYPES.CallExpression) {
|
|
3574
|
-
if (
|
|
3575
|
-
|
|
3669
|
+
if (isDirectAngularSideEffectCall(node, context)) {
|
|
3670
|
+
hasSideEffect = true;
|
|
3671
|
+
return false;
|
|
3576
3672
|
}
|
|
3577
|
-
if (isAngularUntrackedCall(node,
|
|
3673
|
+
if (isAngularUntrackedCall(node, context.program)) {
|
|
3674
|
+
const [arg] = node.arguments;
|
|
3675
|
+
if (isReactiveCallback(arg) &&
|
|
3676
|
+
functionHasObservableSideEffects(arg, context, [...localScopes, arg], visitedFunctions)) {
|
|
3677
|
+
hasSideEffect = true;
|
|
3678
|
+
}
|
|
3679
|
+
return false;
|
|
3680
|
+
}
|
|
3681
|
+
if (isReactiveCallback(node.callee)) {
|
|
3682
|
+
if (functionHasObservableSideEffects(node.callee, context, [...localScopes, node.callee], visitedFunctions)) {
|
|
3683
|
+
hasSideEffect = true;
|
|
3684
|
+
}
|
|
3685
|
+
return false;
|
|
3686
|
+
}
|
|
3687
|
+
for (const calledFunction of resolveCalledFunctions(node, context)) {
|
|
3688
|
+
const key = String(calledFunction.range);
|
|
3689
|
+
if (visitedFunctions.has(key)) {
|
|
3690
|
+
continue;
|
|
3691
|
+
}
|
|
3692
|
+
visitedFunctions.add(key);
|
|
3693
|
+
const calledFunctionHasSideEffects = functionHasObservableSideEffects(calledFunction, context, [...localScopes, calledFunction], visitedFunctions);
|
|
3694
|
+
visitedFunctions.delete(key);
|
|
3695
|
+
if (!calledFunctionHasSideEffects) {
|
|
3696
|
+
continue;
|
|
3697
|
+
}
|
|
3698
|
+
hasSideEffect = true;
|
|
3699
|
+
return false;
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
if (node.type === AST_NODE_TYPES.AssignmentExpression &&
|
|
3703
|
+
hasObservableMutationTarget(node.left, context, localScopes)) {
|
|
3704
|
+
hasSideEffect = true;
|
|
3705
|
+
return false;
|
|
3706
|
+
}
|
|
3707
|
+
if (node.type === AST_NODE_TYPES.UpdateExpression &&
|
|
3708
|
+
hasObservableMutationTarget(node.argument, context, localScopes)) {
|
|
3709
|
+
hasSideEffect = true;
|
|
3710
|
+
return false;
|
|
3711
|
+
}
|
|
3712
|
+
if (node.type === AST_NODE_TYPES.UnaryExpression &&
|
|
3713
|
+
node.operator === 'delete' &&
|
|
3714
|
+
hasObservableMutationTarget(node.argument, context, localScopes)) {
|
|
3715
|
+
hasSideEffect = true;
|
|
3716
|
+
return false;
|
|
3717
|
+
}
|
|
3718
|
+
return;
|
|
3719
|
+
});
|
|
3720
|
+
return hasSideEffect;
|
|
3721
|
+
}
|
|
3722
|
+
function inspectComputedBody(root, context, localScopes, visitedFunctions, report) {
|
|
3723
|
+
walkSynchronousAst(root, (node) => {
|
|
3724
|
+
if (node.type === AST_NODE_TYPES.CallExpression) {
|
|
3725
|
+
if (isDirectAngularSideEffectCall(node, context)) {
|
|
3726
|
+
reportSideEffect(node, context, report);
|
|
3727
|
+
return false;
|
|
3728
|
+
}
|
|
3729
|
+
if (isAngularUntrackedCall(node, context.program)) {
|
|
3578
3730
|
const [arg] = node.arguments;
|
|
3579
3731
|
if (isReactiveCallback(arg)) {
|
|
3580
|
-
inspectComputedBody(arg,
|
|
3732
|
+
inspectComputedBody(arg, context, [...localScopes, arg], visitedFunctions, report);
|
|
3581
3733
|
}
|
|
3582
3734
|
return false;
|
|
3583
3735
|
}
|
|
3584
|
-
if (node.callee
|
|
3585
|
-
node.callee.
|
|
3586
|
-
|
|
3736
|
+
if (isReactiveCallback(node.callee)) {
|
|
3737
|
+
inspectComputedBody(node.callee, context, [...localScopes, node.callee], visitedFunctions, report);
|
|
3738
|
+
return false;
|
|
3739
|
+
}
|
|
3740
|
+
for (const calledFunction of resolveCalledFunctions(node, context)) {
|
|
3741
|
+
const key = String(calledFunction.range);
|
|
3742
|
+
if (visitedFunctions.has(key)) {
|
|
3743
|
+
continue;
|
|
3744
|
+
}
|
|
3745
|
+
visitedFunctions.add(key);
|
|
3746
|
+
const calledFunctionHasSideEffects = functionHasObservableSideEffects(calledFunction, context, [...localScopes, calledFunction], visitedFunctions);
|
|
3747
|
+
visitedFunctions.delete(key);
|
|
3748
|
+
if (!calledFunctionHasSideEffects) {
|
|
3749
|
+
continue;
|
|
3750
|
+
}
|
|
3751
|
+
reportSideEffect(node, context, report);
|
|
3587
3752
|
return false;
|
|
3588
3753
|
}
|
|
3589
3754
|
}
|
|
3590
3755
|
if (node.type === AST_NODE_TYPES.AssignmentExpression &&
|
|
3591
|
-
hasObservableMutationTarget(node.left,
|
|
3592
|
-
reportSideEffect(node.left,
|
|
3756
|
+
hasObservableMutationTarget(node.left, context, localScopes)) {
|
|
3757
|
+
reportSideEffect(node.left, context, report);
|
|
3593
3758
|
}
|
|
3594
3759
|
if (node.type === AST_NODE_TYPES.UpdateExpression &&
|
|
3595
|
-
hasObservableMutationTarget(node.argument,
|
|
3596
|
-
reportSideEffect(node.argument,
|
|
3760
|
+
hasObservableMutationTarget(node.argument, context, localScopes)) {
|
|
3761
|
+
reportSideEffect(node.argument, context, report);
|
|
3597
3762
|
}
|
|
3598
3763
|
if (node.type === AST_NODE_TYPES.UnaryExpression &&
|
|
3599
3764
|
node.operator === 'delete' &&
|
|
3600
|
-
hasObservableMutationTarget(node.argument,
|
|
3601
|
-
reportSideEffect(node.argument,
|
|
3765
|
+
hasObservableMutationTarget(node.argument, context, localScopes)) {
|
|
3766
|
+
reportSideEffect(node.argument, context, report);
|
|
3602
3767
|
}
|
|
3603
3768
|
return;
|
|
3604
3769
|
});
|
|
3605
3770
|
}
|
|
3606
|
-
const rule$
|
|
3771
|
+
const rule$b = createRule$8({
|
|
3607
3772
|
create(context) {
|
|
3608
3773
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
3609
3774
|
const checker = parserServices.program.getTypeChecker();
|
|
@@ -3617,15 +3782,14 @@ const rule$a = createRule$7({
|
|
|
3617
3782
|
if (scope.kind !== 'computed()') {
|
|
3618
3783
|
continue;
|
|
3619
3784
|
}
|
|
3620
|
-
const
|
|
3621
|
-
callback: scope.callback,
|
|
3785
|
+
const analysisContext = {
|
|
3622
3786
|
checker,
|
|
3623
3787
|
esTreeNodeToTSNodeMap,
|
|
3624
3788
|
program,
|
|
3625
3789
|
reported: new Set(),
|
|
3626
3790
|
tsNodeToESTreeNodeMap,
|
|
3627
3791
|
};
|
|
3628
|
-
inspectComputedBody(scope.callback,
|
|
3792
|
+
inspectComputedBody(scope.callback, analysisContext, [scope.callback], new Set([String(scope.callback.range)]), (reportNode) => {
|
|
3629
3793
|
context.report({
|
|
3630
3794
|
data: { expression: sourceCode.getText(reportNode) },
|
|
3631
3795
|
messageId: 'sideEffectInComputed',
|
|
@@ -3650,7 +3814,7 @@ const rule$a = createRule$7({
|
|
|
3650
3814
|
name: 'no-side-effects-in-computed',
|
|
3651
3815
|
});
|
|
3652
3816
|
|
|
3653
|
-
const rule$
|
|
3817
|
+
const rule$a = createUntrackedRule({
|
|
3654
3818
|
create(context) {
|
|
3655
3819
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
3656
3820
|
const checker = parserServices.program.getTypeChecker();
|
|
@@ -3699,7 +3863,7 @@ const rule$9 = createUntrackedRule({
|
|
|
3699
3863
|
name: 'no-signal-reads-after-await-in-reactive-context',
|
|
3700
3864
|
});
|
|
3701
3865
|
|
|
3702
|
-
const createRule$
|
|
3866
|
+
const createRule$7 = ESLintUtils.RuleCreator((name) => name);
|
|
3703
3867
|
function isStringLiteral(node) {
|
|
3704
3868
|
return (node.type === AST_NODE_TYPES.Literal &&
|
|
3705
3869
|
typeof node.value === 'string');
|
|
@@ -3763,7 +3927,7 @@ function hasTemplateLiteralAncestor(node) {
|
|
|
3763
3927
|
}
|
|
3764
3928
|
return false;
|
|
3765
3929
|
}
|
|
3766
|
-
const rule$
|
|
3930
|
+
const rule$9 = createRule$7({
|
|
3767
3931
|
create(context) {
|
|
3768
3932
|
const { sourceCode } = context;
|
|
3769
3933
|
let parserServices = null;
|
|
@@ -4183,7 +4347,7 @@ function buildReactiveCallReplacement(outerUntrackedCall, reactiveCall, sourceCo
|
|
|
4183
4347
|
}
|
|
4184
4348
|
return dedent$1(text, reactiveCall.loc.start.column - outerUntrackedCall.parent.loc.start.column);
|
|
4185
4349
|
}
|
|
4186
|
-
const rule$
|
|
4350
|
+
const rule$8 = createUntrackedRule({
|
|
4187
4351
|
create(context) {
|
|
4188
4352
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
4189
4353
|
const checker = parserServices.program.getTypeChecker();
|
|
@@ -4207,20 +4371,12 @@ const rule$7 = createUntrackedRule({
|
|
|
4207
4371
|
}
|
|
4208
4372
|
return {
|
|
4209
4373
|
CallExpression(node) {
|
|
4210
|
-
if (!isAngularUntrackedCall(node, program)
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
}
|
|
4217
|
-
if (isAllowedImperativeAngularContext(node)) {
|
|
4218
|
-
return;
|
|
4219
|
-
}
|
|
4220
|
-
if (isAllowedDeferredCallbackContext(node, checker, esTreeNodeToTSNodeMap)) {
|
|
4221
|
-
return;
|
|
4222
|
-
}
|
|
4223
|
-
if (isAllowedLazyAngularFactoryContext(node, program)) {
|
|
4374
|
+
if (!isAngularUntrackedCall(node, program) ||
|
|
4375
|
+
findEnclosingReactiveScope(node, program) ||
|
|
4376
|
+
findEnclosingReactiveScopeAfterAsyncBoundary(node, program) ||
|
|
4377
|
+
isAllowedImperativeAngularContext(node) ||
|
|
4378
|
+
isAllowedDeferredCallbackContext(node, checker, esTreeNodeToTSNodeMap) ||
|
|
4379
|
+
isAllowedLazyAngularFactoryContext(node, program)) {
|
|
4224
4380
|
return;
|
|
4225
4381
|
}
|
|
4226
4382
|
const reactiveCall = getFixableReactiveCall(node, program);
|
|
@@ -4327,10 +4483,8 @@ function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program
|
|
|
4327
4483
|
}
|
|
4328
4484
|
return;
|
|
4329
4485
|
}
|
|
4330
|
-
if (node.type !== AST_NODE_TYPES.CallExpression
|
|
4331
|
-
|
|
4332
|
-
}
|
|
4333
|
-
if (isAngularUntrackedCall(node, program) ||
|
|
4486
|
+
if (node.type !== AST_NODE_TYPES.CallExpression ||
|
|
4487
|
+
isAngularUntrackedCall(node, program) ||
|
|
4334
4488
|
isSignalReadCall(node, checker, esTreeNodeToTSNodeMap) ||
|
|
4335
4489
|
isWritableSignalWrite(node, checker, esTreeNodeToTSNodeMap)) {
|
|
4336
4490
|
return;
|
|
@@ -4340,7 +4494,7 @@ function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program
|
|
|
4340
4494
|
});
|
|
4341
4495
|
return found;
|
|
4342
4496
|
}
|
|
4343
|
-
const rule$
|
|
4497
|
+
const rule$7 = createUntrackedRule({
|
|
4344
4498
|
create(context) {
|
|
4345
4499
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
4346
4500
|
const checker = parserServices.program.getTypeChecker();
|
|
@@ -4361,14 +4515,12 @@ const rule$6 = createUntrackedRule({
|
|
|
4361
4515
|
return;
|
|
4362
4516
|
}
|
|
4363
4517
|
const { reads } = collectSignalUsages(arg, checker, esTreeNodeToTSNodeMap, program);
|
|
4364
|
-
if (reads.length > 0
|
|
4518
|
+
if (reads.length > 0 ||
|
|
4519
|
+
hasOpaqueSynchronousCalls(arg, checker, esTreeNodeToTSNodeMap, program)) {
|
|
4365
4520
|
// Snapshot reads inside reactive callbacks are a valid Angular
|
|
4366
4521
|
// pattern even when the snapshot later influences branching.
|
|
4367
4522
|
return;
|
|
4368
4523
|
}
|
|
4369
|
-
if (hasOpaqueSynchronousCalls(arg, checker, esTreeNodeToTSNodeMap, program)) {
|
|
4370
|
-
return;
|
|
4371
|
-
}
|
|
4372
4524
|
// Only fix when the parent is a plain ExpressionStatement so we can
|
|
4373
4525
|
// replace statement-for-statement without breaking surrounding structure.
|
|
4374
4526
|
const parent = untrackedCall.parent;
|
|
@@ -4430,8 +4582,8 @@ const rule$6 = createUntrackedRule({
|
|
|
4430
4582
|
name: 'no-useless-untracked',
|
|
4431
4583
|
});
|
|
4432
4584
|
|
|
4433
|
-
const createRule$
|
|
4434
|
-
const rule$
|
|
4585
|
+
const createRule$6 = ESLintUtils.RuleCreator((name) => name);
|
|
4586
|
+
const rule$6 = createRule$6({
|
|
4435
4587
|
create(context, [{ printWidth }]) {
|
|
4436
4588
|
const sourceCode = context.sourceCode;
|
|
4437
4589
|
const getLineEndIndex = (lineStartIndex) => {
|
|
@@ -4683,6 +4835,169 @@ const rule$5 = createRule$5({
|
|
|
4683
4835
|
name: 'object-single-line',
|
|
4684
4836
|
});
|
|
4685
4837
|
|
|
4838
|
+
const createRule$5 = ESLintUtils.RuleCreator((name) => name);
|
|
4839
|
+
const EMPTY_ARGUMENT = '__EMPTY_ARGUMENT__';
|
|
4840
|
+
function getParenthesizedInner(node) {
|
|
4841
|
+
const maybeNode = node;
|
|
4842
|
+
if (maybeNode.type === 'ParenthesizedExpression') {
|
|
4843
|
+
return maybeNode.expression ?? null;
|
|
4844
|
+
}
|
|
4845
|
+
return null;
|
|
4846
|
+
}
|
|
4847
|
+
function unwrapParenthesized(node) {
|
|
4848
|
+
let current = node;
|
|
4849
|
+
let inner = getParenthesizedInner(current);
|
|
4850
|
+
while (inner) {
|
|
4851
|
+
current = inner;
|
|
4852
|
+
inner = getParenthesizedInner(current);
|
|
4853
|
+
}
|
|
4854
|
+
return current;
|
|
4855
|
+
}
|
|
4856
|
+
function isSupportedControlFlowStatement(node) {
|
|
4857
|
+
return (node.type === AST_NODE_TYPES.BreakStatement ||
|
|
4858
|
+
node.type === AST_NODE_TYPES.ContinueStatement ||
|
|
4859
|
+
node.type === AST_NODE_TYPES.ReturnStatement ||
|
|
4860
|
+
node.type === AST_NODE_TYPES.ThrowStatement);
|
|
4861
|
+
}
|
|
4862
|
+
function getControlFlowStatement(node) {
|
|
4863
|
+
if (isSupportedControlFlowStatement(node)) {
|
|
4864
|
+
return node;
|
|
4865
|
+
}
|
|
4866
|
+
if (node.type === AST_NODE_TYPES.BlockStatement &&
|
|
4867
|
+
node.body.length === 1 &&
|
|
4868
|
+
isSupportedControlFlowStatement(node.body[0])) {
|
|
4869
|
+
return node.body[0];
|
|
4870
|
+
}
|
|
4871
|
+
return null;
|
|
4872
|
+
}
|
|
4873
|
+
function getControlFlowSignature(node, sourceCode) {
|
|
4874
|
+
if (node.alternate) {
|
|
4875
|
+
return null;
|
|
4876
|
+
}
|
|
4877
|
+
const controlFlowStatement = getControlFlowStatement(node.consequent);
|
|
4878
|
+
if (!controlFlowStatement) {
|
|
4879
|
+
return null;
|
|
4880
|
+
}
|
|
4881
|
+
switch (controlFlowStatement.type) {
|
|
4882
|
+
case AST_NODE_TYPES.BreakStatement:
|
|
4883
|
+
return controlFlowStatement.label
|
|
4884
|
+
? `break:${sourceCode.getText(controlFlowStatement.label)}`
|
|
4885
|
+
: `break:${EMPTY_ARGUMENT}`;
|
|
4886
|
+
case AST_NODE_TYPES.ContinueStatement:
|
|
4887
|
+
return controlFlowStatement.label
|
|
4888
|
+
? `continue:${sourceCode.getText(controlFlowStatement.label)}`
|
|
4889
|
+
: `continue:${EMPTY_ARGUMENT}`;
|
|
4890
|
+
case AST_NODE_TYPES.ReturnStatement:
|
|
4891
|
+
return controlFlowStatement.argument
|
|
4892
|
+
? `return:${sourceCode.getText(unwrapParenthesized(controlFlowStatement.argument))}`
|
|
4893
|
+
: `return:${EMPTY_ARGUMENT}`;
|
|
4894
|
+
case AST_NODE_TYPES.ThrowStatement:
|
|
4895
|
+
return `throw:${sourceCode.getText(unwrapParenthesized(controlFlowStatement.argument))}`;
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
function hasNonWhitespaceBetween(sourceCode, left, right) {
|
|
4899
|
+
return sourceCode.text.slice(left.range[1], right.range[0]).trim() !== '';
|
|
4900
|
+
}
|
|
4901
|
+
function needsParenthesesInOrChain(node) {
|
|
4902
|
+
if (getParenthesizedInner(node)) {
|
|
4903
|
+
return false;
|
|
4904
|
+
}
|
|
4905
|
+
switch (node.type) {
|
|
4906
|
+
case AST_NODE_TYPES.AssignmentExpression:
|
|
4907
|
+
case AST_NODE_TYPES.ConditionalExpression:
|
|
4908
|
+
case AST_NODE_TYPES.SequenceExpression:
|
|
4909
|
+
case AST_NODE_TYPES.TSAsExpression:
|
|
4910
|
+
case AST_NODE_TYPES.TSSatisfiesExpression:
|
|
4911
|
+
case AST_NODE_TYPES.YieldExpression:
|
|
4912
|
+
return true;
|
|
4913
|
+
case AST_NODE_TYPES.LogicalExpression:
|
|
4914
|
+
return node.operator !== '||';
|
|
4915
|
+
default:
|
|
4916
|
+
return false;
|
|
4917
|
+
}
|
|
4918
|
+
}
|
|
4919
|
+
function renderTest(node, sourceCode) {
|
|
4920
|
+
const text = sourceCode.getText(node);
|
|
4921
|
+
return needsParenthesesInOrChain(node) ? `(${text})` : text;
|
|
4922
|
+
}
|
|
4923
|
+
const rule$5 = createRule$5({
|
|
4924
|
+
create(context) {
|
|
4925
|
+
const { sourceCode } = context;
|
|
4926
|
+
function checkBody(statements) {
|
|
4927
|
+
let i = 0;
|
|
4928
|
+
while (i < statements.length) {
|
|
4929
|
+
const statement = statements[i];
|
|
4930
|
+
if (statement.type !== AST_NODE_TYPES.IfStatement) {
|
|
4931
|
+
i++;
|
|
4932
|
+
continue;
|
|
4933
|
+
}
|
|
4934
|
+
const signature = getControlFlowSignature(statement, sourceCode);
|
|
4935
|
+
if (!signature) {
|
|
4936
|
+
i++;
|
|
4937
|
+
continue;
|
|
4938
|
+
}
|
|
4939
|
+
const group = [statement];
|
|
4940
|
+
let j = i + 1;
|
|
4941
|
+
while (j < statements.length) {
|
|
4942
|
+
const nextStatement = statements[j];
|
|
4943
|
+
if (nextStatement.type !== AST_NODE_TYPES.IfStatement) {
|
|
4944
|
+
break;
|
|
4945
|
+
}
|
|
4946
|
+
if (!hasNonWhitespaceBetween(sourceCode, group[group.length - 1], nextStatement) &&
|
|
4947
|
+
sourceCode.getCommentsInside(nextStatement).length === 0 &&
|
|
4948
|
+
getControlFlowSignature(nextStatement, sourceCode) === signature) {
|
|
4949
|
+
group.push(nextStatement);
|
|
4950
|
+
j++;
|
|
4951
|
+
continue;
|
|
4952
|
+
}
|
|
4953
|
+
break;
|
|
4954
|
+
}
|
|
4955
|
+
if (group.length > 1) {
|
|
4956
|
+
for (const [index, ifStatement] of group.entries()) {
|
|
4957
|
+
context.report({
|
|
4958
|
+
...(index === 0
|
|
4959
|
+
? {
|
|
4960
|
+
fix(fixer) {
|
|
4961
|
+
const firstIf = group[0];
|
|
4962
|
+
const lastIf = group[group.length - 1];
|
|
4963
|
+
const condition = group
|
|
4964
|
+
.map((item) => renderTest(item.test, sourceCode))
|
|
4965
|
+
.join(' || ');
|
|
4966
|
+
return fixer.replaceTextRange([firstIf.range[0], lastIf.range[1]], `if (${condition}) ${sourceCode.getText(firstIf.consequent)}`);
|
|
4967
|
+
},
|
|
4968
|
+
}
|
|
4969
|
+
: {}),
|
|
4970
|
+
messageId: 'preferCombinedIfControlFlow',
|
|
4971
|
+
node: ifStatement,
|
|
4972
|
+
});
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
i = j;
|
|
4976
|
+
}
|
|
4977
|
+
}
|
|
4978
|
+
return {
|
|
4979
|
+
BlockStatement(node) {
|
|
4980
|
+
checkBody(node.body);
|
|
4981
|
+
},
|
|
4982
|
+
Program(node) {
|
|
4983
|
+
checkBody(node.body);
|
|
4984
|
+
},
|
|
4985
|
+
};
|
|
4986
|
+
},
|
|
4987
|
+
meta: {
|
|
4988
|
+
docs: {
|
|
4989
|
+
description: 'Combine consecutive if statements that use the same return, break, continue, or throw statement into a single if statement.',
|
|
4990
|
+
},
|
|
4991
|
+
fixable: 'code',
|
|
4992
|
+
messages: {
|
|
4993
|
+
preferCombinedIfControlFlow: 'Combine consecutive if statements with identical return, break, continue, or throw statements.',
|
|
4994
|
+
},
|
|
4995
|
+
schema: [],
|
|
4996
|
+
type: 'suggestion',
|
|
4997
|
+
},
|
|
4998
|
+
name: 'prefer-combined-if-control-flow',
|
|
4999
|
+
});
|
|
5000
|
+
|
|
4686
5001
|
const MESSAGE_ID$1 = 'prefer-deep-imports';
|
|
4687
5002
|
const ERROR_MESSAGE = 'Import via root entry point is prohibited when nested entry points exist';
|
|
4688
5003
|
const createRule$4 = ESLintUtils.RuleCreator(() => ERROR_MESSAGE);
|
|
@@ -4700,14 +5015,10 @@ var preferDeepImports = createRule$4({
|
|
|
4700
5015
|
return;
|
|
4701
5016
|
}
|
|
4702
5017
|
const rootPackageName = getRootPackageName(rawImportPath);
|
|
4703
|
-
if (!rootPackageName
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
return;
|
|
4708
|
-
}
|
|
4709
|
-
if (!isStrictMode &&
|
|
4710
|
-
isAlreadyNestedImport(rawImportPath, rootPackageName)) {
|
|
5018
|
+
if (!rootPackageName ||
|
|
5019
|
+
!allowedPackages.includes(rootPackageName) ||
|
|
5020
|
+
(!isStrictMode &&
|
|
5021
|
+
isAlreadyNestedImport(rawImportPath, rootPackageName))) {
|
|
4711
5022
|
return;
|
|
4712
5023
|
}
|
|
4713
5024
|
const importedSymbols = extractNamedImportedSymbols(node);
|
|
@@ -4938,10 +5249,7 @@ function mapSymbolsToEntryPointsUsingTypeChecker(importedSymbols, candidateEntry
|
|
|
4938
5249
|
for (const importedSymbol of importedSymbols) {
|
|
4939
5250
|
for (const relativeEntryDir of candidateEntryPoints) {
|
|
4940
5251
|
const exportedNames = exportTableByEntryPoint.get(relativeEntryDir);
|
|
4941
|
-
if (!exportedNames) {
|
|
4942
|
-
continue;
|
|
4943
|
-
}
|
|
4944
|
-
if (!exportedNames.has(importedSymbol)) {
|
|
5252
|
+
if (!exportedNames?.has(importedSymbol)) {
|
|
4945
5253
|
continue;
|
|
4946
5254
|
}
|
|
4947
5255
|
symbolToEntryPoint.set(importedSymbol, relativeEntryDir);
|
|
@@ -5224,10 +5532,8 @@ function isDomImperativeCall(node, checker, esTreeNodeToTSNodeMap) {
|
|
|
5224
5532
|
}
|
|
5225
5533
|
const signature = checker.getResolvedSignature(tsNode);
|
|
5226
5534
|
const declaration = signature?.declaration;
|
|
5227
|
-
if (!declaration
|
|
5228
|
-
|
|
5229
|
-
}
|
|
5230
|
-
if (!LIB_DOM_FILE_PATTERN.test(declaration.getSourceFile().fileName)) {
|
|
5535
|
+
if (!declaration ||
|
|
5536
|
+
!LIB_DOM_FILE_PATTERN.test(declaration.getSourceFile().fileName)) {
|
|
5231
5537
|
return false;
|
|
5232
5538
|
}
|
|
5233
5539
|
const returnType = checker.typeToString(checker.getReturnTypeOfSignature(signature));
|
|
@@ -5459,18 +5765,15 @@ function getReturnedExpression(node) {
|
|
|
5459
5765
|
}
|
|
5460
5766
|
function getWrappedSignalGetter(node, checker, esTreeNodeToTSNodeMap) {
|
|
5461
5767
|
const [arg] = node.arguments;
|
|
5462
|
-
if (!arg ||
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
5768
|
+
if (!arg ||
|
|
5769
|
+
arg.type === AST_NODE_TYPES.SpreadElement ||
|
|
5770
|
+
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
5771
|
+
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
5467
5772
|
return null;
|
|
5468
5773
|
}
|
|
5469
5774
|
const body = getReturnedExpression(arg);
|
|
5470
|
-
if (body?.type !== AST_NODE_TYPES.CallExpression
|
|
5471
|
-
|
|
5472
|
-
}
|
|
5473
|
-
if (!isSignalReadCall(body, checker, esTreeNodeToTSNodeMap)) {
|
|
5775
|
+
if (body?.type !== AST_NODE_TYPES.CallExpression ||
|
|
5776
|
+
!isSignalReadCall(body, checker, esTreeNodeToTSNodeMap)) {
|
|
5474
5777
|
return null;
|
|
5475
5778
|
}
|
|
5476
5779
|
const getter = unwrapExpression(body.callee);
|
|
@@ -5943,24 +6246,25 @@ const plugin = {
|
|
|
5943
6246
|
'class-property-naming': classPropertyNaming,
|
|
5944
6247
|
'decorator-key-sort': config$3,
|
|
5945
6248
|
'flat-exports': flatExports,
|
|
5946
|
-
'host-attributes-sort': rule$
|
|
6249
|
+
'host-attributes-sort': rule$j,
|
|
5947
6250
|
'html-logical-properties': config$2,
|
|
5948
|
-
'injection-token-description': rule$
|
|
5949
|
-
'no-deep-imports': rule$
|
|
6251
|
+
'injection-token-description': rule$i,
|
|
6252
|
+
'no-deep-imports': rule$h,
|
|
5950
6253
|
'no-deep-imports-to-indexed-packages': noDeepImportsToIndexedPackages,
|
|
5951
|
-
'no-fully-untracked-effect': rule$
|
|
6254
|
+
'no-fully-untracked-effect': rule$g,
|
|
5952
6255
|
'no-href-with-router-link': config$1,
|
|
5953
|
-
'no-implicit-public': rule$
|
|
5954
|
-
'no-legacy-peer-deps': rule$
|
|
5955
|
-
'no-playwright-empty-fill': rule$
|
|
6256
|
+
'no-implicit-public': rule$f,
|
|
6257
|
+
'no-legacy-peer-deps': rule$e,
|
|
6258
|
+
'no-playwright-empty-fill': rule$d,
|
|
5956
6259
|
'no-project-as-in-ng-template': config,
|
|
5957
|
-
'no-redundant-type-annotation': rule$
|
|
5958
|
-
'no-side-effects-in-computed': rule$
|
|
5959
|
-
'no-signal-reads-after-await-in-reactive-context': rule$
|
|
5960
|
-
'no-string-literal-concat': rule$
|
|
5961
|
-
'no-untracked-outside-reactive-context': rule$
|
|
5962
|
-
'no-useless-untracked': rule$
|
|
5963
|
-
'object-single-line': rule$
|
|
6260
|
+
'no-redundant-type-annotation': rule$c,
|
|
6261
|
+
'no-side-effects-in-computed': rule$b,
|
|
6262
|
+
'no-signal-reads-after-await-in-reactive-context': rule$a,
|
|
6263
|
+
'no-string-literal-concat': rule$9,
|
|
6264
|
+
'no-untracked-outside-reactive-context': rule$8,
|
|
6265
|
+
'no-useless-untracked': rule$7,
|
|
6266
|
+
'object-single-line': rule$6,
|
|
6267
|
+
'prefer-combined-if-control-flow': rule$5,
|
|
5964
6268
|
'prefer-deep-imports': preferDeepImports,
|
|
5965
6269
|
'prefer-multi-arg-push': rule$4,
|
|
5966
6270
|
'prefer-untracked-incidental-signal-reads': rule$3,
|
package/package.json
CHANGED
|
@@ -17,6 +17,7 @@ export interface SignalUsage {
|
|
|
17
17
|
readonly writes: TSESTree.CallExpression[];
|
|
18
18
|
}
|
|
19
19
|
export declare function isAngularEffectCall(node: TSESTree.CallExpression, program: TSESTree.Program): boolean;
|
|
20
|
+
export declare function isAngularInjectCall(node: TSESTree.CallExpression, program: TSESTree.Program): boolean;
|
|
20
21
|
export declare function isAngularUntrackedCall(node: TSESTree.CallExpression, program: TSESTree.Program): boolean;
|
|
21
22
|
export declare function getReactiveScopes(node: TSESTree.CallExpression, program: TSESTree.Program): ReactiveScope[];
|
|
22
23
|
export declare function isNodeInsideSynchronousReactiveScope(node: TSESTree.Node, callback: ReactiveCallback): boolean;
|