@taiga-ui/eslint-plugin-experience-next 0.474.0 → 0.475.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 +43 -0
- package/index.d.ts +3 -0
- package/index.esm.js +485 -507
- package/package.json +1 -1
- package/rules/no-fully-untracked-effect.d.ts +1 -2
- package/rules/no-infinite-loop.d.ts +6 -0
- package/rules/no-signal-reads-after-await-in-reactive-context.d.ts +1 -2
- package/rules/no-untracked-outside-reactive-context.d.ts +1 -2
- package/rules/no-useless-untracked.d.ts +1 -2
- package/rules/prefer-untracked-incidental-signal-reads.d.ts +1 -27
- package/rules/prefer-untracked-signal-getter.d.ts +1 -2
- package/rules/utils/{angular-signals.d.ts → angular/angular-signals.d.ts} +9 -5
- package/rules/utils/angular/pipes.d.ts +2 -0
- package/rules/utils/angular/providers.d.ts +3 -0
- package/rules/utils/ast/ancestors.d.ts +12 -0
- package/rules/utils/{ast-walk.d.ts → ast/ast-walk.d.ts} +1 -0
- package/rules/utils/ast/call-expressions.d.ts +2 -0
- package/rules/utils/ast/mutation-targets.d.ts +4 -0
- package/rules/utils/ast/parenthesized.d.ts +3 -0
- package/rules/utils/ast/property-names.d.ts +5 -0
- package/rules/utils/ast/returned-expression.d.ts +2 -0
- package/rules/utils/ast/string-literals.d.ts +10 -0
- package/rules/utils/text/dedent.d.ts +5 -0
- package/rules/utils/typescript/decorators.d.ts +2 -0
- package/rules/utils/typescript/function-usage.d.ts +5 -0
- package/rules/utils/typescript/node-map.d.ts +6 -0
- package/rules/utils/typescript/symbols.d.ts +4 -0
- package/rules/utils/typescript/type-aware-context.d.ts +14 -0
- /package/rules/utils/{angular-imports.d.ts → angular/angular-imports.d.ts} +0 -0
- /package/rules/utils/{get-decorator-metadata.d.ts → angular/get-decorator-metadata.d.ts} +0 -0
- /package/rules/utils/{get-imports-array.d.ts → angular/get-imports-array.d.ts} +0 -0
- /package/rules/utils/{import-fix-helpers.d.ts → angular/import-fix-helpers.d.ts} +0 -0
- /package/rules/utils/{is-imports-array-property.d.ts → angular/is-imports-array-property.d.ts} +0 -0
- /package/rules/utils/{untracked-docs.d.ts → angular/untracked-docs.d.ts} +0 -0
- /package/rules/utils/{ast-expressions.d.ts → ast/ast-expressions.d.ts} +0 -0
- /package/rules/utils/{get-const-array.d.ts → ast/get-const-array.d.ts} +0 -0
- /package/rules/utils/{is-array.d.ts → ast/is-array.d.ts} +0 -0
- /package/rules/utils/{is-object.d.ts → ast/is-object.d.ts} +0 -0
- /package/rules/utils/{is-spread.d.ts → ast/is-spread.d.ts} +0 -0
- /package/rules/utils/{name-of.d.ts → ast/name-of.d.ts} +0 -0
- /package/rules/utils/{intersect.d.ts → collections/intersect.d.ts} +0 -0
- /package/rules/utils/{same-order.d.ts → collections/same-order.d.ts} +0 -0
- /package/rules/utils/{get-imported-name.d.ts → imports/get-imported-name.d.ts} +0 -0
- /package/rules/utils/{npmrc-parser.d.ts → parsers/npmrc-parser.d.ts} +0 -0
- /package/rules/utils/{get-sorted-names.d.ts → sorting/get-sorted-names.d.ts} +0 -0
- /package/rules/utils/{get-field-types.d.ts → typescript/get-field-types.d.ts} +0 -0
- /package/rules/utils/{get-type-name.d.ts → typescript/get-type-name.d.ts} +0 -0
- /package/rules/utils/{is-class-type.d.ts → typescript/is-class-type.d.ts} +0 -0
- /package/rules/utils/{is-external-tuple.d.ts → typescript/is-external-tuple.d.ts} +0 -0
package/index.esm.js
CHANGED
|
@@ -908,6 +908,7 @@ var recommended = defineConfig([
|
|
|
908
908
|
'@taiga-ui/experience-next/no-deep-imports-to-indexed-packages': 'error',
|
|
909
909
|
'@taiga-ui/experience-next/no-fully-untracked-effect': 'error',
|
|
910
910
|
'@taiga-ui/experience-next/no-implicit-public': 'error',
|
|
911
|
+
'@taiga-ui/experience-next/no-infinite-loop': 'error',
|
|
911
912
|
'@taiga-ui/experience-next/no-redundant-type-annotation': 'error',
|
|
912
913
|
'@taiga-ui/experience-next/no-side-effects-in-computed': 'error',
|
|
913
914
|
'@taiga-ui/experience-next/no-signal-reads-after-await-in-reactive-context': 'error',
|
|
@@ -1299,6 +1300,11 @@ const config$4 = {
|
|
|
1299
1300
|
},
|
|
1300
1301
|
};
|
|
1301
1302
|
|
|
1303
|
+
function intersect(a, b) {
|
|
1304
|
+
const origin = new Set(b);
|
|
1305
|
+
return a.some((type) => origin.has(type));
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1302
1308
|
function getFieldTypes(type, checker) {
|
|
1303
1309
|
const typeNames = [];
|
|
1304
1310
|
if (type.isUnionOrIntersection()) {
|
|
@@ -1331,16 +1337,24 @@ function getFieldTypes(type, checker) {
|
|
|
1331
1337
|
return typeNames;
|
|
1332
1338
|
}
|
|
1333
1339
|
|
|
1334
|
-
function
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1340
|
+
function getTypeAwareRuleContext(context) {
|
|
1341
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
1342
|
+
const { sourceCode } = context;
|
|
1343
|
+
return {
|
|
1344
|
+
checker: parserServices.program.getTypeChecker(),
|
|
1345
|
+
esTreeNodeToTSNodeMap: parserServices.esTreeNodeToTSNodeMap,
|
|
1346
|
+
parserServices,
|
|
1347
|
+
program: sourceCode.ast,
|
|
1348
|
+
sourceCode,
|
|
1349
|
+
tsNodeToESTreeNodeMap: parserServices.tsNodeToESTreeNodeMap,
|
|
1350
|
+
tsProgram: parserServices.program,
|
|
1351
|
+
};
|
|
1337
1352
|
}
|
|
1338
1353
|
|
|
1339
|
-
const createRule$
|
|
1340
|
-
var classPropertyNaming = createRule$
|
|
1354
|
+
const createRule$j = ESLintUtils.RuleCreator((name) => name);
|
|
1355
|
+
var classPropertyNaming = createRule$j({
|
|
1341
1356
|
create(context, [configs]) {
|
|
1342
|
-
const
|
|
1343
|
-
const typeChecker = parserServices.program.getTypeChecker();
|
|
1357
|
+
const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
|
|
1344
1358
|
const flatConfig = configs.flat();
|
|
1345
1359
|
return {
|
|
1346
1360
|
PropertyDefinition(node) {
|
|
@@ -1348,7 +1362,7 @@ var classPropertyNaming = createRule$i({
|
|
|
1348
1362
|
if (!fieldName) {
|
|
1349
1363
|
return;
|
|
1350
1364
|
}
|
|
1351
|
-
const tsNode =
|
|
1365
|
+
const tsNode = esTreeNodeToTSNodeMap.get(node);
|
|
1352
1366
|
const nodeType = typeChecker.getTypeAtLocation(tsNode);
|
|
1353
1367
|
const fieldTypes = getFieldTypes(nodeType, typeChecker);
|
|
1354
1368
|
const rule = flatConfig.find((rule) => intersect(fieldTypes, rule.withTypesSpecifier) &&
|
|
@@ -1404,6 +1418,10 @@ var classPropertyNaming = createRule$i({
|
|
|
1404
1418
|
name: 'class-property-naming',
|
|
1405
1419
|
});
|
|
1406
1420
|
|
|
1421
|
+
function sameOrder(a, b) {
|
|
1422
|
+
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1407
1425
|
const config$3 = {
|
|
1408
1426
|
create(context) {
|
|
1409
1427
|
const order = context.options[0] || {};
|
|
@@ -1422,7 +1440,7 @@ const config$3 = {
|
|
|
1422
1440
|
.map((prop) => prop.key?.name)
|
|
1423
1441
|
.filter(Boolean);
|
|
1424
1442
|
const correct = getCorrectOrderRelative(orderList, current);
|
|
1425
|
-
if (!
|
|
1443
|
+
if (!sameOrder(correct, current.filter((item) => correct.includes(item)))) {
|
|
1426
1444
|
context.report({
|
|
1427
1445
|
fix: (fixer) => {
|
|
1428
1446
|
const fileContent = context.sourceCode.text;
|
|
@@ -1458,10 +1476,6 @@ const config$3 = {
|
|
|
1458
1476
|
type: 'problem',
|
|
1459
1477
|
},
|
|
1460
1478
|
};
|
|
1461
|
-
function isCorrectSortedAccording(correct, current) {
|
|
1462
|
-
return (JSON.stringify(correct) ===
|
|
1463
|
-
JSON.stringify(current.filter((item) => correct.includes(item))));
|
|
1464
|
-
}
|
|
1465
1479
|
function getCorrectOrderRelative(correct, current) {
|
|
1466
1480
|
return correct.filter((item) => current.includes(item));
|
|
1467
1481
|
}
|
|
@@ -1506,12 +1520,11 @@ function isExternalPureTuple(typeChecker, type) {
|
|
|
1506
1520
|
return typeArgs.every((item) => isClassType(item));
|
|
1507
1521
|
}
|
|
1508
1522
|
|
|
1509
|
-
const createRule$
|
|
1523
|
+
const createRule$i = ESLintUtils.RuleCreator((name) => name);
|
|
1510
1524
|
const MESSAGE_ID$7 = 'spreadArrays';
|
|
1511
|
-
var flatExports = createRule$
|
|
1525
|
+
var flatExports = createRule$i({
|
|
1512
1526
|
create(context) {
|
|
1513
|
-
const
|
|
1514
|
-
const typeChecker = parserServices.program.getTypeChecker();
|
|
1527
|
+
const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
|
|
1515
1528
|
const arrays = new Map();
|
|
1516
1529
|
const purityCache = new WeakMap();
|
|
1517
1530
|
const isPureArray = (arr) => {
|
|
@@ -1595,7 +1608,7 @@ var flatExports = createRule$h({
|
|
|
1595
1608
|
isDirty = true;
|
|
1596
1609
|
continue;
|
|
1597
1610
|
}
|
|
1598
|
-
const tsNode =
|
|
1611
|
+
const tsNode = esTreeNodeToTSNodeMap.get(el);
|
|
1599
1612
|
const elType = typeChecker.getTypeAtLocation(tsNode);
|
|
1600
1613
|
const isClass = isClassType(elType);
|
|
1601
1614
|
const isArrayLike = typeChecker.isArrayLikeType(elType) ||
|
|
@@ -1675,8 +1688,53 @@ function getDecoratorMetadata(decorator, allowedNames) {
|
|
|
1675
1688
|
return isObject(arg) ? arg : null;
|
|
1676
1689
|
}
|
|
1677
1690
|
|
|
1678
|
-
function
|
|
1679
|
-
return
|
|
1691
|
+
function isStringLiteral(node) {
|
|
1692
|
+
return node?.type === AST_NODE_TYPES.Literal && typeof node.value === 'string';
|
|
1693
|
+
}
|
|
1694
|
+
function isStaticTemplateLiteral(node) {
|
|
1695
|
+
return (node?.type === AST_NODE_TYPES.TemplateLiteral &&
|
|
1696
|
+
node.expressions.length === 0 &&
|
|
1697
|
+
node.quasis.length === 1);
|
|
1698
|
+
}
|
|
1699
|
+
function getStaticStringValue(node) {
|
|
1700
|
+
if (isStringLiteral(node)) {
|
|
1701
|
+
return node.value;
|
|
1702
|
+
}
|
|
1703
|
+
if (!isStaticTemplateLiteral(node)) {
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw ?? '';
|
|
1707
|
+
}
|
|
1708
|
+
function isEmptyStaticString(node) {
|
|
1709
|
+
return getStaticStringValue(node) === '';
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
function getStaticPropertyName(key) {
|
|
1713
|
+
if (key.type === AST_NODE_TYPES.Identifier) {
|
|
1714
|
+
return key.name;
|
|
1715
|
+
}
|
|
1716
|
+
if (key.type === AST_NODE_TYPES.Literal &&
|
|
1717
|
+
(typeof key.value === 'string' || typeof key.value === 'number')) {
|
|
1718
|
+
return String(key.value);
|
|
1719
|
+
}
|
|
1720
|
+
return getStaticStringValue(key);
|
|
1721
|
+
}
|
|
1722
|
+
function getObjectPropertyName(node) {
|
|
1723
|
+
if (node.computed) {
|
|
1724
|
+
return null;
|
|
1725
|
+
}
|
|
1726
|
+
return getStaticPropertyName(node.key);
|
|
1727
|
+
}
|
|
1728
|
+
function getMemberExpressionPropertyName(node) {
|
|
1729
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
1730
|
+
return node.property.name;
|
|
1731
|
+
}
|
|
1732
|
+
return node.computed ? getStaticStringValue(node.property) : null;
|
|
1733
|
+
}
|
|
1734
|
+
function getClassMemberName(member) {
|
|
1735
|
+
return member.key.type === AST_NODE_TYPES.PrivateIdentifier
|
|
1736
|
+
? null
|
|
1737
|
+
: getStaticPropertyName(member.key);
|
|
1680
1738
|
}
|
|
1681
1739
|
|
|
1682
1740
|
const DEFAULT_GROUP = '$DEFAULT';
|
|
@@ -1744,8 +1802,8 @@ const PRESETS = {
|
|
|
1744
1802
|
$VUE: ['$CLASS', '$ID', '$VUE_ATTRIBUTE'],
|
|
1745
1803
|
$VUE_ATTRIBUTE: /^v-/,
|
|
1746
1804
|
};
|
|
1747
|
-
const createRule$
|
|
1748
|
-
const rule$
|
|
1805
|
+
const createRule$h = ESLintUtils.RuleCreator((name) => name);
|
|
1806
|
+
const rule$k = createRule$h({
|
|
1749
1807
|
create(context, [options]) {
|
|
1750
1808
|
const sourceCode = context.sourceCode;
|
|
1751
1809
|
const settings = {
|
|
@@ -1867,21 +1925,6 @@ function getHostAttributeProperties(hostObject) {
|
|
|
1867
1925
|
}
|
|
1868
1926
|
return properties;
|
|
1869
1927
|
}
|
|
1870
|
-
function getStaticPropertyName(key) {
|
|
1871
|
-
if (key.type === AST_NODE_TYPES$1.Identifier) {
|
|
1872
|
-
return key.name;
|
|
1873
|
-
}
|
|
1874
|
-
if (key.type === AST_NODE_TYPES$1.Literal &&
|
|
1875
|
-
(typeof key.value === 'string' || typeof key.value === 'number')) {
|
|
1876
|
-
return String(key.value);
|
|
1877
|
-
}
|
|
1878
|
-
if (key.type === AST_NODE_TYPES$1.TemplateLiteral &&
|
|
1879
|
-
key.expressions.length === 0 &&
|
|
1880
|
-
key.quasis.length === 1) {
|
|
1881
|
-
return key.quasis[0]?.value.cooked ?? null;
|
|
1882
|
-
}
|
|
1883
|
-
return null;
|
|
1884
|
-
}
|
|
1885
1928
|
function organizeProperties(properties, options) {
|
|
1886
1929
|
const groups = getGroups(options.attributeGroups.length > 0 ? options.attributeGroups : ['$ANGULAR'], options.attributeIgnoreCase);
|
|
1887
1930
|
const defaultGroup = ensureDefaultGroup(groups);
|
|
@@ -2021,7 +2064,7 @@ const config$2 = {
|
|
|
2021
2064
|
const MESSAGE_ID$5 = 'invalid-injection-token-description';
|
|
2022
2065
|
const ERROR_MESSAGE$3 = "InjectionToken's description should contain token's name";
|
|
2023
2066
|
const NG_DEV_MODE = 'ngDevMode';
|
|
2024
|
-
const createRule$
|
|
2067
|
+
const createRule$g = ESLintUtils.RuleCreator((name) => name);
|
|
2025
2068
|
function getVariableName(node) {
|
|
2026
2069
|
if (node.parent.type !== AST_NODE_TYPES$1.VariableDeclarator) {
|
|
2027
2070
|
return undefined;
|
|
@@ -2029,21 +2072,18 @@ function getVariableName(node) {
|
|
|
2029
2072
|
const { id } = node.parent;
|
|
2030
2073
|
return id.type === AST_NODE_TYPES$1.Identifier ? id.name : undefined;
|
|
2031
2074
|
}
|
|
2032
|
-
function isStringLiteral$1(node) {
|
|
2033
|
-
return node.type === AST_NODE_TYPES$1.Literal && typeof node.value === 'string';
|
|
2034
|
-
}
|
|
2035
2075
|
function isStringLike(node) {
|
|
2036
|
-
return isStringLiteral
|
|
2076
|
+
return isStringLiteral(node) || node.type === AST_NODE_TYPES$1.TemplateLiteral;
|
|
2037
2077
|
}
|
|
2038
2078
|
function getStringValue(node) {
|
|
2039
|
-
if (isStringLiteral
|
|
2079
|
+
if (isStringLiteral(node)) {
|
|
2040
2080
|
return node.value;
|
|
2041
2081
|
}
|
|
2042
2082
|
return node.quasis[0]?.value.raw || '';
|
|
2043
2083
|
}
|
|
2044
|
-
function isEmptyString
|
|
2045
|
-
return (
|
|
2046
|
-
(!('expressions' in node) ||
|
|
2084
|
+
function isEmptyString(node) {
|
|
2085
|
+
return (isEmptyStaticString(node) &&
|
|
2086
|
+
(!('expressions' in node) || node.expressions.length === 0));
|
|
2047
2087
|
}
|
|
2048
2088
|
function isNgDevModeConditional(node) {
|
|
2049
2089
|
return (node.type === AST_NODE_TYPES$1.ConditionalExpression &&
|
|
@@ -2051,7 +2091,7 @@ function isNgDevModeConditional(node) {
|
|
|
2051
2091
|
node.test.name === NG_DEV_MODE &&
|
|
2052
2092
|
isStringLike(node.consequent) &&
|
|
2053
2093
|
isStringLike(node.alternate) &&
|
|
2054
|
-
isEmptyString
|
|
2094
|
+
isEmptyString(node.alternate));
|
|
2055
2095
|
}
|
|
2056
2096
|
function getDescriptionValue(node) {
|
|
2057
2097
|
if (isStringLike(node)) {
|
|
@@ -2092,7 +2132,7 @@ function getNgDevModeDeclarationFix(program, fixer) {
|
|
|
2092
2132
|
}
|
|
2093
2133
|
return fixer.insertTextBeforeRange([0, 0], 'declare const ngDevMode: boolean;\n');
|
|
2094
2134
|
}
|
|
2095
|
-
const rule$
|
|
2135
|
+
const rule$j = createRule$g({
|
|
2096
2136
|
create(context) {
|
|
2097
2137
|
const { sourceCode } = context;
|
|
2098
2138
|
const program = sourceCode.ast;
|
|
@@ -2160,8 +2200,8 @@ const DEFAULT_OPTIONS = {
|
|
|
2160
2200
|
importDeclaration: '^@taiga-ui*',
|
|
2161
2201
|
projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
|
|
2162
2202
|
};
|
|
2163
|
-
const createRule$
|
|
2164
|
-
const rule$
|
|
2203
|
+
const createRule$f = ESLintUtils.RuleCreator((name) => name);
|
|
2204
|
+
const rule$i = createRule$f({
|
|
2165
2205
|
create(context) {
|
|
2166
2206
|
const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
|
|
2167
2207
|
const hasNonCodeExtension = (source) => {
|
|
@@ -2248,13 +2288,13 @@ const rule$h = createRule$e({
|
|
|
2248
2288
|
name: 'no-deep-imports',
|
|
2249
2289
|
});
|
|
2250
2290
|
|
|
2251
|
-
const createRule$
|
|
2291
|
+
const createRule$e = ESLintUtils.RuleCreator((name) => name);
|
|
2252
2292
|
const resolveCacheByOptions = new WeakMap();
|
|
2253
2293
|
const nearestFileUpCache = new Map();
|
|
2254
2294
|
const markerCache = new Map();
|
|
2255
2295
|
const indexFileCache = new Map();
|
|
2256
2296
|
const indexExportsCache = new Map();
|
|
2257
|
-
var noDeepImportsToIndexedPackages = createRule$
|
|
2297
|
+
var noDeepImportsToIndexedPackages = createRule$e({
|
|
2258
2298
|
create(context) {
|
|
2259
2299
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
2260
2300
|
const program = parserServices.program;
|
|
@@ -2443,63 +2483,13 @@ function stripKnownExtensions(filePathOrSpecifier) {
|
|
|
2443
2483
|
return filePathOrSpecifier.replace(/\.(?:d\.ts|ts|tsx|js|jsx|mjs|cjs)$/, '');
|
|
2444
2484
|
}
|
|
2445
2485
|
|
|
2446
|
-
|
|
2447
|
-
/**
|
|
2448
|
-
* Returns the local name bound to a named import from a given source.
|
|
2449
|
-
* Handles aliased imports: `import { untracked as ngUntracked } from '@angular/core'`
|
|
2450
|
-
* returns `'ngUntracked'` for `exportedName = 'untracked'`.
|
|
2451
|
-
*/
|
|
2452
|
-
function getLocalNameForImport(program, source, exportedName) {
|
|
2453
|
-
for (const node of program.body) {
|
|
2454
|
-
if (node.type !== AST_NODE_TYPES.ImportDeclaration ||
|
|
2455
|
-
node.source.value !== source) {
|
|
2456
|
-
continue;
|
|
2457
|
-
}
|
|
2458
|
-
for (const spec of node.specifiers) {
|
|
2459
|
-
if (spec.type !== AST_NODE_TYPES.ImportSpecifier) {
|
|
2460
|
-
continue;
|
|
2461
|
-
}
|
|
2462
|
-
const imported = spec.imported.type === AST_NODE_TYPES.Identifier
|
|
2463
|
-
? spec.imported.name
|
|
2464
|
-
: spec.imported.value;
|
|
2465
|
-
if (imported === exportedName) {
|
|
2466
|
-
return spec.local.name;
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
return null;
|
|
2471
|
-
}
|
|
2472
|
-
function findAngularCoreImports(program) {
|
|
2473
|
-
return program.body.filter((node) => node.type === AST_NODE_TYPES.ImportDeclaration &&
|
|
2474
|
-
node.source.value === ANGULAR_CORE$1);
|
|
2475
|
-
}
|
|
2476
|
-
function findRuntimeAngularCoreImport(program) {
|
|
2477
|
-
return (findAngularCoreImports(program).find((node) => node.importKind !== 'type') ?? null);
|
|
2478
|
-
}
|
|
2479
|
-
function findAngularCoreImportSpecifier(program, exportedName) {
|
|
2480
|
-
for (const importDecl of findAngularCoreImports(program)) {
|
|
2481
|
-
for (const specifier of importDecl.specifiers) {
|
|
2482
|
-
if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) {
|
|
2483
|
-
continue;
|
|
2484
|
-
}
|
|
2485
|
-
const imported = specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
2486
|
-
? specifier.imported.name
|
|
2487
|
-
: specifier.imported.value;
|
|
2488
|
-
if (imported === exportedName) {
|
|
2489
|
-
return { importDecl, specifier };
|
|
2490
|
-
}
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
|
-
return null;
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
function isFunctionLike$1(node) {
|
|
2486
|
+
function isFunctionLike(node) {
|
|
2497
2487
|
return (node.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
2498
2488
|
node.type === AST_NODE_TYPES.FunctionDeclaration ||
|
|
2499
2489
|
node.type === AST_NODE_TYPES.FunctionExpression);
|
|
2500
2490
|
}
|
|
2501
2491
|
function getOrderedChildren(node) {
|
|
2502
|
-
if (isFunctionLike
|
|
2492
|
+
if (isFunctionLike(node)) {
|
|
2503
2493
|
const children = [
|
|
2504
2494
|
...node.params,
|
|
2505
2495
|
node.body,
|
|
@@ -2568,7 +2558,7 @@ function getOrderedChildren(node) {
|
|
|
2568
2558
|
function walkSynchronousAst(root, visitor) {
|
|
2569
2559
|
traverse(root, true);
|
|
2570
2560
|
function traverse(node, isRoot = false) {
|
|
2571
|
-
if (visitor(node) === false || (!isRoot && isFunctionLike
|
|
2561
|
+
if (visitor(node) === false || (!isRoot && isFunctionLike(node))) {
|
|
2572
2562
|
return false;
|
|
2573
2563
|
}
|
|
2574
2564
|
if (node.type === AST_NODE_TYPES.AwaitExpression) {
|
|
@@ -2597,7 +2587,7 @@ function walkAfterAsyncBoundaryAst(root, visitor) {
|
|
|
2597
2587
|
if (afterBoundary) {
|
|
2598
2588
|
visitor(node);
|
|
2599
2589
|
}
|
|
2600
|
-
if (!isRoot && isFunctionLike
|
|
2590
|
+
if (!isRoot && isFunctionLike(node)) {
|
|
2601
2591
|
return false;
|
|
2602
2592
|
}
|
|
2603
2593
|
if (node.type === AST_NODE_TYPES.AwaitExpression) {
|
|
@@ -2660,6 +2650,56 @@ function walkAst(root, visitor) {
|
|
|
2660
2650
|
}
|
|
2661
2651
|
}
|
|
2662
2652
|
|
|
2653
|
+
const ANGULAR_CORE$1 = '@angular/core';
|
|
2654
|
+
/**
|
|
2655
|
+
* Returns the local name bound to a named import from a given source.
|
|
2656
|
+
* Handles aliased imports: `import { untracked as ngUntracked } from '@angular/core'`
|
|
2657
|
+
* returns `'ngUntracked'` for `exportedName = 'untracked'`.
|
|
2658
|
+
*/
|
|
2659
|
+
function getLocalNameForImport(program, source, exportedName) {
|
|
2660
|
+
for (const node of program.body) {
|
|
2661
|
+
if (node.type !== AST_NODE_TYPES.ImportDeclaration ||
|
|
2662
|
+
node.source.value !== source) {
|
|
2663
|
+
continue;
|
|
2664
|
+
}
|
|
2665
|
+
for (const spec of node.specifiers) {
|
|
2666
|
+
if (spec.type !== AST_NODE_TYPES.ImportSpecifier) {
|
|
2667
|
+
continue;
|
|
2668
|
+
}
|
|
2669
|
+
const imported = spec.imported.type === AST_NODE_TYPES.Identifier
|
|
2670
|
+
? spec.imported.name
|
|
2671
|
+
: spec.imported.value;
|
|
2672
|
+
if (imported === exportedName) {
|
|
2673
|
+
return spec.local.name;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
return null;
|
|
2678
|
+
}
|
|
2679
|
+
function findAngularCoreImports(program) {
|
|
2680
|
+
return program.body.filter((node) => node.type === AST_NODE_TYPES.ImportDeclaration &&
|
|
2681
|
+
node.source.value === ANGULAR_CORE$1);
|
|
2682
|
+
}
|
|
2683
|
+
function findRuntimeAngularCoreImport(program) {
|
|
2684
|
+
return (findAngularCoreImports(program).find((node) => node.importKind !== 'type') ?? null);
|
|
2685
|
+
}
|
|
2686
|
+
function findAngularCoreImportSpecifier(program, exportedName) {
|
|
2687
|
+
for (const importDecl of findAngularCoreImports(program)) {
|
|
2688
|
+
for (const specifier of importDecl.specifiers) {
|
|
2689
|
+
if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) {
|
|
2690
|
+
continue;
|
|
2691
|
+
}
|
|
2692
|
+
const imported = specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
2693
|
+
? specifier.imported.name
|
|
2694
|
+
: specifier.imported.value;
|
|
2695
|
+
if (imported === exportedName) {
|
|
2696
|
+
return { importDecl, specifier };
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
return null;
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2663
2703
|
const ANGULAR_CORE = '@angular/core';
|
|
2664
2704
|
const SIGNAL_WRITE_METHODS = new Set(['mutate', 'set', 'update']);
|
|
2665
2705
|
const AFTER_RENDER_EFFECT_PHASES = new Map([
|
|
@@ -2668,10 +2708,14 @@ const AFTER_RENDER_EFFECT_PHASES = new Map([
|
|
|
2668
2708
|
['read', 'afterRenderEffect().read'],
|
|
2669
2709
|
['write', 'afterRenderEffect().write'],
|
|
2670
2710
|
]);
|
|
2671
|
-
function isReactiveCallback
|
|
2711
|
+
function isReactiveCallback(node) {
|
|
2672
2712
|
return (node?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
2673
2713
|
node?.type === AST_NODE_TYPES.FunctionExpression);
|
|
2674
2714
|
}
|
|
2715
|
+
function getReactiveCallbackArgument(node) {
|
|
2716
|
+
const [arg] = node.arguments;
|
|
2717
|
+
return isReactiveCallback(arg) ? arg : null;
|
|
2718
|
+
}
|
|
2675
2719
|
function getPropertyName(property) {
|
|
2676
2720
|
if (property.computed) {
|
|
2677
2721
|
return null;
|
|
@@ -2691,11 +2735,11 @@ function isAngularCoreCall(node, program, exportedName) {
|
|
|
2691
2735
|
node.arguments.length >= 1);
|
|
2692
2736
|
}
|
|
2693
2737
|
function appendFirstArgReactiveScope(scopes, call, kind) {
|
|
2694
|
-
const
|
|
2695
|
-
if (!
|
|
2738
|
+
const callback = getReactiveCallbackArgument(call);
|
|
2739
|
+
if (!callback) {
|
|
2696
2740
|
return;
|
|
2697
2741
|
}
|
|
2698
|
-
scopes.push({ callback
|
|
2742
|
+
scopes.push({ callback, kind, owner: call, reportNode: call });
|
|
2699
2743
|
}
|
|
2700
2744
|
function appendObjectPropertyReactiveScopes(scopes, call, object, labels) {
|
|
2701
2745
|
for (const property of object.properties) {
|
|
@@ -2704,7 +2748,7 @@ function appendObjectPropertyReactiveScopes(scopes, call, object, labels) {
|
|
|
2704
2748
|
}
|
|
2705
2749
|
const name = getPropertyName(property);
|
|
2706
2750
|
const label = name ? labels.get(name) : null;
|
|
2707
|
-
if (!label || !isReactiveCallback
|
|
2751
|
+
if (!label || !isReactiveCallback(property.value)) {
|
|
2708
2752
|
continue;
|
|
2709
2753
|
}
|
|
2710
2754
|
scopes.push({
|
|
@@ -2914,54 +2958,74 @@ function collectSignalUsages(scopeNode, checker, esTreeNodeToTSNodeMap, program)
|
|
|
2914
2958
|
});
|
|
2915
2959
|
return { reads, writes };
|
|
2916
2960
|
}
|
|
2917
|
-
|
|
2918
|
-
const ANGULAR_SIGNALS_UNTRACKED_GUIDE_URL = 'https://angular.dev/guide/signals#reading-without-tracking-dependencies';
|
|
2919
|
-
const ANGULAR_SIGNALS_ASYNC_GUIDE_URL = 'https://angular.dev/guide/signals#reactive-context-and-async-operations';
|
|
2920
|
-
const UNTRACKED_RULES_README_URL = 'https://github.com/taiga-family/taiga-ui/blob/main/projects/eslint-plugin-experience-next/README.md';
|
|
2921
|
-
const createUntrackedRule = ESLintUtils.RuleCreator((name) => `${UNTRACKED_RULES_README_URL}#${name}`);
|
|
2922
|
-
|
|
2923
|
-
/**
|
|
2924
|
-
* Collects signal reads that appear inside `untracked(...)` callbacks within
|
|
2925
|
-
* `root`, without descending into nested `untracked(...)` scopes.
|
|
2926
|
-
*/
|
|
2927
|
-
function collectReadsInsideUntracked(root, checker, esTreeNodeToTSNodeMap, program) {
|
|
2961
|
+
function collectSignalReadsInsideUntracked(root, checker, esTreeNodeToTSNodeMap, program) {
|
|
2928
2962
|
const reads = [];
|
|
2929
2963
|
walkSynchronousAst(root, (node) => {
|
|
2930
2964
|
if (node.type !== AST_NODE_TYPES.CallExpression ||
|
|
2931
2965
|
!isAngularUntrackedCall(node, program)) {
|
|
2932
2966
|
return;
|
|
2933
2967
|
}
|
|
2934
|
-
const
|
|
2935
|
-
if (!
|
|
2936
|
-
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
2937
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
2968
|
+
const callback = getReactiveCallbackArgument(node);
|
|
2969
|
+
if (!callback) {
|
|
2938
2970
|
return false;
|
|
2939
2971
|
}
|
|
2940
|
-
walkSynchronousAst(
|
|
2972
|
+
walkSynchronousAst(callback, (inner) => {
|
|
2941
2973
|
if (inner.type === AST_NODE_TYPES.CallExpression &&
|
|
2942
2974
|
isSignalReadCall(inner, checker, esTreeNodeToTSNodeMap)) {
|
|
2943
2975
|
reads.push(inner);
|
|
2944
2976
|
}
|
|
2945
2977
|
});
|
|
2946
|
-
return false;
|
|
2978
|
+
return false;
|
|
2947
2979
|
});
|
|
2948
2980
|
return reads;
|
|
2949
2981
|
}
|
|
2950
|
-
|
|
2982
|
+
function isReactiveOwnerCall(node, program) {
|
|
2983
|
+
return (node.type === AST_NODE_TYPES.CallExpression &&
|
|
2984
|
+
getReactiveScopes(node, program).length > 0);
|
|
2985
|
+
}
|
|
2986
|
+
function getReturnedReactiveOwnerCall(node, program) {
|
|
2987
|
+
const callback = getReactiveCallbackArgument(node);
|
|
2988
|
+
if (!callback) {
|
|
2989
|
+
return null;
|
|
2990
|
+
}
|
|
2991
|
+
if (isReactiveOwnerCall(callback.body, program)) {
|
|
2992
|
+
return callback.body;
|
|
2993
|
+
}
|
|
2994
|
+
if (callback.body.type !== AST_NODE_TYPES.BlockStatement ||
|
|
2995
|
+
callback.body.body.length !== 1) {
|
|
2996
|
+
return null;
|
|
2997
|
+
}
|
|
2998
|
+
const [statement] = callback.body.body;
|
|
2999
|
+
if (statement?.type === AST_NODE_TYPES.ReturnStatement &&
|
|
3000
|
+
statement.argument &&
|
|
3001
|
+
isReactiveOwnerCall(statement.argument, program)) {
|
|
3002
|
+
return statement.argument;
|
|
3003
|
+
}
|
|
3004
|
+
if (node.parent.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
3005
|
+
statement?.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
3006
|
+
isReactiveOwnerCall(statement.expression, program)) {
|
|
3007
|
+
return statement.expression;
|
|
3008
|
+
}
|
|
3009
|
+
return null;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
const ANGULAR_SIGNALS_UNTRACKED_GUIDE_URL = 'https://angular.dev/guide/signals#reading-without-tracking-dependencies';
|
|
3013
|
+
const ANGULAR_SIGNALS_ASYNC_GUIDE_URL = 'https://angular.dev/guide/signals#reactive-context-and-async-operations';
|
|
3014
|
+
const UNTRACKED_RULES_README_URL = 'https://github.com/taiga-family/taiga-ui/blob/main/projects/eslint-plugin-experience-next/README.md';
|
|
3015
|
+
const createUntrackedRule = ESLintUtils.RuleCreator((name) => `${UNTRACKED_RULES_README_URL}#${name}`);
|
|
3016
|
+
|
|
3017
|
+
const rule$h = createUntrackedRule({
|
|
2951
3018
|
create(context) {
|
|
2952
|
-
const
|
|
2953
|
-
const
|
|
2954
|
-
const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
|
|
2955
|
-
const { sourceCode } = context;
|
|
2956
|
-
const program = sourceCode.ast;
|
|
3019
|
+
const { checker, esTreeNodeToTSNodeMap, program } = getTypeAwareRuleContext(context);
|
|
3020
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
2957
3021
|
return {
|
|
2958
3022
|
CallExpression(node) {
|
|
2959
3023
|
for (const scope of getReactiveScopes(node, program)) {
|
|
2960
|
-
const { reads: trackedReads } = collectSignalUsages(scope.callback, checker,
|
|
3024
|
+
const { reads: trackedReads } = collectSignalUsages(scope.callback, checker, signalNodeMap, program);
|
|
2961
3025
|
if (trackedReads.length > 0) {
|
|
2962
3026
|
continue;
|
|
2963
3027
|
}
|
|
2964
|
-
const untrackedReads =
|
|
3028
|
+
const untrackedReads = collectSignalReadsInsideUntracked(scope.callback, checker, signalNodeMap, program);
|
|
2965
3029
|
if (untrackedReads.length === 0) {
|
|
2966
3030
|
continue;
|
|
2967
3031
|
}
|
|
@@ -3033,11 +3097,67 @@ const config$1 = {
|
|
|
3033
3097
|
},
|
|
3034
3098
|
};
|
|
3035
3099
|
|
|
3036
|
-
|
|
3037
|
-
|
|
3100
|
+
function findAncestor(node, predicate) {
|
|
3101
|
+
for (let current = node?.parent; current; current = current.parent) {
|
|
3102
|
+
if (predicate(current)) {
|
|
3103
|
+
return current;
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
return null;
|
|
3107
|
+
}
|
|
3108
|
+
function findSelfOrAncestor(node, predicate) {
|
|
3109
|
+
for (let current = node; current; current = current.parent) {
|
|
3110
|
+
if (predicate(current)) {
|
|
3111
|
+
return current;
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
return null;
|
|
3115
|
+
}
|
|
3116
|
+
function hasAncestor(node, predicate) {
|
|
3117
|
+
for (let current = node?.parent; current; current = current.parent) {
|
|
3118
|
+
if (predicate(current)) {
|
|
3119
|
+
return true;
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
return false;
|
|
3123
|
+
}
|
|
3124
|
+
function isNodeInside(node, ancestor) {
|
|
3125
|
+
for (let current = node; current; current = current.parent) {
|
|
3126
|
+
if (current === ancestor) {
|
|
3127
|
+
return true;
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
return false;
|
|
3131
|
+
}
|
|
3132
|
+
function isNodeInsideAny(node, ancestors) {
|
|
3133
|
+
return ancestors.some((ancestor) => isNodeInside(node, ancestor));
|
|
3134
|
+
}
|
|
3135
|
+
function isClassLike(node) {
|
|
3136
|
+
return (node.type === AST_NODE_TYPES.ClassDeclaration ||
|
|
3137
|
+
node.type === AST_NODE_TYPES.ClassExpression);
|
|
3138
|
+
}
|
|
3139
|
+
function isClassMember(node) {
|
|
3140
|
+
return (node.type === AST_NODE_TYPES.MethodDefinition ||
|
|
3141
|
+
node.type === AST_NODE_TYPES.PropertyDefinition);
|
|
3142
|
+
}
|
|
3143
|
+
function getEnclosingFunction(node) {
|
|
3144
|
+
return findAncestor(node, isFunctionLike);
|
|
3145
|
+
}
|
|
3146
|
+
function getEnclosingClass(node) {
|
|
3147
|
+
return findSelfOrAncestor(node, isClassLike);
|
|
3148
|
+
}
|
|
3149
|
+
function getEnclosingClassMember(node) {
|
|
3150
|
+
return findAncestor(node, isClassMember);
|
|
3151
|
+
}
|
|
3152
|
+
function getScopeRoot(node) {
|
|
3153
|
+
return (findAncestor(node, (ancestor) => ancestor.type === AST_NODE_TYPES.Program || isFunctionLike(ancestor)) ?? node);
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
const createRule$d = ESLintUtils.RuleCreator((name) => name);
|
|
3157
|
+
const rule$g = createRule$d({
|
|
3038
3158
|
create(context) {
|
|
3039
3159
|
const checkImplicitPublic = (node) => {
|
|
3040
|
-
const classRef =
|
|
3160
|
+
const classRef = getEnclosingClass(node);
|
|
3041
3161
|
if (!classRef ||
|
|
3042
3162
|
node.kind === 'constructor' ||
|
|
3043
3163
|
!!node?.accessibility ||
|
|
@@ -3095,16 +3215,64 @@ const rule$f = createRule$c({
|
|
|
3095
3215
|
},
|
|
3096
3216
|
name: 'explicit-public-member',
|
|
3097
3217
|
});
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3218
|
+
|
|
3219
|
+
function getParenthesizedInner(node) {
|
|
3220
|
+
const maybeNode = node;
|
|
3221
|
+
if (maybeNode.type === 'ParenthesizedExpression') {
|
|
3222
|
+
return maybeNode.expression ?? null;
|
|
3101
3223
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3224
|
+
return null;
|
|
3225
|
+
}
|
|
3226
|
+
function unwrapParenthesized(node) {
|
|
3227
|
+
let current = node;
|
|
3228
|
+
let inner = getParenthesizedInner(current);
|
|
3229
|
+
while (inner) {
|
|
3230
|
+
current = inner;
|
|
3231
|
+
inner = getParenthesizedInner(current);
|
|
3104
3232
|
}
|
|
3105
|
-
return
|
|
3233
|
+
return current;
|
|
3106
3234
|
}
|
|
3107
3235
|
|
|
3236
|
+
const createRule$c = ESLintUtils.RuleCreator((name) => name);
|
|
3237
|
+
function isBooleanTrue(node) {
|
|
3238
|
+
const unwrapped = unwrapParenthesized(node);
|
|
3239
|
+
return unwrapped.type === AST_NODE_TYPES.Literal && unwrapped.value === true;
|
|
3240
|
+
}
|
|
3241
|
+
const rule$f = createRule$c({
|
|
3242
|
+
create(context) {
|
|
3243
|
+
return {
|
|
3244
|
+
ForStatement(node) {
|
|
3245
|
+
if (!node.test) {
|
|
3246
|
+
context.report({
|
|
3247
|
+
messageId: 'forLoop',
|
|
3248
|
+
node,
|
|
3249
|
+
});
|
|
3250
|
+
}
|
|
3251
|
+
},
|
|
3252
|
+
WhileStatement(node) {
|
|
3253
|
+
if (isBooleanTrue(node.test)) {
|
|
3254
|
+
context.report({
|
|
3255
|
+
messageId: 'whileLoop',
|
|
3256
|
+
node: node.test,
|
|
3257
|
+
});
|
|
3258
|
+
}
|
|
3259
|
+
},
|
|
3260
|
+
};
|
|
3261
|
+
},
|
|
3262
|
+
meta: {
|
|
3263
|
+
docs: {
|
|
3264
|
+
description: 'Disallow `while (true)` and `for` loops without an explicit condition. Prefer loops with meaningful exit conditions.',
|
|
3265
|
+
},
|
|
3266
|
+
messages: {
|
|
3267
|
+
forLoop: 'Use an explicit exit condition instead of a `for` loop without a condition.',
|
|
3268
|
+
whileLoop: 'Use an explicit exit condition instead of `while (true)`.',
|
|
3269
|
+
},
|
|
3270
|
+
schema: [],
|
|
3271
|
+
type: 'suggestion',
|
|
3272
|
+
},
|
|
3273
|
+
name: 'no-infinite-loop',
|
|
3274
|
+
});
|
|
3275
|
+
|
|
3108
3276
|
const createRule$b = ESLintUtils.RuleCreator((name) => name);
|
|
3109
3277
|
const LEGACY_PEER_DEPS_PATTERN = /^legacy-peer-deps\s*=\s*true$/i;
|
|
3110
3278
|
const rule$e = createRule$b({
|
|
@@ -3148,8 +3316,7 @@ const rule$e = createRule$b({
|
|
|
3148
3316
|
const createRule$a = ESLintUtils.RuleCreator((name) => name);
|
|
3149
3317
|
const rule$d = createRule$a({
|
|
3150
3318
|
create(context) {
|
|
3151
|
-
const
|
|
3152
|
-
const checker = services.program.getTypeChecker();
|
|
3319
|
+
const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
|
|
3153
3320
|
return {
|
|
3154
3321
|
CallExpression(node) {
|
|
3155
3322
|
const callee = node.callee;
|
|
@@ -3163,18 +3330,18 @@ const rule$d = createRule$a({
|
|
|
3163
3330
|
return;
|
|
3164
3331
|
}
|
|
3165
3332
|
const [argument] = node.arguments;
|
|
3166
|
-
if (!argument || !
|
|
3333
|
+
if (!argument || !isEmptyStaticString(argument)) {
|
|
3167
3334
|
return;
|
|
3168
3335
|
}
|
|
3169
3336
|
const objectExpression = callee.object;
|
|
3170
|
-
const tsNode =
|
|
3337
|
+
const tsNode = esTreeNodeToTSNodeMap.get(objectExpression);
|
|
3171
3338
|
const type = checker.getTypeAtLocation(tsNode);
|
|
3172
3339
|
if (!isPlaywrightLocator(type, checker)) {
|
|
3173
3340
|
return;
|
|
3174
3341
|
}
|
|
3175
3342
|
context.report({
|
|
3176
3343
|
fix(fixer) {
|
|
3177
|
-
const objectText =
|
|
3344
|
+
const objectText = sourceCode.getText(objectExpression);
|
|
3178
3345
|
return fixer.replaceText(node, `${objectText}.clear()`);
|
|
3179
3346
|
},
|
|
3180
3347
|
messageId: 'useClear',
|
|
@@ -3194,13 +3361,6 @@ const rule$d = createRule$a({
|
|
|
3194
3361
|
},
|
|
3195
3362
|
name: 'no-playwright-empty-fill',
|
|
3196
3363
|
});
|
|
3197
|
-
function isEmptyString(node) {
|
|
3198
|
-
return ((node.type === AST_NODE_TYPES$1.Literal && node.value === '') ||
|
|
3199
|
-
(node.type === AST_NODE_TYPES$1.TemplateLiteral &&
|
|
3200
|
-
node.expressions.length === 0 &&
|
|
3201
|
-
node.quasis.length === 1 &&
|
|
3202
|
-
node.quasis[0]?.value.cooked === ''));
|
|
3203
|
-
}
|
|
3204
3364
|
function isPlaywrightLocator(type, checker) {
|
|
3205
3365
|
if (isPlaywrightLocatorType(type)) {
|
|
3206
3366
|
return true;
|
|
@@ -3339,15 +3499,14 @@ function collectArrayExpressions(node) {
|
|
|
3339
3499
|
}
|
|
3340
3500
|
const rule$c = createRule$9({
|
|
3341
3501
|
create(context) {
|
|
3342
|
-
const
|
|
3343
|
-
const typeChecker = parserServices.program.getTypeChecker();
|
|
3502
|
+
const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
|
|
3344
3503
|
const ignoreTupleContextualTyping = context.options[0]?.ignoreTupleContextualTyping ?? true;
|
|
3345
3504
|
function check(node, typeAnnotation, value) {
|
|
3346
3505
|
if (!typeAnnotation || !value) {
|
|
3347
3506
|
return;
|
|
3348
3507
|
}
|
|
3349
|
-
const tsNode =
|
|
3350
|
-
const tsValueNode =
|
|
3508
|
+
const tsNode = esTreeNodeToTSNodeMap.get(node);
|
|
3509
|
+
const tsValueNode = esTreeNodeToTSNodeMap.get(value);
|
|
3351
3510
|
const declaredType = typeChecker.getTypeAtLocation(tsNode);
|
|
3352
3511
|
const inferredType = typeChecker.getTypeAtLocation(tsValueNode);
|
|
3353
3512
|
if (typeChecker.typeToString(declaredType) !==
|
|
@@ -3371,7 +3530,7 @@ const rule$c = createRule$9({
|
|
|
3371
3530
|
if (ignoreTupleContextualTyping) {
|
|
3372
3531
|
const arrayExpressions = collectArrayExpressions(value);
|
|
3373
3532
|
for (const arrayExpression of arrayExpressions) {
|
|
3374
|
-
const tsArrayNode =
|
|
3533
|
+
const tsArrayNode = esTreeNodeToTSNodeMap.get(arrayExpression);
|
|
3375
3534
|
if (typeChecker.isTupleType(typeChecker.getTypeAtLocation(tsArrayNode))) {
|
|
3376
3535
|
return;
|
|
3377
3536
|
}
|
|
@@ -3465,16 +3624,6 @@ function unwrapExpression(expression) {
|
|
|
3465
3624
|
return current;
|
|
3466
3625
|
}
|
|
3467
3626
|
|
|
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
|
-
}
|
|
3474
|
-
function isReactiveCallback(node) {
|
|
3475
|
-
return (node?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
3476
|
-
node?.type === AST_NODE_TYPES.FunctionExpression);
|
|
3477
|
-
}
|
|
3478
3627
|
function unwrapMutationTarget(node) {
|
|
3479
3628
|
let current = node;
|
|
3480
3629
|
while (current.type === AST_NODE_TYPES.TSAsExpression ||
|
|
@@ -3508,6 +3657,7 @@ function collectMutationTargets(node) {
|
|
|
3508
3657
|
return [];
|
|
3509
3658
|
}
|
|
3510
3659
|
}
|
|
3660
|
+
|
|
3511
3661
|
function getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap) {
|
|
3512
3662
|
const tsNode = esTreeNodeToTSNodeMap.get(node);
|
|
3513
3663
|
if (!tsNode) {
|
|
@@ -3515,6 +3665,11 @@ function getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap) {
|
|
|
3515
3665
|
}
|
|
3516
3666
|
return checker.getSymbolAtLocation(tsNode) ?? null;
|
|
3517
3667
|
}
|
|
3668
|
+
|
|
3669
|
+
const createRule$8 = ESLintUtils.RuleCreator((name) => name);
|
|
3670
|
+
function isFunctionLikeScope(node) {
|
|
3671
|
+
return !!node && isFunctionLike(node);
|
|
3672
|
+
}
|
|
3518
3673
|
function isLocalIdentifier(node, context, localScopes) {
|
|
3519
3674
|
const symbol = getSymbolAtNode(node, context.checker, context.esTreeNodeToTSNodeMap);
|
|
3520
3675
|
if (!symbol) {
|
|
@@ -3671,9 +3826,9 @@ function functionHasObservableSideEffects(root, context, localScopes, visitedFun
|
|
|
3671
3826
|
return false;
|
|
3672
3827
|
}
|
|
3673
3828
|
if (isAngularUntrackedCall(node, context.program)) {
|
|
3674
|
-
const
|
|
3675
|
-
if (
|
|
3676
|
-
functionHasObservableSideEffects(
|
|
3829
|
+
const callback = getReactiveCallbackArgument(node);
|
|
3830
|
+
if (callback &&
|
|
3831
|
+
functionHasObservableSideEffects(callback, context, [...localScopes, callback], visitedFunctions)) {
|
|
3677
3832
|
hasSideEffect = true;
|
|
3678
3833
|
}
|
|
3679
3834
|
return false;
|
|
@@ -3727,9 +3882,9 @@ function inspectComputedBody(root, context, localScopes, visitedFunctions, repor
|
|
|
3727
3882
|
return false;
|
|
3728
3883
|
}
|
|
3729
3884
|
if (isAngularUntrackedCall(node, context.program)) {
|
|
3730
|
-
const
|
|
3731
|
-
if (
|
|
3732
|
-
inspectComputedBody(
|
|
3885
|
+
const callback = getReactiveCallbackArgument(node);
|
|
3886
|
+
if (callback) {
|
|
3887
|
+
inspectComputedBody(callback, context, [...localScopes, callback], visitedFunctions, report);
|
|
3733
3888
|
}
|
|
3734
3889
|
return false;
|
|
3735
3890
|
}
|
|
@@ -3770,12 +3925,9 @@ function inspectComputedBody(root, context, localScopes, visitedFunctions, repor
|
|
|
3770
3925
|
}
|
|
3771
3926
|
const rule$b = createRule$8({
|
|
3772
3927
|
create(context) {
|
|
3773
|
-
const
|
|
3774
|
-
const
|
|
3775
|
-
const
|
|
3776
|
-
const tsNodeToESTreeNodeMap = parserServices.tsNodeToESTreeNodeMap;
|
|
3777
|
-
const { sourceCode } = context;
|
|
3778
|
-
const program = sourceCode.ast;
|
|
3928
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
|
|
3929
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
3930
|
+
const estreeNodeMap = tsNodeToESTreeNodeMap;
|
|
3779
3931
|
return {
|
|
3780
3932
|
CallExpression(node) {
|
|
3781
3933
|
for (const scope of getReactiveScopes(node, program)) {
|
|
@@ -3784,10 +3936,10 @@ const rule$b = createRule$8({
|
|
|
3784
3936
|
}
|
|
3785
3937
|
const analysisContext = {
|
|
3786
3938
|
checker,
|
|
3787
|
-
esTreeNodeToTSNodeMap,
|
|
3939
|
+
esTreeNodeToTSNodeMap: signalNodeMap,
|
|
3788
3940
|
program,
|
|
3789
3941
|
reported: new Set(),
|
|
3790
|
-
tsNodeToESTreeNodeMap,
|
|
3942
|
+
tsNodeToESTreeNodeMap: estreeNodeMap,
|
|
3791
3943
|
};
|
|
3792
3944
|
inspectComputedBody(scope.callback, analysisContext, [scope.callback], new Set([String(scope.callback.range)]), (reportNode) => {
|
|
3793
3945
|
context.report({
|
|
@@ -3816,11 +3968,8 @@ const rule$b = createRule$8({
|
|
|
3816
3968
|
|
|
3817
3969
|
const rule$a = createUntrackedRule({
|
|
3818
3970
|
create(context) {
|
|
3819
|
-
const
|
|
3820
|
-
const
|
|
3821
|
-
const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
|
|
3822
|
-
const { sourceCode } = context;
|
|
3823
|
-
const program = sourceCode.ast;
|
|
3971
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
|
|
3972
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
3824
3973
|
return {
|
|
3825
3974
|
CallExpression(node) {
|
|
3826
3975
|
for (const scope of getReactiveScopes(node, program)) {
|
|
@@ -3828,7 +3977,7 @@ const rule$a = createUntrackedRule({
|
|
|
3828
3977
|
walkAfterAsyncBoundaryAst(scope.callback, (inner) => {
|
|
3829
3978
|
if (inner.type !== AST_NODE_TYPES.CallExpression ||
|
|
3830
3979
|
isAngularUntrackedCall(inner, program) ||
|
|
3831
|
-
!isSignalReadCall(inner, checker,
|
|
3980
|
+
!isSignalReadCall(inner, checker, signalNodeMap)) {
|
|
3832
3981
|
return;
|
|
3833
3982
|
}
|
|
3834
3983
|
const key = String(inner.range);
|
|
@@ -3864,10 +4013,6 @@ const rule$a = createUntrackedRule({
|
|
|
3864
4013
|
});
|
|
3865
4014
|
|
|
3866
4015
|
const createRule$7 = ESLintUtils.RuleCreator((name) => name);
|
|
3867
|
-
function isStringLiteral(node) {
|
|
3868
|
-
return (node.type === AST_NODE_TYPES.Literal &&
|
|
3869
|
-
typeof node.value === 'string');
|
|
3870
|
-
}
|
|
3871
4016
|
function collectParts(node) {
|
|
3872
4017
|
if (node.type === AST_NODE_TYPES.BinaryExpression && node.operator === '+') {
|
|
3873
4018
|
return [...collectParts(node.left), ...collectParts(node.right)];
|
|
@@ -3917,16 +4062,6 @@ function templateContent(template, renderExpr) {
|
|
|
3917
4062
|
: ''}`)
|
|
3918
4063
|
.join('');
|
|
3919
4064
|
}
|
|
3920
|
-
function hasTemplateLiteralAncestor(node) {
|
|
3921
|
-
let current = node.parent;
|
|
3922
|
-
while (current != null) {
|
|
3923
|
-
if (current.type === AST_NODE_TYPES.TemplateLiteral) {
|
|
3924
|
-
return true;
|
|
3925
|
-
}
|
|
3926
|
-
current = current.parent;
|
|
3927
|
-
}
|
|
3928
|
-
return false;
|
|
3929
|
-
}
|
|
3930
4065
|
const rule$9 = createRule$7({
|
|
3931
4066
|
create(context) {
|
|
3932
4067
|
const { sourceCode } = context;
|
|
@@ -3982,7 +4117,7 @@ const rule$9 = createRule$7({
|
|
|
3982
4117
|
}
|
|
3983
4118
|
// Nested inside a template but not direct child — would produce
|
|
3984
4119
|
// `${`${a}${b}`.method()}`, so skip
|
|
3985
|
-
if (
|
|
4120
|
+
if (hasAncestor(node, (ancestor) => ancestor.type === AST_NODE_TYPES.TemplateLiteral)) {
|
|
3986
4121
|
return;
|
|
3987
4122
|
}
|
|
3988
4123
|
context.report({
|
|
@@ -4096,67 +4231,6 @@ function buildImportRemovalFixes(program, fixer, sourceCode) {
|
|
|
4096
4231
|
return [fixer.remove(untrackedSpec)];
|
|
4097
4232
|
}
|
|
4098
4233
|
|
|
4099
|
-
const IMPERATIVE_UNTRACKED_METHODS = new Set(['registerOnChange', 'writeValue']);
|
|
4100
|
-
function dedent$1(text, extraSpaces) {
|
|
4101
|
-
if (extraSpaces <= 0) {
|
|
4102
|
-
return text;
|
|
4103
|
-
}
|
|
4104
|
-
const prefix = ' '.repeat(extraSpaces);
|
|
4105
|
-
return text
|
|
4106
|
-
.split('\n')
|
|
4107
|
-
.map((line) => (line.startsWith(prefix) ? line.slice(extraSpaces) : line))
|
|
4108
|
-
.join('\n');
|
|
4109
|
-
}
|
|
4110
|
-
function isFunctionLike(node) {
|
|
4111
|
-
return (node.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
4112
|
-
node.type === AST_NODE_TYPES.FunctionDeclaration ||
|
|
4113
|
-
node.type === AST_NODE_TYPES.FunctionExpression);
|
|
4114
|
-
}
|
|
4115
|
-
function getObjectPropertyName(node) {
|
|
4116
|
-
if (node.computed) {
|
|
4117
|
-
return null;
|
|
4118
|
-
}
|
|
4119
|
-
if (node.key.type === AST_NODE_TYPES.Identifier) {
|
|
4120
|
-
return node.key.name;
|
|
4121
|
-
}
|
|
4122
|
-
return typeof node.key.value === 'string' ? node.key.value : null;
|
|
4123
|
-
}
|
|
4124
|
-
function getMemberExpressionPropertyName(node) {
|
|
4125
|
-
if (!node.computed && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
4126
|
-
return node.property.name;
|
|
4127
|
-
}
|
|
4128
|
-
return node.property.type === AST_NODE_TYPES.Literal &&
|
|
4129
|
-
typeof node.property.value === 'string'
|
|
4130
|
-
? node.property.value
|
|
4131
|
-
: null;
|
|
4132
|
-
}
|
|
4133
|
-
function getEnclosingClassMember(node) {
|
|
4134
|
-
for (let current = node.parent; current; current = current.parent) {
|
|
4135
|
-
if (current.type === AST_NODE_TYPES.MethodDefinition ||
|
|
4136
|
-
current.type === AST_NODE_TYPES.PropertyDefinition) {
|
|
4137
|
-
return current;
|
|
4138
|
-
}
|
|
4139
|
-
}
|
|
4140
|
-
return null;
|
|
4141
|
-
}
|
|
4142
|
-
function getEnclosingFunction(node) {
|
|
4143
|
-
for (let current = node.parent; current; current = current.parent) {
|
|
4144
|
-
if (isFunctionLike(current)) {
|
|
4145
|
-
return current;
|
|
4146
|
-
}
|
|
4147
|
-
}
|
|
4148
|
-
return null;
|
|
4149
|
-
}
|
|
4150
|
-
function getClassMemberName(member) {
|
|
4151
|
-
if (member.key.type === AST_NODE_TYPES.Identifier) {
|
|
4152
|
-
return member.key.name;
|
|
4153
|
-
}
|
|
4154
|
-
if (member.key.type === AST_NODE_TYPES.Literal &&
|
|
4155
|
-
typeof member.key.value === 'string') {
|
|
4156
|
-
return member.key.value;
|
|
4157
|
-
}
|
|
4158
|
-
return null;
|
|
4159
|
-
}
|
|
4160
4234
|
function hasNamedDecorator(node, name) {
|
|
4161
4235
|
return node.decorators.some((decorator) => {
|
|
4162
4236
|
const expression = decorator.expression;
|
|
@@ -4168,68 +4242,75 @@ function hasNamedDecorator(node, name) {
|
|
|
4168
4242
|
expression.callee.name === name);
|
|
4169
4243
|
});
|
|
4170
4244
|
}
|
|
4245
|
+
|
|
4171
4246
|
function isPipeTransformMember(member) {
|
|
4172
4247
|
if (getClassMemberName(member) !== 'transform') {
|
|
4173
4248
|
return false;
|
|
4174
4249
|
}
|
|
4175
|
-
|
|
4250
|
+
const ownerClass = getEnclosingClass(member);
|
|
4251
|
+
return !!ownerClass && hasNamedDecorator(ownerClass, 'Pipe');
|
|
4176
4252
|
}
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
parent.left.type === AST_NODE_TYPES.MemberExpression) {
|
|
4186
|
-
const memberName = getMemberExpressionPropertyName(parent.left);
|
|
4187
|
-
if (memberName && IMPERATIVE_UNTRACKED_METHODS.has(memberName)) {
|
|
4188
|
-
return true;
|
|
4189
|
-
}
|
|
4190
|
-
}
|
|
4253
|
+
|
|
4254
|
+
function isAngularInjectionTokenFactoryFunction(fn, program) {
|
|
4255
|
+
const parent = fn.parent;
|
|
4256
|
+
const injectionTokenName = getLocalNameForImport(program, '@angular/core', 'InjectionToken');
|
|
4257
|
+
if (!injectionTokenName ||
|
|
4258
|
+
parent.type !== AST_NODE_TYPES.Property ||
|
|
4259
|
+
getObjectPropertyName(parent) !== 'factory') {
|
|
4260
|
+
return false;
|
|
4191
4261
|
}
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
!!memberName &&
|
|
4199
|
-
(IMPERATIVE_UNTRACKED_METHODS.has(memberName) ||
|
|
4200
|
-
isPipeTransformMember(member))) ||
|
|
4201
|
-
hasAllowedImperativeAssignment(node));
|
|
4262
|
+
const objectExpression = parent.parent;
|
|
4263
|
+
return (objectExpression.type === AST_NODE_TYPES.ObjectExpression &&
|
|
4264
|
+
objectExpression.parent.type === AST_NODE_TYPES.NewExpression &&
|
|
4265
|
+
objectExpression.parent.arguments.includes(objectExpression) &&
|
|
4266
|
+
objectExpression.parent.callee.type === AST_NODE_TYPES.Identifier &&
|
|
4267
|
+
objectExpression.parent.callee.name === injectionTokenName);
|
|
4202
4268
|
}
|
|
4203
|
-
function
|
|
4269
|
+
function isAngularUseFactoryFunction(fn) {
|
|
4204
4270
|
const parent = fn.parent;
|
|
4205
|
-
if (
|
|
4206
|
-
|
|
4271
|
+
if (parent.type !== AST_NODE_TYPES.Property ||
|
|
4272
|
+
getObjectPropertyName(parent) !== 'useFactory') {
|
|
4207
4273
|
return false;
|
|
4208
4274
|
}
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4275
|
+
const objectExpression = parent.parent;
|
|
4276
|
+
return (objectExpression.type === AST_NODE_TYPES.ObjectExpression &&
|
|
4277
|
+
objectExpression.properties.some((property) => property.type === AST_NODE_TYPES.Property &&
|
|
4278
|
+
getObjectPropertyName(property) === 'provide'));
|
|
4212
4279
|
}
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4280
|
+
|
|
4281
|
+
/**
|
|
4282
|
+
* Removes `extraSpaces` leading spaces from every line of `text` that starts
|
|
4283
|
+
* with at least that many spaces.
|
|
4284
|
+
*/
|
|
4285
|
+
function dedent(text, extraSpaces) {
|
|
4286
|
+
if (extraSpaces <= 0) {
|
|
4287
|
+
return text;
|
|
4218
4288
|
}
|
|
4219
|
-
|
|
4289
|
+
const prefix = ' '.repeat(extraSpaces);
|
|
4290
|
+
return text
|
|
4291
|
+
.split('\n')
|
|
4292
|
+
.map((line) => (line.startsWith(prefix) ? line.slice(extraSpaces) : line))
|
|
4293
|
+
.join('\n');
|
|
4294
|
+
}
|
|
4295
|
+
|
|
4296
|
+
function isDirectCallOrNewArgument(node) {
|
|
4297
|
+
const parent = node.parent;
|
|
4298
|
+
if (node.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4299
|
+
node.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
4300
|
+
return false;
|
|
4301
|
+
}
|
|
4302
|
+
return ((parent.type === AST_NODE_TYPES.CallExpression ||
|
|
4303
|
+
parent.type === AST_NODE_TYPES.NewExpression) &&
|
|
4304
|
+
parent.arguments.includes(node));
|
|
4220
4305
|
}
|
|
4221
|
-
function
|
|
4306
|
+
function isStoredFunctionUsedAsCallOrNewArgument(fn, checker, esTreeNodeToTSNodeMap) {
|
|
4222
4307
|
const parent = fn.parent;
|
|
4223
4308
|
if (parent.type !== AST_NODE_TYPES.VariableDeclarator ||
|
|
4224
4309
|
parent.id.type !== AST_NODE_TYPES.Identifier) {
|
|
4225
4310
|
return false;
|
|
4226
4311
|
}
|
|
4227
4312
|
const id = parent.id;
|
|
4228
|
-
const
|
|
4229
|
-
if (!tsNode) {
|
|
4230
|
-
return false;
|
|
4231
|
-
}
|
|
4232
|
-
const symbol = checker.getSymbolAtLocation(tsNode);
|
|
4313
|
+
const symbol = getSymbolAtNode(id, checker, esTreeNodeToTSNodeMap);
|
|
4233
4314
|
if (!symbol) {
|
|
4234
4315
|
return false;
|
|
4235
4316
|
}
|
|
@@ -4241,10 +4322,7 @@ function isStoredCallbackUsedAsArgument(fn, checker, esTreeNodeToTSNodeMap) {
|
|
|
4241
4322
|
node.name !== id.name) {
|
|
4242
4323
|
return;
|
|
4243
4324
|
}
|
|
4244
|
-
const
|
|
4245
|
-
const referenceSymbol = referenceTsNode
|
|
4246
|
-
? checker.getSymbolAtLocation(referenceTsNode)
|
|
4247
|
-
: null;
|
|
4325
|
+
const referenceSymbol = getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap);
|
|
4248
4326
|
if (referenceSymbol !== symbol) {
|
|
4249
4327
|
return;
|
|
4250
4328
|
}
|
|
@@ -4259,81 +4337,50 @@ function isStoredCallbackUsedAsArgument(fn, checker, esTreeNodeToTSNodeMap) {
|
|
|
4259
4337
|
});
|
|
4260
4338
|
return found;
|
|
4261
4339
|
}
|
|
4340
|
+
|
|
4341
|
+
const IMPERATIVE_UNTRACKED_METHODS = new Set(['registerOnChange', 'writeValue']);
|
|
4342
|
+
function hasAllowedImperativeAssignment(node) {
|
|
4343
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
4344
|
+
if (current.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4345
|
+
current.type !== AST_NODE_TYPES.FunctionDeclaration &&
|
|
4346
|
+
current.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
4347
|
+
continue;
|
|
4348
|
+
}
|
|
4349
|
+
const parent = current.parent;
|
|
4350
|
+
if (parent.type === AST_NODE_TYPES.AssignmentExpression &&
|
|
4351
|
+
parent.right === current &&
|
|
4352
|
+
parent.left.type === AST_NODE_TYPES.MemberExpression) {
|
|
4353
|
+
const memberName = getMemberExpressionPropertyName(parent.left);
|
|
4354
|
+
if (memberName && IMPERATIVE_UNTRACKED_METHODS.has(memberName)) {
|
|
4355
|
+
return true;
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
return false;
|
|
4360
|
+
}
|
|
4361
|
+
function isAllowedImperativeAngularContext(node) {
|
|
4362
|
+
const member = getEnclosingClassMember(node);
|
|
4363
|
+
const memberName = member ? getClassMemberName(member) : null;
|
|
4364
|
+
return ((!!member &&
|
|
4365
|
+
!!memberName &&
|
|
4366
|
+
(IMPERATIVE_UNTRACKED_METHODS.has(memberName) ||
|
|
4367
|
+
isPipeTransformMember(member))) ||
|
|
4368
|
+
hasAllowedImperativeAssignment(node));
|
|
4369
|
+
}
|
|
4262
4370
|
function isAllowedDeferredCallbackContext(node, checker, esTreeNodeToTSNodeMap) {
|
|
4263
|
-
|
|
4264
|
-
if (!arg ||
|
|
4265
|
-
arg.type === AST_NODE_TYPES.SpreadElement ||
|
|
4266
|
-
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4267
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
4371
|
+
if (!getReactiveCallbackArgument(node)) {
|
|
4268
4372
|
return false;
|
|
4269
4373
|
}
|
|
4270
4374
|
const fn = getEnclosingFunction(node);
|
|
4271
4375
|
if (!fn) {
|
|
4272
4376
|
return false;
|
|
4273
4377
|
}
|
|
4274
|
-
return (
|
|
4275
|
-
|
|
4276
|
-
}
|
|
4277
|
-
function isAngularInjectionTokenFactoryFunction(fn, program) {
|
|
4278
|
-
const parent = fn.parent;
|
|
4279
|
-
const injectionTokenName = getLocalNameForImport(program, '@angular/core', 'InjectionToken');
|
|
4280
|
-
if (!injectionTokenName ||
|
|
4281
|
-
parent.type !== AST_NODE_TYPES.Property ||
|
|
4282
|
-
getObjectPropertyName(parent) !== 'factory') {
|
|
4283
|
-
return false;
|
|
4284
|
-
}
|
|
4285
|
-
const objectExpression = parent.parent;
|
|
4286
|
-
return (objectExpression.type === AST_NODE_TYPES.ObjectExpression &&
|
|
4287
|
-
objectExpression.parent.type === AST_NODE_TYPES.NewExpression &&
|
|
4288
|
-
objectExpression.parent.arguments.includes(objectExpression) &&
|
|
4289
|
-
objectExpression.parent.callee.type === AST_NODE_TYPES.Identifier &&
|
|
4290
|
-
objectExpression.parent.callee.name === injectionTokenName);
|
|
4291
|
-
}
|
|
4292
|
-
function isAngularUseFactoryFunction(fn) {
|
|
4293
|
-
const parent = fn.parent;
|
|
4294
|
-
if (parent.type !== AST_NODE_TYPES.Property ||
|
|
4295
|
-
getObjectPropertyName(parent) !== 'useFactory') {
|
|
4296
|
-
return false;
|
|
4297
|
-
}
|
|
4298
|
-
const objectExpression = parent.parent;
|
|
4299
|
-
return (objectExpression.type === AST_NODE_TYPES.ObjectExpression &&
|
|
4300
|
-
objectExpression.properties.some((property) => property.type === AST_NODE_TYPES.Property &&
|
|
4301
|
-
getObjectPropertyName(property) === 'provide'));
|
|
4302
|
-
}
|
|
4303
|
-
function isReactiveOwnerCall(node, program) {
|
|
4304
|
-
return (node.type === AST_NODE_TYPES.CallExpression &&
|
|
4305
|
-
getReactiveScopes(node, program).length > 0);
|
|
4306
|
-
}
|
|
4307
|
-
function getFixableReactiveCall(node, program) {
|
|
4308
|
-
const [arg] = node.arguments;
|
|
4309
|
-
if (!arg ||
|
|
4310
|
-
arg.type === AST_NODE_TYPES.SpreadElement ||
|
|
4311
|
-
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4312
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
4313
|
-
return null;
|
|
4314
|
-
}
|
|
4315
|
-
if (isReactiveOwnerCall(arg.body, program)) {
|
|
4316
|
-
return arg.body;
|
|
4317
|
-
}
|
|
4318
|
-
if (arg.body.type !== AST_NODE_TYPES.BlockStatement || arg.body.body.length !== 1) {
|
|
4319
|
-
return null;
|
|
4320
|
-
}
|
|
4321
|
-
const [statement] = arg.body.body;
|
|
4322
|
-
if (statement?.type === AST_NODE_TYPES.ReturnStatement &&
|
|
4323
|
-
statement.argument &&
|
|
4324
|
-
isReactiveOwnerCall(statement.argument, program)) {
|
|
4325
|
-
return statement.argument;
|
|
4326
|
-
}
|
|
4327
|
-
if (node.parent.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
4328
|
-
statement?.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
4329
|
-
isReactiveOwnerCall(statement.expression, program)) {
|
|
4330
|
-
return statement.expression;
|
|
4331
|
-
}
|
|
4332
|
-
return null;
|
|
4378
|
+
return (isDirectCallOrNewArgument(fn) ||
|
|
4379
|
+
isStoredFunctionUsedAsCallOrNewArgument(fn, checker, esTreeNodeToTSNodeMap));
|
|
4333
4380
|
}
|
|
4334
4381
|
function isAllowedLazyAngularFactoryContext(node, program) {
|
|
4335
4382
|
const fn = getEnclosingFunction(node);
|
|
4336
|
-
if (!fn || !
|
|
4383
|
+
if (!fn || !getReturnedReactiveOwnerCall(node, program)) {
|
|
4337
4384
|
return false;
|
|
4338
4385
|
}
|
|
4339
4386
|
return (isAngularInjectionTokenFactoryFunction(fn, program) ||
|
|
@@ -4345,16 +4392,12 @@ function buildReactiveCallReplacement(outerUntrackedCall, reactiveCall, sourceCo
|
|
|
4345
4392
|
outerUntrackedCall.parent.type !== AST_NODE_TYPES.ExpressionStatement) {
|
|
4346
4393
|
return text;
|
|
4347
4394
|
}
|
|
4348
|
-
return dedent
|
|
4395
|
+
return dedent(text, reactiveCall.loc.start.column - outerUntrackedCall.parent.loc.start.column);
|
|
4349
4396
|
}
|
|
4350
4397
|
const rule$8 = createUntrackedRule({
|
|
4351
4398
|
create(context) {
|
|
4352
|
-
const
|
|
4353
|
-
const
|
|
4354
|
-
const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
|
|
4355
|
-
const { sourceCode } = context;
|
|
4356
|
-
const program = sourceCode.ast;
|
|
4357
|
-
const getUntrackedLocalName = () => findUntrackedAlias(program);
|
|
4399
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
|
|
4400
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
4358
4401
|
function isUntrackedUsedElsewhere(localName, excludeNode) {
|
|
4359
4402
|
let found = false;
|
|
4360
4403
|
walkAst(program, (node) => {
|
|
@@ -4375,18 +4418,18 @@ const rule$8 = createUntrackedRule({
|
|
|
4375
4418
|
findEnclosingReactiveScope(node, program) ||
|
|
4376
4419
|
findEnclosingReactiveScopeAfterAsyncBoundary(node, program) ||
|
|
4377
4420
|
isAllowedImperativeAngularContext(node) ||
|
|
4378
|
-
isAllowedDeferredCallbackContext(node, checker,
|
|
4421
|
+
isAllowedDeferredCallbackContext(node, checker, signalNodeMap) ||
|
|
4379
4422
|
isAllowedLazyAngularFactoryContext(node, program)) {
|
|
4380
4423
|
return;
|
|
4381
4424
|
}
|
|
4382
|
-
const reactiveCall =
|
|
4425
|
+
const reactiveCall = getReturnedReactiveOwnerCall(node, program);
|
|
4383
4426
|
context.report({
|
|
4384
4427
|
fix: reactiveCall
|
|
4385
4428
|
? (fixer) => {
|
|
4386
4429
|
const fixes = [
|
|
4387
4430
|
fixer.replaceText(node, buildReactiveCallReplacement(node, reactiveCall, sourceCode)),
|
|
4388
4431
|
];
|
|
4389
|
-
const untrackedLocalName =
|
|
4432
|
+
const untrackedLocalName = findUntrackedAlias(program);
|
|
4390
4433
|
const stillUsed = untrackedLocalName !== null &&
|
|
4391
4434
|
isUntrackedUsedElsewhere(untrackedLocalName, node);
|
|
4392
4435
|
if (!stillUsed) {
|
|
@@ -4418,20 +4461,16 @@ const rule$8 = createUntrackedRule({
|
|
|
4418
4461
|
name: 'no-untracked-outside-reactive-context',
|
|
4419
4462
|
});
|
|
4420
4463
|
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
const prefix = ' '.repeat(extraSpaces);
|
|
4430
|
-
return text
|
|
4431
|
-
.split('\n')
|
|
4432
|
-
.map((line) => (line.startsWith(prefix) ? line.slice(extraSpaces) : line))
|
|
4433
|
-
.join('\n');
|
|
4464
|
+
function collectCallExpressions(root) {
|
|
4465
|
+
const result = [];
|
|
4466
|
+
walkAst(root, (node) => {
|
|
4467
|
+
if (node.type === AST_NODE_TYPES.CallExpression) {
|
|
4468
|
+
result.push(node);
|
|
4469
|
+
}
|
|
4470
|
+
});
|
|
4471
|
+
return result;
|
|
4434
4472
|
}
|
|
4473
|
+
|
|
4435
4474
|
/**
|
|
4436
4475
|
* Builds the replacement text for the parent ExpressionStatement of an
|
|
4437
4476
|
* `untracked(...)` call.
|
|
@@ -4443,13 +4482,11 @@ function dedent(text, extraSpaces) {
|
|
|
4443
4482
|
* Returns null if the untracked argument is not a function expression.
|
|
4444
4483
|
*/
|
|
4445
4484
|
function buildReplacement(untrackedCall, parentStatement, sourceCode) {
|
|
4446
|
-
const
|
|
4447
|
-
if (!
|
|
4448
|
-
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4449
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
4485
|
+
const callback = getReactiveCallbackArgument(untrackedCall);
|
|
4486
|
+
if (!callback) {
|
|
4450
4487
|
return null;
|
|
4451
4488
|
}
|
|
4452
|
-
const { body } =
|
|
4489
|
+
const { body } = callback;
|
|
4453
4490
|
if (body.type === AST_NODE_TYPES.BlockStatement) {
|
|
4454
4491
|
const { body: stmts } = body;
|
|
4455
4492
|
if (stmts.length === 0) {
|
|
@@ -4464,15 +4501,6 @@ function buildReplacement(untrackedCall, parentStatement, sourceCode) {
|
|
|
4464
4501
|
// Expression body: arrow `() => expr` — just emit `expr;`
|
|
4465
4502
|
return `${sourceCode.getText(body)};`;
|
|
4466
4503
|
}
|
|
4467
|
-
function getAllCallExpressions(root) {
|
|
4468
|
-
const result = [];
|
|
4469
|
-
walkAst(root, (node) => {
|
|
4470
|
-
if (node.type === AST_NODE_TYPES.CallExpression) {
|
|
4471
|
-
result.push(node);
|
|
4472
|
-
}
|
|
4473
|
-
});
|
|
4474
|
-
return result;
|
|
4475
|
-
}
|
|
4476
4504
|
function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program) {
|
|
4477
4505
|
let found = false;
|
|
4478
4506
|
walkSynchronousAst(root, (node) => {
|
|
@@ -4496,27 +4524,21 @@ function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program
|
|
|
4496
4524
|
}
|
|
4497
4525
|
const rule$7 = createUntrackedRule({
|
|
4498
4526
|
create(context) {
|
|
4499
|
-
const
|
|
4500
|
-
const
|
|
4501
|
-
const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
|
|
4502
|
-
const { sourceCode } = context;
|
|
4503
|
-
const program = sourceCode.ast;
|
|
4504
|
-
const getUntrackedLocalName = () => getLocalNameForImport(program, '@angular/core', 'untracked');
|
|
4527
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
|
|
4528
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
4505
4529
|
function isUntrackedUsedElsewhere(localName, excludeNode) {
|
|
4506
|
-
return
|
|
4530
|
+
return collectCallExpressions(program).some((n) => n !== excludeNode &&
|
|
4507
4531
|
n.callee.type === AST_NODE_TYPES.Identifier &&
|
|
4508
4532
|
n.callee.name === localName);
|
|
4509
4533
|
}
|
|
4510
4534
|
function checkUntrackedCall(untrackedCall, kind) {
|
|
4511
|
-
const
|
|
4512
|
-
if (!
|
|
4513
|
-
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4514
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
4535
|
+
const callback = getReactiveCallbackArgument(untrackedCall);
|
|
4536
|
+
if (!callback) {
|
|
4515
4537
|
return;
|
|
4516
4538
|
}
|
|
4517
|
-
const { reads } = collectSignalUsages(
|
|
4539
|
+
const { reads } = collectSignalUsages(callback, checker, signalNodeMap, program);
|
|
4518
4540
|
if (reads.length > 0 ||
|
|
4519
|
-
hasOpaqueSynchronousCalls(
|
|
4541
|
+
hasOpaqueSynchronousCalls(callback, checker, signalNodeMap, program)) {
|
|
4520
4542
|
// Snapshot reads inside reactive callbacks are a valid Angular
|
|
4521
4543
|
// pattern even when the snapshot later influences branching.
|
|
4522
4544
|
return;
|
|
@@ -4534,7 +4556,7 @@ const rule$7 = createUntrackedRule({
|
|
|
4534
4556
|
if (replacement === null) {
|
|
4535
4557
|
return null;
|
|
4536
4558
|
}
|
|
4537
|
-
const untrackedLocalName =
|
|
4559
|
+
const untrackedLocalName = findUntrackedAlias(program);
|
|
4538
4560
|
const stillUsed = untrackedLocalName !== null &&
|
|
4539
4561
|
isUntrackedUsedElsewhere(untrackedLocalName, untrackedCall);
|
|
4540
4562
|
const fixes = [fixer.replaceText(parentStmt, replacement)];
|
|
@@ -4837,22 +4859,6 @@ const rule$6 = createRule$6({
|
|
|
4837
4859
|
|
|
4838
4860
|
const createRule$5 = ESLintUtils.RuleCreator((name) => name);
|
|
4839
4861
|
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
4862
|
function isSupportedControlFlowStatement(node) {
|
|
4857
4863
|
return (node.type === AST_NODE_TYPES.BreakStatement ||
|
|
4858
4864
|
node.type === AST_NODE_TYPES.ContinueStatement ||
|
|
@@ -5492,17 +5498,6 @@ function unwrapUsageExpression(node) {
|
|
|
5492
5498
|
}
|
|
5493
5499
|
return current;
|
|
5494
5500
|
}
|
|
5495
|
-
function isNodeInside(node, ancestor) {
|
|
5496
|
-
for (let current = node; current; current = current.parent) {
|
|
5497
|
-
if (current === ancestor) {
|
|
5498
|
-
return true;
|
|
5499
|
-
}
|
|
5500
|
-
}
|
|
5501
|
-
return false;
|
|
5502
|
-
}
|
|
5503
|
-
function isNodeInsideAny(node, ancestors) {
|
|
5504
|
-
return ancestors.some((ancestor) => isNodeInside(node, ancestor));
|
|
5505
|
-
}
|
|
5506
5501
|
function isStatementPositionCall(node) {
|
|
5507
5502
|
const usage = unwrapUsageExpression(node);
|
|
5508
5503
|
if (usage.parent?.type === AST_NODE_TYPES.ExpressionStatement) {
|
|
@@ -5555,11 +5550,7 @@ function isAliasDeclarationIdentifier(node) {
|
|
|
5555
5550
|
node.parent.id === node);
|
|
5556
5551
|
}
|
|
5557
5552
|
function aliasHasExternalUsage(alias, consumers, scope, checker, esTreeNodeToTSNodeMap) {
|
|
5558
|
-
const
|
|
5559
|
-
if (!tsNode) {
|
|
5560
|
-
return false;
|
|
5561
|
-
}
|
|
5562
|
-
const symbol = checker.getSymbolAtLocation(tsNode);
|
|
5553
|
+
const symbol = getSymbolAtNode(alias, checker, esTreeNodeToTSNodeMap);
|
|
5563
5554
|
if (!symbol) {
|
|
5564
5555
|
return false;
|
|
5565
5556
|
}
|
|
@@ -5571,10 +5562,7 @@ function aliasHasExternalUsage(alias, consumers, scope, checker, esTreeNodeToTSN
|
|
|
5571
5562
|
isNodeInsideAny(node, consumers)) {
|
|
5572
5563
|
return;
|
|
5573
5564
|
}
|
|
5574
|
-
const
|
|
5575
|
-
const referenceSymbol = referenceTsNode
|
|
5576
|
-
? checker.getSymbolAtLocation(referenceTsNode)
|
|
5577
|
-
: null;
|
|
5565
|
+
const referenceSymbol = getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap);
|
|
5578
5566
|
if (referenceSymbol !== symbol) {
|
|
5579
5567
|
return;
|
|
5580
5568
|
}
|
|
@@ -5601,11 +5589,7 @@ function resolveSignalReadAlias(expression, context, seen = new Set()) {
|
|
|
5601
5589
|
if (unwrapped.type !== AST_NODE_TYPES.Identifier) {
|
|
5602
5590
|
return null;
|
|
5603
5591
|
}
|
|
5604
|
-
const
|
|
5605
|
-
if (!tsNode) {
|
|
5606
|
-
return null;
|
|
5607
|
-
}
|
|
5608
|
-
const symbol = context.checker.getSymbolAtLocation(tsNode);
|
|
5592
|
+
const symbol = getSymbolAtNode(unwrapped, context.checker, context.esTreeNodeToTSNodeMap);
|
|
5609
5593
|
if (!symbol) {
|
|
5610
5594
|
return null;
|
|
5611
5595
|
}
|
|
@@ -5693,12 +5677,9 @@ function collectSuspiciousReads(scope, checker, esTreeNodeToTSNodeMap, tsNodeToE
|
|
|
5693
5677
|
}
|
|
5694
5678
|
const rule$3 = createUntrackedRule({
|
|
5695
5679
|
create(context) {
|
|
5696
|
-
const
|
|
5697
|
-
const
|
|
5698
|
-
const
|
|
5699
|
-
const tsNodeToESTreeNodeMap = parserServices.tsNodeToESTreeNodeMap;
|
|
5700
|
-
const { sourceCode } = context;
|
|
5701
|
-
const program = sourceCode.ast;
|
|
5680
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
|
|
5681
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
5682
|
+
const estreeNodeMap = tsNodeToESTreeNodeMap;
|
|
5702
5683
|
function buildFix(read) {
|
|
5703
5684
|
const untrackedAlias = findUntrackedAlias(program);
|
|
5704
5685
|
const alreadyHasUntracked = untrackedAlias !== null;
|
|
@@ -5714,13 +5695,13 @@ const rule$3 = createUntrackedRule({
|
|
|
5714
5695
|
return {
|
|
5715
5696
|
CallExpression(node) {
|
|
5716
5697
|
for (const scope of getReactiveScopes(node, program)) {
|
|
5717
|
-
const suspicious = collectSuspiciousReads(scope, checker,
|
|
5718
|
-
const { reads: trackedReads } = collectSignalUsages(scope.callback, checker,
|
|
5698
|
+
const suspicious = collectSuspiciousReads(scope, checker, signalNodeMap, estreeNodeMap, program);
|
|
5699
|
+
const { reads: trackedReads } = collectSignalUsages(scope.callback, checker, signalNodeMap, program);
|
|
5719
5700
|
const suspiciousReads = new Set(suspicious.map(({ read }) => read));
|
|
5720
5701
|
for (const { aliases, consumers, read } of suspicious) {
|
|
5721
5702
|
const hasTrackedDependency = consumers.some((consumer) => trackedReads.some((trackedRead) => !suspiciousReads.has(trackedRead) &&
|
|
5722
5703
|
!isNodeInside(trackedRead, consumer)));
|
|
5723
|
-
const hasExternalAliasUsage = aliases.some((alias) => aliasHasExternalUsage(alias, consumers, scope, checker,
|
|
5704
|
+
const hasExternalAliasUsage = aliases.some((alias) => aliasHasExternalUsage(alias, consumers, scope, checker, signalNodeMap));
|
|
5724
5705
|
if (!hasTrackedDependency || hasExternalAliasUsage) {
|
|
5725
5706
|
continue;
|
|
5726
5707
|
}
|
|
@@ -5752,7 +5733,7 @@ const rule$3 = createUntrackedRule({
|
|
|
5752
5733
|
|
|
5753
5734
|
function getReturnedExpression(node) {
|
|
5754
5735
|
if (node.body.type !== AST_NODE_TYPES.BlockStatement) {
|
|
5755
|
-
return
|
|
5736
|
+
return node.body;
|
|
5756
5737
|
}
|
|
5757
5738
|
if (node.body.body.length !== 1) {
|
|
5758
5739
|
return null;
|
|
@@ -5761,17 +5742,16 @@ function getReturnedExpression(node) {
|
|
|
5761
5742
|
if (statement?.type !== AST_NODE_TYPES.ReturnStatement || !statement.argument) {
|
|
5762
5743
|
return null;
|
|
5763
5744
|
}
|
|
5764
|
-
return
|
|
5745
|
+
return statement.argument;
|
|
5765
5746
|
}
|
|
5747
|
+
|
|
5766
5748
|
function getWrappedSignalGetter(node, checker, esTreeNodeToTSNodeMap) {
|
|
5767
|
-
const
|
|
5768
|
-
if (!
|
|
5769
|
-
arg.type === AST_NODE_TYPES.SpreadElement ||
|
|
5770
|
-
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
5771
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
5749
|
+
const callback = getReactiveCallbackArgument(node);
|
|
5750
|
+
if (!callback) {
|
|
5772
5751
|
return null;
|
|
5773
5752
|
}
|
|
5774
|
-
const
|
|
5753
|
+
const returnedExpression = getReturnedExpression(callback);
|
|
5754
|
+
const body = returnedExpression ? unwrapExpression(returnedExpression) : null;
|
|
5775
5755
|
if (body?.type !== AST_NODE_TYPES.CallExpression ||
|
|
5776
5756
|
!isSignalReadCall(body, checker, esTreeNodeToTSNodeMap)) {
|
|
5777
5757
|
return null;
|
|
@@ -5785,17 +5765,14 @@ function getWrappedSignalGetter(node, checker, esTreeNodeToTSNodeMap) {
|
|
|
5785
5765
|
}
|
|
5786
5766
|
const rule$2 = createUntrackedRule({
|
|
5787
5767
|
create(context) {
|
|
5788
|
-
const
|
|
5789
|
-
const
|
|
5790
|
-
const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
|
|
5791
|
-
const { sourceCode } = context;
|
|
5792
|
-
const program = sourceCode.ast;
|
|
5768
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
|
|
5769
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
5793
5770
|
return {
|
|
5794
5771
|
CallExpression(node) {
|
|
5795
5772
|
if (!isAngularUntrackedCall(node, program)) {
|
|
5796
5773
|
return;
|
|
5797
5774
|
}
|
|
5798
|
-
const getter = getWrappedSignalGetter(node, checker,
|
|
5775
|
+
const getter = getWrappedSignalGetter(node, checker, signalNodeMap);
|
|
5799
5776
|
if (!getter) {
|
|
5800
5777
|
return;
|
|
5801
5778
|
}
|
|
@@ -5822,13 +5799,6 @@ const rule$2 = createUntrackedRule({
|
|
|
5822
5799
|
name: 'prefer-untracked-signal-getter',
|
|
5823
5800
|
});
|
|
5824
5801
|
|
|
5825
|
-
function getImportedName(spec) {
|
|
5826
|
-
if (spec.imported.type === AST_NODE_TYPES.Identifier) {
|
|
5827
|
-
return spec.imported.name;
|
|
5828
|
-
}
|
|
5829
|
-
return spec.imported.value;
|
|
5830
|
-
}
|
|
5831
|
-
|
|
5832
5802
|
function isImportsArrayProperty(property) {
|
|
5833
5803
|
const isProperty = property?.type === AST_NODE_TYPES.Property;
|
|
5834
5804
|
const hasIdentifierKey = property?.key.type === AST_NODE_TYPES.Identifier &&
|
|
@@ -5836,6 +5806,13 @@ function isImportsArrayProperty(property) {
|
|
|
5836
5806
|
return isProperty && hasIdentifierKey && isArray(property.value);
|
|
5837
5807
|
}
|
|
5838
5808
|
|
|
5809
|
+
function getImportedName(spec) {
|
|
5810
|
+
if (spec.imported.type === AST_NODE_TYPES.Identifier) {
|
|
5811
|
+
return spec.imported.name;
|
|
5812
|
+
}
|
|
5813
|
+
return spec.imported.value;
|
|
5814
|
+
}
|
|
5815
|
+
|
|
5839
5816
|
const MESSAGE_ID = 'replaceTuiImport';
|
|
5840
5817
|
const DEFAULT_DECORATORS = ['Component', 'Directive', 'NgModule', 'Pipe'];
|
|
5841
5818
|
const DEFAULT_EXCEPTIONS = [
|
|
@@ -6246,14 +6223,15 @@ const plugin = {
|
|
|
6246
6223
|
'class-property-naming': classPropertyNaming,
|
|
6247
6224
|
'decorator-key-sort': config$3,
|
|
6248
6225
|
'flat-exports': flatExports,
|
|
6249
|
-
'host-attributes-sort': rule$
|
|
6226
|
+
'host-attributes-sort': rule$k,
|
|
6250
6227
|
'html-logical-properties': config$2,
|
|
6251
|
-
'injection-token-description': rule$
|
|
6252
|
-
'no-deep-imports': rule$
|
|
6228
|
+
'injection-token-description': rule$j,
|
|
6229
|
+
'no-deep-imports': rule$i,
|
|
6253
6230
|
'no-deep-imports-to-indexed-packages': noDeepImportsToIndexedPackages,
|
|
6254
|
-
'no-fully-untracked-effect': rule$
|
|
6231
|
+
'no-fully-untracked-effect': rule$h,
|
|
6255
6232
|
'no-href-with-router-link': config$1,
|
|
6256
|
-
'no-implicit-public': rule$
|
|
6233
|
+
'no-implicit-public': rule$g,
|
|
6234
|
+
'no-infinite-loop': rule$f,
|
|
6257
6235
|
'no-legacy-peer-deps': rule$e,
|
|
6258
6236
|
'no-playwright-empty-fill': rule$d,
|
|
6259
6237
|
'no-project-as-in-ng-template': config,
|