oxlint-plugin-react-doctor 0.9.1-dev.54ba416 → 0.9.1-dev.7ee59d3
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/dist/index.d.ts +337 -1
- package/dist/index.js +1844 -102
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14551,16 +14551,136 @@ const dangerousHtmlSink = defineRule({
|
|
|
14551
14551
|
}
|
|
14552
14552
|
});
|
|
14553
14553
|
//#endregion
|
|
14554
|
+
//#region src/plugin/utils/get-static-logical-expression-result-branches.ts
|
|
14555
|
+
const deduplicateResultBranches = (branches) => {
|
|
14556
|
+
const resultBranches = [];
|
|
14557
|
+
const seenStatesByExpression = /* @__PURE__ */ new Map();
|
|
14558
|
+
for (const branch of branches) {
|
|
14559
|
+
const branchState = `${branch.truthiness}:${branch.nullishness}`;
|
|
14560
|
+
const seenStates = seenStatesByExpression.get(branch.expression);
|
|
14561
|
+
if (seenStates?.has(branchState)) continue;
|
|
14562
|
+
if (seenStates) seenStates.add(branchState);
|
|
14563
|
+
else seenStatesByExpression.set(branch.expression, new Set([branchState]));
|
|
14564
|
+
resultBranches.push(branch);
|
|
14565
|
+
}
|
|
14566
|
+
return resultBranches;
|
|
14567
|
+
};
|
|
14568
|
+
const getAtomicExpressionResultBranch = (expression) => {
|
|
14569
|
+
if (isNodeOfType(expression, "JSXElement") || isNodeOfType(expression, "JSXFragment") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "ClassExpression") || isNodeOfType(expression, "NewExpression")) return {
|
|
14570
|
+
expression,
|
|
14571
|
+
truthiness: "truthy",
|
|
14572
|
+
nullishness: "non-nullish"
|
|
14573
|
+
};
|
|
14574
|
+
if (isNodeOfType(expression, "Literal")) {
|
|
14575
|
+
if (expression.value === null) return {
|
|
14576
|
+
expression,
|
|
14577
|
+
truthiness: "falsy",
|
|
14578
|
+
nullishness: "nullish"
|
|
14579
|
+
};
|
|
14580
|
+
return {
|
|
14581
|
+
expression,
|
|
14582
|
+
truthiness: expression.value ? "truthy" : "falsy",
|
|
14583
|
+
nullishness: "non-nullish"
|
|
14584
|
+
};
|
|
14585
|
+
}
|
|
14586
|
+
if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return {
|
|
14587
|
+
expression,
|
|
14588
|
+
truthiness: "falsy",
|
|
14589
|
+
nullishness: "nullish"
|
|
14590
|
+
};
|
|
14591
|
+
return {
|
|
14592
|
+
expression,
|
|
14593
|
+
truthiness: "unknown",
|
|
14594
|
+
nullishness: "unknown"
|
|
14595
|
+
};
|
|
14596
|
+
};
|
|
14597
|
+
const getStaticExpressionResultBranches = (expression) => {
|
|
14598
|
+
const finalExpression = getFinalSequenceExpressionValue(expression);
|
|
14599
|
+
if (isNodeOfType(finalExpression, "ConditionalExpression")) return deduplicateResultBranches([...getStaticExpressionResultBranches(finalExpression.consequent), ...getStaticExpressionResultBranches(finalExpression.alternate)]);
|
|
14600
|
+
if (!isNodeOfType(finalExpression, "LogicalExpression")) return [getAtomicExpressionResultBranch(finalExpression)];
|
|
14601
|
+
const leftBranches = getStaticExpressionResultBranches(finalExpression.left);
|
|
14602
|
+
const rightBranches = getStaticExpressionResultBranches(finalExpression.right);
|
|
14603
|
+
const resultBranches = [];
|
|
14604
|
+
for (const leftBranch of leftBranches) {
|
|
14605
|
+
if (finalExpression.operator === "&&") {
|
|
14606
|
+
if (leftBranch.truthiness !== "truthy") resultBranches.push(leftBranch.truthiness === "falsy" ? leftBranch : {
|
|
14607
|
+
...leftBranch,
|
|
14608
|
+
truthiness: "falsy"
|
|
14609
|
+
});
|
|
14610
|
+
if (leftBranch.truthiness !== "falsy") resultBranches.push(...rightBranches);
|
|
14611
|
+
continue;
|
|
14612
|
+
}
|
|
14613
|
+
if (finalExpression.operator === "||") {
|
|
14614
|
+
if (leftBranch.truthiness !== "falsy") resultBranches.push(leftBranch.truthiness === "truthy" ? leftBranch : {
|
|
14615
|
+
...leftBranch,
|
|
14616
|
+
truthiness: "truthy",
|
|
14617
|
+
nullishness: "non-nullish"
|
|
14618
|
+
});
|
|
14619
|
+
if (leftBranch.truthiness !== "truthy") resultBranches.push(...rightBranches);
|
|
14620
|
+
continue;
|
|
14621
|
+
}
|
|
14622
|
+
if (leftBranch.nullishness !== "nullish") resultBranches.push(leftBranch.nullishness === "non-nullish" ? leftBranch : {
|
|
14623
|
+
...leftBranch,
|
|
14624
|
+
nullishness: "non-nullish"
|
|
14625
|
+
});
|
|
14626
|
+
if (leftBranch.nullishness !== "non-nullish") resultBranches.push(...rightBranches);
|
|
14627
|
+
}
|
|
14628
|
+
return deduplicateResultBranches(resultBranches);
|
|
14629
|
+
};
|
|
14630
|
+
const getStaticLogicalExpressionResultBranches = (expression) => {
|
|
14631
|
+
const resultExpressions = [];
|
|
14632
|
+
const seenExpressions = /* @__PURE__ */ new Set();
|
|
14633
|
+
for (const resultBranch of getStaticExpressionResultBranches(expression)) {
|
|
14634
|
+
if (seenExpressions.has(resultBranch.expression)) continue;
|
|
14635
|
+
seenExpressions.add(resultBranch.expression);
|
|
14636
|
+
resultExpressions.push(resultBranch.expression);
|
|
14637
|
+
}
|
|
14638
|
+
return resultExpressions;
|
|
14639
|
+
};
|
|
14640
|
+
//#endregion
|
|
14554
14641
|
//#region src/plugin/utils/get-static-jsx-descendant-opening-elements.ts
|
|
14555
|
-
const
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
|
|
14642
|
+
const appendDescendant = (node, descendants, includeStaticExpressionBranches) => {
|
|
14643
|
+
const expression = stripParenExpression(node);
|
|
14644
|
+
if (isNodeOfType(expression, "JSXElement")) {
|
|
14645
|
+
descendants.push(expression.openingElement);
|
|
14646
|
+
for (const child of expression.children) appendDescendant(child, descendants, includeStaticExpressionBranches);
|
|
14647
|
+
return;
|
|
14648
|
+
}
|
|
14649
|
+
if (isNodeOfType(expression, "JSXFragment")) {
|
|
14650
|
+
for (const child of expression.children) appendDescendant(child, descendants, includeStaticExpressionBranches);
|
|
14651
|
+
return;
|
|
14652
|
+
}
|
|
14653
|
+
if (!includeStaticExpressionBranches) return;
|
|
14654
|
+
if (isNodeOfType(expression, "JSXExpressionContainer")) {
|
|
14655
|
+
appendDescendant(expression.expression, descendants, true);
|
|
14656
|
+
return;
|
|
14657
|
+
}
|
|
14658
|
+
if (isNodeOfType(expression, "ConditionalExpression")) {
|
|
14659
|
+
const staticTestValue = readStaticBoolean(getFinalSequenceExpressionValue(expression.test));
|
|
14660
|
+
if (staticTestValue !== null) {
|
|
14661
|
+
appendDescendant(staticTestValue ? expression.consequent : expression.alternate, descendants, true);
|
|
14662
|
+
return;
|
|
14663
|
+
}
|
|
14664
|
+
appendDescendant(expression.consequent, descendants, true);
|
|
14665
|
+
appendDescendant(expression.alternate, descendants, true);
|
|
14666
|
+
return;
|
|
14667
|
+
}
|
|
14668
|
+
if (isNodeOfType(expression, "LogicalExpression")) {
|
|
14669
|
+
for (const resultBranch of getStaticLogicalExpressionResultBranches(expression)) appendDescendant(resultBranch, descendants, true);
|
|
14670
|
+
return;
|
|
14671
|
+
}
|
|
14672
|
+
if (isNodeOfType(expression, "ArrayExpression")) {
|
|
14673
|
+
for (const element of expression.elements) if (element && !isNodeOfType(element, "SpreadElement")) appendDescendant(element, descendants, true);
|
|
14674
|
+
return;
|
|
14675
|
+
}
|
|
14676
|
+
if (isNodeOfType(expression, "SequenceExpression")) {
|
|
14677
|
+
if (expression.expressions.length === 0) return;
|
|
14678
|
+
appendDescendant(getFinalSequenceExpressionValue(expression), descendants, true);
|
|
14679
|
+
}
|
|
14560
14680
|
};
|
|
14561
|
-
const getStaticJsxDescendantOpeningElements = (element) => {
|
|
14681
|
+
const getStaticJsxDescendantOpeningElements = (element, options = {}) => {
|
|
14562
14682
|
const descendants = [];
|
|
14563
|
-
|
|
14683
|
+
for (const child of element.children) appendDescendant(child, descendants, options.includeStaticExpressionBranches === true);
|
|
14564
14684
|
return descendants;
|
|
14565
14685
|
};
|
|
14566
14686
|
//#endregion
|
|
@@ -25376,6 +25496,27 @@ const flattenLogicalAndChain = (node) => {
|
|
|
25376
25496
|
return [node];
|
|
25377
25497
|
};
|
|
25378
25498
|
//#endregion
|
|
25499
|
+
//#region src/plugin/utils/resolve-imported-jsx-component-name.ts
|
|
25500
|
+
const resolveImportedJsxComponentName = (openingElement, moduleSource, scopes) => {
|
|
25501
|
+
const elementName = openingElement.name;
|
|
25502
|
+
if (isNodeOfType(elementName, "JSXIdentifier")) {
|
|
25503
|
+
const symbol = scopes.symbolFor(elementName);
|
|
25504
|
+
if (!symbol || symbol.kind !== "import") return null;
|
|
25505
|
+
const importDeclaration = getImportDeclarationForSymbol(symbol);
|
|
25506
|
+
if (!importDeclaration || importDeclaration.source.value !== moduleSource || isTypeOnlyImport(importDeclaration)) return null;
|
|
25507
|
+
if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return "default";
|
|
25508
|
+
if (!isNodeOfType(symbol.declarationNode, "ImportSpecifier")) return null;
|
|
25509
|
+
if (symbol.declarationNode.importKind === "type") return null;
|
|
25510
|
+
return getImportedName(symbol.declarationNode) ?? null;
|
|
25511
|
+
}
|
|
25512
|
+
if (!isNodeOfType(elementName, "JSXMemberExpression") || !isNodeOfType(elementName.object, "JSXIdentifier") || !isNodeOfType(elementName.property, "JSXIdentifier")) return null;
|
|
25513
|
+
const namespaceSymbol = scopes.symbolFor(elementName.object);
|
|
25514
|
+
if (!namespaceSymbol || namespaceSymbol.kind !== "import" || !isNodeOfType(namespaceSymbol.declarationNode, "ImportNamespaceSpecifier")) return null;
|
|
25515
|
+
const importDeclaration = getImportDeclarationForSymbol(namespaceSymbol);
|
|
25516
|
+
if (!importDeclaration || importDeclaration.source.value !== moduleSource || isTypeOnlyImport(importDeclaration)) return null;
|
|
25517
|
+
return elementName.property.name;
|
|
25518
|
+
};
|
|
25519
|
+
//#endregion
|
|
25379
25520
|
//#region src/plugin/utils/resolve-ink-api-name.ts
|
|
25380
25521
|
const resolveInkApiName = (node, scopes) => {
|
|
25381
25522
|
if (isNodeOfType(node, "Identifier")) {
|
|
@@ -25385,15 +25526,7 @@ const resolveInkApiName = (node, scopes) => {
|
|
|
25385
25526
|
if (isNodeOfType(node, "MemberExpression") && isNodeOfType(node.object, "Identifier") && scopes.symbolFor(node.object)?.kind === "import" && isNamespaceImportFromModule$1(node, node.object.name, "ink")) return getStaticPropertyName(node);
|
|
25386
25527
|
return null;
|
|
25387
25528
|
};
|
|
25388
|
-
const resolveInkJsxElementName = (openingElement, scopes) =>
|
|
25389
|
-
const elementName = openingElement.name;
|
|
25390
|
-
if (isNodeOfType(elementName, "JSXIdentifier")) {
|
|
25391
|
-
if (scopes.symbolFor(elementName)?.kind !== "import") return null;
|
|
25392
|
-
return getImportedNameFromModule(openingElement, elementName.name, "ink");
|
|
25393
|
-
}
|
|
25394
|
-
if (isNodeOfType(elementName, "JSXMemberExpression") && isNodeOfType(elementName.object, "JSXIdentifier") && scopes.symbolFor(elementName.object)?.kind === "import" && isNamespaceImportFromModule$1(openingElement, elementName.object.name, "ink")) return elementName.property.name;
|
|
25395
|
-
return null;
|
|
25396
|
-
};
|
|
25529
|
+
const resolveInkJsxElementName = (openingElement, scopes) => resolveImportedJsxComponentName(openingElement, "ink", scopes);
|
|
25397
25530
|
//#endregion
|
|
25398
25531
|
//#region src/plugin/utils/resolve-ink-render-calls.ts
|
|
25399
25532
|
const getRenderedComponentName = (renderCall) => {
|
|
@@ -33880,6 +34013,27 @@ const jsxNoTargetBlank = defineRule({
|
|
|
33880
34013
|
}
|
|
33881
34014
|
});
|
|
33882
34015
|
//#endregion
|
|
34016
|
+
//#region src/plugin/utils/get-project-relative-filename.ts
|
|
34017
|
+
const getProjectRelativeFilename = (filename, rootDirectory) => {
|
|
34018
|
+
const normalizedFilename = normalizeFilename(filename);
|
|
34019
|
+
if (!rootDirectory) return normalizedFilename;
|
|
34020
|
+
const rootDirectoryPrefix = `${normalizeFilename(rootDirectory).replace(/\/+$/, "")}/`;
|
|
34021
|
+
if (!normalizedFilename.startsWith(rootDirectoryPrefix)) return normalizedFilename;
|
|
34022
|
+
return normalizedFilename.slice(rootDirectoryPrefix.length);
|
|
34023
|
+
};
|
|
34024
|
+
//#endregion
|
|
34025
|
+
//#region src/plugin/utils/get-project-relative-filename-from-roots.ts
|
|
34026
|
+
const getProjectRelativeFilenameFromRoots = (filename, rootDirectories) => {
|
|
34027
|
+
const normalizedFilename = normalizeFilename(filename);
|
|
34028
|
+
if (normalizedFilename.length === 0) return null;
|
|
34029
|
+
if (!path.isAbsolute(filename)) return normalizedFilename;
|
|
34030
|
+
for (const rootDirectory of rootDirectories) {
|
|
34031
|
+
const relativeFilename = getProjectRelativeFilename(normalizedFilename, rootDirectory);
|
|
34032
|
+
if (relativeFilename !== normalizedFilename) return relativeFilename;
|
|
34033
|
+
}
|
|
34034
|
+
return null;
|
|
34035
|
+
};
|
|
34036
|
+
//#endregion
|
|
33883
34037
|
//#region src/plugin/rules/react-builtins/jsx-no-undef.ts
|
|
33884
34038
|
const buildMessage$18 = (name) => `\`${name}\` crashes at runtime because it isn't defined here.`;
|
|
33885
34039
|
const KNOWN_GLOBALS = new Set([
|
|
@@ -33930,24 +34084,60 @@ const getRootIdentifier$1 = (elementName) => {
|
|
|
33930
34084
|
}
|
|
33931
34085
|
return null;
|
|
33932
34086
|
};
|
|
34087
|
+
const resolveUnpluginAutoImportGlobalScopes = (settings) => {
|
|
34088
|
+
const reactDoctorSettings = settings?.["react-doctor"];
|
|
34089
|
+
if (typeof reactDoctorSettings !== "object" || reactDoctorSettings === null || Array.isArray(reactDoctorSettings)) return [];
|
|
34090
|
+
const rawScopes = Object.getOwnPropertyDescriptor(reactDoctorSettings, "unpluginAutoImportGlobalScopes")?.value;
|
|
34091
|
+
if (!Array.isArray(rawScopes)) return [];
|
|
34092
|
+
const scopes = [];
|
|
34093
|
+
for (const rawScope of rawScopes) {
|
|
34094
|
+
if (typeof rawScope !== "object" || rawScope === null || Array.isArray(rawScope)) continue;
|
|
34095
|
+
const directory = Object.getOwnPropertyDescriptor(rawScope, "directory")?.value;
|
|
34096
|
+
const names = Object.getOwnPropertyDescriptor(rawScope, "names")?.value;
|
|
34097
|
+
if (typeof directory !== "string" || !Array.isArray(names)) continue;
|
|
34098
|
+
const validNames = names.filter((name) => typeof name === "string" && name.length > 0);
|
|
34099
|
+
scopes.push({
|
|
34100
|
+
directory,
|
|
34101
|
+
names: new Set(validNames)
|
|
34102
|
+
});
|
|
34103
|
+
}
|
|
34104
|
+
return scopes;
|
|
34105
|
+
};
|
|
34106
|
+
const isInjectedRuntimeGlobal = (name, relativeFilename, configuredRuntimeGlobals, scopes) => {
|
|
34107
|
+
if (configuredRuntimeGlobals.has(name)) return true;
|
|
34108
|
+
if (relativeFilename === null) return false;
|
|
34109
|
+
let nearestScope = null;
|
|
34110
|
+
for (const scope of scopes) if ((scope.directory.length === 0 || relativeFilename.startsWith(`${scope.directory}/`)) && (!nearestScope || scope.directory.length > nearestScope.directory.length)) nearestScope = scope;
|
|
34111
|
+
return nearestScope?.names.has(name) ?? false;
|
|
34112
|
+
};
|
|
33933
34113
|
const jsxNoUndef = defineRule({
|
|
33934
34114
|
id: "jsx-no-undef",
|
|
33935
34115
|
title: "Undefined JSX component",
|
|
33936
34116
|
severity: "error",
|
|
33937
34117
|
recommendation: "Import the component or fix the typo so React can resolve the JSX identifier at runtime.",
|
|
33938
|
-
create: (context) =>
|
|
33939
|
-
const
|
|
33940
|
-
|
|
33941
|
-
|
|
33942
|
-
const
|
|
33943
|
-
|
|
33944
|
-
|
|
33945
|
-
|
|
33946
|
-
|
|
33947
|
-
|
|
33948
|
-
|
|
33949
|
-
|
|
33950
|
-
|
|
34118
|
+
create: (context) => {
|
|
34119
|
+
const autoImportGlobalScopes = resolveUnpluginAutoImportGlobalScopes(context.settings);
|
|
34120
|
+
const configuredRuntimeGlobals = new Set(getReactDoctorStringArraySetting(context.settings, "runtimeGlobals"));
|
|
34121
|
+
const configuredAutoImportRootDirectories = getReactDoctorStringArraySetting(context.settings, "unpluginAutoImportRootDirectories");
|
|
34122
|
+
const rootDirectory = getReactDoctorStringSetting(context.settings, "rootDirectory");
|
|
34123
|
+
const fallbackAutoImportRootDirectories = rootDirectory ? [rootDirectory] : [];
|
|
34124
|
+
const autoImportRootDirectories = configuredAutoImportRootDirectories.length > 0 ? configuredAutoImportRootDirectories : fallbackAutoImportRootDirectories;
|
|
34125
|
+
const relativeFilename = getProjectRelativeFilenameFromRoots(context.filename ?? "", autoImportRootDirectories);
|
|
34126
|
+
return { JSXOpeningElement(node) {
|
|
34127
|
+
const rootIdentifier = getRootIdentifier$1(node.name);
|
|
34128
|
+
if (!rootIdentifier) return;
|
|
34129
|
+
if (KNOWN_GLOBALS.has(rootIdentifier)) return;
|
|
34130
|
+
if (isInjectedRuntimeGlobal(rootIdentifier, relativeFilename, configuredRuntimeGlobals, autoImportGlobalScopes)) return;
|
|
34131
|
+
const programRoot = findProgramRoot(node);
|
|
34132
|
+
if (!programRoot) return;
|
|
34133
|
+
if (isReactLiveStyleScript(programRoot)) return;
|
|
34134
|
+
if (findVariableInitializer(node, rootIdentifier)) return;
|
|
34135
|
+
context.report({
|
|
34136
|
+
node: node.name,
|
|
34137
|
+
message: buildMessage$18(rootIdentifier)
|
|
34138
|
+
});
|
|
34139
|
+
} };
|
|
34140
|
+
}
|
|
33951
34141
|
});
|
|
33952
34142
|
//#endregion
|
|
33953
34143
|
//#region src/plugin/rules/react-builtins/jsx-no-useless-fragment.ts
|
|
@@ -35937,6 +36127,7 @@ const MOBX_RULE_GATES = {
|
|
|
35937
36127
|
const resolveImportSymbol = (symbol) => {
|
|
35938
36128
|
const importDeclaration = getImportDeclarationForSymbol(symbol);
|
|
35939
36129
|
if (!importDeclaration || typeof importDeclaration.source.value !== "string") return null;
|
|
36130
|
+
if (importDeclaration.importKind === "type" || isNodeOfType(symbol.declarationNode, "ImportSpecifier") && symbol.declarationNode.importKind === "type") return null;
|
|
35940
36131
|
if (isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) return {
|
|
35941
36132
|
source: importDeclaration.source.value,
|
|
35942
36133
|
importedName: null,
|
|
@@ -37422,15 +37613,6 @@ const getSingleReturnExpression = (functionNode) => {
|
|
|
37422
37613
|
return isNodeOfType(statement, "ReturnStatement") && statement.argument ? statement.argument : null;
|
|
37423
37614
|
};
|
|
37424
37615
|
//#endregion
|
|
37425
|
-
//#region src/plugin/utils/get-project-relative-filename.ts
|
|
37426
|
-
const getProjectRelativeFilename = (filename, rootDirectory) => {
|
|
37427
|
-
const normalizedFilename = normalizeFilename(filename);
|
|
37428
|
-
if (!rootDirectory) return normalizedFilename;
|
|
37429
|
-
const rootDirectoryPrefix = `${normalizeFilename(rootDirectory).replace(/\/+$/, "")}/`;
|
|
37430
|
-
if (!normalizedFilename.startsWith(rootDirectoryPrefix)) return normalizedFilename;
|
|
37431
|
-
return normalizedFilename.slice(rootDirectoryPrefix.length);
|
|
37432
|
-
};
|
|
37433
|
-
//#endregion
|
|
37434
37616
|
//#region src/plugin/utils/get-react-router-framework-module-kind.ts
|
|
37435
37617
|
const REACT_ROUTER_ROUTE_DIRECTORY_PATTERN = /(?:^|\/)app\/routes\//;
|
|
37436
37618
|
const REACT_ROUTER_ROOT_FILE_PATTERN = /(?:^|\/)app\/root\.(?:jsx?|tsx?)$/;
|
|
@@ -51167,6 +51349,23 @@ const isProvenReactClassComponent = (classNode, scopes, visitedClassNodes = /* @
|
|
|
51167
51349
|
return isReactComponentClassValue(classNode.superClass, scopes, visitedClassNodes, visitedSymbolIds);
|
|
51168
51350
|
};
|
|
51169
51351
|
//#endregion
|
|
51352
|
+
//#region src/plugin/utils/unwrap-proven-react-hoc-function.ts
|
|
51353
|
+
const unwrapProvenReactHocFunction = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
51354
|
+
if (!node) return null;
|
|
51355
|
+
const expression = stripParenExpression(node);
|
|
51356
|
+
if (isFunctionLike$1(expression)) return expression;
|
|
51357
|
+
if (isNodeOfType(expression, "Identifier")) {
|
|
51358
|
+
const symbol = scopes.symbolFor(expression);
|
|
51359
|
+
if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.initializer || hasSymbolWriteBefore(symbol, expression, scopes)) return null;
|
|
51360
|
+
visitedSymbolIds.add(symbol.id);
|
|
51361
|
+
return unwrapProvenReactHocFunction(symbol.initializer, scopes, visitedSymbolIds);
|
|
51362
|
+
}
|
|
51363
|
+
if (!isNodeOfType(expression, "CallExpression") || !isReactApiCall(expression, "memo", scopes, { resolveNamedAliases: true }) && !isReactApiCall(expression, "forwardRef", scopes, { resolveNamedAliases: true })) return null;
|
|
51364
|
+
const componentArgument = expression.arguments[0];
|
|
51365
|
+
if (!componentArgument || isNodeOfType(componentArgument, "SpreadElement")) return null;
|
|
51366
|
+
return unwrapProvenReactHocFunction(componentArgument, scopes, visitedSymbolIds);
|
|
51367
|
+
};
|
|
51368
|
+
//#endregion
|
|
51170
51369
|
//#region src/plugin/utils/is-inline-intrinsic-ref-callback.ts
|
|
51171
51370
|
const isInlineIntrinsicRefCallback = (functionNode, scopes) => {
|
|
51172
51371
|
const functionExpression = findTransparentExpressionRoot(functionNode);
|
|
@@ -51279,22 +51478,6 @@ const getEnvironment = (program, filename, state) => {
|
|
|
51279
51478
|
state.environmentsByProgram.set(program, environment);
|
|
51280
51479
|
return environment;
|
|
51281
51480
|
};
|
|
51282
|
-
const unwrapProvenReactHocFunction = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
51283
|
-
if (!node) return null;
|
|
51284
|
-
const current = findTransparentExpressionRoot(node);
|
|
51285
|
-
if (isFunctionLike$1(current)) return current;
|
|
51286
|
-
if (isNodeOfType(current, "Identifier")) {
|
|
51287
|
-
const symbol = scopes.symbolFor(current);
|
|
51288
|
-
if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.initializer || hasSymbolWriteBefore(symbol, current, scopes)) return null;
|
|
51289
|
-
visitedSymbolIds.add(symbol.id);
|
|
51290
|
-
return unwrapProvenReactHocFunction(symbol.initializer, scopes, visitedSymbolIds);
|
|
51291
|
-
}
|
|
51292
|
-
if (!isNodeOfType(current, "CallExpression")) return null;
|
|
51293
|
-
if (!isProvenReactCall(current, "memo", scopes) && !isProvenReactCall(current, "forwardRef", scopes)) return null;
|
|
51294
|
-
const firstArgument = current.arguments[0];
|
|
51295
|
-
if (!firstArgument || isNodeOfType(firstArgument, "SpreadElement")) return null;
|
|
51296
|
-
return unwrapProvenReactHocFunction(firstArgument, scopes, visitedSymbolIds);
|
|
51297
|
-
};
|
|
51298
51481
|
const isForwardRefValue = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
51299
51482
|
const current = findTransparentExpressionRoot(node);
|
|
51300
51483
|
if (isNodeOfType(current, "Identifier")) {
|
|
@@ -99790,9 +99973,9 @@ const rawSqlInjectionRisk = defineRule({
|
|
|
99790
99973
|
//#endregion
|
|
99791
99974
|
//#region src/plugin/rules/architecture/react-compiler-no-manual-memoization.ts
|
|
99792
99975
|
const REMOVAL_MESSAGE_BY_REACT_API_NAME = new Map([
|
|
99793
|
-
["useMemo", "
|
|
99794
|
-
["useCallback", "
|
|
99795
|
-
["memo", "
|
|
99976
|
+
["useMemo", "React Compiler can cache this value automatically. Verify that removing `useMemo` preserves behavior before simplifying it."],
|
|
99977
|
+
["useCallback", "React Compiler can cache this function automatically. Verify that removing `useCallback` preserves behavior before simplifying it."],
|
|
99978
|
+
["memo", "React Compiler can cache this component output automatically. Verify that removing `memo()` preserves behavior before simplifying it."]
|
|
99796
99979
|
]);
|
|
99797
99980
|
const resolveReactApiNameForIdentifier = (callee, context) => {
|
|
99798
99981
|
if (!isNodeOfType(callee, "Identifier")) return null;
|
|
@@ -99838,10 +100021,10 @@ const isCompilerInferableFunction = (functionNode) => {
|
|
|
99838
100021
|
};
|
|
99839
100022
|
const reactCompilerNoManualMemoization = defineRule({
|
|
99840
100023
|
id: "react-compiler-no-manual-memoization",
|
|
99841
|
-
title: "
|
|
100024
|
+
title: "Manual memoization in compiler-managed code",
|
|
99842
100025
|
severity: "warn",
|
|
99843
100026
|
requires: ["react-compiler"],
|
|
99844
|
-
recommendation: "
|
|
100027
|
+
recommendation: "Profile compiler-managed code and remove `useMemo`, `useCallback`, or `memo` only when the manual cache no longer carries behavioral or performance intent.",
|
|
99845
100028
|
create: (context) => ({ CallExpression(node) {
|
|
99846
100029
|
const apiName = resolveReactApiNameForCallee(node.callee, context);
|
|
99847
100030
|
if (!apiName) return;
|
|
@@ -106449,7 +106632,7 @@ const rnAnimateLayoutProperty = defineRetiredRule({
|
|
|
106449
106632
|
});
|
|
106450
106633
|
//#endregion
|
|
106451
106634
|
//#region src/plugin/rules/react-native/rn-animation-reaction-as-derived.ts
|
|
106452
|
-
const REANIMATED_MODULE_SOURCE = "react-native-reanimated";
|
|
106635
|
+
const REANIMATED_MODULE_SOURCE$1 = "react-native-reanimated";
|
|
106453
106636
|
const rnAnimationReactionAsDerived = defineRule({
|
|
106454
106637
|
id: "rn-animation-reaction-as-derived",
|
|
106455
106638
|
title: "useAnimatedReaction just copies a value",
|
|
@@ -106459,7 +106642,7 @@ const rnAnimationReactionAsDerived = defineRule({
|
|
|
106459
106642
|
recommendation: "This useAnimatedReaction just copies one value to another. Replace it with `useDerivedValue(() => ..., [deps])`, which is shorter and tracks changes for you.",
|
|
106460
106643
|
create: (context) => ({ CallExpression(node) {
|
|
106461
106644
|
if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== "useAnimatedReaction") return;
|
|
106462
|
-
if (getImportSourceForName(node, node.callee.name) !== REANIMATED_MODULE_SOURCE) return;
|
|
106645
|
+
if (getImportSourceForName(node, node.callee.name) !== REANIMATED_MODULE_SOURCE$1) return;
|
|
106463
106646
|
const reactionFn = node.arguments?.[1];
|
|
106464
106647
|
if (!reactionFn) return;
|
|
106465
106648
|
if (!isNodeOfType(reactionFn, "ArrowFunctionExpression") && !isNodeOfType(reactionFn, "FunctionExpression")) return;
|
|
@@ -106483,6 +106666,69 @@ const rnAnimationReactionAsDerived = defineRule({
|
|
|
106483
106666
|
} })
|
|
106484
106667
|
});
|
|
106485
106668
|
//#endregion
|
|
106669
|
+
//#region src/plugin/rules/react-native/rn-bottom-sheet-no-ignored-scroll-prop.ts
|
|
106670
|
+
const GORHOM_BOTTOM_SHEET_MODULE$2 = "@gorhom/bottom-sheet";
|
|
106671
|
+
const IGNORED_SCROLL_PROPERTY_NAMES = new Set([
|
|
106672
|
+
"decelerationRate",
|
|
106673
|
+
"onScrollBeginDrag",
|
|
106674
|
+
"scrollEventThrottle"
|
|
106675
|
+
]);
|
|
106676
|
+
const rnBottomSheetNoIgnoredScrollProp = defineRule({
|
|
106677
|
+
id: "rn-bottom-sheet-no-ignored-scroll-prop",
|
|
106678
|
+
title: "Ignored BottomSheetScrollView prop",
|
|
106679
|
+
requires: ["react-native"],
|
|
106680
|
+
severity: "warn",
|
|
106681
|
+
recommendation: "Remove scrollEventThrottle, decelerationRate, and onScrollBeginDrag from BottomSheetScrollView because the component ignores them.",
|
|
106682
|
+
create: (context) => ({ JSXOpeningElement(node) {
|
|
106683
|
+
if (resolveImportedJsxComponentName(node, GORHOM_BOTTOM_SHEET_MODULE$2, context.scopes) !== "BottomSheetScrollView") return;
|
|
106684
|
+
for (const attribute of node.attributes) {
|
|
106685
|
+
if (!isNodeOfType(attribute, "JSXAttribute")) continue;
|
|
106686
|
+
const propertyName = getJsxAttributeName(attribute.name);
|
|
106687
|
+
if (!propertyName || !IGNORED_SCROLL_PROPERTY_NAMES.has(propertyName)) continue;
|
|
106688
|
+
context.report({
|
|
106689
|
+
node: attribute,
|
|
106690
|
+
message: `BottomSheetScrollView ignores \`${propertyName}\`, so this prop cannot affect scrolling. Remove it or handle the behavior outside the scrollable.`
|
|
106691
|
+
});
|
|
106692
|
+
}
|
|
106693
|
+
} })
|
|
106694
|
+
});
|
|
106695
|
+
//#endregion
|
|
106696
|
+
//#region src/plugin/rules/react-native/rn-bottom-sheet-no-state-in-on-animate.ts
|
|
106697
|
+
const GORHOM_BOTTOM_SHEET_MODULE$1 = "@gorhom/bottom-sheet";
|
|
106698
|
+
const BOTTOM_SHEET_CONTAINER_NAMES$1 = new Set([
|
|
106699
|
+
"BottomSheet",
|
|
106700
|
+
"BottomSheetModal",
|
|
106701
|
+
"default"
|
|
106702
|
+
]);
|
|
106703
|
+
const rnBottomSheetNoStateInOnAnimate = defineRule({
|
|
106704
|
+
id: "rn-bottom-sheet-no-state-in-on-animate",
|
|
106705
|
+
title: "React state update in Bottom Sheet onAnimate",
|
|
106706
|
+
requires: ["react-native"],
|
|
106707
|
+
severity: "warn",
|
|
106708
|
+
recommendation: "Avoid starting React renders from onAnimate. Use animatedIndex or animatedPosition for animation-coupled UI, or onChange for committed index state.",
|
|
106709
|
+
create: (context) => ({ JSXOpeningElement(node) {
|
|
106710
|
+
const componentName = resolveImportedJsxComponentName(node, GORHOM_BOTTOM_SHEET_MODULE$1, context.scopes);
|
|
106711
|
+
if (!componentName || !BOTTOM_SHEET_CONTAINER_NAMES$1.has(componentName)) return;
|
|
106712
|
+
const onAnimateAttribute = findJsxAttribute(node.attributes, "onAnimate");
|
|
106713
|
+
if (!onAnimateAttribute?.value || !isNodeOfType(onAnimateAttribute.value, "JSXExpressionContainer")) return;
|
|
106714
|
+
const handler = resolveExactLocalFunction(onAnimateAttribute.value.expression, context.scopes);
|
|
106715
|
+
if (!handler) return;
|
|
106716
|
+
let stateSetterCall = null;
|
|
106717
|
+
walkOwnFunctionScope(handler, (child) => {
|
|
106718
|
+
if (stateSetterCall) return false;
|
|
106719
|
+
if (!isNodeOfType(child, "CallExpression") || !isNodeOfType(child.callee, "Identifier")) return;
|
|
106720
|
+
if (!resolveReactUseStatePair(child.callee, context.scopes)) return;
|
|
106721
|
+
stateSetterCall = child;
|
|
106722
|
+
return false;
|
|
106723
|
+
});
|
|
106724
|
+
if (!stateSetterCall) return;
|
|
106725
|
+
context.report({
|
|
106726
|
+
node: stateSetterCall,
|
|
106727
|
+
message: "This onAnimate handler starts a React state update as the Bottom Sheet begins moving, adding render work to the transition. Use animatedIndex or animatedPosition for animation-coupled UI."
|
|
106728
|
+
});
|
|
106729
|
+
} })
|
|
106730
|
+
});
|
|
106731
|
+
//#endregion
|
|
106486
106732
|
//#region src/plugin/rules/react-native/rn-bottom-sheet-prefer-native.ts
|
|
106487
106733
|
const JS_BOTTOM_SHEET_PACKAGES = new Set([
|
|
106488
106734
|
"react-native-bottom-sheet",
|
|
@@ -106499,7 +106745,7 @@ const rnBottomSheetPreferNative = defineRule({
|
|
|
106499
106745
|
tags: ["test-noise"],
|
|
106500
106746
|
requires: ["react-native"],
|
|
106501
106747
|
severity: "warn",
|
|
106502
|
-
recommendation: "
|
|
106748
|
+
recommendation: "When native presentation fits the design, use `<Modal presentationStyle=\"formSheet\">` for platform-native gestures, accessibility, and presentation behavior.",
|
|
106503
106749
|
create: (context) => ({ ImportDeclaration(node) {
|
|
106504
106750
|
const source = node.source?.value;
|
|
106505
106751
|
if (typeof source !== "string" || !JS_BOTTOM_SHEET_PACKAGES.has(source)) return;
|
|
@@ -106511,6 +106757,45 @@ const rnBottomSheetPreferNative = defineRule({
|
|
|
106511
106757
|
} })
|
|
106512
106758
|
});
|
|
106513
106759
|
//#endregion
|
|
106760
|
+
//#region src/plugin/rules/react-native/rn-bottom-sheet-use-integrated-scrollable.ts
|
|
106761
|
+
const GORHOM_BOTTOM_SHEET_MODULE = "@gorhom/bottom-sheet";
|
|
106762
|
+
const REACT_NATIVE_MODULE$1 = "react-native";
|
|
106763
|
+
const BOTTOM_SHEET_CONTAINER_NAMES = new Set([
|
|
106764
|
+
"BottomSheet",
|
|
106765
|
+
"BottomSheetModal",
|
|
106766
|
+
"default"
|
|
106767
|
+
]);
|
|
106768
|
+
const REACT_NATIVE_SCROLLABLE_NAMES = new Set([
|
|
106769
|
+
"FlatList",
|
|
106770
|
+
"ScrollView",
|
|
106771
|
+
"SectionList",
|
|
106772
|
+
"VirtualizedList"
|
|
106773
|
+
]);
|
|
106774
|
+
const rnBottomSheetUseIntegratedScrollable = defineRule({
|
|
106775
|
+
id: "rn-bottom-sheet-use-integrated-scrollable",
|
|
106776
|
+
title: "React Native scrollable inside a Bottom Sheet",
|
|
106777
|
+
requires: ["react-native"],
|
|
106778
|
+
severity: "warn",
|
|
106779
|
+
recommendation: "Use @gorhom/bottom-sheet's integrated BottomSheetScrollView, BottomSheetFlatList, BottomSheetSectionList, or BottomSheetVirtualizedList so gestures coordinate with the sheet.",
|
|
106780
|
+
create: (context) => {
|
|
106781
|
+
const reportedScrollables = /* @__PURE__ */ new WeakSet();
|
|
106782
|
+
return { JSXElement(node) {
|
|
106783
|
+
const containerName = resolveImportedJsxComponentName(node.openingElement, GORHOM_BOTTOM_SHEET_MODULE, context.scopes);
|
|
106784
|
+
if (!containerName || !BOTTOM_SHEET_CONTAINER_NAMES.has(containerName)) return;
|
|
106785
|
+
for (const descendant of getStaticJsxDescendantOpeningElements(node, { includeStaticExpressionBranches: true })) {
|
|
106786
|
+
if (reportedScrollables.has(descendant)) continue;
|
|
106787
|
+
const scrollableName = resolveImportedJsxComponentName(descendant, REACT_NATIVE_MODULE$1, context.scopes);
|
|
106788
|
+
if (!scrollableName || !REACT_NATIVE_SCROLLABLE_NAMES.has(scrollableName)) continue;
|
|
106789
|
+
reportedScrollables.add(descendant);
|
|
106790
|
+
context.report({
|
|
106791
|
+
node: descendant,
|
|
106792
|
+
message: `React Native's \`${scrollableName}\` does not coordinate gestures with this Bottom Sheet. Use \`BottomSheet${scrollableName}\` from @gorhom/bottom-sheet.`
|
|
106793
|
+
});
|
|
106794
|
+
}
|
|
106795
|
+
} };
|
|
106796
|
+
}
|
|
106797
|
+
});
|
|
106798
|
+
//#endregion
|
|
106514
106799
|
//#region src/plugin/rules/react-native/rn-detox-missing-await.ts
|
|
106515
106800
|
const EMPTY_VISITORS$6 = {};
|
|
106516
106801
|
const DETOX_TEST_FILE = /(\.e2e\.[cm]?[jt]sx?$)|((^|\/)e2e\/)/;
|
|
@@ -106800,8 +107085,20 @@ const REACT_NATIVE_BUILTIN_LIST_COMPONENTS = new Set([
|
|
|
106800
107085
|
]);
|
|
106801
107086
|
const RECYCLABLE_LIST_PACKAGES = {
|
|
106802
107087
|
FlashList: ["@shopify/flash-list"],
|
|
106803
|
-
|
|
106804
|
-
|
|
107088
|
+
AnimatedFlashList: ["@shopify/flash-list"],
|
|
107089
|
+
LegendList: ["@legendapp/list", "@legendapp/list/react-native"],
|
|
107090
|
+
AnimatedLegendList: ["@legendapp/list/animated", "@legendapp/list/reanimated"],
|
|
107091
|
+
KeyboardAwareLegendList: ["@legendapp/list/keyboard"],
|
|
107092
|
+
KeyboardAvoidingLegendList: ["@legendapp/list/keyboard-legacy"]
|
|
107093
|
+
};
|
|
107094
|
+
const SHOPIFY_FLASH_LIST_COMPONENTS = new Set(["FlashList", "AnimatedFlashList"]);
|
|
107095
|
+
const LEGEND_LIST_V3_PACKAGE_SOURCES = new Set([
|
|
107096
|
+
"@legendapp/list/react-native",
|
|
107097
|
+
"@legendapp/list/animated",
|
|
107098
|
+
"@legendapp/list/reanimated",
|
|
107099
|
+
"@legendapp/list/keyboard",
|
|
107100
|
+
"@legendapp/list/keyboard-legacy"
|
|
107101
|
+
]);
|
|
106805
107102
|
const RECYCLABLE_LIST_PACKAGE_SOURCES = Object.values(RECYCLABLE_LIST_PACKAGES).flat();
|
|
106806
107103
|
const REACT_NATIVE_LIST_COMPONENTS = new Set([...REACT_NATIVE_BUILTIN_LIST_COMPONENTS, ...Object.keys(RECYCLABLE_LIST_PACKAGES)]);
|
|
106807
107104
|
const RENDER_ITEM_PROP_NAMES = new Set([
|
|
@@ -106889,21 +107186,9 @@ const getInitializerModuleSource = (contextNode, initializer) => {
|
|
|
106889
107186
|
};
|
|
106890
107187
|
//#endregion
|
|
106891
107188
|
//#region src/plugin/rules/react-native/utils/resolve-imported-recycler-name.ts
|
|
106892
|
-
const
|
|
106893
|
-
if (
|
|
106894
|
-
const
|
|
106895
|
-
if (!elementName || !isNodeOfType(elementName, "JSXMemberExpression")) return null;
|
|
106896
|
-
return isNodeOfType(elementName.object, "JSXIdentifier") ? elementName.object.name : null;
|
|
106897
|
-
};
|
|
106898
|
-
const resolveImportedRecyclerName = (node, localName, options) => {
|
|
106899
|
-
const jsxMemberObjectName = options?.allowNamespaceMemberAccess ? getJsxMemberObjectName(node) : null;
|
|
106900
|
-
for (const [canonicalName, packageSources] of Object.entries(RECYCLABLE_LIST_PACKAGES)) {
|
|
106901
|
-
if (jsxMemberObjectName !== null) {
|
|
106902
|
-
if (localName === canonicalName && packageSources.some((packageSource) => isNamespaceImportFromModule$1(node, jsxMemberObjectName, packageSource))) return canonicalName;
|
|
106903
|
-
continue;
|
|
106904
|
-
}
|
|
106905
|
-
if (packageSources.some((packageSource) => getImportedNameFromModule(node, localName, packageSource) === canonicalName)) return canonicalName;
|
|
106906
|
-
}
|
|
107189
|
+
const resolveImportedRecyclerName = (node, scopes, options) => {
|
|
107190
|
+
if (isNodeOfType(node.name, "JSXMemberExpression") && !options?.allowNamespaceMemberAccess) return null;
|
|
107191
|
+
for (const [canonicalName, packageSources] of Object.entries(RECYCLABLE_LIST_PACKAGES)) for (const packageSource of packageSources) if (resolveImportedJsxComponentName(node, packageSource, scopes) === canonicalName) return canonicalName;
|
|
106907
107192
|
return null;
|
|
106908
107193
|
};
|
|
106909
107194
|
//#endregion
|
|
@@ -106923,8 +107208,8 @@ const isLocalBindingReactNativeList = (node, elementName) => {
|
|
|
106923
107208
|
const initializerModuleSource = getInitializerModuleSource(node, declaratorInitializer);
|
|
106924
107209
|
return initializerModuleSource !== null && REACT_NATIVE_LIST_MODULE_SOURCES.has(initializerModuleSource);
|
|
106925
107210
|
};
|
|
106926
|
-
const isVirtualizedList = (node, elementName) => {
|
|
106927
|
-
if (resolveImportedRecyclerName(node,
|
|
107211
|
+
const isVirtualizedList = (node, elementName, scopes) => {
|
|
107212
|
+
if (resolveImportedRecyclerName(node, scopes, { allowNamespaceMemberAccess: true }) !== null) return true;
|
|
106928
107213
|
if (isNodeOfType(node.name, "JSXMemberExpression")) {
|
|
106929
107214
|
if (!REACT_NATIVE_BUILTIN_LIST_COMPONENTS.has(elementName)) return false;
|
|
106930
107215
|
const memberObjectName = getJsxMemberRootObjectName(node.name);
|
|
@@ -106981,7 +107266,7 @@ const rnListDataMapped = defineRule({
|
|
|
106981
107266
|
recommendation: "This builds a new array each time the parent redraws, so every row redraws too. Wrap it in `useMemo(() => items.map(...), [items])` to keep the same array.",
|
|
106982
107267
|
create: (context) => ({ JSXOpeningElement(node) {
|
|
106983
107268
|
const elementName = resolveJsxElementName(node);
|
|
106984
|
-
if (!elementName || !isVirtualizedList(node, elementName)) return;
|
|
107269
|
+
if (!elementName || !isVirtualizedList(node, elementName, context.scopes)) return;
|
|
106985
107270
|
for (const attr of node.attributes ?? []) {
|
|
106986
107271
|
if (!isNodeOfType(attr, "JSXAttribute")) continue;
|
|
106987
107272
|
if (!isNodeOfType(attr.name, "JSXIdentifier") || attr.name.name !== "data") continue;
|
|
@@ -106997,12 +107282,14 @@ const rnListDataMapped = defineRule({
|
|
|
106997
107282
|
} })
|
|
106998
107283
|
});
|
|
106999
107284
|
//#endregion
|
|
107000
|
-
//#region src/plugin/rules/react-native/
|
|
107001
|
-
const SIZING_HINT_ATTRIBUTE_NAMES = new Set(["estimatedItemSize", "estimatedListSize"]);
|
|
107285
|
+
//#region src/plugin/rules/react-native/utils/is-flash-list-v2-or-newer.ts
|
|
107002
107286
|
const isFlashListV2OrNewer = (context) => {
|
|
107003
107287
|
const flashListMajorVersion = getReactDoctorNumberSetting(context.settings, "shopifyFlashListMajorVersion");
|
|
107004
107288
|
return flashListMajorVersion !== void 0 && flashListMajorVersion >= 2;
|
|
107005
107289
|
};
|
|
107290
|
+
//#endregion
|
|
107291
|
+
//#region src/plugin/rules/react-native/rn-list-missing-estimated-item-size.ts
|
|
107292
|
+
const SIZING_HINT_ATTRIBUTE_NAMES = new Set(["estimatedItemSize", "estimatedListSize"]);
|
|
107006
107293
|
const isEmptyArrayLiteral = (node) => {
|
|
107007
107294
|
if (!isNodeOfType(node.value, "JSXExpressionContainer")) return false;
|
|
107008
107295
|
const expression = node.value.expression;
|
|
@@ -107025,9 +107312,10 @@ const rnListMissingEstimatedItemSize = defineRule({
|
|
|
107025
107312
|
if (!fileImportsRecycler) return;
|
|
107026
107313
|
const localElementName = resolveJsxElementName(node);
|
|
107027
107314
|
if (!localElementName) return;
|
|
107028
|
-
const canonicalRecyclerName = resolveImportedRecyclerName(node,
|
|
107315
|
+
const canonicalRecyclerName = resolveImportedRecyclerName(node, context.scopes);
|
|
107029
107316
|
if (canonicalRecyclerName === null) return;
|
|
107030
|
-
if (
|
|
107317
|
+
if ([...LEGEND_LIST_V3_PACKAGE_SOURCES].some((packageSource) => resolveImportedJsxComponentName(node, packageSource, context.scopes) !== null)) return;
|
|
107318
|
+
if (SHOPIFY_FLASH_LIST_COMPONENTS.has(canonicalRecyclerName) && isFlashListV2OrNewer(context)) return;
|
|
107031
107319
|
let hasSizingHint = false;
|
|
107032
107320
|
let dataIsEmptyLiteral = false;
|
|
107033
107321
|
let hasDataProp = false;
|
|
@@ -107054,15 +107342,1272 @@ const rnListMissingEstimatedItemSize = defineRule({
|
|
|
107054
107342
|
});
|
|
107055
107343
|
//#endregion
|
|
107056
107344
|
//#region src/plugin/rules/react-native/rn-list-recyclable-without-types.ts
|
|
107345
|
+
const RENDER_ITEM_INPUT_NAMES = new Set(["item", "index"]);
|
|
107346
|
+
const EMPTY_RENDERED_ROOT_NAME = "empty";
|
|
107347
|
+
const isSymbolStable = (symbol) => symbol.references.every((reference) => reference.flag === "read");
|
|
107348
|
+
const getSymbolVariableDeclarator = (symbol) => {
|
|
107349
|
+
let declaration = symbol.declarationNode;
|
|
107350
|
+
while (declaration && !isNodeOfType(declaration, "VariableDeclarator")) declaration = declaration.parent;
|
|
107351
|
+
return declaration && isNodeOfType(declaration, "VariableDeclarator") ? declaration : null;
|
|
107352
|
+
};
|
|
107353
|
+
const getConstInitializerExpressions = (symbol) => {
|
|
107354
|
+
if (symbol.kind !== "const" || !symbol.initializer) return [];
|
|
107355
|
+
const declarationInitializer = getSymbolVariableDeclarator(symbol)?.init;
|
|
107356
|
+
return declarationInitializer && declarationInitializer !== symbol.initializer ? [declarationInitializer, symbol.initializer] : [symbol.initializer];
|
|
107357
|
+
};
|
|
107358
|
+
const getSymbolIdentity = (symbol) => {
|
|
107359
|
+
if (symbol.kind !== "import") return `symbol:${symbol.id}`;
|
|
107360
|
+
const source = getImportDeclarationForSymbol(symbol)?.source.value;
|
|
107361
|
+
if (typeof source !== "string") return `symbol:${symbol.id}`;
|
|
107362
|
+
if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return `import:${source}:default`;
|
|
107363
|
+
if (isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) return `import:${source}:*`;
|
|
107364
|
+
return `import:${source}:${getImportedName(symbol.declarationNode) ?? symbol.name}`;
|
|
107365
|
+
};
|
|
107366
|
+
const appendComponentMemberIdentity = (receiverIdentity, propertyName) => {
|
|
107367
|
+
if (!receiverIdentity || !receiverIdentity.startsWith("import:") && !receiverIdentity.startsWith("global:")) return null;
|
|
107368
|
+
if (receiverIdentity.endsWith(":*")) return `${receiverIdentity.slice(0, -1)}${propertyName}`;
|
|
107369
|
+
return `${receiverIdentity}.${propertyName}`;
|
|
107370
|
+
};
|
|
107371
|
+
const getComponentReferenceIdentity = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
107372
|
+
const componentReference = stripParenExpression(expression);
|
|
107373
|
+
if (isNodeOfType(componentReference, "Identifier") || isNodeOfType(componentReference, "JSXIdentifier")) {
|
|
107374
|
+
const symbol = scopes.symbolFor(componentReference);
|
|
107375
|
+
if (!symbol) return `global:${componentReference.name}`;
|
|
107376
|
+
if (visitedSymbolIds.has(symbol.id) || !isSymbolStable(symbol)) return null;
|
|
107377
|
+
visitedSymbolIds.add(symbol.id);
|
|
107378
|
+
if (symbol.kind === "import" || symbol.kind === "function" || symbol.kind === "class") return getSymbolIdentity(symbol);
|
|
107379
|
+
if (symbol.kind !== "const" || !symbol.initializer) return null;
|
|
107380
|
+
const initializer = stripParenExpression(symbol.initializer);
|
|
107381
|
+
const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
|
|
107382
|
+
if (destructuredPropertyName) return appendComponentMemberIdentity(getComponentReferenceIdentity(initializer, scopes, visitedSymbolIds), destructuredPropertyName);
|
|
107383
|
+
const isProvenReactHocCall = isNodeOfType(initializer, "CallExpression") && (isReactApiCall(initializer, "memo", scopes, { resolveNamedAliases: true }) || isReactApiCall(initializer, "forwardRef", scopes, { resolveNamedAliases: true })) && initializer.arguments[0] !== void 0 && !isNodeOfType(initializer.arguments[0], "SpreadElement");
|
|
107384
|
+
if (isFunctionLike$1(initializer) || isNodeOfType(initializer, "ClassExpression") || isProvenReactHocCall) return getSymbolIdentity(symbol);
|
|
107385
|
+
if (!isNodeOfType(initializer, "Identifier") && !isNodeOfType(initializer, "MemberExpression")) return null;
|
|
107386
|
+
return getComponentReferenceIdentity(initializer, scopes, visitedSymbolIds);
|
|
107387
|
+
}
|
|
107388
|
+
if (!isNodeOfType(componentReference, "MemberExpression")) return null;
|
|
107389
|
+
const propertyName = getStaticPropertyName(componentReference);
|
|
107390
|
+
if (propertyName === null) return null;
|
|
107391
|
+
return appendComponentMemberIdentity(getComponentReferenceIdentity(componentReference.object, scopes, visitedSymbolIds), propertyName);
|
|
107392
|
+
};
|
|
107393
|
+
const getJsxElementIdentity = (node, scopes) => {
|
|
107394
|
+
if (isNodeOfType(node, "JSXIdentifier")) {
|
|
107395
|
+
const identity = getComponentReferenceIdentity(node, scopes);
|
|
107396
|
+
if (identity !== `global:${node.name}`) return identity;
|
|
107397
|
+
return /^[a-z]/u.test(node.name) ? `intrinsic:${node.name}` : identity;
|
|
107398
|
+
}
|
|
107399
|
+
if (!isNodeOfType(node, "JSXMemberExpression")) return null;
|
|
107400
|
+
const objectIdentity = getJsxElementIdentity(node.object, scopes);
|
|
107401
|
+
if (!isNodeOfType(node.property, "JSXIdentifier")) return null;
|
|
107402
|
+
return appendComponentMemberIdentity(objectIdentity, node.property.name);
|
|
107403
|
+
};
|
|
107404
|
+
const isStaticallyEmptyJsxChild = (node) => {
|
|
107405
|
+
const expression = getFinalSequenceExpressionValue(node);
|
|
107406
|
+
if (isNodeOfType(expression, "JSXEmptyExpression")) return true;
|
|
107407
|
+
if (isNodeOfType(expression, "Literal") && (expression.value === null || typeof expression.value === "boolean")) return true;
|
|
107408
|
+
if (isNodeOfType(expression, "UnaryExpression")) return expression.operator === "!" || expression.operator === "void";
|
|
107409
|
+
if (!isNodeOfType(expression, "BinaryExpression")) return false;
|
|
107410
|
+
switch (expression.operator) {
|
|
107411
|
+
case "==":
|
|
107412
|
+
case "!=":
|
|
107413
|
+
case "===":
|
|
107414
|
+
case "!==":
|
|
107415
|
+
case "<":
|
|
107416
|
+
case "<=":
|
|
107417
|
+
case ">":
|
|
107418
|
+
case ">=":
|
|
107419
|
+
case "in":
|
|
107420
|
+
case "instanceof": return true;
|
|
107421
|
+
default: return false;
|
|
107422
|
+
}
|
|
107423
|
+
};
|
|
107424
|
+
const getStaticSelectorBindingPath = (pattern, bindingIdentifier, scopes) => {
|
|
107425
|
+
if (pattern === bindingIdentifier) return [];
|
|
107426
|
+
if (isNodeOfType(pattern, "AssignmentPattern")) return readStaticSelectorTruthiness(pattern.right, scopes) === false ? getStaticSelectorBindingPath(pattern.left, bindingIdentifier, scopes) : null;
|
|
107427
|
+
if (isNodeOfType(pattern, "RestElement")) return null;
|
|
107428
|
+
if (isNodeOfType(pattern, "ArrayPattern")) {
|
|
107429
|
+
for (const [elementIndex, element] of pattern.elements.entries()) {
|
|
107430
|
+
if (!element) continue;
|
|
107431
|
+
const nestedPath = getStaticSelectorBindingPath(element, bindingIdentifier, scopes);
|
|
107432
|
+
if (nestedPath !== null) return [String(elementIndex), ...nestedPath];
|
|
107433
|
+
}
|
|
107434
|
+
return null;
|
|
107435
|
+
}
|
|
107436
|
+
if (!isNodeOfType(pattern, "ObjectPattern")) return null;
|
|
107437
|
+
for (const property of pattern.properties) {
|
|
107438
|
+
if (!isNodeOfType(property, "Property")) continue;
|
|
107439
|
+
const nestedPath = getStaticSelectorBindingPath(property.value, bindingIdentifier, scopes);
|
|
107440
|
+
if (nestedPath === null) continue;
|
|
107441
|
+
const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
|
|
107442
|
+
return propertyName === null ? null : [propertyName, ...nestedPath];
|
|
107443
|
+
}
|
|
107444
|
+
return null;
|
|
107445
|
+
};
|
|
107446
|
+
const getStaticSelectorPropertyName = (memberExpression) => getStaticPropertyName(memberExpression) ?? getStaticPropertyKeyName(memberExpression, {
|
|
107447
|
+
allowComputedString: true,
|
|
107448
|
+
stringifyNonStringLiterals: true
|
|
107449
|
+
});
|
|
107450
|
+
const getSelectorReferenceKey = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
107451
|
+
const selector = stripParenExpression(expression);
|
|
107452
|
+
if (isNodeOfType(selector, "Identifier")) {
|
|
107453
|
+
const symbol = scopes.referenceFor(selector)?.resolvedSymbol;
|
|
107454
|
+
if (!symbol || !isSymbolStable(symbol) || visitedSymbolIds.has(symbol.id)) return null;
|
|
107455
|
+
if (symbol.kind === "const" && symbol.initializer) {
|
|
107456
|
+
visitedSymbolIds.add(symbol.id);
|
|
107457
|
+
const declaration = getSymbolVariableDeclarator(symbol);
|
|
107458
|
+
if (declaration?.id === symbol.bindingIdentifier) return getSelectorReferenceKey(symbol.initializer, scopes, visitedSymbolIds) ?? JSON.stringify(["symbol", symbol.id]);
|
|
107459
|
+
if (declaration?.init) {
|
|
107460
|
+
const bindingPath = getStaticSelectorBindingPath(declaration.id, symbol.bindingIdentifier, scopes);
|
|
107461
|
+
let receiverKey = getSelectorReferenceKey(declaration.init, scopes, visitedSymbolIds);
|
|
107462
|
+
if (bindingPath !== null && receiverKey !== null) {
|
|
107463
|
+
for (const propertyName of bindingPath) receiverKey = JSON.stringify([
|
|
107464
|
+
"member",
|
|
107465
|
+
receiverKey,
|
|
107466
|
+
propertyName
|
|
107467
|
+
]);
|
|
107468
|
+
return receiverKey;
|
|
107469
|
+
}
|
|
107470
|
+
}
|
|
107471
|
+
return JSON.stringify(["symbol", symbol.id]);
|
|
107472
|
+
}
|
|
107473
|
+
return JSON.stringify(["symbol", symbol.id]);
|
|
107474
|
+
}
|
|
107475
|
+
if (!isNodeOfType(selector, "MemberExpression")) return null;
|
|
107476
|
+
const propertyName = getStaticSelectorPropertyName(selector);
|
|
107477
|
+
if (propertyName === null) return null;
|
|
107478
|
+
const receiverKey = getSelectorReferenceKey(selector.object, scopes, visitedSymbolIds);
|
|
107479
|
+
return receiverKey === null ? null : JSON.stringify([
|
|
107480
|
+
"member",
|
|
107481
|
+
receiverKey,
|
|
107482
|
+
propertyName
|
|
107483
|
+
]);
|
|
107484
|
+
};
|
|
107485
|
+
const getStaticComparisonOperandKey = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
107486
|
+
const operand = stripParenExpression(expression);
|
|
107487
|
+
if (isNodeOfType(operand, "Identifier")) {
|
|
107488
|
+
const symbol = scopes.referenceFor(operand)?.resolvedSymbol;
|
|
107489
|
+
if (symbol?.kind === "const" && symbol.initializer && isSymbolStable(symbol) && getSymbolVariableDeclarator(symbol)?.id === symbol.bindingIdentifier && !visitedSymbolIds.has(symbol.id)) {
|
|
107490
|
+
visitedSymbolIds.add(symbol.id);
|
|
107491
|
+
return getStaticComparisonOperandKey(symbol.initializer, scopes, visitedSymbolIds) ?? JSON.stringify(["symbol", symbol.id]);
|
|
107492
|
+
}
|
|
107493
|
+
}
|
|
107494
|
+
if (isNodeOfType(operand, "UnaryExpression") && operand.operator === "typeof") {
|
|
107495
|
+
const argumentKey = getSelectorReferenceKey(operand.argument, scopes, visitedSymbolIds);
|
|
107496
|
+
return argumentKey === null ? null : JSON.stringify(["typeof", argumentKey]);
|
|
107497
|
+
}
|
|
107498
|
+
return getSelectorReferenceKey(operand, scopes, visitedSymbolIds);
|
|
107499
|
+
};
|
|
107500
|
+
const getStaticPrimitiveLiteralKey = (expression) => {
|
|
107501
|
+
const literal = stripParenExpression(expression);
|
|
107502
|
+
if (isNodeOfType(literal, "UnaryExpression") && (literal.operator === "-" || literal.operator === "+")) {
|
|
107503
|
+
const argument = stripParenExpression(literal.argument);
|
|
107504
|
+
if (isNodeOfType(argument, "Literal") && typeof argument.value === "number") {
|
|
107505
|
+
const numericValue = literal.operator === "-" ? -argument.value : argument.value;
|
|
107506
|
+
return `number:${String(numericValue)}`;
|
|
107507
|
+
}
|
|
107508
|
+
}
|
|
107509
|
+
if (!isNodeOfType(literal, "Literal")) return null;
|
|
107510
|
+
if (literal.value === null) return "null";
|
|
107511
|
+
if (typeof literal.value === "string") return `string:${JSON.stringify(literal.value)}`;
|
|
107512
|
+
if (typeof literal.value === "number") return `number:${String(literal.value)}`;
|
|
107513
|
+
if (typeof literal.value === "boolean") return `boolean:${String(literal.value)}`;
|
|
107514
|
+
if (typeof literal.value === "bigint") return `bigint:${String(literal.value)}`;
|
|
107515
|
+
return null;
|
|
107516
|
+
};
|
|
107517
|
+
const getImportedStaticReferenceKey = (expression, scopes) => {
|
|
107518
|
+
const reference = stripParenExpression(expression);
|
|
107519
|
+
if (isNodeOfType(reference, "Identifier")) {
|
|
107520
|
+
const symbol = scopes.referenceFor(reference)?.resolvedSymbol;
|
|
107521
|
+
return symbol?.kind === "import" ? getSymbolIdentity(symbol) : null;
|
|
107522
|
+
}
|
|
107523
|
+
if (!isNodeOfType(reference, "MemberExpression")) return null;
|
|
107524
|
+
const propertyName = getStaticPropertyName(reference);
|
|
107525
|
+
if (propertyName === null) return null;
|
|
107526
|
+
const receiverKey = getImportedStaticReferenceKey(reference.object, scopes);
|
|
107527
|
+
return receiverKey === null ? null : JSON.stringify([
|
|
107528
|
+
"member",
|
|
107529
|
+
receiverKey,
|
|
107530
|
+
propertyName
|
|
107531
|
+
]);
|
|
107532
|
+
};
|
|
107533
|
+
const getStaticComparisonConstantKey = (expression, scopes) => getStaticPrimitiveLiteralKey(expression) ?? getImportedStaticReferenceKey(expression, scopes);
|
|
107534
|
+
const getStaticSelectorIdentity = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
107535
|
+
const selector = getFinalSequenceExpressionValue(expression);
|
|
107536
|
+
if (isNodeOfType(selector, "Identifier")) {
|
|
107537
|
+
const symbol = scopes.referenceFor(selector)?.resolvedSymbol;
|
|
107538
|
+
if (symbol?.kind === "const" && symbol.initializer && isSymbolStable(symbol) && getSymbolVariableDeclarator(symbol)?.id === symbol.bindingIdentifier && !visitedSymbolIds.has(symbol.id)) {
|
|
107539
|
+
visitedSymbolIds.add(symbol.id);
|
|
107540
|
+
const initializerIdentity = getStaticSelectorIdentity(symbol.initializer, scopes, visitedSymbolIds);
|
|
107541
|
+
if (initializerIdentity) return initializerIdentity;
|
|
107542
|
+
}
|
|
107543
|
+
}
|
|
107544
|
+
if (isNodeOfType(selector, "UnaryExpression") && selector.operator === "!") {
|
|
107545
|
+
const argumentIdentity = getStaticSelectorIdentity(selector.argument, scopes, visitedSymbolIds);
|
|
107546
|
+
return argumentIdentity ? {
|
|
107547
|
+
isInverted: !argumentIdentity.isInverted,
|
|
107548
|
+
key: argumentIdentity.key
|
|
107549
|
+
} : null;
|
|
107550
|
+
}
|
|
107551
|
+
if (isNodeOfType(selector, "BinaryExpression") && [
|
|
107552
|
+
"==",
|
|
107553
|
+
"!=",
|
|
107554
|
+
"===",
|
|
107555
|
+
"!=="
|
|
107556
|
+
].includes(selector.operator)) {
|
|
107557
|
+
const operandPairs = [{
|
|
107558
|
+
literal: selector.right,
|
|
107559
|
+
selectorOperand: selector.left
|
|
107560
|
+
}, {
|
|
107561
|
+
literal: selector.left,
|
|
107562
|
+
selectorOperand: selector.right
|
|
107563
|
+
}];
|
|
107564
|
+
for (const operandPair of operandPairs) {
|
|
107565
|
+
const constantKey = getStaticComparisonConstantKey(operandPair.literal, scopes);
|
|
107566
|
+
if (constantKey === null) continue;
|
|
107567
|
+
const operandKey = getStaticComparisonOperandKey(operandPair.selectorOperand, scopes);
|
|
107568
|
+
if (operandKey === null) continue;
|
|
107569
|
+
return {
|
|
107570
|
+
isInverted: selector.operator === "!=" || selector.operator === "!==",
|
|
107571
|
+
key: JSON.stringify([
|
|
107572
|
+
"comparison",
|
|
107573
|
+
selector.operator.length === 3 ? "strict" : "loose",
|
|
107574
|
+
operandKey,
|
|
107575
|
+
constantKey
|
|
107576
|
+
])
|
|
107577
|
+
};
|
|
107578
|
+
}
|
|
107579
|
+
return null;
|
|
107580
|
+
}
|
|
107581
|
+
if (isNodeOfType(selector, "BinaryExpression") && [
|
|
107582
|
+
"<",
|
|
107583
|
+
"<=",
|
|
107584
|
+
">",
|
|
107585
|
+
">=",
|
|
107586
|
+
"in",
|
|
107587
|
+
"instanceof"
|
|
107588
|
+
].includes(selector.operator)) {
|
|
107589
|
+
const leftKey = getStaticPrimitiveLiteralKey(selector.left) ?? getStaticComparisonOperandKey(selector.left, scopes);
|
|
107590
|
+
const rightKey = getStaticPrimitiveLiteralKey(selector.right) ?? getStaticComparisonOperandKey(selector.right, scopes);
|
|
107591
|
+
if (leftKey !== null && rightKey !== null) return {
|
|
107592
|
+
isInverted: false,
|
|
107593
|
+
key: JSON.stringify([
|
|
107594
|
+
"binary-comparison",
|
|
107595
|
+
selector.operator,
|
|
107596
|
+
leftKey,
|
|
107597
|
+
rightKey
|
|
107598
|
+
])
|
|
107599
|
+
};
|
|
107600
|
+
}
|
|
107601
|
+
const key = getSelectorReferenceKey(selector, scopes);
|
|
107602
|
+
return key === null ? null : {
|
|
107603
|
+
isInverted: false,
|
|
107604
|
+
key
|
|
107605
|
+
};
|
|
107606
|
+
};
|
|
107607
|
+
const getRenderedRootShapeAlternativeKey = (alternative) => JSON.stringify({
|
|
107608
|
+
facts: [...alternative.facts].map(([key, fact]) => ({
|
|
107609
|
+
key,
|
|
107610
|
+
outcome: fact.outcome
|
|
107611
|
+
})).sort((firstFact, secondFact) => firstFact.key.localeCompare(secondFact.key)),
|
|
107612
|
+
roots: alternative.roots
|
|
107613
|
+
});
|
|
107614
|
+
const getRenderedRootFactStateKey = (alternative) => JSON.stringify([...alternative.facts].map(([key, fact]) => ({
|
|
107615
|
+
key,
|
|
107616
|
+
outcome: fact.outcome
|
|
107617
|
+
})).sort((firstFact, secondFact) => firstFact.key.localeCompare(secondFact.key)));
|
|
107618
|
+
const deduplicateRenderedRootShapeAlternatives = (alternatives) => {
|
|
107619
|
+
const deduplicatedAlternatives = [];
|
|
107620
|
+
const alternativeKeys = /* @__PURE__ */ new Set();
|
|
107621
|
+
const alternativeCountsByFactState = /* @__PURE__ */ new Map();
|
|
107622
|
+
for (const alternative of alternatives) {
|
|
107623
|
+
const alternativeKey = getRenderedRootShapeAlternativeKey(alternative);
|
|
107624
|
+
if (alternativeKeys.has(alternativeKey)) continue;
|
|
107625
|
+
const factStateKey = getRenderedRootFactStateKey(alternative);
|
|
107626
|
+
const factStateAlternativeCount = alternativeCountsByFactState.get(factStateKey) ?? 0;
|
|
107627
|
+
if (factStateAlternativeCount > 1) continue;
|
|
107628
|
+
alternativeKeys.add(alternativeKey);
|
|
107629
|
+
alternativeCountsByFactState.set(factStateKey, factStateAlternativeCount + 1);
|
|
107630
|
+
deduplicatedAlternatives.push(alternative);
|
|
107631
|
+
if (deduplicatedAlternatives.length > 64) return null;
|
|
107632
|
+
}
|
|
107633
|
+
return deduplicatedAlternatives;
|
|
107634
|
+
};
|
|
107635
|
+
const addRenderedRootSelectorFact = (alternatives, identity, expressionOutcome, selector) => {
|
|
107636
|
+
const outcome = identity.isInverted ? !expressionOutcome : expressionOutcome;
|
|
107637
|
+
const constrainedAlternatives = [];
|
|
107638
|
+
for (const alternative of alternatives) {
|
|
107639
|
+
const existingFact = alternative.facts.get(identity.key);
|
|
107640
|
+
if (existingFact && existingFact.outcome !== outcome) continue;
|
|
107641
|
+
constrainedAlternatives.push({
|
|
107642
|
+
facts: new Map(alternative.facts).set(identity.key, {
|
|
107643
|
+
outcome,
|
|
107644
|
+
selector
|
|
107645
|
+
}),
|
|
107646
|
+
roots: alternative.roots
|
|
107647
|
+
});
|
|
107648
|
+
}
|
|
107649
|
+
return constrainedAlternatives;
|
|
107650
|
+
};
|
|
107651
|
+
const mergeRenderedRootShapeAlternatives = (existingAlternatives, appendedAlternatives) => {
|
|
107652
|
+
const mergedAlternatives = [];
|
|
107653
|
+
for (const existingAlternative of existingAlternatives) for (const appendedAlternative of appendedAlternatives) {
|
|
107654
|
+
const mergedFacts = new Map(existingAlternative.facts);
|
|
107655
|
+
let hasContradictoryFact = false;
|
|
107656
|
+
for (const [key, appendedFact] of appendedAlternative.facts) {
|
|
107657
|
+
const existingFact = mergedFacts.get(key);
|
|
107658
|
+
if (existingFact && existingFact.outcome !== appendedFact.outcome) {
|
|
107659
|
+
hasContradictoryFact = true;
|
|
107660
|
+
break;
|
|
107661
|
+
}
|
|
107662
|
+
mergedFacts.set(key, appendedFact);
|
|
107663
|
+
}
|
|
107664
|
+
if (hasContradictoryFact) continue;
|
|
107665
|
+
mergedAlternatives.push({
|
|
107666
|
+
facts: mergedFacts,
|
|
107667
|
+
roots: [...existingAlternative.roots, ...appendedAlternative.roots]
|
|
107668
|
+
});
|
|
107669
|
+
}
|
|
107670
|
+
return deduplicateRenderedRootShapeAlternatives(mergedAlternatives);
|
|
107671
|
+
};
|
|
107672
|
+
const combineRenderedRootShapeAlternativeBranches = (branches) => deduplicateRenderedRootShapeAlternatives(branches.flat());
|
|
107673
|
+
const getStaticRenderedRootAlternatives = (node, scopes) => {
|
|
107674
|
+
const renderedNode = getFinalSequenceExpressionValue(node);
|
|
107675
|
+
if (isNodeOfType(renderedNode, "JSXElement") && !isJsxFragmentElement(renderedNode.openingElement, scopes)) {
|
|
107676
|
+
const elementIdentity = getJsxElementIdentity(renderedNode.openingElement.name, scopes);
|
|
107677
|
+
return elementIdentity === null ? null : [{
|
|
107678
|
+
facts: /* @__PURE__ */ new Map(),
|
|
107679
|
+
roots: [elementIdentity]
|
|
107680
|
+
}];
|
|
107681
|
+
}
|
|
107682
|
+
if (isNodeOfType(renderedNode, "JSXElement") || isNodeOfType(renderedNode, "JSXFragment")) {
|
|
107683
|
+
let alternatives = [{
|
|
107684
|
+
facts: /* @__PURE__ */ new Map(),
|
|
107685
|
+
roots: []
|
|
107686
|
+
}];
|
|
107687
|
+
for (const child of renderedNode.children) {
|
|
107688
|
+
const childAlternatives = getStaticRenderedRootAlternatives(child, scopes);
|
|
107689
|
+
if (childAlternatives === null) return null;
|
|
107690
|
+
const mergedAlternatives = mergeRenderedRootShapeAlternatives(alternatives, childAlternatives);
|
|
107691
|
+
if (mergedAlternatives === null) return null;
|
|
107692
|
+
alternatives = mergedAlternatives;
|
|
107693
|
+
}
|
|
107694
|
+
return alternatives;
|
|
107695
|
+
}
|
|
107696
|
+
if (isNodeOfType(renderedNode, "JSXText")) return renderedNode.value?.trim() ? null : [{
|
|
107697
|
+
facts: /* @__PURE__ */ new Map(),
|
|
107698
|
+
roots: []
|
|
107699
|
+
}];
|
|
107700
|
+
if (isNodeOfType(renderedNode, "JSXExpressionContainer")) return getStaticRenderedRootAlternatives(renderedNode.expression, scopes);
|
|
107701
|
+
if (isStaticallyEmptyJsxChild(renderedNode)) return [{
|
|
107702
|
+
facts: /* @__PURE__ */ new Map(),
|
|
107703
|
+
roots: []
|
|
107704
|
+
}];
|
|
107705
|
+
if (isNodeOfType(renderedNode, "ConditionalExpression")) {
|
|
107706
|
+
const staticTestValue = readStaticSelectorTruthiness(renderedNode.test, scopes);
|
|
107707
|
+
if (staticTestValue !== null) return getStaticRenderedRootAlternatives(staticTestValue ? renderedNode.consequent : renderedNode.alternate, scopes);
|
|
107708
|
+
const consequentAlternatives = getStaticRenderedRootAlternatives(renderedNode.consequent, scopes);
|
|
107709
|
+
const alternateAlternatives = getStaticRenderedRootAlternatives(renderedNode.alternate, scopes);
|
|
107710
|
+
if (consequentAlternatives === null || alternateAlternatives === null) return null;
|
|
107711
|
+
const selectorIdentity = getStaticSelectorIdentity(renderedNode.test, scopes);
|
|
107712
|
+
return combineRenderedRootShapeAlternativeBranches([selectorIdentity ? addRenderedRootSelectorFact(consequentAlternatives, selectorIdentity, true, renderedNode.test) : consequentAlternatives, selectorIdentity ? addRenderedRootSelectorFact(alternateAlternatives, selectorIdentity, false, renderedNode.test) : alternateAlternatives]);
|
|
107713
|
+
}
|
|
107714
|
+
if (isNodeOfType(renderedNode, "LogicalExpression")) {
|
|
107715
|
+
const selectorIdentity = (renderedNode.operator === "&&" || renderedNode.operator === "||") && isStaticallyEmptyJsxChild(renderedNode.left) ? getStaticSelectorIdentity(renderedNode.left, scopes) : null;
|
|
107716
|
+
if (selectorIdentity) {
|
|
107717
|
+
const rightAlternatives = getStaticRenderedRootAlternatives(renderedNode.right, scopes);
|
|
107718
|
+
if (rightAlternatives === null) return null;
|
|
107719
|
+
const emptyAlternatives = [{
|
|
107720
|
+
facts: /* @__PURE__ */ new Map(),
|
|
107721
|
+
roots: []
|
|
107722
|
+
}];
|
|
107723
|
+
return combineRenderedRootShapeAlternativeBranches([addRenderedRootSelectorFact(renderedNode.operator === "&&" ? rightAlternatives : emptyAlternatives, selectorIdentity, true, renderedNode.left), addRenderedRootSelectorFact(renderedNode.operator === "&&" ? emptyAlternatives : rightAlternatives, selectorIdentity, false, renderedNode.left)]);
|
|
107724
|
+
}
|
|
107725
|
+
const resultAlternatives = [];
|
|
107726
|
+
for (const resultBranch of getStaticLogicalExpressionResultBranches(renderedNode)) {
|
|
107727
|
+
const branchAlternatives = getStaticRenderedRootAlternatives(resultBranch, scopes);
|
|
107728
|
+
if (branchAlternatives === null) return null;
|
|
107729
|
+
resultAlternatives.push(branchAlternatives);
|
|
107730
|
+
}
|
|
107731
|
+
return combineRenderedRootShapeAlternativeBranches(resultAlternatives);
|
|
107732
|
+
}
|
|
107733
|
+
return null;
|
|
107734
|
+
};
|
|
107735
|
+
const getFlattenedFragmentChildren = (node, scopes) => {
|
|
107736
|
+
const flattenChild = (child) => {
|
|
107737
|
+
const renderedChild = getFinalSequenceExpressionValue(child);
|
|
107738
|
+
if (isNodeOfType(renderedChild, "JSXExpressionContainer")) return flattenChild(renderedChild.expression);
|
|
107739
|
+
if (!isNodeOfType(renderedChild, "JSXFragment") && (!isNodeOfType(renderedChild, "JSXElement") || !isJsxFragmentElement(renderedChild.openingElement, scopes))) return [child];
|
|
107740
|
+
return renderedChild.children.flatMap(flattenChild);
|
|
107741
|
+
};
|
|
107742
|
+
const renderedNode = getFinalSequenceExpressionValue(node);
|
|
107743
|
+
if (!isNodeOfType(renderedNode, "JSXFragment") && (!isNodeOfType(renderedNode, "JSXElement") || !isJsxFragmentElement(renderedNode.openingElement, scopes))) return null;
|
|
107744
|
+
return renderedNode.children.flatMap(flattenChild);
|
|
107745
|
+
};
|
|
107746
|
+
const forgetFinalizedRenderedRootFacts = (alternatives, futureFactKeyCounts) => deduplicateRenderedRootShapeAlternatives(alternatives.map((alternative) => ({
|
|
107747
|
+
facts: new Map([...alternative.facts].filter(([key]) => (futureFactKeyCounts.get(key) ?? 0) > 0)),
|
|
107748
|
+
roots: alternative.roots
|
|
107749
|
+
})));
|
|
107750
|
+
const getStaticRenderedRootShapes = (node, scopes) => {
|
|
107751
|
+
let alternatives = getStaticRenderedRootAlternatives(node, scopes);
|
|
107752
|
+
if (alternatives === null) {
|
|
107753
|
+
const fragmentChildren = getFlattenedFragmentChildren(node, scopes);
|
|
107754
|
+
if (fragmentChildren === null) return null;
|
|
107755
|
+
const childAlternatives = [];
|
|
107756
|
+
for (const child of fragmentChildren) {
|
|
107757
|
+
const staticChildAlternatives = getStaticRenderedRootAlternatives(child, scopes);
|
|
107758
|
+
if (staticChildAlternatives === null) return null;
|
|
107759
|
+
childAlternatives.push(staticChildAlternatives);
|
|
107760
|
+
}
|
|
107761
|
+
const futureFactKeyCounts = /* @__PURE__ */ new Map();
|
|
107762
|
+
for (const staticChildAlternatives of childAlternatives) {
|
|
107763
|
+
const childFactKeys = /* @__PURE__ */ new Set();
|
|
107764
|
+
for (const alternative of staticChildAlternatives) for (const key of alternative.facts.keys()) childFactKeys.add(key);
|
|
107765
|
+
for (const key of childFactKeys) futureFactKeyCounts.set(key, (futureFactKeyCounts.get(key) ?? 0) + 1);
|
|
107766
|
+
}
|
|
107767
|
+
let prefixAlternatives = [{
|
|
107768
|
+
facts: /* @__PURE__ */ new Map(),
|
|
107769
|
+
roots: []
|
|
107770
|
+
}];
|
|
107771
|
+
let witnessAlternatives = null;
|
|
107772
|
+
for (const staticChildAlternatives of childAlternatives) {
|
|
107773
|
+
const currentFactKeys = /* @__PURE__ */ new Set();
|
|
107774
|
+
for (const alternative of staticChildAlternatives) for (const key of alternative.facts.keys()) currentFactKeys.add(key);
|
|
107775
|
+
for (const key of currentFactKeys) futureFactKeyCounts.set(key, (futureFactKeyCounts.get(key) ?? 1) - 1);
|
|
107776
|
+
const mergedAlternatives = mergeRenderedRootShapeAlternatives(prefixAlternatives, staticChildAlternatives);
|
|
107777
|
+
if (mergedAlternatives === null) break;
|
|
107778
|
+
prefixAlternatives = mergedAlternatives;
|
|
107779
|
+
for (let firstAlternativeIndex = 0; firstAlternativeIndex < prefixAlternatives.length; firstAlternativeIndex += 1) {
|
|
107780
|
+
const firstAlternative = prefixAlternatives[firstAlternativeIndex];
|
|
107781
|
+
for (let secondAlternativeIndex = firstAlternativeIndex + 1; secondAlternativeIndex < prefixAlternatives.length; secondAlternativeIndex += 1) {
|
|
107782
|
+
const secondAlternative = prefixAlternatives[secondAlternativeIndex];
|
|
107783
|
+
if (JSON.stringify(firstAlternative.roots) === JSON.stringify(secondAlternative.roots)) continue;
|
|
107784
|
+
if ([...firstAlternative.facts].some(([key, firstFact]) => {
|
|
107785
|
+
const secondFact = secondAlternative.facts.get(key);
|
|
107786
|
+
return futureFactKeyCounts.get(key) === 0 && secondFact !== void 0 && secondFact.outcome !== firstFact.outcome;
|
|
107787
|
+
})) {
|
|
107788
|
+
witnessAlternatives = [firstAlternative, secondAlternative];
|
|
107789
|
+
break;
|
|
107790
|
+
}
|
|
107791
|
+
}
|
|
107792
|
+
if (witnessAlternatives) break;
|
|
107793
|
+
}
|
|
107794
|
+
if (witnessAlternatives) break;
|
|
107795
|
+
const remainingAlternatives = forgetFinalizedRenderedRootFacts(prefixAlternatives, futureFactKeyCounts);
|
|
107796
|
+
if (remainingAlternatives === null) break;
|
|
107797
|
+
prefixAlternatives = remainingAlternatives;
|
|
107798
|
+
}
|
|
107799
|
+
if (witnessAlternatives === null) return null;
|
|
107800
|
+
alternatives = witnessAlternatives;
|
|
107801
|
+
}
|
|
107802
|
+
const rootShapes = [];
|
|
107803
|
+
const rootShapeKeys = /* @__PURE__ */ new Set();
|
|
107804
|
+
for (const alternative of alternatives) {
|
|
107805
|
+
const rootShapeKey = JSON.stringify(alternative.roots);
|
|
107806
|
+
if (rootShapeKeys.has(rootShapeKey)) continue;
|
|
107807
|
+
rootShapeKeys.add(rootShapeKey);
|
|
107808
|
+
rootShapes.push([...alternative.roots]);
|
|
107809
|
+
}
|
|
107810
|
+
return rootShapes;
|
|
107811
|
+
};
|
|
107812
|
+
const getRenderedRootNames = (root, scopes) => {
|
|
107813
|
+
const renderedRootShapes = getStaticRenderedRootShapes(root, scopes);
|
|
107814
|
+
if (renderedRootShapes === null) return null;
|
|
107815
|
+
return renderedRootShapes.map((rootShape) => {
|
|
107816
|
+
const onlyRootName = rootShape[0];
|
|
107817
|
+
return rootShape.length === 1 && onlyRootName ? onlyRootName : `fragment:${JSON.stringify(rootShape)}`;
|
|
107818
|
+
});
|
|
107819
|
+
};
|
|
107820
|
+
const getPatternBindingIdentifier = (pattern) => {
|
|
107821
|
+
const unwrappedPattern = stripParenExpression(pattern);
|
|
107822
|
+
if (isNodeOfType(unwrappedPattern, "Identifier")) return unwrappedPattern;
|
|
107823
|
+
if (isNodeOfType(unwrappedPattern, "AssignmentPattern")) return getPatternBindingIdentifier(unwrappedPattern.left);
|
|
107824
|
+
return null;
|
|
107825
|
+
};
|
|
107826
|
+
const getObjectPatternPropertyBinding = (pattern, propertyName) => {
|
|
107827
|
+
const unwrappedPattern = stripParenExpression(pattern);
|
|
107828
|
+
if (!isNodeOfType(unwrappedPattern, "ObjectPattern")) return null;
|
|
107829
|
+
for (const property of unwrappedPattern.properties) {
|
|
107830
|
+
if (!isNodeOfType(property, "Property") || getStaticPropertyKeyName(property, { allowComputedString: true }) !== propertyName) continue;
|
|
107831
|
+
return getPatternBindingIdentifier(property.value);
|
|
107832
|
+
}
|
|
107833
|
+
return null;
|
|
107834
|
+
};
|
|
107835
|
+
const isUnconditionallyTerminalStatement = (statement) => {
|
|
107836
|
+
if (isNodeOfType(statement, "ReturnStatement") || isNodeOfType(statement, "ThrowStatement")) return true;
|
|
107837
|
+
if (isNodeOfType(statement, "BlockStatement")) return statement.body.some(isUnconditionallyTerminalStatement);
|
|
107838
|
+
if (isNodeOfType(statement, "IfStatement")) return Boolean(statement.alternate && isUnconditionallyTerminalStatement(statement.consequent) && isUnconditionallyTerminalStatement(statement.alternate));
|
|
107839
|
+
if (isNodeOfType(statement, "SwitchStatement")) return statement.cases.some((switchCase) => switchCase.test === null) && statement.cases.every((switchCase) => switchCase.consequent.some(isUnconditionallyTerminalStatement));
|
|
107840
|
+
return false;
|
|
107841
|
+
};
|
|
107842
|
+
const getReachableFunctionReturnStatements = (functionNode, scopes) => collectFunctionReturnStatements(functionNode).filter((returnStatement) => {
|
|
107843
|
+
let descendant = returnStatement;
|
|
107844
|
+
let ancestor = returnStatement.parent;
|
|
107845
|
+
while (ancestor && ancestor !== functionNode) {
|
|
107846
|
+
if (isNodeOfType(ancestor, "IfStatement")) {
|
|
107847
|
+
const staticTestValue = readStaticSelectorTruthiness(ancestor.test, scopes);
|
|
107848
|
+
if (staticTestValue !== null && (staticTestValue && ancestor.alternate === descendant || !staticTestValue && ancestor.consequent === descendant)) return false;
|
|
107849
|
+
}
|
|
107850
|
+
if (isNodeOfType(ancestor, "BlockStatement")) {
|
|
107851
|
+
const descendantIndex = ancestor.body.findIndex((statement) => statement === descendant);
|
|
107852
|
+
if (descendantIndex > 0 && ancestor.body.slice(0, descendantIndex).some((statement) => {
|
|
107853
|
+
if (isNodeOfType(statement, "IfStatement")) {
|
|
107854
|
+
const staticTestValue = readStaticSelectorTruthiness(statement.test, scopes);
|
|
107855
|
+
if (staticTestValue === true) return isUnconditionallyTerminalStatement(statement.consequent);
|
|
107856
|
+
if (staticTestValue === false) return Boolean(statement.alternate && isUnconditionallyTerminalStatement(statement.alternate));
|
|
107857
|
+
}
|
|
107858
|
+
return isUnconditionallyTerminalStatement(statement);
|
|
107859
|
+
})) return false;
|
|
107860
|
+
}
|
|
107861
|
+
descendant = ancestor;
|
|
107862
|
+
ancestor = ancestor.parent;
|
|
107863
|
+
}
|
|
107864
|
+
return true;
|
|
107865
|
+
});
|
|
107866
|
+
const getRenderItemInputReferences = (functionNode, scopes) => {
|
|
107867
|
+
if (!isFunctionLike$1(functionNode)) return [];
|
|
107868
|
+
const parameter = functionNode.params[0];
|
|
107869
|
+
if (!parameter) return [];
|
|
107870
|
+
const unwrappedParameter = stripParenExpression(parameter);
|
|
107871
|
+
if (isNodeOfType(unwrappedParameter, "Identifier")) {
|
|
107872
|
+
const symbol = scopes.symbolFor(unwrappedParameter);
|
|
107873
|
+
if (!symbol) return [];
|
|
107874
|
+
return [...RENDER_ITEM_INPUT_NAMES].map((inputName) => ({
|
|
107875
|
+
inputName,
|
|
107876
|
+
isStable: isSymbolStable(symbol),
|
|
107877
|
+
propertyName: inputName,
|
|
107878
|
+
symbolId: symbol.id
|
|
107879
|
+
}));
|
|
107880
|
+
}
|
|
107881
|
+
if (!isNodeOfType(unwrappedParameter, "ObjectPattern")) return [];
|
|
107882
|
+
const references = [];
|
|
107883
|
+
for (const inputName of RENDER_ITEM_INPUT_NAMES) {
|
|
107884
|
+
const bindingIdentifier = getObjectPatternPropertyBinding(unwrappedParameter, inputName);
|
|
107885
|
+
const symbol = bindingIdentifier ? scopes.symbolFor(bindingIdentifier) : null;
|
|
107886
|
+
if (symbol) references.push({
|
|
107887
|
+
inputName,
|
|
107888
|
+
isStable: isSymbolStable(symbol),
|
|
107889
|
+
propertyName: null,
|
|
107890
|
+
symbolId: symbol.id
|
|
107891
|
+
});
|
|
107892
|
+
}
|
|
107893
|
+
return references;
|
|
107894
|
+
};
|
|
107895
|
+
const isStaticallyTruthyContainer = (node) => isNodeOfType(node, "ArrayExpression") || isNodeOfType(node, "ObjectExpression") || isNodeOfType(node, "ArrowFunctionExpression") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "ClassExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "JSXElement") || isNodeOfType(node, "JSXFragment");
|
|
107896
|
+
const expressionReadsInput = (expression, inputReferences, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
107897
|
+
const inputExpression = getFinalSequenceExpressionValue(expression);
|
|
107898
|
+
if (isStaticallyTruthyContainer(inputExpression)) return false;
|
|
107899
|
+
let didReadInput = false;
|
|
107900
|
+
walkAst(inputExpression, (node) => {
|
|
107901
|
+
if (didReadInput) return false;
|
|
107902
|
+
if (node !== inputExpression && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
|
|
107903
|
+
if (isNodeOfType(node, "Identifier")) {
|
|
107904
|
+
const reference = scopes.referenceFor(node);
|
|
107905
|
+
if (reference && reference.flag !== "write" && inputReferences.some((inputReference) => inputReference.isStable && inputReference.propertyName === null && inputReference.symbolId === reference.resolvedSymbol?.id)) {
|
|
107906
|
+
didReadInput = true;
|
|
107907
|
+
return false;
|
|
107908
|
+
}
|
|
107909
|
+
const symbol = reference?.resolvedSymbol;
|
|
107910
|
+
if (symbol?.kind === "const" && symbol.initializer && isSymbolStable(symbol) && !visitedSymbolIds.has(symbol.id)) {
|
|
107911
|
+
visitedSymbolIds.add(symbol.id);
|
|
107912
|
+
for (const initializer of getConstInitializerExpressions(symbol)) if (expressionReadsInput(initializer, inputReferences, scopes, visitedSymbolIds)) {
|
|
107913
|
+
didReadInput = true;
|
|
107914
|
+
return false;
|
|
107915
|
+
}
|
|
107916
|
+
}
|
|
107917
|
+
}
|
|
107918
|
+
if (!isNodeOfType(node, "MemberExpression")) return;
|
|
107919
|
+
const propertyName = getStaticPropertyName(node);
|
|
107920
|
+
const receiver = stripParenExpression(node.object);
|
|
107921
|
+
if (propertyName === null || !isNodeOfType(receiver, "Identifier")) return;
|
|
107922
|
+
const receiverReference = scopes.referenceFor(receiver);
|
|
107923
|
+
if (receiverReference && receiverReference.flag !== "write" && inputReferences.some((inputReference) => inputReference.isStable && inputReference.propertyName === propertyName && inputReference.symbolId === receiverReference.resolvedSymbol?.id)) {
|
|
107924
|
+
didReadInput = true;
|
|
107925
|
+
return false;
|
|
107926
|
+
}
|
|
107927
|
+
});
|
|
107928
|
+
return didReadInput;
|
|
107929
|
+
};
|
|
107930
|
+
const climbTransparentExpressionWrappers = (node) => {
|
|
107931
|
+
let expression = node;
|
|
107932
|
+
while (expression.parent && TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(expression.parent.type) && "expression" in expression.parent && expression.parent.expression === expression) expression = expression.parent;
|
|
107933
|
+
return expression;
|
|
107934
|
+
};
|
|
107935
|
+
const isProvenStaticCallableReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
107936
|
+
const reference = stripParenExpression(expression);
|
|
107937
|
+
if (isNodeOfType(reference, "MemberExpression")) return getImportedStaticReferenceKey(reference, scopes) !== null;
|
|
107938
|
+
if (!isNodeOfType(reference, "Identifier")) return false;
|
|
107939
|
+
const symbol = scopes.referenceFor(reference)?.resolvedSymbol;
|
|
107940
|
+
if (!symbol) return scopes.isGlobalReference(reference);
|
|
107941
|
+
if (!isSymbolStable(symbol) || visitedSymbolIds.has(symbol.id)) return false;
|
|
107942
|
+
if (symbol.kind === "import" || symbol.kind === "function") return true;
|
|
107943
|
+
if (symbol.kind !== "const" || !symbol.initializer) return false;
|
|
107944
|
+
visitedSymbolIds.add(symbol.id);
|
|
107945
|
+
const initializer = stripParenExpression(symbol.initializer);
|
|
107946
|
+
if (isFunctionLike$1(initializer)) return true;
|
|
107947
|
+
return isNodeOfType(initializer, "Identifier") || isNodeOfType(initializer, "MemberExpression") ? isProvenStaticCallableReference(initializer, scopes, visitedSymbolIds) : false;
|
|
107948
|
+
};
|
|
107949
|
+
const isProvenStaticCallCallee = (identifier, scopes) => {
|
|
107950
|
+
let callee = climbTransparentExpressionWrappers(identifier);
|
|
107951
|
+
while (isNodeOfType(callee.parent, "MemberExpression") && callee.parent.object === callee) callee = climbTransparentExpressionWrappers(callee.parent);
|
|
107952
|
+
return isNodeOfType(callee.parent, "CallExpression") && callee.parent.callee === callee && isProvenStaticCallableReference(callee, scopes);
|
|
107953
|
+
};
|
|
107954
|
+
const isInsideComparisonOperand = (identifier) => {
|
|
107955
|
+
let descendant = climbTransparentExpressionWrappers(identifier);
|
|
107956
|
+
let ancestor = descendant.parent;
|
|
107957
|
+
while (isNodeOfType(ancestor, "MemberExpression") && (ancestor.object === descendant || ancestor.property === descendant)) {
|
|
107958
|
+
descendant = climbTransparentExpressionWrappers(ancestor);
|
|
107959
|
+
ancestor = descendant.parent;
|
|
107960
|
+
}
|
|
107961
|
+
return isNodeOfType(ancestor, "BinaryExpression") && [
|
|
107962
|
+
"==",
|
|
107963
|
+
"!=",
|
|
107964
|
+
"===",
|
|
107965
|
+
"!==",
|
|
107966
|
+
"<",
|
|
107967
|
+
"<=",
|
|
107968
|
+
">",
|
|
107969
|
+
">=",
|
|
107970
|
+
"in",
|
|
107971
|
+
"instanceof"
|
|
107972
|
+
].includes(ancestor.operator);
|
|
107973
|
+
};
|
|
107974
|
+
const expressionReadsOnlyInput = (expression, inputReferences, scopes) => {
|
|
107975
|
+
if (!expressionReadsInput(expression, inputReferences, scopes)) return false;
|
|
107976
|
+
const selector = getFinalSequenceExpressionValue(expression);
|
|
107977
|
+
let hasUnrelatedRead = false;
|
|
107978
|
+
const visitedSymbolIds = /* @__PURE__ */ new Set();
|
|
107979
|
+
const inspectExpression = (candidate) => {
|
|
107980
|
+
walkAst(candidate, (node) => {
|
|
107981
|
+
if (hasUnrelatedRead) return false;
|
|
107982
|
+
if (node !== candidate && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
|
|
107983
|
+
if (!isNodeOfType(node, "Identifier")) return;
|
|
107984
|
+
const parent = node.parent;
|
|
107985
|
+
if (isNodeOfType(parent, "MemberExpression") && parent.property === node && !parent.computed || isNodeOfType(parent, "Property") && parent.key === node && !parent.computed || isProvenStaticCallCallee(node, scopes)) return;
|
|
107986
|
+
const symbol = scopes.referenceFor(node)?.resolvedSymbol;
|
|
107987
|
+
const isDirectInput = inputReferences.some((inputReference) => inputReference.isStable && inputReference.propertyName === null && inputReference.symbolId === symbol?.id);
|
|
107988
|
+
const isInputContainer = isNodeOfType(parent, "MemberExpression") && parent.object === node && inputReferences.some((inputReference) => inputReference.isStable && inputReference.propertyName === getStaticPropertyName(parent) && inputReference.symbolId === symbol?.id);
|
|
107989
|
+
if (isDirectInput || isInputContainer) return;
|
|
107990
|
+
if (symbol?.kind === "import" && isInsideComparisonOperand(node)) return;
|
|
107991
|
+
if (symbol?.kind === "const" && symbol.initializer && isSymbolStable(symbol) && visitedSymbolIds.has(symbol.id)) return;
|
|
107992
|
+
if (symbol?.kind === "const" && symbol.initializer && isSymbolStable(symbol) && !visitedSymbolIds.has(symbol.id)) {
|
|
107993
|
+
visitedSymbolIds.add(symbol.id);
|
|
107994
|
+
const initializers = getConstInitializerExpressions(symbol);
|
|
107995
|
+
if (!initializers.some((initializer) => expressionReadsInput(initializer, inputReferences, scopes)) && initializers.some((initializer) => readStaticSelectorTruthiness(initializer, scopes) === null)) {
|
|
107996
|
+
hasUnrelatedRead = true;
|
|
107997
|
+
return false;
|
|
107998
|
+
}
|
|
107999
|
+
for (const initializer of initializers) inspectExpression(initializer);
|
|
108000
|
+
return;
|
|
108001
|
+
}
|
|
108002
|
+
hasUnrelatedRead = true;
|
|
108003
|
+
return false;
|
|
108004
|
+
});
|
|
108005
|
+
};
|
|
108006
|
+
inspectExpression(selector);
|
|
108007
|
+
return !hasUnrelatedRead;
|
|
108008
|
+
};
|
|
108009
|
+
const hasInputDependentRenderedRootDifference = (expression, inputReferences, scopes) => {
|
|
108010
|
+
const alternativesHaveInputDependentDifference = (alternatives, eligibleFactKeys) => {
|
|
108011
|
+
for (let firstAlternativeIndex = 0; firstAlternativeIndex < alternatives.length; firstAlternativeIndex += 1) {
|
|
108012
|
+
const firstAlternative = alternatives[firstAlternativeIndex];
|
|
108013
|
+
for (let secondAlternativeIndex = firstAlternativeIndex + 1; secondAlternativeIndex < alternatives.length; secondAlternativeIndex += 1) {
|
|
108014
|
+
const secondAlternative = alternatives[secondAlternativeIndex];
|
|
108015
|
+
if (JSON.stringify(firstAlternative.roots) === JSON.stringify(secondAlternative.roots)) continue;
|
|
108016
|
+
let hasInputDependentDifference = false;
|
|
108017
|
+
let hasAmbientConflict = false;
|
|
108018
|
+
for (const [key, firstFact] of firstAlternative.facts) {
|
|
108019
|
+
const secondFact = secondAlternative.facts.get(key);
|
|
108020
|
+
if (!secondFact || firstFact.outcome === secondFact.outcome) continue;
|
|
108021
|
+
if ((!eligibleFactKeys || eligibleFactKeys.has(key)) && expressionReadsOnlyInput(firstFact.selector, inputReferences, scopes) && expressionReadsOnlyInput(secondFact.selector, inputReferences, scopes)) hasInputDependentDifference = true;
|
|
108022
|
+
else hasAmbientConflict = true;
|
|
108023
|
+
}
|
|
108024
|
+
if (hasInputDependentDifference && !hasAmbientConflict) return true;
|
|
108025
|
+
}
|
|
108026
|
+
}
|
|
108027
|
+
return false;
|
|
108028
|
+
};
|
|
108029
|
+
const renderedExpression = getFinalSequenceExpressionValue(expression);
|
|
108030
|
+
const fragmentChildren = getFlattenedFragmentChildren(renderedExpression, scopes);
|
|
108031
|
+
if (fragmentChildren !== null) {
|
|
108032
|
+
const childAlternatives = [];
|
|
108033
|
+
for (const child of fragmentChildren) {
|
|
108034
|
+
const alternatives = getStaticRenderedRootAlternatives(child, scopes);
|
|
108035
|
+
if (alternatives === null) return false;
|
|
108036
|
+
childAlternatives.push(alternatives);
|
|
108037
|
+
}
|
|
108038
|
+
const futureFactKeyCounts = /* @__PURE__ */ new Map();
|
|
108039
|
+
for (const alternatives of childAlternatives) {
|
|
108040
|
+
const childFactKeys = /* @__PURE__ */ new Set();
|
|
108041
|
+
for (const alternative of alternatives) for (const key of alternative.facts.keys()) childFactKeys.add(key);
|
|
108042
|
+
for (const key of childFactKeys) futureFactKeyCounts.set(key, (futureFactKeyCounts.get(key) ?? 0) + 1);
|
|
108043
|
+
}
|
|
108044
|
+
let prefixAlternatives = [{
|
|
108045
|
+
facts: /* @__PURE__ */ new Map(),
|
|
108046
|
+
roots: []
|
|
108047
|
+
}];
|
|
108048
|
+
for (const alternatives of childAlternatives) {
|
|
108049
|
+
const currentFactKeys = /* @__PURE__ */ new Set();
|
|
108050
|
+
for (const alternative of alternatives) for (const key of alternative.facts.keys()) currentFactKeys.add(key);
|
|
108051
|
+
for (const key of currentFactKeys) futureFactKeyCounts.set(key, (futureFactKeyCounts.get(key) ?? 1) - 1);
|
|
108052
|
+
const mergedAlternatives = mergeRenderedRootShapeAlternatives(prefixAlternatives, alternatives);
|
|
108053
|
+
if (mergedAlternatives === null) return false;
|
|
108054
|
+
prefixAlternatives = mergedAlternatives;
|
|
108055
|
+
const finalFactKeys = /* @__PURE__ */ new Set();
|
|
108056
|
+
for (const alternative of prefixAlternatives) for (const key of alternative.facts.keys()) if (futureFactKeyCounts.get(key) === 0) finalFactKeys.add(key);
|
|
108057
|
+
if (finalFactKeys.size > 0 && alternativesHaveInputDependentDifference(prefixAlternatives, finalFactKeys)) return true;
|
|
108058
|
+
const remainingAlternatives = forgetFinalizedRenderedRootFacts(prefixAlternatives, futureFactKeyCounts);
|
|
108059
|
+
if (remainingAlternatives === null) return false;
|
|
108060
|
+
prefixAlternatives = remainingAlternatives;
|
|
108061
|
+
}
|
|
108062
|
+
return false;
|
|
108063
|
+
}
|
|
108064
|
+
const alternatives = getStaticRenderedRootAlternatives(renderedExpression, scopes);
|
|
108065
|
+
return alternatives !== null && alternativesHaveInputDependentDifference(alternatives);
|
|
108066
|
+
};
|
|
108067
|
+
const getKnownReturnedRootNames = (expression, scopes) => {
|
|
108068
|
+
const returnedExpression = getFinalSequenceExpressionValue(expression);
|
|
108069
|
+
if (isNodeOfType(returnedExpression, "JSXElement") || isNodeOfType(returnedExpression, "JSXFragment")) {
|
|
108070
|
+
const rootNames = getRenderedRootNames(returnedExpression, scopes);
|
|
108071
|
+
return rootNames === null ? null : new Set(rootNames);
|
|
108072
|
+
}
|
|
108073
|
+
if (isStaticallyEmptyJsxChild(returnedExpression)) return new Set([EMPTY_RENDERED_ROOT_NAME]);
|
|
108074
|
+
let branches;
|
|
108075
|
+
if (isNodeOfType(returnedExpression, "ConditionalExpression")) branches = [returnedExpression.consequent, returnedExpression.alternate];
|
|
108076
|
+
else if (isNodeOfType(returnedExpression, "LogicalExpression")) branches = getStaticLogicalExpressionResultBranches(returnedExpression);
|
|
108077
|
+
else return null;
|
|
108078
|
+
const rootNames = /* @__PURE__ */ new Set();
|
|
108079
|
+
for (const branch of branches) {
|
|
108080
|
+
const branchRootNames = getKnownReturnedRootNames(branch, scopes);
|
|
108081
|
+
if (branchRootNames === null) return null;
|
|
108082
|
+
for (const rootName of branchRootNames) rootNames.add(rootName);
|
|
108083
|
+
}
|
|
108084
|
+
return rootNames;
|
|
108085
|
+
};
|
|
108086
|
+
const collectKnownStatementRootNames = (statement, scopes) => {
|
|
108087
|
+
const rootNames = /* @__PURE__ */ new Set();
|
|
108088
|
+
let hasUnknownRoot = false;
|
|
108089
|
+
walkAst(statement, (node) => {
|
|
108090
|
+
if (hasUnknownRoot) return false;
|
|
108091
|
+
if (node !== statement && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
|
|
108092
|
+
if (!isNodeOfType(node, "ReturnStatement")) return;
|
|
108093
|
+
const returnedRootNames = node.argument ? getKnownReturnedRootNames(node.argument, scopes) : new Set([EMPTY_RENDERED_ROOT_NAME]);
|
|
108094
|
+
if (returnedRootNames === null) {
|
|
108095
|
+
hasUnknownRoot = true;
|
|
108096
|
+
return false;
|
|
108097
|
+
}
|
|
108098
|
+
for (const rootName of returnedRootNames) rootNames.add(rootName);
|
|
108099
|
+
return false;
|
|
108100
|
+
});
|
|
108101
|
+
return hasUnknownRoot || rootNames.size === 0 ? null : rootNames;
|
|
108102
|
+
};
|
|
108103
|
+
const collectKnownContinuationRootNames = (ifStatement, scopes) => {
|
|
108104
|
+
const parent = ifStatement.parent;
|
|
108105
|
+
if (!parent || !isNodeOfType(parent, "BlockStatement")) return null;
|
|
108106
|
+
const statementIndex = parent.body.findIndex((statement) => statement === ifStatement);
|
|
108107
|
+
if (statementIndex < 0) return null;
|
|
108108
|
+
const rootNames = /* @__PURE__ */ new Set();
|
|
108109
|
+
for (const statement of parent.body.slice(statementIndex + 1)) {
|
|
108110
|
+
const statementRootNames = collectKnownStatementRootNames(statement, scopes);
|
|
108111
|
+
if (statementRootNames) for (const rootName of statementRootNames) rootNames.add(rootName);
|
|
108112
|
+
if (isUnconditionallyTerminalStatement(statement)) break;
|
|
108113
|
+
}
|
|
108114
|
+
return rootNames.size === 0 ? null : rootNames;
|
|
108115
|
+
};
|
|
108116
|
+
const getDirectStatementRootAlternatives = (statement, scopes) => {
|
|
108117
|
+
if (isNodeOfType(statement, "ReturnStatement")) return statement.argument ? getStaticRenderedRootAlternatives(statement.argument, scopes) : [{
|
|
108118
|
+
facts: /* @__PURE__ */ new Map(),
|
|
108119
|
+
roots: []
|
|
108120
|
+
}];
|
|
108121
|
+
if (isNodeOfType(statement, "BlockStatement") && statement.body.length === 1) return getDirectStatementRootAlternatives(statement.body[0], scopes);
|
|
108122
|
+
return null;
|
|
108123
|
+
};
|
|
108124
|
+
const getContinuationRootAlternatives = (ifStatement, scopes) => {
|
|
108125
|
+
const parent = ifStatement.parent;
|
|
108126
|
+
if (!parent || !isNodeOfType(parent, "BlockStatement")) return null;
|
|
108127
|
+
const statementIndex = parent.body.findIndex((statement) => statement === ifStatement);
|
|
108128
|
+
if (statementIndex < 0) return null;
|
|
108129
|
+
for (const statement of parent.body.slice(statementIndex + 1)) {
|
|
108130
|
+
const alternatives = getDirectStatementRootAlternatives(statement, scopes);
|
|
108131
|
+
if (alternatives !== null) return alternatives;
|
|
108132
|
+
if (isUnconditionallyTerminalStatement(statement)) return null;
|
|
108133
|
+
}
|
|
108134
|
+
return null;
|
|
108135
|
+
};
|
|
108136
|
+
const renderedRootFactsAreCompatible = (firstFacts, secondFacts) => {
|
|
108137
|
+
for (const [key, firstFact] of firstFacts) {
|
|
108138
|
+
const secondFact = secondFacts.get(key);
|
|
108139
|
+
if (secondFact && secondFact.outcome !== firstFact.outcome) return false;
|
|
108140
|
+
}
|
|
108141
|
+
return true;
|
|
108142
|
+
};
|
|
108143
|
+
const hasDistinctKnownIfRootOutcomes = (ifStatement, scopes) => {
|
|
108144
|
+
if (!isUnconditionallyTerminalStatement(ifStatement.consequent)) return false;
|
|
108145
|
+
const consequentAlternatives = getDirectStatementRootAlternatives(ifStatement.consequent, scopes);
|
|
108146
|
+
const alternateAlternatives = ifStatement.alternate ? isUnconditionallyTerminalStatement(ifStatement.alternate) ? getDirectStatementRootAlternatives(ifStatement.alternate, scopes) : null : getContinuationRootAlternatives(ifStatement, scopes);
|
|
108147
|
+
if (consequentAlternatives && alternateAlternatives) return consequentAlternatives.some((consequentAlternative) => alternateAlternatives.some((alternateAlternative) => renderedRootFactsAreCompatible(consequentAlternative.facts, alternateAlternative.facts) && JSON.stringify(consequentAlternative.roots) !== JSON.stringify(alternateAlternative.roots)));
|
|
108148
|
+
const consequentRootNames = collectKnownStatementRootNames(ifStatement.consequent, scopes);
|
|
108149
|
+
const alternateRootNames = ifStatement.alternate ? isUnconditionallyTerminalStatement(ifStatement.alternate) ? collectKnownStatementRootNames(ifStatement.alternate, scopes) : null : collectKnownContinuationRootNames(ifStatement, scopes);
|
|
108150
|
+
if (!consequentRootNames || !alternateRootNames) return false;
|
|
108151
|
+
return [...consequentRootNames].some((rootName) => !alternateRootNames.has(rootName)) || [...alternateRootNames].some((rootName) => !consequentRootNames.has(rootName));
|
|
108152
|
+
};
|
|
108153
|
+
const hasDistinctKnownSwitchRootOutcomes = (switchStatement, scopes) => {
|
|
108154
|
+
if (!switchStatement.cases.some((switchCase) => switchCase.test === null) || !switchStatement.cases.every((switchCase) => switchCase.consequent.some(isUnconditionallyTerminalStatement))) return false;
|
|
108155
|
+
const caseRootNames = [];
|
|
108156
|
+
for (const switchCase of switchStatement.cases) {
|
|
108157
|
+
const rootNames = collectKnownStatementRootNames(switchCase, scopes);
|
|
108158
|
+
if (!rootNames) return false;
|
|
108159
|
+
caseRootNames.push(rootNames);
|
|
108160
|
+
}
|
|
108161
|
+
for (let firstCaseIndex = 0; firstCaseIndex < caseRootNames.length; firstCaseIndex += 1) {
|
|
108162
|
+
const firstRootNames = caseRootNames[firstCaseIndex];
|
|
108163
|
+
for (let secondCaseIndex = firstCaseIndex + 1; secondCaseIndex < caseRootNames.length; secondCaseIndex += 1) {
|
|
108164
|
+
const secondRootNames = caseRootNames[secondCaseIndex];
|
|
108165
|
+
if ([...firstRootNames].every((rootName) => !secondRootNames.has(rootName))) return true;
|
|
108166
|
+
}
|
|
108167
|
+
}
|
|
108168
|
+
return false;
|
|
108169
|
+
};
|
|
108170
|
+
const readStaticSelectorTruthiness = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
|
|
108171
|
+
const selector = getFinalSequenceExpressionValue(expression);
|
|
108172
|
+
if (isStaticallyTruthyContainer(selector)) return true;
|
|
108173
|
+
if (isNodeOfType(selector, "Literal")) return Boolean(selector.value);
|
|
108174
|
+
if (isNodeOfType(selector, "Identifier")) {
|
|
108175
|
+
const symbol = scopes.symbolFor(selector);
|
|
108176
|
+
if (symbol && (symbol.kind === "function" || symbol.kind === "class") && isSymbolStable(symbol)) return true;
|
|
108177
|
+
if (symbol?.kind === "const" && symbol.initializer && isSymbolStable(symbol) && getSymbolVariableDeclarator(symbol)?.id === symbol.bindingIdentifier && !visitedSymbolIds.has(symbol.id)) {
|
|
108178
|
+
visitedSymbolIds.add(symbol.id);
|
|
108179
|
+
return readStaticSelectorTruthiness(symbol.initializer, scopes, visitedSymbolIds);
|
|
108180
|
+
}
|
|
108181
|
+
}
|
|
108182
|
+
if (isNodeOfType(selector, "UnaryExpression") && selector.operator === "!") {
|
|
108183
|
+
const argumentTruthiness = readStaticSelectorTruthiness(selector.argument, scopes, visitedSymbolIds);
|
|
108184
|
+
return argumentTruthiness === null ? null : !argumentTruthiness;
|
|
108185
|
+
}
|
|
108186
|
+
if (!isNodeOfType(selector, "LogicalExpression")) return null;
|
|
108187
|
+
const leftTruthiness = readStaticSelectorTruthiness(selector.left, scopes, new Set(visitedSymbolIds));
|
|
108188
|
+
const rightTruthiness = readStaticSelectorTruthiness(selector.right, scopes, new Set(visitedSymbolIds));
|
|
108189
|
+
if (selector.operator === "&&") {
|
|
108190
|
+
if (leftTruthiness === false || rightTruthiness === false) return false;
|
|
108191
|
+
return leftTruthiness === true ? rightTruthiness : null;
|
|
108192
|
+
}
|
|
108193
|
+
if (selector.operator === "||") {
|
|
108194
|
+
if (leftTruthiness === true || rightTruthiness === true) return true;
|
|
108195
|
+
return leftTruthiness === false ? rightTruthiness : null;
|
|
108196
|
+
}
|
|
108197
|
+
return leftTruthiness;
|
|
108198
|
+
};
|
|
108199
|
+
const analyzeReturnedExpressionSelections = (expression, inputReferences, scopes) => {
|
|
108200
|
+
const returnedExpression = getFinalSequenceExpressionValue(expression);
|
|
108201
|
+
if (isNodeOfType(returnedExpression, "JSXExpressionContainer")) return analyzeReturnedExpressionSelections(returnedExpression.expression, inputReferences, scopes);
|
|
108202
|
+
if (isNodeOfType(returnedExpression, "JSXFragment") || isNodeOfType(returnedExpression, "JSXElement") && isJsxFragmentElement(returnedExpression.openingElement, scopes)) {
|
|
108203
|
+
let hasInputDependentSelection = false;
|
|
108204
|
+
let hasUnrelatedSelection = false;
|
|
108205
|
+
for (const child of returnedExpression.children) {
|
|
108206
|
+
const childAnalysis = analyzeReturnedExpressionSelections(child, inputReferences, scopes);
|
|
108207
|
+
hasInputDependentSelection ||= childAnalysis.hasInputDependentSelection;
|
|
108208
|
+
hasUnrelatedSelection ||= childAnalysis.hasUnrelatedSelection;
|
|
108209
|
+
}
|
|
108210
|
+
return {
|
|
108211
|
+
hasInputDependentSelection,
|
|
108212
|
+
hasProvenInputDependentRootSelection: hasInputDependentRenderedRootDifference(returnedExpression, inputReferences, scopes),
|
|
108213
|
+
hasUnrelatedSelection
|
|
108214
|
+
};
|
|
108215
|
+
}
|
|
108216
|
+
if (isNodeOfType(returnedExpression, "ConditionalExpression")) {
|
|
108217
|
+
const staticTestValue = readStaticSelectorTruthiness(returnedExpression.test, scopes);
|
|
108218
|
+
if (staticTestValue !== null) return analyzeReturnedExpressionSelections(staticTestValue ? returnedExpression.consequent : returnedExpression.alternate, inputReferences, scopes);
|
|
108219
|
+
const consequentAnalysis = analyzeReturnedExpressionSelections(returnedExpression.consequent, inputReferences, scopes);
|
|
108220
|
+
const alternateAnalysis = analyzeReturnedExpressionSelections(returnedExpression.alternate, inputReferences, scopes);
|
|
108221
|
+
const selectorReadsInput = expressionReadsOnlyInput(returnedExpression.test, inputReferences, scopes);
|
|
108222
|
+
const selectedRootNames = getKnownReturnedRootNames(returnedExpression, scopes);
|
|
108223
|
+
return {
|
|
108224
|
+
hasInputDependentSelection: selectorReadsInput || consequentAnalysis.hasInputDependentSelection || alternateAnalysis.hasInputDependentSelection,
|
|
108225
|
+
hasProvenInputDependentRootSelection: selectorReadsInput && selectedRootNames !== null && selectedRootNames.size > 1 || consequentAnalysis.hasProvenInputDependentRootSelection || alternateAnalysis.hasProvenInputDependentRootSelection,
|
|
108226
|
+
hasUnrelatedSelection: !selectorReadsInput && (selectedRootNames === null || selectedRootNames.size > 1) || consequentAnalysis.hasUnrelatedSelection || alternateAnalysis.hasUnrelatedSelection
|
|
108227
|
+
};
|
|
108228
|
+
}
|
|
108229
|
+
if (isNodeOfType(returnedExpression, "LogicalExpression")) {
|
|
108230
|
+
const resultBranches = getStaticLogicalExpressionResultBranches(returnedExpression);
|
|
108231
|
+
if (resultBranches.length < 2) {
|
|
108232
|
+
const onlyResult = resultBranches[0];
|
|
108233
|
+
return onlyResult ? analyzeReturnedExpressionSelections(onlyResult, inputReferences, scopes) : {
|
|
108234
|
+
hasInputDependentSelection: false,
|
|
108235
|
+
hasProvenInputDependentRootSelection: false,
|
|
108236
|
+
hasUnrelatedSelection: false
|
|
108237
|
+
};
|
|
108238
|
+
}
|
|
108239
|
+
const leftAnalysis = analyzeReturnedExpressionSelections(returnedExpression.left, inputReferences, scopes);
|
|
108240
|
+
const rightAnalysis = analyzeReturnedExpressionSelections(returnedExpression.right, inputReferences, scopes);
|
|
108241
|
+
const selectorReadsInput = expressionReadsOnlyInput(returnedExpression.left, inputReferences, scopes);
|
|
108242
|
+
const selectedRootNames = getKnownReturnedRootNames(returnedExpression, scopes);
|
|
108243
|
+
return {
|
|
108244
|
+
hasInputDependentSelection: selectorReadsInput || leftAnalysis.hasInputDependentSelection || rightAnalysis.hasInputDependentSelection,
|
|
108245
|
+
hasProvenInputDependentRootSelection: selectorReadsInput && selectedRootNames !== null && selectedRootNames.size > 1 || leftAnalysis.hasProvenInputDependentRootSelection || rightAnalysis.hasProvenInputDependentRootSelection,
|
|
108246
|
+
hasUnrelatedSelection: !selectorReadsInput && (selectedRootNames === null || selectedRootNames.size > 1) || leftAnalysis.hasUnrelatedSelection || rightAnalysis.hasUnrelatedSelection
|
|
108247
|
+
};
|
|
108248
|
+
}
|
|
108249
|
+
if (isNodeOfType(returnedExpression, "CallExpression") && isReactApiCall(returnedExpression, "createElement", scopes, {
|
|
108250
|
+
allowGlobalReactNamespace: true,
|
|
108251
|
+
resolveNamedAliases: true
|
|
108252
|
+
})) {
|
|
108253
|
+
const componentArgument = returnedExpression.arguments[0];
|
|
108254
|
+
if (componentArgument && !isNodeOfType(componentArgument, "SpreadElement")) return analyzeReturnedExpressionSelections(componentArgument, inputReferences, scopes);
|
|
108255
|
+
}
|
|
108256
|
+
return {
|
|
108257
|
+
hasInputDependentSelection: false,
|
|
108258
|
+
hasProvenInputDependentRootSelection: false,
|
|
108259
|
+
hasUnrelatedSelection: false
|
|
108260
|
+
};
|
|
108261
|
+
};
|
|
108262
|
+
const analyzeFunctionInputSelections = (functionNode, inputReferences, scopes) => {
|
|
108263
|
+
if (!isFunctionLike$1(functionNode)) return {
|
|
108264
|
+
hasInputDependentSelection: false,
|
|
108265
|
+
hasProvenInputDependentRootSelection: false,
|
|
108266
|
+
hasUnrelatedSelection: false
|
|
108267
|
+
};
|
|
108268
|
+
if (!isNodeOfType(functionNode.body, "BlockStatement")) return analyzeReturnedExpressionSelections(functionNode.body, inputReferences, scopes);
|
|
108269
|
+
let hasInputDependentSelection = false;
|
|
108270
|
+
let hasProvenInputDependentRootSelection = false;
|
|
108271
|
+
let hasUnrelatedSelection = false;
|
|
108272
|
+
const analyzedAncestors = /* @__PURE__ */ new Set();
|
|
108273
|
+
for (const returnStatement of getReachableFunctionReturnStatements(functionNode, scopes)) {
|
|
108274
|
+
const returnedRootNames = returnStatement.argument ? getKnownReturnedRootNames(returnStatement.argument, scopes) : /* @__PURE__ */ new Set();
|
|
108275
|
+
if (returnStatement.argument) {
|
|
108276
|
+
const returnAnalysis = analyzeReturnedExpressionSelections(returnStatement.argument, inputReferences, scopes);
|
|
108277
|
+
hasInputDependentSelection ||= returnAnalysis.hasInputDependentSelection;
|
|
108278
|
+
hasProvenInputDependentRootSelection ||= returnAnalysis.hasProvenInputDependentRootSelection;
|
|
108279
|
+
hasUnrelatedSelection ||= returnAnalysis.hasUnrelatedSelection;
|
|
108280
|
+
}
|
|
108281
|
+
if (returnedRootNames && [...returnedRootNames].every((rootName) => rootName === EMPTY_RENDERED_ROOT_NAME)) continue;
|
|
108282
|
+
let ancestor = returnStatement.parent;
|
|
108283
|
+
while (ancestor && ancestor !== functionNode) {
|
|
108284
|
+
if (analyzedAncestors.has(ancestor)) break;
|
|
108285
|
+
analyzedAncestors.add(ancestor);
|
|
108286
|
+
let selector = null;
|
|
108287
|
+
if (isNodeOfType(ancestor, "IfStatement")) selector = ancestor.test;
|
|
108288
|
+
else if (isNodeOfType(ancestor, "SwitchStatement")) selector = ancestor.discriminant;
|
|
108289
|
+
else if (isNodeOfType(ancestor, "TryStatement") || isNodeOfType(ancestor, "ForStatement") || isNodeOfType(ancestor, "ForInStatement") || isNodeOfType(ancestor, "ForOfStatement") || isNodeOfType(ancestor, "WhileStatement") || isNodeOfType(ancestor, "DoWhileStatement")) hasUnrelatedSelection = true;
|
|
108290
|
+
if (selector) if (expressionReadsOnlyInput(selector, inputReferences, scopes)) {
|
|
108291
|
+
hasInputDependentSelection = true;
|
|
108292
|
+
if (isNodeOfType(ancestor, "IfStatement") && hasDistinctKnownIfRootOutcomes(ancestor, scopes) || isNodeOfType(ancestor, "SwitchStatement") && hasDistinctKnownSwitchRootOutcomes(ancestor, scopes)) hasProvenInputDependentRootSelection = true;
|
|
108293
|
+
} else hasUnrelatedSelection = true;
|
|
108294
|
+
ancestor = ancestor.parent;
|
|
108295
|
+
}
|
|
108296
|
+
}
|
|
108297
|
+
return {
|
|
108298
|
+
hasInputDependentSelection,
|
|
108299
|
+
hasProvenInputDependentRootSelection,
|
|
108300
|
+
hasUnrelatedSelection
|
|
108301
|
+
};
|
|
108302
|
+
};
|
|
108303
|
+
const expressionHasOnlyInputDependentSelections = (expression, inputReferences, scopes) => {
|
|
108304
|
+
const selectionAnalysis = analyzeReturnedExpressionSelections(expression, inputReferences, scopes);
|
|
108305
|
+
return selectionAnalysis.hasInputDependentSelection && !selectionAnalysis.hasUnrelatedSelection;
|
|
108306
|
+
};
|
|
108307
|
+
const functionHasOnlyInputDependentSelections = (functionNode, inputReferences, scopes) => {
|
|
108308
|
+
const selectionAnalysis = analyzeFunctionInputSelections(functionNode, inputReferences, scopes);
|
|
108309
|
+
return selectionAnalysis.hasInputDependentSelection && !selectionAnalysis.hasUnrelatedSelection;
|
|
108310
|
+
};
|
|
108311
|
+
const getComponentExpressionIdentity = (expression, scopes) => {
|
|
108312
|
+
const componentExpression = stripParenExpression(expression);
|
|
108313
|
+
if (isNodeOfType(componentExpression, "Literal")) return typeof componentExpression.value === "string" ? `intrinsic:${componentExpression.value}` : null;
|
|
108314
|
+
if (isNodeOfType(componentExpression, "Identifier")) return getComponentReferenceIdentity(componentExpression, scopes);
|
|
108315
|
+
if (!isNodeOfType(componentExpression, "MemberExpression")) return null;
|
|
108316
|
+
return getComponentReferenceIdentity(componentExpression, scopes);
|
|
108317
|
+
};
|
|
108318
|
+
const collectStaticComponentIdentities = (expression, identities, scopes) => {
|
|
108319
|
+
const componentExpression = getFinalSequenceExpressionValue(expression);
|
|
108320
|
+
if (isNodeOfType(componentExpression, "ConditionalExpression")) return collectStaticComponentIdentities(componentExpression.consequent, identities, scopes) && collectStaticComponentIdentities(componentExpression.alternate, identities, scopes);
|
|
108321
|
+
if (isNodeOfType(componentExpression, "LogicalExpression")) return getStaticLogicalExpressionResultBranches(componentExpression).every((resultBranch) => collectStaticComponentIdentities(resultBranch, identities, scopes));
|
|
108322
|
+
const identity = getComponentExpressionIdentity(componentExpression, scopes);
|
|
108323
|
+
if (identity === null) return false;
|
|
108324
|
+
identities.add(identity);
|
|
108325
|
+
return true;
|
|
108326
|
+
};
|
|
108327
|
+
const isReactFragmentReference = (expression, scopes) => {
|
|
108328
|
+
const fragmentExpression = stripParenExpression(expression);
|
|
108329
|
+
const componentIdentity = getComponentReferenceIdentity(fragmentExpression, scopes);
|
|
108330
|
+
if (componentIdentity === "import:react:Fragment" || componentIdentity === "import:react:default.Fragment") return true;
|
|
108331
|
+
if (isNodeOfType(fragmentExpression, "Identifier")) {
|
|
108332
|
+
const symbol = resolveConstIdentifierAlias(fragmentExpression, scopes);
|
|
108333
|
+
return Boolean(symbol && isImportedFromReact(symbol) && getImportedName(symbol.declarationNode) === "Fragment");
|
|
108334
|
+
}
|
|
108335
|
+
if (!isNodeOfType(fragmentExpression, "MemberExpression") || getStaticPropertyName(fragmentExpression) !== "Fragment") return false;
|
|
108336
|
+
const receiver = stripParenExpression(fragmentExpression.object);
|
|
108337
|
+
return Boolean(isNodeOfType(receiver, "Identifier") && (isReactNamespaceImport(receiver, scopes) || receiver.name === "React" && scopes.isGlobalReference(receiver)));
|
|
108338
|
+
};
|
|
108339
|
+
const getForwardedInput = (expression, inputReferences, scopes) => {
|
|
108340
|
+
const forwardedExpression = stripParenExpression(expression);
|
|
108341
|
+
if (isNodeOfType(forwardedExpression, "Identifier")) {
|
|
108342
|
+
const reference = scopes.referenceFor(forwardedExpression);
|
|
108343
|
+
if (!reference?.resolvedSymbol) return null;
|
|
108344
|
+
const directInput = inputReferences.find((inputReference) => inputReference.isStable && inputReference.propertyName === null && inputReference.symbolId === reference.resolvedSymbol?.id);
|
|
108345
|
+
if (directInput) return {
|
|
108346
|
+
inputNames: new Set([directInput.inputName]),
|
|
108347
|
+
isWholeContainer: false
|
|
108348
|
+
};
|
|
108349
|
+
const containedInputNames = /* @__PURE__ */ new Set();
|
|
108350
|
+
for (const inputReference of inputReferences) if (inputReference.isStable && inputReference.propertyName !== null && inputReference.symbolId === reference.resolvedSymbol.id) containedInputNames.add(inputReference.inputName);
|
|
108351
|
+
return containedInputNames.size > 0 ? {
|
|
108352
|
+
inputNames: containedInputNames,
|
|
108353
|
+
isWholeContainer: true
|
|
108354
|
+
} : null;
|
|
108355
|
+
}
|
|
108356
|
+
if (!isNodeOfType(forwardedExpression, "MemberExpression")) return null;
|
|
108357
|
+
const propertyName = getStaticPropertyName(forwardedExpression);
|
|
108358
|
+
const receiver = stripParenExpression(forwardedExpression.object);
|
|
108359
|
+
if (propertyName === null || !isNodeOfType(receiver, "Identifier")) return null;
|
|
108360
|
+
const receiverReference = scopes.referenceFor(receiver);
|
|
108361
|
+
const matchedInput = inputReferences.find((inputReference) => inputReference.isStable && inputReference.propertyName === propertyName && inputReference.symbolId === receiverReference?.resolvedSymbol?.id);
|
|
108362
|
+
return matchedInput ? {
|
|
108363
|
+
inputNames: new Set([matchedInput.inputName]),
|
|
108364
|
+
isWholeContainer: false
|
|
108365
|
+
} : null;
|
|
108366
|
+
};
|
|
108367
|
+
const getParameterInputReferences = (parameter, forwardedInput, scopes) => {
|
|
108368
|
+
const unwrappedParameter = stripParenExpression(parameter);
|
|
108369
|
+
if (isNodeOfType(unwrappedParameter, "Identifier")) {
|
|
108370
|
+
if (forwardedInput.isWholeContainer) return [];
|
|
108371
|
+
const symbol = scopes.symbolFor(unwrappedParameter);
|
|
108372
|
+
const inputName = [...forwardedInput.inputNames][0];
|
|
108373
|
+
return symbol && inputName ? [{
|
|
108374
|
+
inputName,
|
|
108375
|
+
isStable: isSymbolStable(symbol),
|
|
108376
|
+
propertyName: null,
|
|
108377
|
+
symbolId: symbol.id
|
|
108378
|
+
}] : [];
|
|
108379
|
+
}
|
|
108380
|
+
if (!isNodeOfType(unwrappedParameter, "ObjectPattern")) return [];
|
|
108381
|
+
const references = [];
|
|
108382
|
+
for (const property of unwrappedParameter.properties) {
|
|
108383
|
+
if (!isNodeOfType(property, "Property")) continue;
|
|
108384
|
+
const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
|
|
108385
|
+
if (propertyName === null || forwardedInput.isWholeContainer && !forwardedInput.inputNames.has(propertyName)) continue;
|
|
108386
|
+
const bindingIdentifier = getPatternBindingIdentifier(property.value);
|
|
108387
|
+
const symbol = bindingIdentifier ? scopes.symbolFor(bindingIdentifier) : null;
|
|
108388
|
+
if (symbol) references.push({
|
|
108389
|
+
inputName: propertyName,
|
|
108390
|
+
isStable: isSymbolStable(symbol),
|
|
108391
|
+
propertyName: null,
|
|
108392
|
+
symbolId: symbol.id
|
|
108393
|
+
});
|
|
108394
|
+
}
|
|
108395
|
+
return references;
|
|
108396
|
+
};
|
|
108397
|
+
const getComponentPropInputReferences = (openingElement, componentFunction, inputReferences, scopes) => {
|
|
108398
|
+
if (!isFunctionLike$1(componentFunction)) return [];
|
|
108399
|
+
const parameter = componentFunction.params[0];
|
|
108400
|
+
if (!parameter) return [];
|
|
108401
|
+
const references = [];
|
|
108402
|
+
for (const attribute of openingElement.attributes) {
|
|
108403
|
+
if (!isNodeOfType(attribute, "JSXAttribute") || !isNodeOfType(attribute.name, "JSXIdentifier") || !isNodeOfType(attribute.value, "JSXExpressionContainer")) continue;
|
|
108404
|
+
const attributeName = attribute.name.name;
|
|
108405
|
+
if (getAuthoritativeJsxAttribute(openingElement.attributes, attributeName) !== attribute) continue;
|
|
108406
|
+
const forwardedInput = getForwardedInput(attribute.value.expression, inputReferences, scopes);
|
|
108407
|
+
if (!forwardedInput || forwardedInput.isWholeContainer) continue;
|
|
108408
|
+
const unwrappedParameter = stripParenExpression(parameter);
|
|
108409
|
+
if (isNodeOfType(unwrappedParameter, "Identifier")) {
|
|
108410
|
+
const symbol = scopes.symbolFor(unwrappedParameter);
|
|
108411
|
+
if (symbol) references.push({
|
|
108412
|
+
inputName: attributeName,
|
|
108413
|
+
isStable: isSymbolStable(symbol),
|
|
108414
|
+
propertyName: attributeName,
|
|
108415
|
+
symbolId: symbol.id
|
|
108416
|
+
});
|
|
108417
|
+
continue;
|
|
108418
|
+
}
|
|
108419
|
+
const bindingIdentifier = getObjectPatternPropertyBinding(unwrappedParameter, attributeName);
|
|
108420
|
+
const symbol = bindingIdentifier ? scopes.symbolFor(bindingIdentifier) : null;
|
|
108421
|
+
if (symbol) references.push({
|
|
108422
|
+
inputName: attributeName,
|
|
108423
|
+
isStable: isSymbolStable(symbol),
|
|
108424
|
+
propertyName: null,
|
|
108425
|
+
symbolId: symbol.id
|
|
108426
|
+
});
|
|
108427
|
+
}
|
|
108428
|
+
return references;
|
|
108429
|
+
};
|
|
108430
|
+
const resolveLocalComponentFunction = (openingElement, scopes) => {
|
|
108431
|
+
if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return null;
|
|
108432
|
+
const symbol = resolveConstIdentifierAlias(openingElement.name, scopes);
|
|
108433
|
+
if (!symbol || symbol.kind === "import" || !symbol.initializer || !isSymbolStable(symbol) || hasSymbolWriteBefore(symbol, openingElement.name, scopes)) return null;
|
|
108434
|
+
return unwrapProvenReactHocFunction(symbol.initializer, scopes);
|
|
108435
|
+
};
|
|
108436
|
+
const collectFunctionRenderedRootNames = (functionNode, names, scopes, analysis) => {
|
|
108437
|
+
if (!isFunctionLike$1(functionNode) || analysis.visitedFunctionNodes.has(functionNode)) return;
|
|
108438
|
+
analysis.visitedFunctionNodes.add(functionNode);
|
|
108439
|
+
if (!isNodeOfType(functionNode.body, "BlockStatement")) {
|
|
108440
|
+
collectReturnedJsxRootNames(functionNode.body, names, scopes, analysis);
|
|
108441
|
+
return;
|
|
108442
|
+
}
|
|
108443
|
+
for (const returnStatement of getReachableFunctionReturnStatements(functionNode, scopes)) if (returnStatement.argument) collectReturnedJsxRootNames(returnStatement.argument, names, scopes, analysis);
|
|
108444
|
+
};
|
|
108445
|
+
const collectLocalComponentRenderedRootNames = (element, names, scopes, analysis) => {
|
|
108446
|
+
if (!analysis.canFollowLocalRenderer) return false;
|
|
108447
|
+
const componentFunction = resolveLocalComponentFunction(element.openingElement, scopes);
|
|
108448
|
+
if (!componentFunction) return false;
|
|
108449
|
+
if (analysis.visitedFunctionNodes.has(componentFunction)) return true;
|
|
108450
|
+
const componentInputReferences = getComponentPropInputReferences(element.openingElement, componentFunction, analysis.inputReferences, scopes);
|
|
108451
|
+
if (componentInputReferences.length === 0 || !functionHasOnlyInputDependentSelections(componentFunction, componentInputReferences, scopes)) return false;
|
|
108452
|
+
const componentRootNames = /* @__PURE__ */ new Set();
|
|
108453
|
+
collectFunctionRenderedRootNames(componentFunction, componentRootNames, scopes, {
|
|
108454
|
+
canFollowLocalRenderer: false,
|
|
108455
|
+
inputReferences: componentInputReferences,
|
|
108456
|
+
visitedFunctionNodes: analysis.visitedFunctionNodes,
|
|
108457
|
+
visitedSymbolIds: analysis.visitedSymbolIds
|
|
108458
|
+
});
|
|
108459
|
+
if (componentRootNames.size === 0) return false;
|
|
108460
|
+
for (const componentRootName of componentRootNames) names.add(componentRootName);
|
|
108461
|
+
return true;
|
|
108462
|
+
};
|
|
108463
|
+
const collectItemSelectedComponentRootNames = (element, names, scopes, analysis) => {
|
|
108464
|
+
const componentName = element.openingElement.name;
|
|
108465
|
+
if (!isNodeOfType(componentName, "JSXIdentifier")) return false;
|
|
108466
|
+
const symbol = resolveConstIdentifierAlias(componentName, scopes);
|
|
108467
|
+
if (symbol?.kind !== "const" || !symbol.initializer || analysis.visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, componentName, scopes) || !expressionHasOnlyInputDependentSelections(symbol.initializer, analysis.inputReferences, scopes)) return false;
|
|
108468
|
+
analysis.visitedSymbolIds.add(symbol.id);
|
|
108469
|
+
const componentIdentities = /* @__PURE__ */ new Set();
|
|
108470
|
+
const didResolveEveryComponent = collectStaticComponentIdentities(symbol.initializer, componentIdentities, scopes);
|
|
108471
|
+
analysis.visitedSymbolIds.delete(symbol.id);
|
|
108472
|
+
if (!didResolveEveryComponent || componentIdentities.size === 0) return false;
|
|
108473
|
+
for (const componentIdentity of componentIdentities) names.add(componentIdentity);
|
|
108474
|
+
return true;
|
|
108475
|
+
};
|
|
108476
|
+
const collectReactCreateElementRootNames = (callExpression, names, scopes, analysis) => {
|
|
108477
|
+
if (!isReactApiCall(callExpression, "createElement", scopes, {
|
|
108478
|
+
allowGlobalReactNamespace: true,
|
|
108479
|
+
resolveNamedAliases: true
|
|
108480
|
+
})) return false;
|
|
108481
|
+
const componentArgument = callExpression.arguments[0];
|
|
108482
|
+
if (!componentArgument || isNodeOfType(componentArgument, "SpreadElement") || isReactFragmentReference(componentArgument, scopes)) return false;
|
|
108483
|
+
const componentIdentities = /* @__PURE__ */ new Set();
|
|
108484
|
+
if (!collectStaticComponentIdentities(componentArgument, componentIdentities, scopes)) return false;
|
|
108485
|
+
if (componentIdentities.size > 1 && !expressionHasOnlyInputDependentSelections(componentArgument, analysis.inputReferences, scopes)) return false;
|
|
108486
|
+
for (const componentIdentity of componentIdentities) names.add(componentIdentity);
|
|
108487
|
+
return componentIdentities.size > 0;
|
|
108488
|
+
};
|
|
108489
|
+
const collectLocalHelperRenderedRootNames = (callExpression, names, scopes, analysis) => {
|
|
108490
|
+
if (!analysis.canFollowLocalRenderer) return false;
|
|
108491
|
+
const helperCallee = stripParenExpression(callExpression.callee);
|
|
108492
|
+
if (!isNodeOfType(helperCallee, "Identifier")) return false;
|
|
108493
|
+
const helperFunction = resolveStaticLocalCallFunction(callExpression, scopes);
|
|
108494
|
+
if (!helperFunction || !isFunctionLike$1(helperFunction) || helperFunction.async || helperFunction.generator || analysis.visitedFunctionNodes.has(helperFunction)) return false;
|
|
108495
|
+
const helperSymbol = scopes.symbolFor(helperCallee);
|
|
108496
|
+
const functionSymbol = isNodeOfType(helperFunction, "FunctionDeclaration") && helperFunction.id ? scopes.symbolFor(helperFunction.id) : null;
|
|
108497
|
+
if (helperSymbol && !isSymbolStable(helperSymbol) || functionSymbol && !isSymbolStable(functionSymbol)) return false;
|
|
108498
|
+
const helperInputReferences = [];
|
|
108499
|
+
for (let argumentIndex = 0; argumentIndex < callExpression.arguments.length; argumentIndex += 1) {
|
|
108500
|
+
const argument = callExpression.arguments[argumentIndex];
|
|
108501
|
+
const parameter = helperFunction.params[argumentIndex];
|
|
108502
|
+
if (!argument || isNodeOfType(argument, "SpreadElement") || !parameter) continue;
|
|
108503
|
+
const forwardedInput = getForwardedInput(argument, analysis.inputReferences, scopes);
|
|
108504
|
+
if (!forwardedInput) continue;
|
|
108505
|
+
helperInputReferences.push(...getParameterInputReferences(parameter, forwardedInput, scopes));
|
|
108506
|
+
}
|
|
108507
|
+
if (helperInputReferences.length === 0 || !functionHasOnlyInputDependentSelections(helperFunction, helperInputReferences, scopes)) return false;
|
|
108508
|
+
const helperRootNames = /* @__PURE__ */ new Set();
|
|
108509
|
+
collectFunctionRenderedRootNames(helperFunction, helperRootNames, scopes, {
|
|
108510
|
+
canFollowLocalRenderer: false,
|
|
108511
|
+
inputReferences: helperInputReferences,
|
|
108512
|
+
visitedFunctionNodes: analysis.visitedFunctionNodes,
|
|
108513
|
+
visitedSymbolIds: analysis.visitedSymbolIds
|
|
108514
|
+
});
|
|
108515
|
+
if (helperRootNames.size === 0) return false;
|
|
108516
|
+
for (const helperRootName of helperRootNames) names.add(helperRootName);
|
|
108517
|
+
return true;
|
|
108518
|
+
};
|
|
108519
|
+
const collectReturnedJsxRootNames = (expression, names, scopes, analysis) => {
|
|
108520
|
+
const unwrappedExpression = getFinalSequenceExpressionValue(expression);
|
|
108521
|
+
if (isNodeOfType(unwrappedExpression, "JSXElement")) {
|
|
108522
|
+
if (collectItemSelectedComponentRootNames(unwrappedExpression, names, scopes, analysis) || collectLocalComponentRenderedRootNames(unwrappedExpression, names, scopes, analysis)) return;
|
|
108523
|
+
const rootNames = getRenderedRootNames(unwrappedExpression, scopes);
|
|
108524
|
+
if (rootNames) for (const rootName of rootNames) names.add(rootName);
|
|
108525
|
+
return;
|
|
108526
|
+
}
|
|
108527
|
+
if (isNodeOfType(unwrappedExpression, "JSXFragment")) {
|
|
108528
|
+
const rootNames = getRenderedRootNames(unwrappedExpression, scopes);
|
|
108529
|
+
if (rootNames) for (const rootName of rootNames) names.add(rootName);
|
|
108530
|
+
return;
|
|
108531
|
+
}
|
|
108532
|
+
if (isNodeOfType(unwrappedExpression, "CallExpression")) {
|
|
108533
|
+
if (collectReactCreateElementRootNames(unwrappedExpression, names, scopes, analysis) || collectLocalHelperRenderedRootNames(unwrappedExpression, names, scopes, analysis)) return;
|
|
108534
|
+
}
|
|
108535
|
+
if (isNodeOfType(unwrappedExpression, "Identifier")) {
|
|
108536
|
+
const symbol = resolveConstIdentifierAlias(unwrappedExpression, scopes);
|
|
108537
|
+
if (symbol?.kind === "const" && symbol.initializer && !analysis.visitedSymbolIds.has(symbol.id) && !hasSymbolWriteBefore(symbol, unwrappedExpression, scopes) && expressionHasOnlyInputDependentSelections(symbol.initializer, analysis.inputReferences, scopes)) {
|
|
108538
|
+
analysis.visitedSymbolIds.add(symbol.id);
|
|
108539
|
+
collectReturnedJsxRootNames(symbol.initializer, names, scopes, analysis);
|
|
108540
|
+
analysis.visitedSymbolIds.delete(symbol.id);
|
|
108541
|
+
return;
|
|
108542
|
+
}
|
|
108543
|
+
}
|
|
108544
|
+
if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
|
|
108545
|
+
const staticTestValue = readStaticSelectorTruthiness(unwrappedExpression.test, scopes);
|
|
108546
|
+
if (staticTestValue !== null) {
|
|
108547
|
+
collectReturnedJsxRootNames(staticTestValue ? unwrappedExpression.consequent : unwrappedExpression.alternate, names, scopes, analysis);
|
|
108548
|
+
return;
|
|
108549
|
+
}
|
|
108550
|
+
collectReturnedJsxRootNames(unwrappedExpression.consequent, names, scopes, analysis);
|
|
108551
|
+
collectReturnedJsxRootNames(unwrappedExpression.alternate, names, scopes, analysis);
|
|
108552
|
+
return;
|
|
108553
|
+
}
|
|
108554
|
+
if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
|
|
108555
|
+
for (const resultBranch of getStaticLogicalExpressionResultBranches(unwrappedExpression)) collectReturnedJsxRootNames(resultBranch, names, scopes, analysis);
|
|
108556
|
+
return;
|
|
108557
|
+
}
|
|
108558
|
+
};
|
|
108559
|
+
const resolveFunctionFromInitializer = (initializer, resultSymbol, scopes) => {
|
|
108560
|
+
const expression = stripParenExpression(initializer);
|
|
108561
|
+
if (isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "FunctionDeclaration")) return expression;
|
|
108562
|
+
const callbackArgument = getTransparentReactCallbackWrapperArgument(expression, resultSymbol, scopes);
|
|
108563
|
+
if (callbackArgument && (isNodeOfType(callbackArgument, "ArrowFunctionExpression") || isNodeOfType(callbackArgument, "FunctionExpression"))) return callbackArgument;
|
|
108564
|
+
return null;
|
|
108565
|
+
};
|
|
108566
|
+
const resolveRenderItemFunction = (attribute, scopes) => {
|
|
108567
|
+
if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
|
|
108568
|
+
const expression = stripParenExpression(attribute.value.expression);
|
|
108569
|
+
const directFunction = resolveFunctionFromInitializer(expression, null, scopes);
|
|
108570
|
+
if (directFunction) return directFunction;
|
|
108571
|
+
if (!isNodeOfType(expression, "Identifier")) return null;
|
|
108572
|
+
const localFunction = resolveExactLocalFunction(expression, scopes);
|
|
108573
|
+
if (localFunction) return localFunction;
|
|
108574
|
+
const symbol = scopes.symbolFor(expression);
|
|
108575
|
+
if (symbol?.kind !== "const" || !symbol.initializer) return null;
|
|
108576
|
+
return resolveFunctionFromInitializer(symbol.initializer, symbol, scopes);
|
|
108577
|
+
};
|
|
108578
|
+
const renderItemHasHeterogeneousRootTypes = (attribute, scopes, resultCache) => {
|
|
108579
|
+
const renderItemFunction = resolveRenderItemFunction(attribute, scopes);
|
|
108580
|
+
if (!renderItemFunction || !isNodeOfType(renderItemFunction, "ArrowFunctionExpression") && !isNodeOfType(renderItemFunction, "FunctionExpression") && !isNodeOfType(renderItemFunction, "FunctionDeclaration")) return false;
|
|
108581
|
+
const cachedResult = resultCache.get(renderItemFunction);
|
|
108582
|
+
if (cachedResult !== void 0) return cachedResult;
|
|
108583
|
+
const returnedRootNames = /* @__PURE__ */ new Set();
|
|
108584
|
+
const inputReferences = getRenderItemInputReferences(renderItemFunction, scopes);
|
|
108585
|
+
const selectionAnalysis = analyzeFunctionInputSelections(renderItemFunction, inputReferences, scopes);
|
|
108586
|
+
const returnStatements = isNodeOfType(renderItemFunction.body, "BlockStatement") ? getReachableFunctionReturnStatements(renderItemFunction, scopes) : [];
|
|
108587
|
+
if (selectionAnalysis.hasUnrelatedSelection && !selectionAnalysis.hasProvenInputDependentRootSelection || returnStatements.length > 1 && !selectionAnalysis.hasInputDependentSelection) {
|
|
108588
|
+
resultCache.set(renderItemFunction, false);
|
|
108589
|
+
return false;
|
|
108590
|
+
}
|
|
108591
|
+
collectFunctionRenderedRootNames(renderItemFunction, returnedRootNames, scopes, {
|
|
108592
|
+
canFollowLocalRenderer: true,
|
|
108593
|
+
inputReferences,
|
|
108594
|
+
visitedFunctionNodes: /* @__PURE__ */ new Set(),
|
|
108595
|
+
visitedSymbolIds: /* @__PURE__ */ new Set()
|
|
108596
|
+
});
|
|
108597
|
+
const hasHeterogeneousRootTypes = returnedRootNames.size > 1;
|
|
108598
|
+
resultCache.set(renderItemFunction, hasHeterogeneousRootTypes);
|
|
108599
|
+
return hasHeterogeneousRootTypes;
|
|
108600
|
+
};
|
|
107057
108601
|
const rnListRecyclableWithoutTypes = defineRule({
|
|
107058
108602
|
id: "rn-list-recyclable-without-types",
|
|
107059
108603
|
title: "Recyclable list missing getItemType",
|
|
107060
108604
|
tags: ["test-noise"],
|
|
107061
108605
|
requires: ["react-native"],
|
|
107062
108606
|
severity: "warn",
|
|
107063
|
-
recommendation: "When rows have different shapes, reused cells can show the wrong layout. Add `getItemType
|
|
108607
|
+
recommendation: "When rows have different shapes, reused cells can show the wrong layout. Add `getItemType` that returns a stable type for each row shape so FlashList keeps separate recycling pools.",
|
|
107064
108608
|
create: (context) => {
|
|
107065
108609
|
let fileImportsRecycler = false;
|
|
108610
|
+
const renderItemResultCache = /* @__PURE__ */ new WeakMap();
|
|
107066
108611
|
return {
|
|
107067
108612
|
Program(node) {
|
|
107068
108613
|
fileImportsRecycler = hasImportFromModules(node, RECYCLABLE_LIST_PACKAGE_SOURCES);
|
|
@@ -107071,20 +108616,20 @@ const rnListRecyclableWithoutTypes = defineRule({
|
|
|
107071
108616
|
if (!fileImportsRecycler) return;
|
|
107072
108617
|
const elementName = resolveJsxElementName(node);
|
|
107073
108618
|
if (!elementName) return;
|
|
107074
|
-
|
|
107075
|
-
|
|
107076
|
-
let
|
|
107077
|
-
|
|
107078
|
-
|
|
107079
|
-
|
|
107080
|
-
|
|
107081
|
-
|
|
107082
|
-
|
|
107083
|
-
|
|
107084
|
-
|
|
107085
|
-
if (hasRecycleItemsEnabled && !hasGetItemType) context.report({
|
|
108619
|
+
const canonicalRecyclerName = resolveImportedRecyclerName(node, context.scopes, { allowNamespaceMemberAccess: true });
|
|
108620
|
+
if (canonicalRecyclerName === null) return;
|
|
108621
|
+
let hasRecycleItemsEnabled = SHOPIFY_FLASH_LIST_COMPONENTS.has(canonicalRecyclerName) && isFlashListV2OrNewer(context);
|
|
108622
|
+
const recycleItemsAttribute = getAuthoritativeJsxAttribute(node.attributes, "recycleItems");
|
|
108623
|
+
if (recycleItemsAttribute) if (!recycleItemsAttribute.value) hasRecycleItemsEnabled = true;
|
|
108624
|
+
else if (isNodeOfType(recycleItemsAttribute.value, "JSXExpressionContainer") && isNodeOfType(recycleItemsAttribute.value.expression, "Literal")) hasRecycleItemsEnabled = recycleItemsAttribute.value.expression.value === true;
|
|
108625
|
+
else hasRecycleItemsEnabled = true;
|
|
108626
|
+
else if (node.attributes.some((attribute) => isNodeOfType(attribute, "JSXSpreadAttribute") && canExpressionOverrideJsxAttribute(attribute.argument, "recycleItems", true, context.scopes))) hasRecycleItemsEnabled = false;
|
|
108627
|
+
const hasPossibleSpreadGetItemType = node.attributes.some((attribute) => isNodeOfType(attribute, "JSXSpreadAttribute") && canExpressionOverrideJsxAttribute(attribute.argument, "getItemType", true, context.scopes));
|
|
108628
|
+
const hasGetItemType = getAuthoritativeJsxAttribute(node.attributes, "getItemType") !== null || hasPossibleSpreadGetItemType;
|
|
108629
|
+
const renderItemAttribute = getAuthoritativeJsxAttribute(node.attributes, "renderItem");
|
|
108630
|
+
if (hasRecycleItemsEnabled && !hasGetItemType && renderItemAttribute && renderItemHasHeterogeneousRootTypes(renderItemAttribute, context.scopes, renderItemResultCache)) context.report({
|
|
107086
108631
|
node,
|
|
107087
|
-
message: `Your users see rows of different shapes reuse the wrong cells when <${elementName}
|
|
108632
|
+
message: `Your users see rows of different shapes reuse the wrong cells when <${elementName}> recycles them without \`getItemType\`.`
|
|
107088
108633
|
});
|
|
107089
108634
|
}
|
|
107090
108635
|
};
|
|
@@ -107731,7 +109276,7 @@ const rnNoNonNativeNavigator = defineRule({
|
|
|
107731
109276
|
tags: ["test-noise"],
|
|
107732
109277
|
requires: ["react-native"],
|
|
107733
109278
|
severity: "warn",
|
|
107734
|
-
recommendation: "Use `@react-navigation/native-stack`
|
|
109279
|
+
recommendation: "Use `@react-navigation/native-stack` for stack navigation. Treat drawers separately because standalone React Navigation has no native drawer replacement.",
|
|
107735
109280
|
create: (context) => ({ ImportDeclaration(node) {
|
|
107736
109281
|
const source = node.source?.value;
|
|
107737
109282
|
if (typeof source !== "string") return;
|
|
@@ -108305,6 +109850,28 @@ const rnNoSingleElementStyleArray = defineRule({
|
|
|
108305
109850
|
} })
|
|
108306
109851
|
});
|
|
108307
109852
|
//#endregion
|
|
109853
|
+
//#region src/plugin/rules/react-native/rn-platform-shaking-use-direct-import.ts
|
|
109854
|
+
const REACT_NATIVE_MODULE_SOURCE = "react-native";
|
|
109855
|
+
const rnPlatformShakingUseDirectImport = defineRule({
|
|
109856
|
+
id: "rn-platform-shaking-use-direct-import",
|
|
109857
|
+
title: "Platform reached through React Native namespace",
|
|
109858
|
+
tags: ["test-noise"],
|
|
109859
|
+
requires: ["expo:54"],
|
|
109860
|
+
severity: "warn",
|
|
109861
|
+
recommendation: "Import `Platform` directly with `import { Platform } from \"react-native\"` so Expo can remove code for the other platform.",
|
|
109862
|
+
create: (context) => ({ MemberExpression(node) {
|
|
109863
|
+
if (node.computed) return;
|
|
109864
|
+
if (!isNodeOfType(node.object, "Identifier")) return;
|
|
109865
|
+
if (!isNodeOfType(node.property, "Identifier") || node.property.name !== "Platform") return;
|
|
109866
|
+
if (context.scopes.symbolFor(node.object)?.kind !== "import") return;
|
|
109867
|
+
if (!isNamespaceImportFromModule$1(node, node.object.name, REACT_NATIVE_MODULE_SOURCE)) return;
|
|
109868
|
+
context.report({
|
|
109869
|
+
node,
|
|
109870
|
+
message: "Expo cannot tree-shake platform branches reached through the React Native namespace, so both platform paths stay in the bundle."
|
|
109871
|
+
});
|
|
109872
|
+
} })
|
|
109873
|
+
});
|
|
109874
|
+
//#endregion
|
|
108308
109875
|
//#region src/plugin/rules/react-native/rn-prefer-content-inset-adjustment.ts
|
|
108309
109876
|
const rnPreferContentInsetAdjustment = defineRetiredRule({
|
|
108310
109877
|
id: "rn-prefer-content-inset-adjustment",
|
|
@@ -108671,6 +110238,97 @@ const rnPressableSharedValueMutation = defineRule({
|
|
|
108671
110238
|
}
|
|
108672
110239
|
});
|
|
108673
110240
|
//#endregion
|
|
110241
|
+
//#region src/plugin/rules/react-native/utils/resolve-reanimated-api-name.ts
|
|
110242
|
+
const REANIMATED_MODULE_SOURCE = "react-native-reanimated";
|
|
110243
|
+
const resolveReanimatedApiName = (callExpression, scopes, supportedApiNames) => {
|
|
110244
|
+
const reference = resolveImportedApiReference(callExpression.callee, scopes);
|
|
110245
|
+
if (reference?.source !== REANIMATED_MODULE_SOURCE || reference.importedName === null || !supportedApiNames.has(reference.importedName)) return null;
|
|
110246
|
+
return reference.importedName;
|
|
110247
|
+
};
|
|
110248
|
+
//#endregion
|
|
110249
|
+
//#region src/plugin/rules/react-native/rn-reanimated-4-no-legacy-spring-thresholds.ts
|
|
110250
|
+
const WITH_SPRING_API_NAMES = new Set(["withSpring"]);
|
|
110251
|
+
const LEGACY_SPRING_THRESHOLD_NAMES = new Set(["restDisplacementThreshold", "restSpeedThreshold"]);
|
|
110252
|
+
const rnReanimated4NoLegacySpringThresholds = defineRule({
|
|
110253
|
+
id: "rn-reanimated-4-no-legacy-spring-thresholds",
|
|
110254
|
+
title: "Legacy Reanimated spring threshold",
|
|
110255
|
+
tags: ["migration-hint"],
|
|
110256
|
+
requires: ["reanimated:4"],
|
|
110257
|
+
severity: "warn",
|
|
110258
|
+
recommendation: "Replace Reanimated 3 rest thresholds with Reanimated 4's `energyThreshold` spring option.",
|
|
110259
|
+
create: (context) => ({ CallExpression(node) {
|
|
110260
|
+
if (!resolveReanimatedApiName(node, context.scopes, WITH_SPRING_API_NAMES)) return;
|
|
110261
|
+
const configArgument = node.arguments[1];
|
|
110262
|
+
if (!configArgument || isNodeOfType(configArgument, "SpreadElement")) return;
|
|
110263
|
+
const unwrappedConfig = stripParenExpression(configArgument);
|
|
110264
|
+
if (!isNodeOfType(unwrappedConfig, "ObjectExpression")) return;
|
|
110265
|
+
for (const property of unwrappedConfig.properties) {
|
|
110266
|
+
if (!isNodeOfType(property, "Property")) continue;
|
|
110267
|
+
const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
|
|
110268
|
+
if (!propertyName || !LEGACY_SPRING_THRESHOLD_NAMES.has(propertyName)) continue;
|
|
110269
|
+
context.report({
|
|
110270
|
+
node: property,
|
|
110271
|
+
message: `Reanimated 4 removed \`${propertyName}\`; use the \`energyThreshold\` spring option instead.`
|
|
110272
|
+
});
|
|
110273
|
+
}
|
|
110274
|
+
} })
|
|
110275
|
+
});
|
|
110276
|
+
//#endregion
|
|
110277
|
+
//#region src/plugin/rules/react-native/rn-reanimated-4-no-removed-api.ts
|
|
110278
|
+
const REMOVED_API_MESSAGE_BY_NAME = new Map([
|
|
110279
|
+
["useAnimatedGestureHandler", "Reanimated 4 removed `useAnimatedGestureHandler`; migrate this gesture to the Gesture API."],
|
|
110280
|
+
["useWorkletCallback", "Reanimated 4 removed `useWorkletCallback`; use React's `useCallback` with a `worklet` directive instead."],
|
|
110281
|
+
["combineTransition", "Reanimated 4 removed `combineTransition`; compose the transition with `EntryExitTransition` instead."],
|
|
110282
|
+
["addWhitelistedNativeProps", "Reanimated 4 removed `addWhitelistedNativeProps` because prop whitelisting is no longer needed."],
|
|
110283
|
+
["addWhitelistedUIProps", "Reanimated 4 removed `addWhitelistedUIProps` because prop whitelisting is no longer needed."]
|
|
110284
|
+
]);
|
|
110285
|
+
const REMOVED_API_NAMES = new Set(REMOVED_API_MESSAGE_BY_NAME.keys());
|
|
110286
|
+
const rnReanimated4NoRemovedApi = defineRule({
|
|
110287
|
+
id: "rn-reanimated-4-no-removed-api",
|
|
110288
|
+
title: "API removed in Reanimated 4",
|
|
110289
|
+
tags: ["migration-hint"],
|
|
110290
|
+
requires: ["reanimated:4"],
|
|
110291
|
+
severity: "warn",
|
|
110292
|
+
recommendation: "Migrate removed Reanimated APIs to their Reanimated 4 or Gesture API replacements before upgrading.",
|
|
110293
|
+
create: (context) => ({ CallExpression(node) {
|
|
110294
|
+
const apiName = resolveReanimatedApiName(node, context.scopes, REMOVED_API_NAMES);
|
|
110295
|
+
if (!apiName) return;
|
|
110296
|
+
const message = REMOVED_API_MESSAGE_BY_NAME.get(apiName);
|
|
110297
|
+
if (!message) return;
|
|
110298
|
+
context.report({
|
|
110299
|
+
node,
|
|
110300
|
+
message
|
|
110301
|
+
});
|
|
110302
|
+
} })
|
|
110303
|
+
});
|
|
110304
|
+
//#endregion
|
|
110305
|
+
//#region src/plugin/rules/react-native/rn-reanimated-4-use-worklets-scheduler.ts
|
|
110306
|
+
const WORKLETS_MIGRATION_BY_REANIMATED_API = new Map([
|
|
110307
|
+
["runOnUI", "replace `runOnUI(fn)(...args)` with `scheduleOnUI(fn, ...args)`"],
|
|
110308
|
+
["runOnJS", "replace `runOnJS(fn)(...args)` with `scheduleOnRN(fn, ...args)`"],
|
|
110309
|
+
["executeOnUIRuntimeSync", "replace `executeOnUIRuntimeSync(fn)(...args)` with `runOnUISync(fn, ...args)`"],
|
|
110310
|
+
["runOnRuntime", "replace `runOnRuntime(runtime, fn)(...args)` with `scheduleOnRuntime(runtime, fn, ...args)`"]
|
|
110311
|
+
]);
|
|
110312
|
+
const REANIMATED_SCHEDULER_API_NAMES = new Set(WORKLETS_MIGRATION_BY_REANIMATED_API.keys());
|
|
110313
|
+
const rnReanimated4UseWorkletsScheduler = defineRule({
|
|
110314
|
+
id: "rn-reanimated-4-use-worklets-scheduler",
|
|
110315
|
+
title: "Scheduler moved to Worklets",
|
|
110316
|
+
tags: ["migration-hint"],
|
|
110317
|
+
requires: ["reanimated:4"],
|
|
110318
|
+
severity: "warn",
|
|
110319
|
+
recommendation: "Import the corresponding scheduler from `react-native-worklets` when migrating to Reanimated 4.",
|
|
110320
|
+
create: (context) => ({ CallExpression(node) {
|
|
110321
|
+
const apiName = resolveReanimatedApiName(node, context.scopes, REANIMATED_SCHEDULER_API_NAMES);
|
|
110322
|
+
if (!apiName) return;
|
|
110323
|
+
const migration = WORKLETS_MIGRATION_BY_REANIMATED_API.get(apiName);
|
|
110324
|
+
if (!migration) return;
|
|
110325
|
+
context.report({
|
|
110326
|
+
node,
|
|
110327
|
+
message: `For Reanimated 4, ${migration} from \`react-native-worklets\`.`
|
|
110328
|
+
});
|
|
110329
|
+
} })
|
|
110330
|
+
});
|
|
110331
|
+
//#endregion
|
|
108674
110332
|
//#region src/plugin/rules/react-native/utils/scrollview_names.ts
|
|
108675
110333
|
const SCROLLVIEW_NAMES = new Set([
|
|
108676
110334
|
"ScrollView",
|
|
@@ -108934,7 +110592,7 @@ const rnStylePreferBoxShadow = defineRule({
|
|
|
108934
110592
|
tags: ["test-noise"],
|
|
108935
110593
|
requires: ["react-native"],
|
|
108936
110594
|
severity: "warn",
|
|
108937
|
-
recommendation: "These shadow keys only work on one platform. On
|
|
110595
|
+
recommendation: "These shadow keys only work on one platform. On the New Architecture, use a CSS `boxShadow` string like `boxShadow: \"0 2px 8px rgba(0,0,0,0.1)\"`, which works on both.",
|
|
108938
110596
|
create: (context) => {
|
|
108939
110597
|
if (context.filename && isLegacyArchReactNativeFile(normalizeFilename(context.filename))) return EMPTY_VISITORS$3;
|
|
108940
110598
|
return {
|
|
@@ -130125,6 +131783,30 @@ const reactDoctorRules = [
|
|
|
130125
131783
|
tags: [...new Set(["react-native", ...rnAnimationReactionAsDerived.tags ?? []])]
|
|
130126
131784
|
}
|
|
130127
131785
|
},
|
|
131786
|
+
{
|
|
131787
|
+
key: "react-doctor/rn-bottom-sheet-no-ignored-scroll-prop",
|
|
131788
|
+
id: "rn-bottom-sheet-no-ignored-scroll-prop",
|
|
131789
|
+
source: "react-doctor",
|
|
131790
|
+
originallyExternal: false,
|
|
131791
|
+
rule: {
|
|
131792
|
+
...rnBottomSheetNoIgnoredScrollProp,
|
|
131793
|
+
framework: "react-native",
|
|
131794
|
+
category: "Bugs",
|
|
131795
|
+
tags: [...new Set(["react-native", ...rnBottomSheetNoIgnoredScrollProp.tags ?? []])]
|
|
131796
|
+
}
|
|
131797
|
+
},
|
|
131798
|
+
{
|
|
131799
|
+
key: "react-doctor/rn-bottom-sheet-no-state-in-on-animate",
|
|
131800
|
+
id: "rn-bottom-sheet-no-state-in-on-animate",
|
|
131801
|
+
source: "react-doctor",
|
|
131802
|
+
originallyExternal: false,
|
|
131803
|
+
rule: {
|
|
131804
|
+
...rnBottomSheetNoStateInOnAnimate,
|
|
131805
|
+
framework: "react-native",
|
|
131806
|
+
category: "Bugs",
|
|
131807
|
+
tags: [...new Set(["react-native", ...rnBottomSheetNoStateInOnAnimate.tags ?? []])]
|
|
131808
|
+
}
|
|
131809
|
+
},
|
|
130128
131810
|
{
|
|
130129
131811
|
key: "react-doctor/rn-bottom-sheet-prefer-native",
|
|
130130
131812
|
id: "rn-bottom-sheet-prefer-native",
|
|
@@ -130137,6 +131819,18 @@ const reactDoctorRules = [
|
|
|
130137
131819
|
tags: [...new Set(["react-native", ...rnBottomSheetPreferNative.tags ?? []])]
|
|
130138
131820
|
}
|
|
130139
131821
|
},
|
|
131822
|
+
{
|
|
131823
|
+
key: "react-doctor/rn-bottom-sheet-use-integrated-scrollable",
|
|
131824
|
+
id: "rn-bottom-sheet-use-integrated-scrollable",
|
|
131825
|
+
source: "react-doctor",
|
|
131826
|
+
originallyExternal: false,
|
|
131827
|
+
rule: {
|
|
131828
|
+
...rnBottomSheetUseIntegratedScrollable,
|
|
131829
|
+
framework: "react-native",
|
|
131830
|
+
category: "Bugs",
|
|
131831
|
+
tags: [...new Set(["react-native", ...rnBottomSheetUseIntegratedScrollable.tags ?? []])]
|
|
131832
|
+
}
|
|
131833
|
+
},
|
|
130140
131834
|
{
|
|
130141
131835
|
key: "react-doctor/rn-detox-missing-await",
|
|
130142
131836
|
id: "rn-detox-missing-await",
|
|
@@ -130401,6 +132095,18 @@ const reactDoctorRules = [
|
|
|
130401
132095
|
tags: [...new Set(["react-native", ...rnNoSingleElementStyleArray.tags ?? []])]
|
|
130402
132096
|
}
|
|
130403
132097
|
},
|
|
132098
|
+
{
|
|
132099
|
+
key: "react-doctor/rn-platform-shaking-use-direct-import",
|
|
132100
|
+
id: "rn-platform-shaking-use-direct-import",
|
|
132101
|
+
source: "react-doctor",
|
|
132102
|
+
originallyExternal: false,
|
|
132103
|
+
rule: {
|
|
132104
|
+
...rnPlatformShakingUseDirectImport,
|
|
132105
|
+
framework: "react-native",
|
|
132106
|
+
category: "Bugs",
|
|
132107
|
+
tags: [...new Set(["react-native", ...rnPlatformShakingUseDirectImport.tags ?? []])]
|
|
132108
|
+
}
|
|
132109
|
+
},
|
|
130404
132110
|
{
|
|
130405
132111
|
key: "react-doctor/rn-prefer-content-inset-adjustment",
|
|
130406
132112
|
id: "rn-prefer-content-inset-adjustment",
|
|
@@ -130473,6 +132179,42 @@ const reactDoctorRules = [
|
|
|
130473
132179
|
tags: [...new Set(["react-native", ...rnPressableSharedValueMutation.tags ?? []])]
|
|
130474
132180
|
}
|
|
130475
132181
|
},
|
|
132182
|
+
{
|
|
132183
|
+
key: "react-doctor/rn-reanimated-4-no-legacy-spring-thresholds",
|
|
132184
|
+
id: "rn-reanimated-4-no-legacy-spring-thresholds",
|
|
132185
|
+
source: "react-doctor",
|
|
132186
|
+
originallyExternal: false,
|
|
132187
|
+
rule: {
|
|
132188
|
+
...rnReanimated4NoLegacySpringThresholds,
|
|
132189
|
+
framework: "react-native",
|
|
132190
|
+
category: "Bugs",
|
|
132191
|
+
tags: [...new Set(["react-native", ...rnReanimated4NoLegacySpringThresholds.tags ?? []])]
|
|
132192
|
+
}
|
|
132193
|
+
},
|
|
132194
|
+
{
|
|
132195
|
+
key: "react-doctor/rn-reanimated-4-no-removed-api",
|
|
132196
|
+
id: "rn-reanimated-4-no-removed-api",
|
|
132197
|
+
source: "react-doctor",
|
|
132198
|
+
originallyExternal: false,
|
|
132199
|
+
rule: {
|
|
132200
|
+
...rnReanimated4NoRemovedApi,
|
|
132201
|
+
framework: "react-native",
|
|
132202
|
+
category: "Bugs",
|
|
132203
|
+
tags: [...new Set(["react-native", ...rnReanimated4NoRemovedApi.tags ?? []])]
|
|
132204
|
+
}
|
|
132205
|
+
},
|
|
132206
|
+
{
|
|
132207
|
+
key: "react-doctor/rn-reanimated-4-use-worklets-scheduler",
|
|
132208
|
+
id: "rn-reanimated-4-use-worklets-scheduler",
|
|
132209
|
+
source: "react-doctor",
|
|
132210
|
+
originallyExternal: false,
|
|
132211
|
+
rule: {
|
|
132212
|
+
...rnReanimated4UseWorkletsScheduler,
|
|
132213
|
+
framework: "react-native",
|
|
132214
|
+
category: "Bugs",
|
|
132215
|
+
tags: [...new Set(["react-native", ...rnReanimated4UseWorkletsScheduler.tags ?? []])]
|
|
132216
|
+
}
|
|
132217
|
+
},
|
|
130476
132218
|
{
|
|
130477
132219
|
key: "react-doctor/rn-scrollview-dynamic-padding",
|
|
130478
132220
|
id: "rn-scrollview-dynamic-padding",
|