@taiga-ui/eslint-plugin-experience-next 0.474.0 → 0.476.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 +106 -0
- package/index.d.ts +12 -0
- package/index.esm.js +698 -538
- package/package.json +1 -1
- package/rules/no-commonjs-import-patterns.d.ts +6 -0
- package/rules/no-fully-untracked-effect.d.ts +1 -2
- package/rules/no-import-assertions.d.ts +5 -0
- 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-namespace-keyword.d.ts +5 -0
- 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
|
@@ -898,6 +898,7 @@ var recommended = defineConfig([
|
|
|
898
898
|
],
|
|
899
899
|
'@taiga-ui/experience-next/host-attributes-sort': 'error',
|
|
900
900
|
'@taiga-ui/experience-next/injection-token-description': 'error',
|
|
901
|
+
'@taiga-ui/experience-next/no-commonjs-import-patterns': 'error',
|
|
901
902
|
'@taiga-ui/experience-next/no-deep-imports': [
|
|
902
903
|
'error',
|
|
903
904
|
{
|
|
@@ -908,6 +909,8 @@ var recommended = defineConfig([
|
|
|
908
909
|
'@taiga-ui/experience-next/no-deep-imports-to-indexed-packages': 'error',
|
|
909
910
|
'@taiga-ui/experience-next/no-fully-untracked-effect': 'error',
|
|
910
911
|
'@taiga-ui/experience-next/no-implicit-public': 'error',
|
|
912
|
+
'@taiga-ui/experience-next/no-import-assertions': 'error',
|
|
913
|
+
'@taiga-ui/experience-next/no-infinite-loop': 'error',
|
|
911
914
|
'@taiga-ui/experience-next/no-redundant-type-annotation': 'error',
|
|
912
915
|
'@taiga-ui/experience-next/no-side-effects-in-computed': 'error',
|
|
913
916
|
'@taiga-ui/experience-next/no-signal-reads-after-await-in-reactive-context': 'error',
|
|
@@ -916,6 +919,7 @@ var recommended = defineConfig([
|
|
|
916
919
|
'@taiga-ui/experience-next/object-single-line': ['error', { printWidth: 90 }],
|
|
917
920
|
'@taiga-ui/experience-next/prefer-combined-if-control-flow': 'error',
|
|
918
921
|
'@taiga-ui/experience-next/prefer-multi-arg-push': 'error',
|
|
922
|
+
'@taiga-ui/experience-next/prefer-namespace-keyword': 'error',
|
|
919
923
|
'@taiga-ui/experience-next/prefer-untracked-incidental-signal-reads': 'error',
|
|
920
924
|
'@taiga-ui/experience-next/prefer-untracked-signal-getter': 'error',
|
|
921
925
|
'@taiga-ui/experience-next/short-tui-imports': 'error',
|
|
@@ -1299,6 +1303,11 @@ const config$4 = {
|
|
|
1299
1303
|
},
|
|
1300
1304
|
};
|
|
1301
1305
|
|
|
1306
|
+
function intersect(a, b) {
|
|
1307
|
+
const origin = new Set(b);
|
|
1308
|
+
return a.some((type) => origin.has(type));
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1302
1311
|
function getFieldTypes(type, checker) {
|
|
1303
1312
|
const typeNames = [];
|
|
1304
1313
|
if (type.isUnionOrIntersection()) {
|
|
@@ -1331,16 +1340,24 @@ function getFieldTypes(type, checker) {
|
|
|
1331
1340
|
return typeNames;
|
|
1332
1341
|
}
|
|
1333
1342
|
|
|
1334
|
-
function
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1343
|
+
function getTypeAwareRuleContext(context) {
|
|
1344
|
+
const parserServices = ESLintUtils.getParserServices(context);
|
|
1345
|
+
const { sourceCode } = context;
|
|
1346
|
+
return {
|
|
1347
|
+
checker: parserServices.program.getTypeChecker(),
|
|
1348
|
+
esTreeNodeToTSNodeMap: parserServices.esTreeNodeToTSNodeMap,
|
|
1349
|
+
parserServices,
|
|
1350
|
+
program: sourceCode.ast,
|
|
1351
|
+
sourceCode,
|
|
1352
|
+
tsNodeToESTreeNodeMap: parserServices.tsNodeToESTreeNodeMap,
|
|
1353
|
+
tsProgram: parserServices.program,
|
|
1354
|
+
};
|
|
1337
1355
|
}
|
|
1338
1356
|
|
|
1339
|
-
const createRule$
|
|
1340
|
-
var classPropertyNaming = createRule$
|
|
1357
|
+
const createRule$m = ESLintUtils.RuleCreator((name) => name);
|
|
1358
|
+
var classPropertyNaming = createRule$m({
|
|
1341
1359
|
create(context, [configs]) {
|
|
1342
|
-
const
|
|
1343
|
-
const typeChecker = parserServices.program.getTypeChecker();
|
|
1360
|
+
const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
|
|
1344
1361
|
const flatConfig = configs.flat();
|
|
1345
1362
|
return {
|
|
1346
1363
|
PropertyDefinition(node) {
|
|
@@ -1348,7 +1365,7 @@ var classPropertyNaming = createRule$i({
|
|
|
1348
1365
|
if (!fieldName) {
|
|
1349
1366
|
return;
|
|
1350
1367
|
}
|
|
1351
|
-
const tsNode =
|
|
1368
|
+
const tsNode = esTreeNodeToTSNodeMap.get(node);
|
|
1352
1369
|
const nodeType = typeChecker.getTypeAtLocation(tsNode);
|
|
1353
1370
|
const fieldTypes = getFieldTypes(nodeType, typeChecker);
|
|
1354
1371
|
const rule = flatConfig.find((rule) => intersect(fieldTypes, rule.withTypesSpecifier) &&
|
|
@@ -1404,6 +1421,10 @@ var classPropertyNaming = createRule$i({
|
|
|
1404
1421
|
name: 'class-property-naming',
|
|
1405
1422
|
});
|
|
1406
1423
|
|
|
1424
|
+
function sameOrder(a, b) {
|
|
1425
|
+
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1407
1428
|
const config$3 = {
|
|
1408
1429
|
create(context) {
|
|
1409
1430
|
const order = context.options[0] || {};
|
|
@@ -1422,7 +1443,7 @@ const config$3 = {
|
|
|
1422
1443
|
.map((prop) => prop.key?.name)
|
|
1423
1444
|
.filter(Boolean);
|
|
1424
1445
|
const correct = getCorrectOrderRelative(orderList, current);
|
|
1425
|
-
if (!
|
|
1446
|
+
if (!sameOrder(correct, current.filter((item) => correct.includes(item)))) {
|
|
1426
1447
|
context.report({
|
|
1427
1448
|
fix: (fixer) => {
|
|
1428
1449
|
const fileContent = context.sourceCode.text;
|
|
@@ -1458,10 +1479,6 @@ const config$3 = {
|
|
|
1458
1479
|
type: 'problem',
|
|
1459
1480
|
},
|
|
1460
1481
|
};
|
|
1461
|
-
function isCorrectSortedAccording(correct, current) {
|
|
1462
|
-
return (JSON.stringify(correct) ===
|
|
1463
|
-
JSON.stringify(current.filter((item) => correct.includes(item))));
|
|
1464
|
-
}
|
|
1465
1482
|
function getCorrectOrderRelative(correct, current) {
|
|
1466
1483
|
return correct.filter((item) => current.includes(item));
|
|
1467
1484
|
}
|
|
@@ -1506,12 +1523,11 @@ function isExternalPureTuple(typeChecker, type) {
|
|
|
1506
1523
|
return typeArgs.every((item) => isClassType(item));
|
|
1507
1524
|
}
|
|
1508
1525
|
|
|
1509
|
-
const createRule$
|
|
1526
|
+
const createRule$l = ESLintUtils.RuleCreator((name) => name);
|
|
1510
1527
|
const MESSAGE_ID$7 = 'spreadArrays';
|
|
1511
|
-
var flatExports = createRule$
|
|
1528
|
+
var flatExports = createRule$l({
|
|
1512
1529
|
create(context) {
|
|
1513
|
-
const
|
|
1514
|
-
const typeChecker = parserServices.program.getTypeChecker();
|
|
1530
|
+
const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
|
|
1515
1531
|
const arrays = new Map();
|
|
1516
1532
|
const purityCache = new WeakMap();
|
|
1517
1533
|
const isPureArray = (arr) => {
|
|
@@ -1595,7 +1611,7 @@ var flatExports = createRule$h({
|
|
|
1595
1611
|
isDirty = true;
|
|
1596
1612
|
continue;
|
|
1597
1613
|
}
|
|
1598
|
-
const tsNode =
|
|
1614
|
+
const tsNode = esTreeNodeToTSNodeMap.get(el);
|
|
1599
1615
|
const elType = typeChecker.getTypeAtLocation(tsNode);
|
|
1600
1616
|
const isClass = isClassType(elType);
|
|
1601
1617
|
const isArrayLike = typeChecker.isArrayLikeType(elType) ||
|
|
@@ -1675,8 +1691,53 @@ function getDecoratorMetadata(decorator, allowedNames) {
|
|
|
1675
1691
|
return isObject(arg) ? arg : null;
|
|
1676
1692
|
}
|
|
1677
1693
|
|
|
1678
|
-
function
|
|
1679
|
-
return
|
|
1694
|
+
function isStringLiteral(node) {
|
|
1695
|
+
return node?.type === AST_NODE_TYPES.Literal && typeof node.value === 'string';
|
|
1696
|
+
}
|
|
1697
|
+
function isStaticTemplateLiteral(node) {
|
|
1698
|
+
return (node?.type === AST_NODE_TYPES.TemplateLiteral &&
|
|
1699
|
+
node.expressions.length === 0 &&
|
|
1700
|
+
node.quasis.length === 1);
|
|
1701
|
+
}
|
|
1702
|
+
function getStaticStringValue(node) {
|
|
1703
|
+
if (isStringLiteral(node)) {
|
|
1704
|
+
return node.value;
|
|
1705
|
+
}
|
|
1706
|
+
if (!isStaticTemplateLiteral(node)) {
|
|
1707
|
+
return null;
|
|
1708
|
+
}
|
|
1709
|
+
return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw ?? '';
|
|
1710
|
+
}
|
|
1711
|
+
function isEmptyStaticString(node) {
|
|
1712
|
+
return getStaticStringValue(node) === '';
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
function getStaticPropertyName(key) {
|
|
1716
|
+
if (key.type === AST_NODE_TYPES.Identifier) {
|
|
1717
|
+
return key.name;
|
|
1718
|
+
}
|
|
1719
|
+
if (key.type === AST_NODE_TYPES.Literal &&
|
|
1720
|
+
(typeof key.value === 'string' || typeof key.value === 'number')) {
|
|
1721
|
+
return String(key.value);
|
|
1722
|
+
}
|
|
1723
|
+
return getStaticStringValue(key);
|
|
1724
|
+
}
|
|
1725
|
+
function getObjectPropertyName(node) {
|
|
1726
|
+
if (node.computed) {
|
|
1727
|
+
return null;
|
|
1728
|
+
}
|
|
1729
|
+
return getStaticPropertyName(node.key);
|
|
1730
|
+
}
|
|
1731
|
+
function getMemberExpressionPropertyName(node) {
|
|
1732
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
1733
|
+
return node.property.name;
|
|
1734
|
+
}
|
|
1735
|
+
return node.computed ? getStaticStringValue(node.property) : null;
|
|
1736
|
+
}
|
|
1737
|
+
function getClassMemberName(member) {
|
|
1738
|
+
return member.key.type === AST_NODE_TYPES.PrivateIdentifier
|
|
1739
|
+
? null
|
|
1740
|
+
: getStaticPropertyName(member.key);
|
|
1680
1741
|
}
|
|
1681
1742
|
|
|
1682
1743
|
const DEFAULT_GROUP = '$DEFAULT';
|
|
@@ -1744,8 +1805,8 @@ const PRESETS = {
|
|
|
1744
1805
|
$VUE: ['$CLASS', '$ID', '$VUE_ATTRIBUTE'],
|
|
1745
1806
|
$VUE_ATTRIBUTE: /^v-/,
|
|
1746
1807
|
};
|
|
1747
|
-
const createRule$
|
|
1748
|
-
const rule$
|
|
1808
|
+
const createRule$k = ESLintUtils.RuleCreator((name) => name);
|
|
1809
|
+
const rule$n = createRule$k({
|
|
1749
1810
|
create(context, [options]) {
|
|
1750
1811
|
const sourceCode = context.sourceCode;
|
|
1751
1812
|
const settings = {
|
|
@@ -1867,21 +1928,6 @@ function getHostAttributeProperties(hostObject) {
|
|
|
1867
1928
|
}
|
|
1868
1929
|
return properties;
|
|
1869
1930
|
}
|
|
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
1931
|
function organizeProperties(properties, options) {
|
|
1886
1932
|
const groups = getGroups(options.attributeGroups.length > 0 ? options.attributeGroups : ['$ANGULAR'], options.attributeIgnoreCase);
|
|
1887
1933
|
const defaultGroup = ensureDefaultGroup(groups);
|
|
@@ -2021,7 +2067,7 @@ const config$2 = {
|
|
|
2021
2067
|
const MESSAGE_ID$5 = 'invalid-injection-token-description';
|
|
2022
2068
|
const ERROR_MESSAGE$3 = "InjectionToken's description should contain token's name";
|
|
2023
2069
|
const NG_DEV_MODE = 'ngDevMode';
|
|
2024
|
-
const createRule$
|
|
2070
|
+
const createRule$j = ESLintUtils.RuleCreator((name) => name);
|
|
2025
2071
|
function getVariableName(node) {
|
|
2026
2072
|
if (node.parent.type !== AST_NODE_TYPES$1.VariableDeclarator) {
|
|
2027
2073
|
return undefined;
|
|
@@ -2029,21 +2075,18 @@ function getVariableName(node) {
|
|
|
2029
2075
|
const { id } = node.parent;
|
|
2030
2076
|
return id.type === AST_NODE_TYPES$1.Identifier ? id.name : undefined;
|
|
2031
2077
|
}
|
|
2032
|
-
function isStringLiteral$1(node) {
|
|
2033
|
-
return node.type === AST_NODE_TYPES$1.Literal && typeof node.value === 'string';
|
|
2034
|
-
}
|
|
2035
2078
|
function isStringLike(node) {
|
|
2036
|
-
return isStringLiteral
|
|
2079
|
+
return isStringLiteral(node) || node.type === AST_NODE_TYPES$1.TemplateLiteral;
|
|
2037
2080
|
}
|
|
2038
2081
|
function getStringValue(node) {
|
|
2039
|
-
if (isStringLiteral
|
|
2082
|
+
if (isStringLiteral(node)) {
|
|
2040
2083
|
return node.value;
|
|
2041
2084
|
}
|
|
2042
2085
|
return node.quasis[0]?.value.raw || '';
|
|
2043
2086
|
}
|
|
2044
|
-
function isEmptyString
|
|
2045
|
-
return (
|
|
2046
|
-
(!('expressions' in node) ||
|
|
2087
|
+
function isEmptyString(node) {
|
|
2088
|
+
return (isEmptyStaticString(node) &&
|
|
2089
|
+
(!('expressions' in node) || node.expressions.length === 0));
|
|
2047
2090
|
}
|
|
2048
2091
|
function isNgDevModeConditional(node) {
|
|
2049
2092
|
return (node.type === AST_NODE_TYPES$1.ConditionalExpression &&
|
|
@@ -2051,7 +2094,7 @@ function isNgDevModeConditional(node) {
|
|
|
2051
2094
|
node.test.name === NG_DEV_MODE &&
|
|
2052
2095
|
isStringLike(node.consequent) &&
|
|
2053
2096
|
isStringLike(node.alternate) &&
|
|
2054
|
-
isEmptyString
|
|
2097
|
+
isEmptyString(node.alternate));
|
|
2055
2098
|
}
|
|
2056
2099
|
function getDescriptionValue(node) {
|
|
2057
2100
|
if (isStringLike(node)) {
|
|
@@ -2092,7 +2135,7 @@ function getNgDevModeDeclarationFix(program, fixer) {
|
|
|
2092
2135
|
}
|
|
2093
2136
|
return fixer.insertTextBeforeRange([0, 0], 'declare const ngDevMode: boolean;\n');
|
|
2094
2137
|
}
|
|
2095
|
-
const rule$
|
|
2138
|
+
const rule$m = createRule$j({
|
|
2096
2139
|
create(context) {
|
|
2097
2140
|
const { sourceCode } = context;
|
|
2098
2141
|
const program = sourceCode.ast;
|
|
@@ -2141,6 +2184,88 @@ const rule$i = createRule$f({
|
|
|
2141
2184
|
name: 'injection-token-description',
|
|
2142
2185
|
});
|
|
2143
2186
|
|
|
2187
|
+
const createRule$i = ESLintUtils.RuleCreator((name) => name);
|
|
2188
|
+
function getResolvedVariable(sourceCode, node) {
|
|
2189
|
+
const scope = sourceCode.getScope(node);
|
|
2190
|
+
const reference = scope.references.find((item) => item.identifier === node);
|
|
2191
|
+
return reference?.resolved ?? null;
|
|
2192
|
+
}
|
|
2193
|
+
const rule$l = createRule$i({
|
|
2194
|
+
create(context) {
|
|
2195
|
+
const { sourceCode } = context;
|
|
2196
|
+
const namespaceImports = new Map();
|
|
2197
|
+
const markNamespaceImportAsUsedLikeValue = (identifier) => {
|
|
2198
|
+
const usage = namespaceImports.get(identifier.name);
|
|
2199
|
+
if (!usage ||
|
|
2200
|
+
usage.usedLikeValue ||
|
|
2201
|
+
getResolvedVariable(sourceCode, identifier) !== usage.variable) {
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
usage.usedLikeValue = true;
|
|
2205
|
+
};
|
|
2206
|
+
return {
|
|
2207
|
+
'CallExpression > Identifier.callee'(node) {
|
|
2208
|
+
markNamespaceImportAsUsedLikeValue(node);
|
|
2209
|
+
},
|
|
2210
|
+
ImportDeclaration(node) {
|
|
2211
|
+
const namespaceImport = node.specifiers.find((specifier) => specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier);
|
|
2212
|
+
if (!namespaceImport) {
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
const [variable] = sourceCode.getDeclaredVariables(namespaceImport);
|
|
2216
|
+
if (!variable) {
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2219
|
+
namespaceImports.set(namespaceImport.local.name, {
|
|
2220
|
+
node: namespaceImport,
|
|
2221
|
+
usedLikeValue: false,
|
|
2222
|
+
variable,
|
|
2223
|
+
});
|
|
2224
|
+
},
|
|
2225
|
+
'NewExpression > Identifier.callee'(node) {
|
|
2226
|
+
markNamespaceImportAsUsedLikeValue(node);
|
|
2227
|
+
},
|
|
2228
|
+
'Program:exit'() {
|
|
2229
|
+
for (const usage of namespaceImports.values()) {
|
|
2230
|
+
if (!usage.usedLikeValue) {
|
|
2231
|
+
continue;
|
|
2232
|
+
}
|
|
2233
|
+
context.report({
|
|
2234
|
+
data: { name: usage.node.local.name },
|
|
2235
|
+
messageId: 'avoidCallableNamespaceImport',
|
|
2236
|
+
node: usage.node,
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2239
|
+
},
|
|
2240
|
+
'TaggedTemplateExpression > Identifier.tag'(node) {
|
|
2241
|
+
markNamespaceImportAsUsedLikeValue(node);
|
|
2242
|
+
},
|
|
2243
|
+
TSImportEqualsDeclaration(node) {
|
|
2244
|
+
if (node.moduleReference.type !== AST_NODE_TYPES.TSExternalModuleReference) {
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
context.report({
|
|
2248
|
+
data: { name: node.id.name },
|
|
2249
|
+
messageId: 'avoidImportEquals',
|
|
2250
|
+
node,
|
|
2251
|
+
});
|
|
2252
|
+
},
|
|
2253
|
+
};
|
|
2254
|
+
},
|
|
2255
|
+
meta: {
|
|
2256
|
+
docs: {
|
|
2257
|
+
description: 'Disallow legacy CommonJS interop import patterns such as `import = require(...)` and namespace imports used like callable values.',
|
|
2258
|
+
},
|
|
2259
|
+
messages: {
|
|
2260
|
+
avoidCallableNamespaceImport: 'Namespace import "{{name}}" is used like a value instead of a namespace. This is a brittle interop pattern and often should become a default import.',
|
|
2261
|
+
avoidImportEquals: '`import {{name}} = require(...)` is a legacy CommonJS import pattern.',
|
|
2262
|
+
},
|
|
2263
|
+
schema: [],
|
|
2264
|
+
type: 'problem',
|
|
2265
|
+
},
|
|
2266
|
+
name: 'no-commonjs-import-patterns',
|
|
2267
|
+
});
|
|
2268
|
+
|
|
2144
2269
|
const MESSAGE_ID$4 = 'no-deep-imports';
|
|
2145
2270
|
const ERROR_MESSAGE$2 = 'Deep imports of Taiga UI packages are prohibited';
|
|
2146
2271
|
const CODE_EXTENSIONS = new Set([
|
|
@@ -2160,8 +2285,8 @@ const DEFAULT_OPTIONS = {
|
|
|
2160
2285
|
importDeclaration: '^@taiga-ui*',
|
|
2161
2286
|
projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
|
|
2162
2287
|
};
|
|
2163
|
-
const createRule$
|
|
2164
|
-
const rule$
|
|
2288
|
+
const createRule$h = ESLintUtils.RuleCreator((name) => name);
|
|
2289
|
+
const rule$k = createRule$h({
|
|
2165
2290
|
create(context) {
|
|
2166
2291
|
const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
|
|
2167
2292
|
const hasNonCodeExtension = (source) => {
|
|
@@ -2248,13 +2373,13 @@ const rule$h = createRule$e({
|
|
|
2248
2373
|
name: 'no-deep-imports',
|
|
2249
2374
|
});
|
|
2250
2375
|
|
|
2251
|
-
const createRule$
|
|
2376
|
+
const createRule$g = ESLintUtils.RuleCreator((name) => name);
|
|
2252
2377
|
const resolveCacheByOptions = new WeakMap();
|
|
2253
2378
|
const nearestFileUpCache = new Map();
|
|
2254
2379
|
const markerCache = new Map();
|
|
2255
2380
|
const indexFileCache = new Map();
|
|
2256
2381
|
const indexExportsCache = new Map();
|
|
2257
|
-
var noDeepImportsToIndexedPackages = createRule$
|
|
2382
|
+
var noDeepImportsToIndexedPackages = createRule$g({
|
|
2258
2383
|
create(context) {
|
|
2259
2384
|
const parserServices = ESLintUtils.getParserServices(context);
|
|
2260
2385
|
const program = parserServices.program;
|
|
@@ -2443,63 +2568,13 @@ function stripKnownExtensions(filePathOrSpecifier) {
|
|
|
2443
2568
|
return filePathOrSpecifier.replace(/\.(?:d\.ts|ts|tsx|js|jsx|mjs|cjs)$/, '');
|
|
2444
2569
|
}
|
|
2445
2570
|
|
|
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) {
|
|
2571
|
+
function isFunctionLike(node) {
|
|
2497
2572
|
return (node.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
2498
2573
|
node.type === AST_NODE_TYPES.FunctionDeclaration ||
|
|
2499
2574
|
node.type === AST_NODE_TYPES.FunctionExpression);
|
|
2500
2575
|
}
|
|
2501
2576
|
function getOrderedChildren(node) {
|
|
2502
|
-
if (isFunctionLike
|
|
2577
|
+
if (isFunctionLike(node)) {
|
|
2503
2578
|
const children = [
|
|
2504
2579
|
...node.params,
|
|
2505
2580
|
node.body,
|
|
@@ -2568,7 +2643,7 @@ function getOrderedChildren(node) {
|
|
|
2568
2643
|
function walkSynchronousAst(root, visitor) {
|
|
2569
2644
|
traverse(root, true);
|
|
2570
2645
|
function traverse(node, isRoot = false) {
|
|
2571
|
-
if (visitor(node) === false || (!isRoot && isFunctionLike
|
|
2646
|
+
if (visitor(node) === false || (!isRoot && isFunctionLike(node))) {
|
|
2572
2647
|
return false;
|
|
2573
2648
|
}
|
|
2574
2649
|
if (node.type === AST_NODE_TYPES.AwaitExpression) {
|
|
@@ -2597,7 +2672,7 @@ function walkAfterAsyncBoundaryAst(root, visitor) {
|
|
|
2597
2672
|
if (afterBoundary) {
|
|
2598
2673
|
visitor(node);
|
|
2599
2674
|
}
|
|
2600
|
-
if (!isRoot && isFunctionLike
|
|
2675
|
+
if (!isRoot && isFunctionLike(node)) {
|
|
2601
2676
|
return false;
|
|
2602
2677
|
}
|
|
2603
2678
|
if (node.type === AST_NODE_TYPES.AwaitExpression) {
|
|
@@ -2660,6 +2735,56 @@ function walkAst(root, visitor) {
|
|
|
2660
2735
|
}
|
|
2661
2736
|
}
|
|
2662
2737
|
|
|
2738
|
+
const ANGULAR_CORE$1 = '@angular/core';
|
|
2739
|
+
/**
|
|
2740
|
+
* Returns the local name bound to a named import from a given source.
|
|
2741
|
+
* Handles aliased imports: `import { untracked as ngUntracked } from '@angular/core'`
|
|
2742
|
+
* returns `'ngUntracked'` for `exportedName = 'untracked'`.
|
|
2743
|
+
*/
|
|
2744
|
+
function getLocalNameForImport(program, source, exportedName) {
|
|
2745
|
+
for (const node of program.body) {
|
|
2746
|
+
if (node.type !== AST_NODE_TYPES.ImportDeclaration ||
|
|
2747
|
+
node.source.value !== source) {
|
|
2748
|
+
continue;
|
|
2749
|
+
}
|
|
2750
|
+
for (const spec of node.specifiers) {
|
|
2751
|
+
if (spec.type !== AST_NODE_TYPES.ImportSpecifier) {
|
|
2752
|
+
continue;
|
|
2753
|
+
}
|
|
2754
|
+
const imported = spec.imported.type === AST_NODE_TYPES.Identifier
|
|
2755
|
+
? spec.imported.name
|
|
2756
|
+
: spec.imported.value;
|
|
2757
|
+
if (imported === exportedName) {
|
|
2758
|
+
return spec.local.name;
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
return null;
|
|
2763
|
+
}
|
|
2764
|
+
function findAngularCoreImports(program) {
|
|
2765
|
+
return program.body.filter((node) => node.type === AST_NODE_TYPES.ImportDeclaration &&
|
|
2766
|
+
node.source.value === ANGULAR_CORE$1);
|
|
2767
|
+
}
|
|
2768
|
+
function findRuntimeAngularCoreImport(program) {
|
|
2769
|
+
return (findAngularCoreImports(program).find((node) => node.importKind !== 'type') ?? null);
|
|
2770
|
+
}
|
|
2771
|
+
function findAngularCoreImportSpecifier(program, exportedName) {
|
|
2772
|
+
for (const importDecl of findAngularCoreImports(program)) {
|
|
2773
|
+
for (const specifier of importDecl.specifiers) {
|
|
2774
|
+
if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) {
|
|
2775
|
+
continue;
|
|
2776
|
+
}
|
|
2777
|
+
const imported = specifier.imported.type === AST_NODE_TYPES.Identifier
|
|
2778
|
+
? specifier.imported.name
|
|
2779
|
+
: specifier.imported.value;
|
|
2780
|
+
if (imported === exportedName) {
|
|
2781
|
+
return { importDecl, specifier };
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
return null;
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2663
2788
|
const ANGULAR_CORE = '@angular/core';
|
|
2664
2789
|
const SIGNAL_WRITE_METHODS = new Set(['mutate', 'set', 'update']);
|
|
2665
2790
|
const AFTER_RENDER_EFFECT_PHASES = new Map([
|
|
@@ -2668,10 +2793,14 @@ const AFTER_RENDER_EFFECT_PHASES = new Map([
|
|
|
2668
2793
|
['read', 'afterRenderEffect().read'],
|
|
2669
2794
|
['write', 'afterRenderEffect().write'],
|
|
2670
2795
|
]);
|
|
2671
|
-
function isReactiveCallback
|
|
2796
|
+
function isReactiveCallback(node) {
|
|
2672
2797
|
return (node?.type === AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
2673
2798
|
node?.type === AST_NODE_TYPES.FunctionExpression);
|
|
2674
2799
|
}
|
|
2800
|
+
function getReactiveCallbackArgument(node) {
|
|
2801
|
+
const [arg] = node.arguments;
|
|
2802
|
+
return isReactiveCallback(arg) ? arg : null;
|
|
2803
|
+
}
|
|
2675
2804
|
function getPropertyName(property) {
|
|
2676
2805
|
if (property.computed) {
|
|
2677
2806
|
return null;
|
|
@@ -2691,11 +2820,11 @@ function isAngularCoreCall(node, program, exportedName) {
|
|
|
2691
2820
|
node.arguments.length >= 1);
|
|
2692
2821
|
}
|
|
2693
2822
|
function appendFirstArgReactiveScope(scopes, call, kind) {
|
|
2694
|
-
const
|
|
2695
|
-
if (!
|
|
2823
|
+
const callback = getReactiveCallbackArgument(call);
|
|
2824
|
+
if (!callback) {
|
|
2696
2825
|
return;
|
|
2697
2826
|
}
|
|
2698
|
-
scopes.push({ callback
|
|
2827
|
+
scopes.push({ callback, kind, owner: call, reportNode: call });
|
|
2699
2828
|
}
|
|
2700
2829
|
function appendObjectPropertyReactiveScopes(scopes, call, object, labels) {
|
|
2701
2830
|
for (const property of object.properties) {
|
|
@@ -2704,7 +2833,7 @@ function appendObjectPropertyReactiveScopes(scopes, call, object, labels) {
|
|
|
2704
2833
|
}
|
|
2705
2834
|
const name = getPropertyName(property);
|
|
2706
2835
|
const label = name ? labels.get(name) : null;
|
|
2707
|
-
if (!label || !isReactiveCallback
|
|
2836
|
+
if (!label || !isReactiveCallback(property.value)) {
|
|
2708
2837
|
continue;
|
|
2709
2838
|
}
|
|
2710
2839
|
scopes.push({
|
|
@@ -2914,54 +3043,74 @@ function collectSignalUsages(scopeNode, checker, esTreeNodeToTSNodeMap, program)
|
|
|
2914
3043
|
});
|
|
2915
3044
|
return { reads, writes };
|
|
2916
3045
|
}
|
|
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) {
|
|
3046
|
+
function collectSignalReadsInsideUntracked(root, checker, esTreeNodeToTSNodeMap, program) {
|
|
2928
3047
|
const reads = [];
|
|
2929
3048
|
walkSynchronousAst(root, (node) => {
|
|
2930
3049
|
if (node.type !== AST_NODE_TYPES.CallExpression ||
|
|
2931
3050
|
!isAngularUntrackedCall(node, program)) {
|
|
2932
3051
|
return;
|
|
2933
3052
|
}
|
|
2934
|
-
const
|
|
2935
|
-
if (!
|
|
2936
|
-
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
2937
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
3053
|
+
const callback = getReactiveCallbackArgument(node);
|
|
3054
|
+
if (!callback) {
|
|
2938
3055
|
return false;
|
|
2939
3056
|
}
|
|
2940
|
-
walkSynchronousAst(
|
|
3057
|
+
walkSynchronousAst(callback, (inner) => {
|
|
2941
3058
|
if (inner.type === AST_NODE_TYPES.CallExpression &&
|
|
2942
3059
|
isSignalReadCall(inner, checker, esTreeNodeToTSNodeMap)) {
|
|
2943
3060
|
reads.push(inner);
|
|
2944
3061
|
}
|
|
2945
3062
|
});
|
|
2946
|
-
return false;
|
|
3063
|
+
return false;
|
|
2947
3064
|
});
|
|
2948
3065
|
return reads;
|
|
2949
3066
|
}
|
|
2950
|
-
|
|
3067
|
+
function isReactiveOwnerCall(node, program) {
|
|
3068
|
+
return (node.type === AST_NODE_TYPES.CallExpression &&
|
|
3069
|
+
getReactiveScopes(node, program).length > 0);
|
|
3070
|
+
}
|
|
3071
|
+
function getReturnedReactiveOwnerCall(node, program) {
|
|
3072
|
+
const callback = getReactiveCallbackArgument(node);
|
|
3073
|
+
if (!callback) {
|
|
3074
|
+
return null;
|
|
3075
|
+
}
|
|
3076
|
+
if (isReactiveOwnerCall(callback.body, program)) {
|
|
3077
|
+
return callback.body;
|
|
3078
|
+
}
|
|
3079
|
+
if (callback.body.type !== AST_NODE_TYPES.BlockStatement ||
|
|
3080
|
+
callback.body.body.length !== 1) {
|
|
3081
|
+
return null;
|
|
3082
|
+
}
|
|
3083
|
+
const [statement] = callback.body.body;
|
|
3084
|
+
if (statement?.type === AST_NODE_TYPES.ReturnStatement &&
|
|
3085
|
+
statement.argument &&
|
|
3086
|
+
isReactiveOwnerCall(statement.argument, program)) {
|
|
3087
|
+
return statement.argument;
|
|
3088
|
+
}
|
|
3089
|
+
if (node.parent.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
3090
|
+
statement?.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
3091
|
+
isReactiveOwnerCall(statement.expression, program)) {
|
|
3092
|
+
return statement.expression;
|
|
3093
|
+
}
|
|
3094
|
+
return null;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
const ANGULAR_SIGNALS_UNTRACKED_GUIDE_URL = 'https://angular.dev/guide/signals#reading-without-tracking-dependencies';
|
|
3098
|
+
const ANGULAR_SIGNALS_ASYNC_GUIDE_URL = 'https://angular.dev/guide/signals#reactive-context-and-async-operations';
|
|
3099
|
+
const UNTRACKED_RULES_README_URL = 'https://github.com/taiga-family/taiga-ui/blob/main/projects/eslint-plugin-experience-next/README.md';
|
|
3100
|
+
const createUntrackedRule = ESLintUtils.RuleCreator((name) => `${UNTRACKED_RULES_README_URL}#${name}`);
|
|
3101
|
+
|
|
3102
|
+
const rule$j = createUntrackedRule({
|
|
2951
3103
|
create(context) {
|
|
2952
|
-
const
|
|
2953
|
-
const
|
|
2954
|
-
const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
|
|
2955
|
-
const { sourceCode } = context;
|
|
2956
|
-
const program = sourceCode.ast;
|
|
3104
|
+
const { checker, esTreeNodeToTSNodeMap, program } = getTypeAwareRuleContext(context);
|
|
3105
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
2957
3106
|
return {
|
|
2958
3107
|
CallExpression(node) {
|
|
2959
3108
|
for (const scope of getReactiveScopes(node, program)) {
|
|
2960
|
-
const { reads: trackedReads } = collectSignalUsages(scope.callback, checker,
|
|
3109
|
+
const { reads: trackedReads } = collectSignalUsages(scope.callback, checker, signalNodeMap, program);
|
|
2961
3110
|
if (trackedReads.length > 0) {
|
|
2962
3111
|
continue;
|
|
2963
3112
|
}
|
|
2964
|
-
const untrackedReads =
|
|
3113
|
+
const untrackedReads = collectSignalReadsInsideUntracked(scope.callback, checker, signalNodeMap, program);
|
|
2965
3114
|
if (untrackedReads.length === 0) {
|
|
2966
3115
|
continue;
|
|
2967
3116
|
}
|
|
@@ -3033,11 +3182,67 @@ const config$1 = {
|
|
|
3033
3182
|
},
|
|
3034
3183
|
};
|
|
3035
3184
|
|
|
3036
|
-
|
|
3037
|
-
|
|
3185
|
+
function findAncestor(node, predicate) {
|
|
3186
|
+
for (let current = node?.parent; current; current = current.parent) {
|
|
3187
|
+
if (predicate(current)) {
|
|
3188
|
+
return current;
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
return null;
|
|
3192
|
+
}
|
|
3193
|
+
function findSelfOrAncestor(node, predicate) {
|
|
3194
|
+
for (let current = node; current; current = current.parent) {
|
|
3195
|
+
if (predicate(current)) {
|
|
3196
|
+
return current;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
return null;
|
|
3200
|
+
}
|
|
3201
|
+
function hasAncestor(node, predicate) {
|
|
3202
|
+
for (let current = node?.parent; current; current = current.parent) {
|
|
3203
|
+
if (predicate(current)) {
|
|
3204
|
+
return true;
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
return false;
|
|
3208
|
+
}
|
|
3209
|
+
function isNodeInside(node, ancestor) {
|
|
3210
|
+
for (let current = node; current; current = current.parent) {
|
|
3211
|
+
if (current === ancestor) {
|
|
3212
|
+
return true;
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
return false;
|
|
3216
|
+
}
|
|
3217
|
+
function isNodeInsideAny(node, ancestors) {
|
|
3218
|
+
return ancestors.some((ancestor) => isNodeInside(node, ancestor));
|
|
3219
|
+
}
|
|
3220
|
+
function isClassLike(node) {
|
|
3221
|
+
return (node.type === AST_NODE_TYPES.ClassDeclaration ||
|
|
3222
|
+
node.type === AST_NODE_TYPES.ClassExpression);
|
|
3223
|
+
}
|
|
3224
|
+
function isClassMember(node) {
|
|
3225
|
+
return (node.type === AST_NODE_TYPES.MethodDefinition ||
|
|
3226
|
+
node.type === AST_NODE_TYPES.PropertyDefinition);
|
|
3227
|
+
}
|
|
3228
|
+
function getEnclosingFunction(node) {
|
|
3229
|
+
return findAncestor(node, isFunctionLike);
|
|
3230
|
+
}
|
|
3231
|
+
function getEnclosingClass(node) {
|
|
3232
|
+
return findSelfOrAncestor(node, isClassLike);
|
|
3233
|
+
}
|
|
3234
|
+
function getEnclosingClassMember(node) {
|
|
3235
|
+
return findAncestor(node, isClassMember);
|
|
3236
|
+
}
|
|
3237
|
+
function getScopeRoot(node) {
|
|
3238
|
+
return (findAncestor(node, (ancestor) => ancestor.type === AST_NODE_TYPES.Program || isFunctionLike(ancestor)) ?? node);
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
const createRule$f = ESLintUtils.RuleCreator((name) => name);
|
|
3242
|
+
const rule$i = createRule$f({
|
|
3038
3243
|
create(context) {
|
|
3039
3244
|
const checkImplicitPublic = (node) => {
|
|
3040
|
-
const classRef =
|
|
3245
|
+
const classRef = getEnclosingClass(node);
|
|
3041
3246
|
if (!classRef ||
|
|
3042
3247
|
node.kind === 'constructor' ||
|
|
3043
3248
|
!!node?.accessibility ||
|
|
@@ -3095,19 +3300,114 @@ const rule$f = createRule$c({
|
|
|
3095
3300
|
},
|
|
3096
3301
|
name: 'explicit-public-member',
|
|
3097
3302
|
});
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3303
|
+
|
|
3304
|
+
const createRule$e = ESLintUtils.RuleCreator((name) => name);
|
|
3305
|
+
const rule$h = createRule$e({
|
|
3306
|
+
create(context) {
|
|
3307
|
+
const { sourceCode } = context;
|
|
3308
|
+
return {
|
|
3309
|
+
ImportDeclaration(node) {
|
|
3310
|
+
const importAttributesToken = sourceCode.getTokenAfter(node.source);
|
|
3311
|
+
if (importAttributesToken?.value !== 'assert') {
|
|
3312
|
+
return;
|
|
3313
|
+
}
|
|
3314
|
+
context.report({
|
|
3315
|
+
fix: (fixer) => fixer.replaceText(importAttributesToken, 'with'),
|
|
3316
|
+
messageId: 'useWithImportAttributes',
|
|
3317
|
+
node: importAttributesToken,
|
|
3318
|
+
});
|
|
3319
|
+
},
|
|
3320
|
+
};
|
|
3321
|
+
},
|
|
3322
|
+
meta: {
|
|
3323
|
+
docs: {
|
|
3324
|
+
description: 'Disallow legacy `assert { ... }` import assertions. Use `with { ... }` import attributes instead.',
|
|
3325
|
+
},
|
|
3326
|
+
fixable: 'code',
|
|
3327
|
+
messages: {
|
|
3328
|
+
useWithImportAttributes: 'Use `with { ... }` import attributes instead of `assert { ... }`.',
|
|
3329
|
+
},
|
|
3330
|
+
schema: [],
|
|
3331
|
+
type: 'problem',
|
|
3332
|
+
},
|
|
3333
|
+
name: 'no-import-assertions',
|
|
3334
|
+
});
|
|
3335
|
+
|
|
3336
|
+
function getParenthesizedInner(node) {
|
|
3337
|
+
const maybeNode = node;
|
|
3338
|
+
if (maybeNode.type === 'ParenthesizedExpression') {
|
|
3339
|
+
return maybeNode.expression ?? null;
|
|
3101
3340
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3341
|
+
return null;
|
|
3342
|
+
}
|
|
3343
|
+
function unwrapParenthesized(node) {
|
|
3344
|
+
let current = node;
|
|
3345
|
+
let inner = getParenthesizedInner(current);
|
|
3346
|
+
while (inner) {
|
|
3347
|
+
current = inner;
|
|
3348
|
+
inner = getParenthesizedInner(current);
|
|
3104
3349
|
}
|
|
3105
|
-
return
|
|
3350
|
+
return current;
|
|
3106
3351
|
}
|
|
3107
3352
|
|
|
3108
|
-
const createRule$
|
|
3353
|
+
const createRule$d = ESLintUtils.RuleCreator((name) => name);
|
|
3354
|
+
function isInfiniteLoopLiteral(node) {
|
|
3355
|
+
const unwrapped = unwrapParenthesized(node);
|
|
3356
|
+
if (unwrapped.type !== AST_NODE_TYPES.Literal) {
|
|
3357
|
+
return false;
|
|
3358
|
+
}
|
|
3359
|
+
return unwrapped.value === true || unwrapped.value === 1;
|
|
3360
|
+
}
|
|
3361
|
+
function isInfiniteLoopTest(test) {
|
|
3362
|
+
return test === null || isInfiniteLoopLiteral(test);
|
|
3363
|
+
}
|
|
3364
|
+
const rule$g = createRule$d({
|
|
3365
|
+
create(context) {
|
|
3366
|
+
return {
|
|
3367
|
+
DoWhileStatement(node) {
|
|
3368
|
+
if (isInfiniteLoopTest(node.test)) {
|
|
3369
|
+
context.report({
|
|
3370
|
+
messageId: 'doWhileLoop',
|
|
3371
|
+
node: node.test,
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
},
|
|
3375
|
+
ForStatement(node) {
|
|
3376
|
+
if (isInfiniteLoopTest(node.test)) {
|
|
3377
|
+
context.report({
|
|
3378
|
+
messageId: 'forLoop',
|
|
3379
|
+
node,
|
|
3380
|
+
});
|
|
3381
|
+
}
|
|
3382
|
+
},
|
|
3383
|
+
WhileStatement(node) {
|
|
3384
|
+
if (isInfiniteLoopTest(node.test)) {
|
|
3385
|
+
context.report({
|
|
3386
|
+
messageId: 'whileLoop',
|
|
3387
|
+
node: node.test,
|
|
3388
|
+
});
|
|
3389
|
+
}
|
|
3390
|
+
},
|
|
3391
|
+
};
|
|
3392
|
+
},
|
|
3393
|
+
meta: {
|
|
3394
|
+
docs: {
|
|
3395
|
+
description: 'Disallow `for (;;)` / conditionals `for`, `while (true)`, and `do ... while (true)`. Prefer loops with meaningful exit conditions.',
|
|
3396
|
+
},
|
|
3397
|
+
messages: {
|
|
3398
|
+
doWhileLoop: 'Use an explicit exit condition instead of `do ... while (true)`.',
|
|
3399
|
+
forLoop: 'Use an explicit exit condition instead of a `for` loop without a condition.',
|
|
3400
|
+
whileLoop: 'Use an explicit exit condition instead of `while (true)`.',
|
|
3401
|
+
},
|
|
3402
|
+
schema: [],
|
|
3403
|
+
type: 'suggestion',
|
|
3404
|
+
},
|
|
3405
|
+
name: 'no-infinite-loop',
|
|
3406
|
+
});
|
|
3407
|
+
|
|
3408
|
+
const createRule$c = ESLintUtils.RuleCreator((name) => name);
|
|
3109
3409
|
const LEGACY_PEER_DEPS_PATTERN = /^legacy-peer-deps\s*=\s*true$/i;
|
|
3110
|
-
const rule$
|
|
3410
|
+
const rule$f = createRule$c({
|
|
3111
3411
|
create(context) {
|
|
3112
3412
|
return {
|
|
3113
3413
|
Program(node) {
|
|
@@ -3145,11 +3445,10 @@ const rule$e = createRule$b({
|
|
|
3145
3445
|
name: 'no-legacy-peer-deps',
|
|
3146
3446
|
});
|
|
3147
3447
|
|
|
3148
|
-
const createRule$
|
|
3149
|
-
const rule$
|
|
3448
|
+
const createRule$b = ESLintUtils.RuleCreator((name) => name);
|
|
3449
|
+
const rule$e = createRule$b({
|
|
3150
3450
|
create(context) {
|
|
3151
|
-
const
|
|
3152
|
-
const checker = services.program.getTypeChecker();
|
|
3451
|
+
const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
|
|
3153
3452
|
return {
|
|
3154
3453
|
CallExpression(node) {
|
|
3155
3454
|
const callee = node.callee;
|
|
@@ -3163,18 +3462,18 @@ const rule$d = createRule$a({
|
|
|
3163
3462
|
return;
|
|
3164
3463
|
}
|
|
3165
3464
|
const [argument] = node.arguments;
|
|
3166
|
-
if (!argument || !
|
|
3465
|
+
if (!argument || !isEmptyStaticString(argument)) {
|
|
3167
3466
|
return;
|
|
3168
3467
|
}
|
|
3169
3468
|
const objectExpression = callee.object;
|
|
3170
|
-
const tsNode =
|
|
3469
|
+
const tsNode = esTreeNodeToTSNodeMap.get(objectExpression);
|
|
3171
3470
|
const type = checker.getTypeAtLocation(tsNode);
|
|
3172
3471
|
if (!isPlaywrightLocator(type, checker)) {
|
|
3173
3472
|
return;
|
|
3174
3473
|
}
|
|
3175
3474
|
context.report({
|
|
3176
3475
|
fix(fixer) {
|
|
3177
|
-
const objectText =
|
|
3476
|
+
const objectText = sourceCode.getText(objectExpression);
|
|
3178
3477
|
return fixer.replaceText(node, `${objectText}.clear()`);
|
|
3179
3478
|
},
|
|
3180
3479
|
messageId: 'useClear',
|
|
@@ -3194,13 +3493,6 @@ const rule$d = createRule$a({
|
|
|
3194
3493
|
},
|
|
3195
3494
|
name: 'no-playwright-empty-fill',
|
|
3196
3495
|
});
|
|
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
3496
|
function isPlaywrightLocator(type, checker) {
|
|
3205
3497
|
if (isPlaywrightLocatorType(type)) {
|
|
3206
3498
|
return true;
|
|
@@ -3311,7 +3603,7 @@ const config = {
|
|
|
3311
3603
|
},
|
|
3312
3604
|
};
|
|
3313
3605
|
|
|
3314
|
-
const createRule$
|
|
3606
|
+
const createRule$a = ESLintUtils.RuleCreator((name) => name);
|
|
3315
3607
|
function collectArrayExpressions(node) {
|
|
3316
3608
|
const result = [];
|
|
3317
3609
|
if (node.type === AST_NODE_TYPES.ArrayExpression) {
|
|
@@ -3337,17 +3629,16 @@ function collectArrayExpressions(node) {
|
|
|
3337
3629
|
}
|
|
3338
3630
|
return result;
|
|
3339
3631
|
}
|
|
3340
|
-
const rule$
|
|
3632
|
+
const rule$d = createRule$a({
|
|
3341
3633
|
create(context) {
|
|
3342
|
-
const
|
|
3343
|
-
const typeChecker = parserServices.program.getTypeChecker();
|
|
3634
|
+
const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
|
|
3344
3635
|
const ignoreTupleContextualTyping = context.options[0]?.ignoreTupleContextualTyping ?? true;
|
|
3345
3636
|
function check(node, typeAnnotation, value) {
|
|
3346
3637
|
if (!typeAnnotation || !value) {
|
|
3347
3638
|
return;
|
|
3348
3639
|
}
|
|
3349
|
-
const tsNode =
|
|
3350
|
-
const tsValueNode =
|
|
3640
|
+
const tsNode = esTreeNodeToTSNodeMap.get(node);
|
|
3641
|
+
const tsValueNode = esTreeNodeToTSNodeMap.get(value);
|
|
3351
3642
|
const declaredType = typeChecker.getTypeAtLocation(tsNode);
|
|
3352
3643
|
const inferredType = typeChecker.getTypeAtLocation(tsValueNode);
|
|
3353
3644
|
if (typeChecker.typeToString(declaredType) !==
|
|
@@ -3371,7 +3662,7 @@ const rule$c = createRule$9({
|
|
|
3371
3662
|
if (ignoreTupleContextualTyping) {
|
|
3372
3663
|
const arrayExpressions = collectArrayExpressions(value);
|
|
3373
3664
|
for (const arrayExpression of arrayExpressions) {
|
|
3374
|
-
const tsArrayNode =
|
|
3665
|
+
const tsArrayNode = esTreeNodeToTSNodeMap.get(arrayExpression);
|
|
3375
3666
|
if (typeChecker.isTupleType(typeChecker.getTypeAtLocation(tsArrayNode))) {
|
|
3376
3667
|
return;
|
|
3377
3668
|
}
|
|
@@ -3465,16 +3756,6 @@ function unwrapExpression(expression) {
|
|
|
3465
3756
|
return current;
|
|
3466
3757
|
}
|
|
3467
3758
|
|
|
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
3759
|
function unwrapMutationTarget(node) {
|
|
3479
3760
|
let current = node;
|
|
3480
3761
|
while (current.type === AST_NODE_TYPES.TSAsExpression ||
|
|
@@ -3508,6 +3789,7 @@ function collectMutationTargets(node) {
|
|
|
3508
3789
|
return [];
|
|
3509
3790
|
}
|
|
3510
3791
|
}
|
|
3792
|
+
|
|
3511
3793
|
function getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap) {
|
|
3512
3794
|
const tsNode = esTreeNodeToTSNodeMap.get(node);
|
|
3513
3795
|
if (!tsNode) {
|
|
@@ -3515,6 +3797,11 @@ function getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap) {
|
|
|
3515
3797
|
}
|
|
3516
3798
|
return checker.getSymbolAtLocation(tsNode) ?? null;
|
|
3517
3799
|
}
|
|
3800
|
+
|
|
3801
|
+
const createRule$9 = ESLintUtils.RuleCreator((name) => name);
|
|
3802
|
+
function isFunctionLikeScope(node) {
|
|
3803
|
+
return !!node && isFunctionLike(node);
|
|
3804
|
+
}
|
|
3518
3805
|
function isLocalIdentifier(node, context, localScopes) {
|
|
3519
3806
|
const symbol = getSymbolAtNode(node, context.checker, context.esTreeNodeToTSNodeMap);
|
|
3520
3807
|
if (!symbol) {
|
|
@@ -3671,9 +3958,9 @@ function functionHasObservableSideEffects(root, context, localScopes, visitedFun
|
|
|
3671
3958
|
return false;
|
|
3672
3959
|
}
|
|
3673
3960
|
if (isAngularUntrackedCall(node, context.program)) {
|
|
3674
|
-
const
|
|
3675
|
-
if (
|
|
3676
|
-
functionHasObservableSideEffects(
|
|
3961
|
+
const callback = getReactiveCallbackArgument(node);
|
|
3962
|
+
if (callback &&
|
|
3963
|
+
functionHasObservableSideEffects(callback, context, [...localScopes, callback], visitedFunctions)) {
|
|
3677
3964
|
hasSideEffect = true;
|
|
3678
3965
|
}
|
|
3679
3966
|
return false;
|
|
@@ -3727,9 +4014,9 @@ function inspectComputedBody(root, context, localScopes, visitedFunctions, repor
|
|
|
3727
4014
|
return false;
|
|
3728
4015
|
}
|
|
3729
4016
|
if (isAngularUntrackedCall(node, context.program)) {
|
|
3730
|
-
const
|
|
3731
|
-
if (
|
|
3732
|
-
inspectComputedBody(
|
|
4017
|
+
const callback = getReactiveCallbackArgument(node);
|
|
4018
|
+
if (callback) {
|
|
4019
|
+
inspectComputedBody(callback, context, [...localScopes, callback], visitedFunctions, report);
|
|
3733
4020
|
}
|
|
3734
4021
|
return false;
|
|
3735
4022
|
}
|
|
@@ -3768,14 +4055,11 @@ function inspectComputedBody(root, context, localScopes, visitedFunctions, repor
|
|
|
3768
4055
|
return;
|
|
3769
4056
|
});
|
|
3770
4057
|
}
|
|
3771
|
-
const rule$
|
|
4058
|
+
const rule$c = createRule$9({
|
|
3772
4059
|
create(context) {
|
|
3773
|
-
const
|
|
3774
|
-
const
|
|
3775
|
-
const
|
|
3776
|
-
const tsNodeToESTreeNodeMap = parserServices.tsNodeToESTreeNodeMap;
|
|
3777
|
-
const { sourceCode } = context;
|
|
3778
|
-
const program = sourceCode.ast;
|
|
4060
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
|
|
4061
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
4062
|
+
const estreeNodeMap = tsNodeToESTreeNodeMap;
|
|
3779
4063
|
return {
|
|
3780
4064
|
CallExpression(node) {
|
|
3781
4065
|
for (const scope of getReactiveScopes(node, program)) {
|
|
@@ -3784,10 +4068,10 @@ const rule$b = createRule$8({
|
|
|
3784
4068
|
}
|
|
3785
4069
|
const analysisContext = {
|
|
3786
4070
|
checker,
|
|
3787
|
-
esTreeNodeToTSNodeMap,
|
|
4071
|
+
esTreeNodeToTSNodeMap: signalNodeMap,
|
|
3788
4072
|
program,
|
|
3789
4073
|
reported: new Set(),
|
|
3790
|
-
tsNodeToESTreeNodeMap,
|
|
4074
|
+
tsNodeToESTreeNodeMap: estreeNodeMap,
|
|
3791
4075
|
};
|
|
3792
4076
|
inspectComputedBody(scope.callback, analysisContext, [scope.callback], new Set([String(scope.callback.range)]), (reportNode) => {
|
|
3793
4077
|
context.report({
|
|
@@ -3814,13 +4098,10 @@ const rule$b = createRule$8({
|
|
|
3814
4098
|
name: 'no-side-effects-in-computed',
|
|
3815
4099
|
});
|
|
3816
4100
|
|
|
3817
|
-
const rule$
|
|
4101
|
+
const rule$b = createUntrackedRule({
|
|
3818
4102
|
create(context) {
|
|
3819
|
-
const
|
|
3820
|
-
const
|
|
3821
|
-
const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
|
|
3822
|
-
const { sourceCode } = context;
|
|
3823
|
-
const program = sourceCode.ast;
|
|
4103
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
|
|
4104
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
3824
4105
|
return {
|
|
3825
4106
|
CallExpression(node) {
|
|
3826
4107
|
for (const scope of getReactiveScopes(node, program)) {
|
|
@@ -3828,7 +4109,7 @@ const rule$a = createUntrackedRule({
|
|
|
3828
4109
|
walkAfterAsyncBoundaryAst(scope.callback, (inner) => {
|
|
3829
4110
|
if (inner.type !== AST_NODE_TYPES.CallExpression ||
|
|
3830
4111
|
isAngularUntrackedCall(inner, program) ||
|
|
3831
|
-
!isSignalReadCall(inner, checker,
|
|
4112
|
+
!isSignalReadCall(inner, checker, signalNodeMap)) {
|
|
3832
4113
|
return;
|
|
3833
4114
|
}
|
|
3834
4115
|
const key = String(inner.range);
|
|
@@ -3863,11 +4144,7 @@ const rule$a = createUntrackedRule({
|
|
|
3863
4144
|
name: 'no-signal-reads-after-await-in-reactive-context',
|
|
3864
4145
|
});
|
|
3865
4146
|
|
|
3866
|
-
const createRule$
|
|
3867
|
-
function isStringLiteral(node) {
|
|
3868
|
-
return (node.type === AST_NODE_TYPES.Literal &&
|
|
3869
|
-
typeof node.value === 'string');
|
|
3870
|
-
}
|
|
4147
|
+
const createRule$8 = ESLintUtils.RuleCreator((name) => name);
|
|
3871
4148
|
function collectParts(node) {
|
|
3872
4149
|
if (node.type === AST_NODE_TYPES.BinaryExpression && node.operator === '+') {
|
|
3873
4150
|
return [...collectParts(node.left), ...collectParts(node.right)];
|
|
@@ -3917,17 +4194,7 @@ function templateContent(template, renderExpr) {
|
|
|
3917
4194
|
: ''}`)
|
|
3918
4195
|
.join('');
|
|
3919
4196
|
}
|
|
3920
|
-
|
|
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
|
-
const rule$9 = createRule$7({
|
|
4197
|
+
const rule$a = createRule$8({
|
|
3931
4198
|
create(context) {
|
|
3932
4199
|
const { sourceCode } = context;
|
|
3933
4200
|
let parserServices = null;
|
|
@@ -3982,7 +4249,7 @@ const rule$9 = createRule$7({
|
|
|
3982
4249
|
}
|
|
3983
4250
|
// Nested inside a template but not direct child — would produce
|
|
3984
4251
|
// `${`${a}${b}`.method()}`, so skip
|
|
3985
|
-
if (
|
|
4252
|
+
if (hasAncestor(node, (ancestor) => ancestor.type === AST_NODE_TYPES.TemplateLiteral)) {
|
|
3986
4253
|
return;
|
|
3987
4254
|
}
|
|
3988
4255
|
context.report({
|
|
@@ -4096,67 +4363,6 @@ function buildImportRemovalFixes(program, fixer, sourceCode) {
|
|
|
4096
4363
|
return [fixer.remove(untrackedSpec)];
|
|
4097
4364
|
}
|
|
4098
4365
|
|
|
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
4366
|
function hasNamedDecorator(node, name) {
|
|
4161
4367
|
return node.decorators.some((decorator) => {
|
|
4162
4368
|
const expression = decorator.expression;
|
|
@@ -4168,68 +4374,75 @@ function hasNamedDecorator(node, name) {
|
|
|
4168
4374
|
expression.callee.name === name);
|
|
4169
4375
|
});
|
|
4170
4376
|
}
|
|
4377
|
+
|
|
4171
4378
|
function isPipeTransformMember(member) {
|
|
4172
4379
|
if (getClassMemberName(member) !== 'transform') {
|
|
4173
4380
|
return false;
|
|
4174
4381
|
}
|
|
4175
|
-
|
|
4382
|
+
const ownerClass = getEnclosingClass(member);
|
|
4383
|
+
return !!ownerClass && hasNamedDecorator(ownerClass, 'Pipe');
|
|
4176
4384
|
}
|
|
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
|
-
}
|
|
4385
|
+
|
|
4386
|
+
function isAngularInjectionTokenFactoryFunction(fn, program) {
|
|
4387
|
+
const parent = fn.parent;
|
|
4388
|
+
const injectionTokenName = getLocalNameForImport(program, '@angular/core', 'InjectionToken');
|
|
4389
|
+
if (!injectionTokenName ||
|
|
4390
|
+
parent.type !== AST_NODE_TYPES.Property ||
|
|
4391
|
+
getObjectPropertyName(parent) !== 'factory') {
|
|
4392
|
+
return false;
|
|
4191
4393
|
}
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
!!memberName &&
|
|
4199
|
-
(IMPERATIVE_UNTRACKED_METHODS.has(memberName) ||
|
|
4200
|
-
isPipeTransformMember(member))) ||
|
|
4201
|
-
hasAllowedImperativeAssignment(node));
|
|
4394
|
+
const objectExpression = parent.parent;
|
|
4395
|
+
return (objectExpression.type === AST_NODE_TYPES.ObjectExpression &&
|
|
4396
|
+
objectExpression.parent.type === AST_NODE_TYPES.NewExpression &&
|
|
4397
|
+
objectExpression.parent.arguments.includes(objectExpression) &&
|
|
4398
|
+
objectExpression.parent.callee.type === AST_NODE_TYPES.Identifier &&
|
|
4399
|
+
objectExpression.parent.callee.name === injectionTokenName);
|
|
4202
4400
|
}
|
|
4203
|
-
function
|
|
4401
|
+
function isAngularUseFactoryFunction(fn) {
|
|
4204
4402
|
const parent = fn.parent;
|
|
4205
|
-
if (
|
|
4206
|
-
|
|
4403
|
+
if (parent.type !== AST_NODE_TYPES.Property ||
|
|
4404
|
+
getObjectPropertyName(parent) !== 'useFactory') {
|
|
4207
4405
|
return false;
|
|
4208
4406
|
}
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4407
|
+
const objectExpression = parent.parent;
|
|
4408
|
+
return (objectExpression.type === AST_NODE_TYPES.ObjectExpression &&
|
|
4409
|
+
objectExpression.properties.some((property) => property.type === AST_NODE_TYPES.Property &&
|
|
4410
|
+
getObjectPropertyName(property) === 'provide'));
|
|
4212
4411
|
}
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4412
|
+
|
|
4413
|
+
/**
|
|
4414
|
+
* Removes `extraSpaces` leading spaces from every line of `text` that starts
|
|
4415
|
+
* with at least that many spaces.
|
|
4416
|
+
*/
|
|
4417
|
+
function dedent(text, extraSpaces) {
|
|
4418
|
+
if (extraSpaces <= 0) {
|
|
4419
|
+
return text;
|
|
4218
4420
|
}
|
|
4219
|
-
|
|
4421
|
+
const prefix = ' '.repeat(extraSpaces);
|
|
4422
|
+
return text
|
|
4423
|
+
.split('\n')
|
|
4424
|
+
.map((line) => (line.startsWith(prefix) ? line.slice(extraSpaces) : line))
|
|
4425
|
+
.join('\n');
|
|
4426
|
+
}
|
|
4427
|
+
|
|
4428
|
+
function isDirectCallOrNewArgument(node) {
|
|
4429
|
+
const parent = node.parent;
|
|
4430
|
+
if (node.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4431
|
+
node.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
4432
|
+
return false;
|
|
4433
|
+
}
|
|
4434
|
+
return ((parent.type === AST_NODE_TYPES.CallExpression ||
|
|
4435
|
+
parent.type === AST_NODE_TYPES.NewExpression) &&
|
|
4436
|
+
parent.arguments.includes(node));
|
|
4220
4437
|
}
|
|
4221
|
-
function
|
|
4438
|
+
function isStoredFunctionUsedAsCallOrNewArgument(fn, checker, esTreeNodeToTSNodeMap) {
|
|
4222
4439
|
const parent = fn.parent;
|
|
4223
4440
|
if (parent.type !== AST_NODE_TYPES.VariableDeclarator ||
|
|
4224
4441
|
parent.id.type !== AST_NODE_TYPES.Identifier) {
|
|
4225
4442
|
return false;
|
|
4226
4443
|
}
|
|
4227
4444
|
const id = parent.id;
|
|
4228
|
-
const
|
|
4229
|
-
if (!tsNode) {
|
|
4230
|
-
return false;
|
|
4231
|
-
}
|
|
4232
|
-
const symbol = checker.getSymbolAtLocation(tsNode);
|
|
4445
|
+
const symbol = getSymbolAtNode(id, checker, esTreeNodeToTSNodeMap);
|
|
4233
4446
|
if (!symbol) {
|
|
4234
4447
|
return false;
|
|
4235
4448
|
}
|
|
@@ -4241,10 +4454,7 @@ function isStoredCallbackUsedAsArgument(fn, checker, esTreeNodeToTSNodeMap) {
|
|
|
4241
4454
|
node.name !== id.name) {
|
|
4242
4455
|
return;
|
|
4243
4456
|
}
|
|
4244
|
-
const
|
|
4245
|
-
const referenceSymbol = referenceTsNode
|
|
4246
|
-
? checker.getSymbolAtLocation(referenceTsNode)
|
|
4247
|
-
: null;
|
|
4457
|
+
const referenceSymbol = getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap);
|
|
4248
4458
|
if (referenceSymbol !== symbol) {
|
|
4249
4459
|
return;
|
|
4250
4460
|
}
|
|
@@ -4259,81 +4469,50 @@ function isStoredCallbackUsedAsArgument(fn, checker, esTreeNodeToTSNodeMap) {
|
|
|
4259
4469
|
});
|
|
4260
4470
|
return found;
|
|
4261
4471
|
}
|
|
4472
|
+
|
|
4473
|
+
const IMPERATIVE_UNTRACKED_METHODS = new Set(['registerOnChange', 'writeValue']);
|
|
4474
|
+
function hasAllowedImperativeAssignment(node) {
|
|
4475
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
4476
|
+
if (current.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4477
|
+
current.type !== AST_NODE_TYPES.FunctionDeclaration &&
|
|
4478
|
+
current.type !== AST_NODE_TYPES.FunctionExpression) {
|
|
4479
|
+
continue;
|
|
4480
|
+
}
|
|
4481
|
+
const parent = current.parent;
|
|
4482
|
+
if (parent.type === AST_NODE_TYPES.AssignmentExpression &&
|
|
4483
|
+
parent.right === current &&
|
|
4484
|
+
parent.left.type === AST_NODE_TYPES.MemberExpression) {
|
|
4485
|
+
const memberName = getMemberExpressionPropertyName(parent.left);
|
|
4486
|
+
if (memberName && IMPERATIVE_UNTRACKED_METHODS.has(memberName)) {
|
|
4487
|
+
return true;
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
return false;
|
|
4492
|
+
}
|
|
4493
|
+
function isAllowedImperativeAngularContext(node) {
|
|
4494
|
+
const member = getEnclosingClassMember(node);
|
|
4495
|
+
const memberName = member ? getClassMemberName(member) : null;
|
|
4496
|
+
return ((!!member &&
|
|
4497
|
+
!!memberName &&
|
|
4498
|
+
(IMPERATIVE_UNTRACKED_METHODS.has(memberName) ||
|
|
4499
|
+
isPipeTransformMember(member))) ||
|
|
4500
|
+
hasAllowedImperativeAssignment(node));
|
|
4501
|
+
}
|
|
4262
4502
|
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)) {
|
|
4503
|
+
if (!getReactiveCallbackArgument(node)) {
|
|
4268
4504
|
return false;
|
|
4269
4505
|
}
|
|
4270
4506
|
const fn = getEnclosingFunction(node);
|
|
4271
4507
|
if (!fn) {
|
|
4272
4508
|
return false;
|
|
4273
4509
|
}
|
|
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;
|
|
4510
|
+
return (isDirectCallOrNewArgument(fn) ||
|
|
4511
|
+
isStoredFunctionUsedAsCallOrNewArgument(fn, checker, esTreeNodeToTSNodeMap));
|
|
4333
4512
|
}
|
|
4334
4513
|
function isAllowedLazyAngularFactoryContext(node, program) {
|
|
4335
4514
|
const fn = getEnclosingFunction(node);
|
|
4336
|
-
if (!fn || !
|
|
4515
|
+
if (!fn || !getReturnedReactiveOwnerCall(node, program)) {
|
|
4337
4516
|
return false;
|
|
4338
4517
|
}
|
|
4339
4518
|
return (isAngularInjectionTokenFactoryFunction(fn, program) ||
|
|
@@ -4345,16 +4524,12 @@ function buildReactiveCallReplacement(outerUntrackedCall, reactiveCall, sourceCo
|
|
|
4345
4524
|
outerUntrackedCall.parent.type !== AST_NODE_TYPES.ExpressionStatement) {
|
|
4346
4525
|
return text;
|
|
4347
4526
|
}
|
|
4348
|
-
return dedent
|
|
4527
|
+
return dedent(text, reactiveCall.loc.start.column - outerUntrackedCall.parent.loc.start.column);
|
|
4349
4528
|
}
|
|
4350
|
-
const rule$
|
|
4529
|
+
const rule$9 = createUntrackedRule({
|
|
4351
4530
|
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);
|
|
4531
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
|
|
4532
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
4358
4533
|
function isUntrackedUsedElsewhere(localName, excludeNode) {
|
|
4359
4534
|
let found = false;
|
|
4360
4535
|
walkAst(program, (node) => {
|
|
@@ -4375,18 +4550,18 @@ const rule$8 = createUntrackedRule({
|
|
|
4375
4550
|
findEnclosingReactiveScope(node, program) ||
|
|
4376
4551
|
findEnclosingReactiveScopeAfterAsyncBoundary(node, program) ||
|
|
4377
4552
|
isAllowedImperativeAngularContext(node) ||
|
|
4378
|
-
isAllowedDeferredCallbackContext(node, checker,
|
|
4553
|
+
isAllowedDeferredCallbackContext(node, checker, signalNodeMap) ||
|
|
4379
4554
|
isAllowedLazyAngularFactoryContext(node, program)) {
|
|
4380
4555
|
return;
|
|
4381
4556
|
}
|
|
4382
|
-
const reactiveCall =
|
|
4557
|
+
const reactiveCall = getReturnedReactiveOwnerCall(node, program);
|
|
4383
4558
|
context.report({
|
|
4384
4559
|
fix: reactiveCall
|
|
4385
4560
|
? (fixer) => {
|
|
4386
4561
|
const fixes = [
|
|
4387
4562
|
fixer.replaceText(node, buildReactiveCallReplacement(node, reactiveCall, sourceCode)),
|
|
4388
4563
|
];
|
|
4389
|
-
const untrackedLocalName =
|
|
4564
|
+
const untrackedLocalName = findUntrackedAlias(program);
|
|
4390
4565
|
const stillUsed = untrackedLocalName !== null &&
|
|
4391
4566
|
isUntrackedUsedElsewhere(untrackedLocalName, node);
|
|
4392
4567
|
if (!stillUsed) {
|
|
@@ -4418,20 +4593,16 @@ const rule$8 = createUntrackedRule({
|
|
|
4418
4593
|
name: 'no-untracked-outside-reactive-context',
|
|
4419
4594
|
});
|
|
4420
4595
|
|
|
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');
|
|
4596
|
+
function collectCallExpressions(root) {
|
|
4597
|
+
const result = [];
|
|
4598
|
+
walkAst(root, (node) => {
|
|
4599
|
+
if (node.type === AST_NODE_TYPES.CallExpression) {
|
|
4600
|
+
result.push(node);
|
|
4601
|
+
}
|
|
4602
|
+
});
|
|
4603
|
+
return result;
|
|
4434
4604
|
}
|
|
4605
|
+
|
|
4435
4606
|
/**
|
|
4436
4607
|
* Builds the replacement text for the parent ExpressionStatement of an
|
|
4437
4608
|
* `untracked(...)` call.
|
|
@@ -4443,13 +4614,11 @@ function dedent(text, extraSpaces) {
|
|
|
4443
4614
|
* Returns null if the untracked argument is not a function expression.
|
|
4444
4615
|
*/
|
|
4445
4616
|
function buildReplacement(untrackedCall, parentStatement, sourceCode) {
|
|
4446
|
-
const
|
|
4447
|
-
if (!
|
|
4448
|
-
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4449
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
4617
|
+
const callback = getReactiveCallbackArgument(untrackedCall);
|
|
4618
|
+
if (!callback) {
|
|
4450
4619
|
return null;
|
|
4451
4620
|
}
|
|
4452
|
-
const { body } =
|
|
4621
|
+
const { body } = callback;
|
|
4453
4622
|
if (body.type === AST_NODE_TYPES.BlockStatement) {
|
|
4454
4623
|
const { body: stmts } = body;
|
|
4455
4624
|
if (stmts.length === 0) {
|
|
@@ -4464,15 +4633,6 @@ function buildReplacement(untrackedCall, parentStatement, sourceCode) {
|
|
|
4464
4633
|
// Expression body: arrow `() => expr` — just emit `expr;`
|
|
4465
4634
|
return `${sourceCode.getText(body)};`;
|
|
4466
4635
|
}
|
|
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
4636
|
function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program) {
|
|
4477
4637
|
let found = false;
|
|
4478
4638
|
walkSynchronousAst(root, (node) => {
|
|
@@ -4494,29 +4654,23 @@ function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program
|
|
|
4494
4654
|
});
|
|
4495
4655
|
return found;
|
|
4496
4656
|
}
|
|
4497
|
-
const rule$
|
|
4657
|
+
const rule$8 = createUntrackedRule({
|
|
4498
4658
|
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');
|
|
4659
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
|
|
4660
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
4505
4661
|
function isUntrackedUsedElsewhere(localName, excludeNode) {
|
|
4506
|
-
return
|
|
4662
|
+
return collectCallExpressions(program).some((n) => n !== excludeNode &&
|
|
4507
4663
|
n.callee.type === AST_NODE_TYPES.Identifier &&
|
|
4508
4664
|
n.callee.name === localName);
|
|
4509
4665
|
}
|
|
4510
4666
|
function checkUntrackedCall(untrackedCall, kind) {
|
|
4511
|
-
const
|
|
4512
|
-
if (!
|
|
4513
|
-
(arg.type !== AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
4514
|
-
arg.type !== AST_NODE_TYPES.FunctionExpression)) {
|
|
4667
|
+
const callback = getReactiveCallbackArgument(untrackedCall);
|
|
4668
|
+
if (!callback) {
|
|
4515
4669
|
return;
|
|
4516
4670
|
}
|
|
4517
|
-
const { reads } = collectSignalUsages(
|
|
4671
|
+
const { reads } = collectSignalUsages(callback, checker, signalNodeMap, program);
|
|
4518
4672
|
if (reads.length > 0 ||
|
|
4519
|
-
hasOpaqueSynchronousCalls(
|
|
4673
|
+
hasOpaqueSynchronousCalls(callback, checker, signalNodeMap, program)) {
|
|
4520
4674
|
// Snapshot reads inside reactive callbacks are a valid Angular
|
|
4521
4675
|
// pattern even when the snapshot later influences branching.
|
|
4522
4676
|
return;
|
|
@@ -4534,7 +4688,7 @@ const rule$7 = createUntrackedRule({
|
|
|
4534
4688
|
if (replacement === null) {
|
|
4535
4689
|
return null;
|
|
4536
4690
|
}
|
|
4537
|
-
const untrackedLocalName =
|
|
4691
|
+
const untrackedLocalName = findUntrackedAlias(program);
|
|
4538
4692
|
const stillUsed = untrackedLocalName !== null &&
|
|
4539
4693
|
isUntrackedUsedElsewhere(untrackedLocalName, untrackedCall);
|
|
4540
4694
|
const fixes = [fixer.replaceText(parentStmt, replacement)];
|
|
@@ -4582,8 +4736,8 @@ const rule$7 = createUntrackedRule({
|
|
|
4582
4736
|
name: 'no-useless-untracked',
|
|
4583
4737
|
});
|
|
4584
4738
|
|
|
4585
|
-
const createRule$
|
|
4586
|
-
const rule$
|
|
4739
|
+
const createRule$7 = ESLintUtils.RuleCreator((name) => name);
|
|
4740
|
+
const rule$7 = createRule$7({
|
|
4587
4741
|
create(context, [{ printWidth }]) {
|
|
4588
4742
|
const sourceCode = context.sourceCode;
|
|
4589
4743
|
const getLineEndIndex = (lineStartIndex) => {
|
|
@@ -4835,24 +4989,8 @@ const rule$6 = createRule$6({
|
|
|
4835
4989
|
name: 'object-single-line',
|
|
4836
4990
|
});
|
|
4837
4991
|
|
|
4838
|
-
const createRule$
|
|
4992
|
+
const createRule$6 = ESLintUtils.RuleCreator((name) => name);
|
|
4839
4993
|
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
4994
|
function isSupportedControlFlowStatement(node) {
|
|
4857
4995
|
return (node.type === AST_NODE_TYPES.BreakStatement ||
|
|
4858
4996
|
node.type === AST_NODE_TYPES.ContinueStatement ||
|
|
@@ -4920,7 +5058,7 @@ function renderTest(node, sourceCode) {
|
|
|
4920
5058
|
const text = sourceCode.getText(node);
|
|
4921
5059
|
return needsParenthesesInOrChain(node) ? `(${text})` : text;
|
|
4922
5060
|
}
|
|
4923
|
-
const rule$
|
|
5061
|
+
const rule$6 = createRule$6({
|
|
4924
5062
|
create(context) {
|
|
4925
5063
|
const { sourceCode } = context;
|
|
4926
5064
|
function checkBody(statements) {
|
|
@@ -5000,8 +5138,8 @@ const rule$5 = createRule$5({
|
|
|
5000
5138
|
|
|
5001
5139
|
const MESSAGE_ID$1 = 'prefer-deep-imports';
|
|
5002
5140
|
const ERROR_MESSAGE = 'Import via root entry point is prohibited when nested entry points exist';
|
|
5003
|
-
const createRule$
|
|
5004
|
-
var preferDeepImports = createRule$
|
|
5141
|
+
const createRule$5 = ESLintUtils.RuleCreator(() => ERROR_MESSAGE);
|
|
5142
|
+
var preferDeepImports = createRule$5({
|
|
5005
5143
|
create(context, [options]) {
|
|
5006
5144
|
const allowedPackages = normalizeImportFilter(options.importFilter);
|
|
5007
5145
|
const isStrictMode = options.strict ?? false;
|
|
@@ -5342,7 +5480,7 @@ function buildRewrittenImports(node, baseImportPath, symbolToEntryPoint) {
|
|
|
5342
5480
|
return importStatements.join('\n');
|
|
5343
5481
|
}
|
|
5344
5482
|
|
|
5345
|
-
const createRule$
|
|
5483
|
+
const createRule$4 = ESLintUtils.RuleCreator((name) => name);
|
|
5346
5484
|
function getPushCall(node) {
|
|
5347
5485
|
if (node.expression.type !== AST_NODE_TYPES.CallExpression) {
|
|
5348
5486
|
return null;
|
|
@@ -5357,7 +5495,7 @@ function getPushCall(node) {
|
|
|
5357
5495
|
}
|
|
5358
5496
|
return call;
|
|
5359
5497
|
}
|
|
5360
|
-
const rule$
|
|
5498
|
+
const rule$5 = createRule$4({
|
|
5361
5499
|
create(context) {
|
|
5362
5500
|
const { sourceCode } = context;
|
|
5363
5501
|
function checkBody(statements) {
|
|
@@ -5435,6 +5573,53 @@ const rule$4 = createRule$3({
|
|
|
5435
5573
|
name: 'prefer-multi-arg-push',
|
|
5436
5574
|
});
|
|
5437
5575
|
|
|
5576
|
+
const createRule$3 = ESLintUtils.RuleCreator((name) => name);
|
|
5577
|
+
function getModuleKeywordToken(sourceCode, node) {
|
|
5578
|
+
const firstToken = sourceCode.getFirstToken(node);
|
|
5579
|
+
if (!firstToken) {
|
|
5580
|
+
return null;
|
|
5581
|
+
}
|
|
5582
|
+
if (firstToken.value === 'declare') {
|
|
5583
|
+
return sourceCode.getTokenAfter(firstToken) ?? null;
|
|
5584
|
+
}
|
|
5585
|
+
return firstToken;
|
|
5586
|
+
}
|
|
5587
|
+
const rule$4 = createRule$3({
|
|
5588
|
+
create(context) {
|
|
5589
|
+
const { sourceCode } = context;
|
|
5590
|
+
return {
|
|
5591
|
+
TSModuleDeclaration(node) {
|
|
5592
|
+
if (node.kind !== 'module' ||
|
|
5593
|
+
node.global ||
|
|
5594
|
+
node.id.type === AST_NODE_TYPES.Literal) {
|
|
5595
|
+
return;
|
|
5596
|
+
}
|
|
5597
|
+
const moduleKeywordToken = getModuleKeywordToken(sourceCode, node);
|
|
5598
|
+
if (moduleKeywordToken?.value !== 'module') {
|
|
5599
|
+
return;
|
|
5600
|
+
}
|
|
5601
|
+
context.report({
|
|
5602
|
+
fix: (fixer) => fixer.replaceText(moduleKeywordToken, 'namespace'),
|
|
5603
|
+
messageId: 'useNamespaceKeyword',
|
|
5604
|
+
node: moduleKeywordToken,
|
|
5605
|
+
});
|
|
5606
|
+
},
|
|
5607
|
+
};
|
|
5608
|
+
},
|
|
5609
|
+
meta: {
|
|
5610
|
+
docs: {
|
|
5611
|
+
description: 'Prefer `namespace Foo {}` over the older `module Foo {}` syntax for TypeScript namespace declarations.',
|
|
5612
|
+
},
|
|
5613
|
+
fixable: 'code',
|
|
5614
|
+
messages: {
|
|
5615
|
+
useNamespaceKeyword: 'Use `namespace` instead of `module` for TypeScript namespace declarations.',
|
|
5616
|
+
},
|
|
5617
|
+
schema: [],
|
|
5618
|
+
type: 'problem',
|
|
5619
|
+
},
|
|
5620
|
+
name: 'prefer-namespace-keyword',
|
|
5621
|
+
});
|
|
5622
|
+
|
|
5438
5623
|
/**
|
|
5439
5624
|
* prefer-untracked-incidental-signal-reads
|
|
5440
5625
|
*
|
|
@@ -5492,17 +5677,6 @@ function unwrapUsageExpression(node) {
|
|
|
5492
5677
|
}
|
|
5493
5678
|
return current;
|
|
5494
5679
|
}
|
|
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
5680
|
function isStatementPositionCall(node) {
|
|
5507
5681
|
const usage = unwrapUsageExpression(node);
|
|
5508
5682
|
if (usage.parent?.type === AST_NODE_TYPES.ExpressionStatement) {
|
|
@@ -5555,11 +5729,7 @@ function isAliasDeclarationIdentifier(node) {
|
|
|
5555
5729
|
node.parent.id === node);
|
|
5556
5730
|
}
|
|
5557
5731
|
function aliasHasExternalUsage(alias, consumers, scope, checker, esTreeNodeToTSNodeMap) {
|
|
5558
|
-
const
|
|
5559
|
-
if (!tsNode) {
|
|
5560
|
-
return false;
|
|
5561
|
-
}
|
|
5562
|
-
const symbol = checker.getSymbolAtLocation(tsNode);
|
|
5732
|
+
const symbol = getSymbolAtNode(alias, checker, esTreeNodeToTSNodeMap);
|
|
5563
5733
|
if (!symbol) {
|
|
5564
5734
|
return false;
|
|
5565
5735
|
}
|
|
@@ -5571,10 +5741,7 @@ function aliasHasExternalUsage(alias, consumers, scope, checker, esTreeNodeToTSN
|
|
|
5571
5741
|
isNodeInsideAny(node, consumers)) {
|
|
5572
5742
|
return;
|
|
5573
5743
|
}
|
|
5574
|
-
const
|
|
5575
|
-
const referenceSymbol = referenceTsNode
|
|
5576
|
-
? checker.getSymbolAtLocation(referenceTsNode)
|
|
5577
|
-
: null;
|
|
5744
|
+
const referenceSymbol = getSymbolAtNode(node, checker, esTreeNodeToTSNodeMap);
|
|
5578
5745
|
if (referenceSymbol !== symbol) {
|
|
5579
5746
|
return;
|
|
5580
5747
|
}
|
|
@@ -5601,11 +5768,7 @@ function resolveSignalReadAlias(expression, context, seen = new Set()) {
|
|
|
5601
5768
|
if (unwrapped.type !== AST_NODE_TYPES.Identifier) {
|
|
5602
5769
|
return null;
|
|
5603
5770
|
}
|
|
5604
|
-
const
|
|
5605
|
-
if (!tsNode) {
|
|
5606
|
-
return null;
|
|
5607
|
-
}
|
|
5608
|
-
const symbol = context.checker.getSymbolAtLocation(tsNode);
|
|
5771
|
+
const symbol = getSymbolAtNode(unwrapped, context.checker, context.esTreeNodeToTSNodeMap);
|
|
5609
5772
|
if (!symbol) {
|
|
5610
5773
|
return null;
|
|
5611
5774
|
}
|
|
@@ -5693,12 +5856,9 @@ function collectSuspiciousReads(scope, checker, esTreeNodeToTSNodeMap, tsNodeToE
|
|
|
5693
5856
|
}
|
|
5694
5857
|
const rule$3 = createUntrackedRule({
|
|
5695
5858
|
create(context) {
|
|
5696
|
-
const
|
|
5697
|
-
const
|
|
5698
|
-
const
|
|
5699
|
-
const tsNodeToESTreeNodeMap = parserServices.tsNodeToESTreeNodeMap;
|
|
5700
|
-
const { sourceCode } = context;
|
|
5701
|
-
const program = sourceCode.ast;
|
|
5859
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
|
|
5860
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
5861
|
+
const estreeNodeMap = tsNodeToESTreeNodeMap;
|
|
5702
5862
|
function buildFix(read) {
|
|
5703
5863
|
const untrackedAlias = findUntrackedAlias(program);
|
|
5704
5864
|
const alreadyHasUntracked = untrackedAlias !== null;
|
|
@@ -5714,13 +5874,13 @@ const rule$3 = createUntrackedRule({
|
|
|
5714
5874
|
return {
|
|
5715
5875
|
CallExpression(node) {
|
|
5716
5876
|
for (const scope of getReactiveScopes(node, program)) {
|
|
5717
|
-
const suspicious = collectSuspiciousReads(scope, checker,
|
|
5718
|
-
const { reads: trackedReads } = collectSignalUsages(scope.callback, checker,
|
|
5877
|
+
const suspicious = collectSuspiciousReads(scope, checker, signalNodeMap, estreeNodeMap, program);
|
|
5878
|
+
const { reads: trackedReads } = collectSignalUsages(scope.callback, checker, signalNodeMap, program);
|
|
5719
5879
|
const suspiciousReads = new Set(suspicious.map(({ read }) => read));
|
|
5720
5880
|
for (const { aliases, consumers, read } of suspicious) {
|
|
5721
5881
|
const hasTrackedDependency = consumers.some((consumer) => trackedReads.some((trackedRead) => !suspiciousReads.has(trackedRead) &&
|
|
5722
5882
|
!isNodeInside(trackedRead, consumer)));
|
|
5723
|
-
const hasExternalAliasUsage = aliases.some((alias) => aliasHasExternalUsage(alias, consumers, scope, checker,
|
|
5883
|
+
const hasExternalAliasUsage = aliases.some((alias) => aliasHasExternalUsage(alias, consumers, scope, checker, signalNodeMap));
|
|
5724
5884
|
if (!hasTrackedDependency || hasExternalAliasUsage) {
|
|
5725
5885
|
continue;
|
|
5726
5886
|
}
|
|
@@ -5752,7 +5912,7 @@ const rule$3 = createUntrackedRule({
|
|
|
5752
5912
|
|
|
5753
5913
|
function getReturnedExpression(node) {
|
|
5754
5914
|
if (node.body.type !== AST_NODE_TYPES.BlockStatement) {
|
|
5755
|
-
return
|
|
5915
|
+
return node.body;
|
|
5756
5916
|
}
|
|
5757
5917
|
if (node.body.body.length !== 1) {
|
|
5758
5918
|
return null;
|
|
@@ -5761,17 +5921,16 @@ function getReturnedExpression(node) {
|
|
|
5761
5921
|
if (statement?.type !== AST_NODE_TYPES.ReturnStatement || !statement.argument) {
|
|
5762
5922
|
return null;
|
|
5763
5923
|
}
|
|
5764
|
-
return
|
|
5924
|
+
return statement.argument;
|
|
5765
5925
|
}
|
|
5926
|
+
|
|
5766
5927
|
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)) {
|
|
5928
|
+
const callback = getReactiveCallbackArgument(node);
|
|
5929
|
+
if (!callback) {
|
|
5772
5930
|
return null;
|
|
5773
5931
|
}
|
|
5774
|
-
const
|
|
5932
|
+
const returnedExpression = getReturnedExpression(callback);
|
|
5933
|
+
const body = returnedExpression ? unwrapExpression(returnedExpression) : null;
|
|
5775
5934
|
if (body?.type !== AST_NODE_TYPES.CallExpression ||
|
|
5776
5935
|
!isSignalReadCall(body, checker, esTreeNodeToTSNodeMap)) {
|
|
5777
5936
|
return null;
|
|
@@ -5785,17 +5944,14 @@ function getWrappedSignalGetter(node, checker, esTreeNodeToTSNodeMap) {
|
|
|
5785
5944
|
}
|
|
5786
5945
|
const rule$2 = createUntrackedRule({
|
|
5787
5946
|
create(context) {
|
|
5788
|
-
const
|
|
5789
|
-
const
|
|
5790
|
-
const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
|
|
5791
|
-
const { sourceCode } = context;
|
|
5792
|
-
const program = sourceCode.ast;
|
|
5947
|
+
const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
|
|
5948
|
+
const signalNodeMap = esTreeNodeToTSNodeMap;
|
|
5793
5949
|
return {
|
|
5794
5950
|
CallExpression(node) {
|
|
5795
5951
|
if (!isAngularUntrackedCall(node, program)) {
|
|
5796
5952
|
return;
|
|
5797
5953
|
}
|
|
5798
|
-
const getter = getWrappedSignalGetter(node, checker,
|
|
5954
|
+
const getter = getWrappedSignalGetter(node, checker, signalNodeMap);
|
|
5799
5955
|
if (!getter) {
|
|
5800
5956
|
return;
|
|
5801
5957
|
}
|
|
@@ -5822,13 +5978,6 @@ const rule$2 = createUntrackedRule({
|
|
|
5822
5978
|
name: 'prefer-untracked-signal-getter',
|
|
5823
5979
|
});
|
|
5824
5980
|
|
|
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
5981
|
function isImportsArrayProperty(property) {
|
|
5833
5982
|
const isProperty = property?.type === AST_NODE_TYPES.Property;
|
|
5834
5983
|
const hasIdentifierKey = property?.key.type === AST_NODE_TYPES.Identifier &&
|
|
@@ -5836,6 +5985,13 @@ function isImportsArrayProperty(property) {
|
|
|
5836
5985
|
return isProperty && hasIdentifierKey && isArray(property.value);
|
|
5837
5986
|
}
|
|
5838
5987
|
|
|
5988
|
+
function getImportedName(spec) {
|
|
5989
|
+
if (spec.imported.type === AST_NODE_TYPES.Identifier) {
|
|
5990
|
+
return spec.imported.name;
|
|
5991
|
+
}
|
|
5992
|
+
return spec.imported.value;
|
|
5993
|
+
}
|
|
5994
|
+
|
|
5839
5995
|
const MESSAGE_ID = 'replaceTuiImport';
|
|
5840
5996
|
const DEFAULT_DECORATORS = ['Component', 'Directive', 'NgModule', 'Pipe'];
|
|
5841
5997
|
const DEFAULT_EXCEPTIONS = [
|
|
@@ -6246,27 +6402,31 @@ const plugin = {
|
|
|
6246
6402
|
'class-property-naming': classPropertyNaming,
|
|
6247
6403
|
'decorator-key-sort': config$3,
|
|
6248
6404
|
'flat-exports': flatExports,
|
|
6249
|
-
'host-attributes-sort': rule$
|
|
6405
|
+
'host-attributes-sort': rule$n,
|
|
6250
6406
|
'html-logical-properties': config$2,
|
|
6251
|
-
'injection-token-description': rule$
|
|
6252
|
-
'no-
|
|
6407
|
+
'injection-token-description': rule$m,
|
|
6408
|
+
'no-commonjs-import-patterns': rule$l,
|
|
6409
|
+
'no-deep-imports': rule$k,
|
|
6253
6410
|
'no-deep-imports-to-indexed-packages': noDeepImportsToIndexedPackages,
|
|
6254
|
-
'no-fully-untracked-effect': rule$
|
|
6411
|
+
'no-fully-untracked-effect': rule$j,
|
|
6255
6412
|
'no-href-with-router-link': config$1,
|
|
6256
|
-
'no-implicit-public': rule$
|
|
6257
|
-
'no-
|
|
6258
|
-
'no-
|
|
6413
|
+
'no-implicit-public': rule$i,
|
|
6414
|
+
'no-import-assertions': rule$h,
|
|
6415
|
+
'no-infinite-loop': rule$g,
|
|
6416
|
+
'no-legacy-peer-deps': rule$f,
|
|
6417
|
+
'no-playwright-empty-fill': rule$e,
|
|
6259
6418
|
'no-project-as-in-ng-template': config,
|
|
6260
|
-
'no-redundant-type-annotation': rule$
|
|
6261
|
-
'no-side-effects-in-computed': rule$
|
|
6262
|
-
'no-signal-reads-after-await-in-reactive-context': rule$
|
|
6263
|
-
'no-string-literal-concat': rule$
|
|
6264
|
-
'no-untracked-outside-reactive-context': rule$
|
|
6265
|
-
'no-useless-untracked': rule$
|
|
6266
|
-
'object-single-line': rule$
|
|
6267
|
-
'prefer-combined-if-control-flow': rule$
|
|
6419
|
+
'no-redundant-type-annotation': rule$d,
|
|
6420
|
+
'no-side-effects-in-computed': rule$c,
|
|
6421
|
+
'no-signal-reads-after-await-in-reactive-context': rule$b,
|
|
6422
|
+
'no-string-literal-concat': rule$a,
|
|
6423
|
+
'no-untracked-outside-reactive-context': rule$9,
|
|
6424
|
+
'no-useless-untracked': rule$8,
|
|
6425
|
+
'object-single-line': rule$7,
|
|
6426
|
+
'prefer-combined-if-control-flow': rule$6,
|
|
6268
6427
|
'prefer-deep-imports': preferDeepImports,
|
|
6269
|
-
'prefer-multi-arg-push': rule$
|
|
6428
|
+
'prefer-multi-arg-push': rule$5,
|
|
6429
|
+
'prefer-namespace-keyword': rule$4,
|
|
6270
6430
|
'prefer-untracked-incidental-signal-reads': rule$3,
|
|
6271
6431
|
'prefer-untracked-signal-getter': rule$2,
|
|
6272
6432
|
'short-tui-imports': rule$1,
|