@codeyam/codeyam-cli 0.1.0-staging.323686 → 0.1.0-staging.483fdc2
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/analyzer-template/.build-info.json +7 -7
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +2 -2
- package/analyzer-template/packages/ai/index.ts +6 -1
- package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +39 -17
- package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +67 -9
- package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +308 -50
- package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +15 -6
- package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +664 -242
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.ts +16 -3
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.ts +6 -4
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +20 -1
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +35 -13
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +160 -0
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.ts +40 -30
- package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +289 -83
- package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +269 -1
- package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +9 -5
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +11 -3
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +1 -1
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +297 -7
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +1 -1
- package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +90 -96
- package/analyzer-template/packages/ai/src/lib/promptGenerators/gatherAttributesMap.ts +10 -7
- package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +25 -13
- package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +4 -3
- package/analyzer-template/packages/analyze/src/lib/FileAnalyzer.ts +65 -59
- package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +113 -26
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.ts +19 -0
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.ts +19 -0
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllExports.ts +11 -0
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.ts +8 -0
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.ts +49 -1
- package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.ts +2 -1
- package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +20 -6
- package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +14 -4
- package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +4 -2
- package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +0 -3
- package/analyzer-template/packages/analyze/src/lib/files/analyzeRemixRoute.ts +4 -5
- package/analyzer-template/packages/analyze/src/lib/files/getImportedExports.ts +14 -12
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +57 -13
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +29 -0
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +35 -4
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +117 -9
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +199 -17
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/propagateArrayItemSchemas.ts +474 -0
- package/analyzer-template/packages/analyze/src/lib/files/setImportedExports.ts +2 -1
- package/analyzer-template/packages/analyze/src/lib/utils/getFileByPath.ts +19 -0
- package/analyzer-template/packages/aws/package.json +1 -1
- package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
- package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
- package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
- package/analyzer-template/packages/github/package.json +1 -1
- package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +6 -5
- package/analyzer-template/packages/types/src/types/ScopeAnalysis.ts +6 -1
- package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
- package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
- package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
- package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
- package/analyzer-template/project/constructMockCode.ts +54 -9
- package/analyzer-template/project/writeMockDataTsx.ts +73 -2
- package/background/src/lib/virtualized/project/constructMockCode.js +45 -3
- package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
- package/background/src/lib/virtualized/project/writeMockDataTsx.js +71 -2
- package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
- package/codeyam-cli/scripts/apply-setup.js +146 -0
- package/codeyam-cli/scripts/apply-setup.js.map +1 -1
- package/codeyam-cli/src/commands/debug.js +7 -5
- package/codeyam-cli/src/commands/debug.js.map +1 -1
- package/codeyam-cli/src/utils/install-skills.js +22 -0
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/reviewedRules.js +92 -0
- package/codeyam-cli/src/utils/reviewedRules.js.map +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-CX9f-5xM.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{manifest-7522edd4.js → manifest-bba56ec1.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/memory-DuTFSyJ2.js +92 -0
- package/codeyam-cli/src/webserver/build/client/assets/{root-eVAaavTS.js → root-DTfSQARG.js} +6 -6
- package/codeyam-cli/src/webserver/build/server/assets/{index-DVzYx8PN.js → index-TD1f-DHV.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-BQ-1XyEa.js +258 -0
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/templates/codeyam:memory.md +174 -233
- package/codeyam-cli/templates/codeyam:new-rule.md +41 -2
- package/codeyam-cli/templates/rule-reflection-hook.py +161 -0
- package/codeyam-cli/templates/rules-instructions.md +126 -0
- package/package.json +1 -1
- package/packages/ai/index.js +2 -1
- package/packages/ai/index.js.map +1 -1
- package/packages/ai/src/lib/analyzeScope.js +29 -12
- package/packages/ai/src/lib/analyzeScope.js.map +1 -1
- package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +54 -8
- package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
- package/packages/ai/src/lib/astScopes/processExpression.js +239 -43
- package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +503 -165
- package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js +13 -3
- package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js +6 -4
- package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +22 -1
- package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +34 -9
- package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +159 -0
- package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js.map +1 -0
- package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js +37 -20
- package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js.map +1 -1
- package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +237 -73
- package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
- package/packages/ai/src/lib/generateEntityScenarioData.js +195 -1
- package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
- package/packages/ai/src/lib/generateEntityScenarios.js +7 -1
- package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
- package/packages/ai/src/lib/generateExecutionFlows.js +10 -2
- package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
- package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +209 -3
- package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -1
- package/packages/ai/src/lib/mergeStatements.js +70 -51
- package/packages/ai/src/lib/mergeStatements.js.map +1 -1
- package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js +10 -4
- package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js.map +1 -1
- package/packages/ai/src/lib/resolvePathToControllable.js +24 -14
- package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -1
- package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
- package/packages/analyze/src/lib/FileAnalyzer.js +60 -36
- package/packages/analyze/src/lib/FileAnalyzer.js.map +1 -1
- package/packages/analyze/src/lib/ProjectAnalyzer.js +96 -26
- package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js +14 -0
- package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js +14 -0
- package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js +6 -0
- package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js +6 -0
- package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js +39 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js.map +1 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js +2 -1
- package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +13 -5
- package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +14 -4
- package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +2 -1
- package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +0 -3
- package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
- package/packages/analyze/src/lib/files/analyzeRemixRoute.js +3 -2
- package/packages/analyze/src/lib/files/analyzeRemixRoute.js.map +1 -1
- package/packages/analyze/src/lib/files/getImportedExports.js +11 -7
- package/packages/analyze/src/lib/files/getImportedExports.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +52 -10
- package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +25 -8
- package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +34 -4
- package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +56 -8
- package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +168 -9
- package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
- package/packages/analyze/src/lib/files/setImportedExports.js +2 -1
- package/packages/analyze/src/lib/files/setImportedExports.js.map +1 -1
- package/packages/analyze/src/lib/utils/getFileByPath.js +12 -0
- package/packages/analyze/src/lib/utils/getFileByPath.js.map +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-D3yhhV8x.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/memory-yxFcrxBX.js +0 -92
- package/codeyam-cli/src/webserver/build/server/assets/server-build-4Cr0uToj.js +0 -257
|
@@ -152,7 +152,7 @@ export interface ScopeInfo {
|
|
|
152
152
|
[childComponentName: string]: Array<{
|
|
153
153
|
path: string;
|
|
154
154
|
conditionType: 'truthiness' | 'comparison';
|
|
155
|
-
location: 'if' | 'ternary' | 'logical-and' | 'switch';
|
|
155
|
+
location: 'if' | 'ternary' | 'logical-and' | 'switch' | 'unconditional';
|
|
156
156
|
isNegated?: boolean;
|
|
157
157
|
}>;
|
|
158
158
|
};
|
|
@@ -424,7 +424,7 @@ export class ScopeDataStructure {
|
|
|
424
424
|
path: string;
|
|
425
425
|
conditionType: 'truthiness' | 'comparison' | 'switch';
|
|
426
426
|
comparedValues?: string[];
|
|
427
|
-
location: 'if' | 'ternary' | 'logical-and' | 'switch';
|
|
427
|
+
location: 'if' | 'ternary' | 'logical-and' | 'switch' | 'unconditional';
|
|
428
428
|
}>
|
|
429
429
|
> = {};
|
|
430
430
|
|
|
@@ -1650,17 +1650,26 @@ export class ScopeDataStructure {
|
|
|
1650
1650
|
private setInstantiatedVariables(scopeNode: ScopeNode) {
|
|
1651
1651
|
let instantiatedVariables = scopeNode.analysis?.instantiatedVariables ?? [];
|
|
1652
1652
|
|
|
1653
|
-
for (const [path,
|
|
1653
|
+
for (const [path, rawEquivalentPath] of Object.entries(
|
|
1654
1654
|
scopeNode.analysis.isolatedEquivalentVariables ?? {},
|
|
1655
1655
|
)) {
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1656
|
+
// Normalize to array for consistent handling (supports both string and string[])
|
|
1657
|
+
const equivalentPaths = Array.isArray(rawEquivalentPath)
|
|
1658
|
+
? rawEquivalentPath
|
|
1659
|
+
: rawEquivalentPath
|
|
1660
|
+
? [rawEquivalentPath]
|
|
1661
|
+
: [];
|
|
1662
|
+
|
|
1663
|
+
for (const equivalentPath of equivalentPaths) {
|
|
1664
|
+
if (typeof equivalentPath !== 'string') {
|
|
1665
|
+
continue;
|
|
1666
|
+
}
|
|
1659
1667
|
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1668
|
+
if (equivalentPath.startsWith('signature[')) {
|
|
1669
|
+
const equivalentPathParts = this.splitPath(equivalentPath);
|
|
1670
|
+
instantiatedVariables.push(equivalentPathParts[0]);
|
|
1671
|
+
instantiatedVariables.push(path);
|
|
1672
|
+
}
|
|
1664
1673
|
}
|
|
1665
1674
|
|
|
1666
1675
|
const duplicateInstantiated = instantiatedVariables.find(
|
|
@@ -1673,9 +1682,14 @@ export class ScopeDataStructure {
|
|
|
1673
1682
|
}
|
|
1674
1683
|
}
|
|
1675
1684
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1685
|
+
const instantiatedSeen = new Set<string>();
|
|
1686
|
+
instantiatedVariables = instantiatedVariables.filter((varName) => {
|
|
1687
|
+
if (instantiatedSeen.has(varName)) {
|
|
1688
|
+
return false;
|
|
1689
|
+
}
|
|
1690
|
+
instantiatedSeen.add(varName);
|
|
1691
|
+
return true;
|
|
1692
|
+
});
|
|
1679
1693
|
|
|
1680
1694
|
scopeNode.instantiatedVariables = instantiatedVariables;
|
|
1681
1695
|
|
|
@@ -1696,13 +1710,19 @@ export class ScopeDataStructure {
|
|
|
1696
1710
|
...parentScopeNode.instantiatedVariables.filter(
|
|
1697
1711
|
(v) => !v.startsWith('signature[') && !v.startsWith('returnValue'),
|
|
1698
1712
|
),
|
|
1699
|
-
].filter(
|
|
1700
|
-
(varName, index, self) =>
|
|
1701
|
-
!instantiatedVariables.includes(varName) &&
|
|
1702
|
-
self.indexOf(varName) === index,
|
|
1703
|
-
);
|
|
1713
|
+
].filter((varName) => !instantiatedSeen.has(varName));
|
|
1704
1714
|
|
|
1705
|
-
|
|
1715
|
+
const parentInstantiatedSeen = new Set<string>();
|
|
1716
|
+
const dedupedParentInstantiatedVariables =
|
|
1717
|
+
parentInstantiatedVariables.filter((varName) => {
|
|
1718
|
+
if (parentInstantiatedSeen.has(varName)) {
|
|
1719
|
+
return false;
|
|
1720
|
+
}
|
|
1721
|
+
parentInstantiatedSeen.add(varName);
|
|
1722
|
+
return true;
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
scopeNode.parentInstantiatedVariables = dedupedParentInstantiatedVariables;
|
|
1706
1726
|
}
|
|
1707
1727
|
|
|
1708
1728
|
private trackFunctionCalls(scopeNode: ScopeNode) {
|
|
@@ -1718,172 +1738,198 @@ export class ScopeDataStructure {
|
|
|
1718
1738
|
const { isolatedStructure, isolatedEquivalentVariables } =
|
|
1719
1739
|
scopeNode.analysis;
|
|
1720
1740
|
|
|
1741
|
+
// Flatten isolatedEquivalentVariables values for allPaths (handles both string and string[])
|
|
1742
|
+
const flattenedEquivValues = Object.values(
|
|
1743
|
+
isolatedEquivalentVariables || {},
|
|
1744
|
+
).flatMap((v) => (Array.isArray(v) ? v : [v]));
|
|
1745
|
+
|
|
1721
1746
|
const allPaths = Array.from(
|
|
1722
1747
|
new Set([
|
|
1723
1748
|
...Object.keys(isolatedStructure || {}),
|
|
1724
1749
|
...Object.keys(isolatedEquivalentVariables || {}),
|
|
1725
|
-
...
|
|
1750
|
+
...flattenedEquivValues,
|
|
1726
1751
|
]),
|
|
1727
1752
|
);
|
|
1728
1753
|
|
|
1729
1754
|
for (let path in isolatedEquivalentVariables) {
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1755
|
+
const rawEquivalentValue = isolatedEquivalentVariables?.[path];
|
|
1756
|
+
// Normalize to array for consistent handling
|
|
1757
|
+
const equivalentValues = Array.isArray(rawEquivalentValue)
|
|
1758
|
+
? rawEquivalentValue
|
|
1759
|
+
: [rawEquivalentValue];
|
|
1760
|
+
|
|
1761
|
+
for (let equivalentValue of equivalentValues) {
|
|
1762
|
+
if (equivalentValue && this.isValidPath(equivalentValue)) {
|
|
1763
|
+
// IMPORTANT: DO NOT strip ::cyDuplicateKey:: markers from equivalencies.
|
|
1764
|
+
// These markers are critical for distinguishing variable reassignments.
|
|
1765
|
+
// For example, with:
|
|
1766
|
+
// let fetcher = useFetcher<ConfigData>();
|
|
1767
|
+
// const configData = fetcher.data?.data;
|
|
1768
|
+
// fetcher = useFetcher<SettingsData>();
|
|
1769
|
+
// const settingsData = fetcher.data?.data;
|
|
1770
|
+
//
|
|
1771
|
+
// mergeStatements creates:
|
|
1772
|
+
// fetcher → useFetcher<ConfigData>()...
|
|
1773
|
+
// fetcher::cyDuplicateKey1:: → useFetcher<SettingsData>()...
|
|
1774
|
+
// configData → fetcher.data.data
|
|
1775
|
+
// settingsData → fetcher::cyDuplicateKey1::.data.data
|
|
1776
|
+
//
|
|
1777
|
+
// If we strip ::cyDuplicateKey::, settingsData would incorrectly trace
|
|
1778
|
+
// to useFetcher<ConfigData>() instead of useFetcher<SettingsData>().
|
|
1779
|
+
path = cleanPath(path, allPaths);
|
|
1780
|
+
equivalentValue = cleanPath(equivalentValue, allPaths);
|
|
1781
|
+
|
|
1782
|
+
this.addEquivalency(
|
|
1783
|
+
path,
|
|
1784
|
+
equivalentValue,
|
|
1785
|
+
scopeNode.name,
|
|
1786
|
+
scopeNode,
|
|
1787
|
+
'original equivalency',
|
|
1788
|
+
);
|
|
1759
1789
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1790
|
+
// Propagate equivalencies involving parent-scope variables to those parent scopes.
|
|
1791
|
+
// This handles patterns like: collected.push({...entity}) where 'collected' is defined
|
|
1792
|
+
// in a parent scope. The equivalency collected[] -> push().signature[0] needs to be
|
|
1793
|
+
// visible when tracing from the parent scope.
|
|
1794
|
+
const rootVariable = this.extractRootVariable(path);
|
|
1795
|
+
const equivalentRootVariable =
|
|
1796
|
+
this.extractRootVariable(equivalentValue);
|
|
1797
|
+
|
|
1798
|
+
// Skip propagation for self-referential reassignment patterns like:
|
|
1799
|
+
// x = x.method().functionCallReturnValue
|
|
1800
|
+
// where the path IS the variable itself (not a sub-path like x[] or x.prop).
|
|
1801
|
+
// These create circular references since both sides reference the same variable.
|
|
1802
|
+
//
|
|
1803
|
+
// But DO propagate for patterns like collected[] -> collected.push(...).signature[0]
|
|
1804
|
+
// where the path has additional segments beyond the root variable.
|
|
1805
|
+
const pathIsJustRootVariable = path === rootVariable;
|
|
1806
|
+
const isSelfReferentialReassignment =
|
|
1807
|
+
pathIsJustRootVariable && rootVariable === equivalentRootVariable;
|
|
1778
1808
|
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1809
|
+
if (
|
|
1810
|
+
rootVariable &&
|
|
1811
|
+
!isSelfReferentialReassignment &&
|
|
1812
|
+
scopeNode.parentInstantiatedVariables?.includes(rootVariable)
|
|
1813
|
+
) {
|
|
1814
|
+
// Find the parent scope where this variable is defined
|
|
1815
|
+
for (const parentScopeName of scopeNode.tree || []) {
|
|
1816
|
+
const parentScope = this.scopeNodes[parentScopeName];
|
|
1817
|
+
if (parentScope?.instantiatedVariables?.includes(rootVariable)) {
|
|
1818
|
+
// Add the equivalency to the parent scope as well
|
|
1819
|
+
this.addEquivalency(
|
|
1820
|
+
path,
|
|
1821
|
+
equivalentValue,
|
|
1822
|
+
scopeNode.name, // The equivalent path's scope remains the child scope
|
|
1823
|
+
parentScope, // But store it in the parent scope's equivalencies
|
|
1824
|
+
'propagated parent-variable equivalency',
|
|
1825
|
+
);
|
|
1826
|
+
break;
|
|
1827
|
+
}
|
|
1797
1828
|
}
|
|
1798
1829
|
}
|
|
1799
|
-
}
|
|
1800
1830
|
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
const
|
|
1832
|
-
|
|
1833
|
-
|
|
1831
|
+
// Propagate sub-property equivalencies when the equivalentValue is a simple variable
|
|
1832
|
+
// that has sub-properties defined in the isolatedEquivalentVariables.
|
|
1833
|
+
// This handles cases like: dataItem={{ structure: completeDataStructure }}
|
|
1834
|
+
// where completeDataStructure has sub-properties like completeDataStructure['Function Arguments']
|
|
1835
|
+
// We need to propagate these to create: dataItem.structure['Function Arguments'] equivalencies
|
|
1836
|
+
const isSimpleVariable =
|
|
1837
|
+
!equivalentValue.startsWith('signature[') &&
|
|
1838
|
+
!equivalentValue.includes('functionCallReturnValue') &&
|
|
1839
|
+
!equivalentValue.includes('.') &&
|
|
1840
|
+
!equivalentValue.includes('[');
|
|
1841
|
+
|
|
1842
|
+
if (isSimpleVariable) {
|
|
1843
|
+
// Look in current scope and all parent scopes for sub-properties
|
|
1844
|
+
const scopesToCheck = [scopeNode.name, ...scopeNode.tree];
|
|
1845
|
+
for (const scopeName of scopesToCheck) {
|
|
1846
|
+
const checkScope = this.scopeNodes[scopeName];
|
|
1847
|
+
if (!checkScope?.analysis?.isolatedEquivalentVariables) continue;
|
|
1848
|
+
|
|
1849
|
+
for (const [subPath, rawSubValue] of Object.entries(
|
|
1850
|
+
checkScope.analysis.isolatedEquivalentVariables,
|
|
1851
|
+
)) {
|
|
1852
|
+
// Normalize to array for consistent handling
|
|
1853
|
+
const subValues = Array.isArray(rawSubValue)
|
|
1854
|
+
? rawSubValue
|
|
1855
|
+
: rawSubValue
|
|
1856
|
+
? [rawSubValue]
|
|
1857
|
+
: [];
|
|
1858
|
+
|
|
1859
|
+
// Check if this is a sub-property of the equivalentValue variable
|
|
1860
|
+
// e.g., completeDataStructure['Function Arguments'] or completeDataStructure.foo
|
|
1861
|
+
const matchesDot = subPath.startsWith(equivalentValue + '.');
|
|
1862
|
+
const matchesBracket = subPath.startsWith(
|
|
1863
|
+
equivalentValue + '[',
|
|
1834
1864
|
);
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
this.isValidPath(newEquivalentValue)
|
|
1839
|
-
) {
|
|
1840
|
-
this.addEquivalency(
|
|
1841
|
-
newPath,
|
|
1842
|
-
newEquivalentValue,
|
|
1843
|
-
checkScope.name, // Use the scope where the sub-property was found
|
|
1844
|
-
scopeNode,
|
|
1845
|
-
'propagated sub-property equivalency',
|
|
1865
|
+
if (matchesDot || matchesBracket) {
|
|
1866
|
+
const subPropertyPath = subPath.substring(
|
|
1867
|
+
equivalentValue.length,
|
|
1846
1868
|
);
|
|
1869
|
+
const newPath = cleanPath(path + subPropertyPath, allPaths);
|
|
1870
|
+
|
|
1871
|
+
for (const subValue of subValues) {
|
|
1872
|
+
if (typeof subValue !== 'string') continue;
|
|
1873
|
+
const newEquivalentValue = cleanPath(
|
|
1874
|
+
subValue.replace(/::cyDuplicateKey\d+::/g, ''),
|
|
1875
|
+
allPaths,
|
|
1876
|
+
);
|
|
1877
|
+
|
|
1878
|
+
if (
|
|
1879
|
+
newEquivalentValue &&
|
|
1880
|
+
this.isValidPath(newEquivalentValue)
|
|
1881
|
+
) {
|
|
1882
|
+
this.addEquivalency(
|
|
1883
|
+
newPath,
|
|
1884
|
+
newEquivalentValue,
|
|
1885
|
+
checkScope.name, // Use the scope where the sub-property was found
|
|
1886
|
+
scopeNode,
|
|
1887
|
+
'propagated sub-property equivalency',
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1847
1891
|
}
|
|
1848
|
-
}
|
|
1849
1892
|
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1893
|
+
// Also check if equivalentValue itself maps to a functionCallReturnValue
|
|
1894
|
+
// e.g., result = useMemo(...).functionCallReturnValue
|
|
1895
|
+
for (const subValue of subValues) {
|
|
1896
|
+
if (
|
|
1897
|
+
subPath === equivalentValue &&
|
|
1898
|
+
typeof subValue === 'string' &&
|
|
1899
|
+
subValue.endsWith('.functionCallReturnValue')
|
|
1900
|
+
) {
|
|
1901
|
+
this.propagateFunctionCallReturnSubProperties(
|
|
1902
|
+
path,
|
|
1903
|
+
subValue,
|
|
1904
|
+
scopeNode,
|
|
1905
|
+
allPaths,
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1863
1909
|
}
|
|
1864
1910
|
}
|
|
1865
1911
|
}
|
|
1866
|
-
}
|
|
1867
1912
|
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1913
|
+
// Handle function call return values by propagating returnValue.* sub-properties
|
|
1914
|
+
// from the callback scope to the usage path
|
|
1915
|
+
if (equivalentValue.endsWith('.functionCallReturnValue')) {
|
|
1916
|
+
this.propagateFunctionCallReturnSubProperties(
|
|
1917
|
+
path,
|
|
1918
|
+
equivalentValue,
|
|
1919
|
+
scopeNode,
|
|
1920
|
+
allPaths,
|
|
1921
|
+
);
|
|
1877
1922
|
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1923
|
+
// Track which variable receives the return value of each function call
|
|
1924
|
+
// This enables generating separate mock data for each call site
|
|
1925
|
+
this.trackReceivingVariable(path, equivalentValue);
|
|
1926
|
+
}
|
|
1882
1927
|
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1928
|
+
// Also track variables that receive destructured properties from function call return values
|
|
1929
|
+
// e.g., "userData" -> "db.query('users').functionCallReturnValue.data"
|
|
1930
|
+
if (equivalentValue.includes('.functionCallReturnValue.')) {
|
|
1931
|
+
this.trackReceivingVariable(path, equivalentValue);
|
|
1932
|
+
}
|
|
1887
1933
|
}
|
|
1888
1934
|
}
|
|
1889
1935
|
}
|
|
@@ -2064,9 +2110,18 @@ export class ScopeDataStructure {
|
|
|
2064
2110
|
const checkScope = this.scopeNodes[scopeName];
|
|
2065
2111
|
if (!checkScope?.analysis?.isolatedEquivalentVariables) continue;
|
|
2066
2112
|
|
|
2067
|
-
const
|
|
2113
|
+
const rawFunctionRef =
|
|
2068
2114
|
checkScope.analysis.isolatedEquivalentVariables[functionName];
|
|
2069
|
-
|
|
2115
|
+
// Normalize to array and find first string ending with 'F'
|
|
2116
|
+
const functionRefs = Array.isArray(rawFunctionRef)
|
|
2117
|
+
? rawFunctionRef
|
|
2118
|
+
: rawFunctionRef
|
|
2119
|
+
? [rawFunctionRef]
|
|
2120
|
+
: [];
|
|
2121
|
+
const functionRef = functionRefs.find(
|
|
2122
|
+
(r) => typeof r === 'string' && r.endsWith('F'),
|
|
2123
|
+
);
|
|
2124
|
+
if (typeof functionRef === 'string') {
|
|
2070
2125
|
callbackScopeName = functionRef.slice(0, -1);
|
|
2071
2126
|
break;
|
|
2072
2127
|
}
|
|
@@ -2094,19 +2149,24 @@ export class ScopeDataStructure {
|
|
|
2094
2149
|
|
|
2095
2150
|
const isolatedVars = callbackScope.analysis.isolatedEquivalentVariables;
|
|
2096
2151
|
|
|
2152
|
+
// Get the first returnValue equivalency (normalize array to single value for these checks)
|
|
2153
|
+
const rawReturnValue = isolatedVars.returnValue;
|
|
2154
|
+
const firstReturnValue = Array.isArray(rawReturnValue)
|
|
2155
|
+
? rawReturnValue[0]
|
|
2156
|
+
: rawReturnValue;
|
|
2157
|
+
|
|
2097
2158
|
// First, check if returnValue is an alias to another variable (e.g., returnValue = intermediate)
|
|
2098
2159
|
// If so, we need to look for that variable's sub-properties too
|
|
2099
2160
|
const returnValueAlias =
|
|
2100
|
-
typeof
|
|
2101
|
-
|
|
2102
|
-
? isolatedVars.returnValue
|
|
2161
|
+
typeof firstReturnValue === 'string' && !firstReturnValue.includes('.')
|
|
2162
|
+
? firstReturnValue
|
|
2103
2163
|
: undefined;
|
|
2104
2164
|
|
|
2105
2165
|
// Pattern 3: Object.keys(X).reduce() - the reduce result has the same sub-properties as X
|
|
2106
2166
|
// When returnValue = "Object.keys(source).reduce(...).functionCallReturnValue", look for source.* sub-properties
|
|
2107
2167
|
let reduceSourceVar: string | undefined;
|
|
2108
|
-
if (typeof
|
|
2109
|
-
const reduceMatch =
|
|
2168
|
+
if (typeof firstReturnValue === 'string') {
|
|
2169
|
+
const reduceMatch = firstReturnValue.match(
|
|
2110
2170
|
/^Object\.keys\((\w+)\)\.reduce\(.*\)\.functionCallReturnValue$/,
|
|
2111
2171
|
);
|
|
2112
2172
|
if (reduceMatch) {
|
|
@@ -2114,7 +2174,14 @@ export class ScopeDataStructure {
|
|
|
2114
2174
|
}
|
|
2115
2175
|
}
|
|
2116
2176
|
|
|
2117
|
-
for (const [subPath,
|
|
2177
|
+
for (const [subPath, rawSubValue] of Object.entries(isolatedVars)) {
|
|
2178
|
+
// Normalize to array for consistent handling
|
|
2179
|
+
const subValues = Array.isArray(rawSubValue)
|
|
2180
|
+
? rawSubValue
|
|
2181
|
+
: rawSubValue
|
|
2182
|
+
? [rawSubValue]
|
|
2183
|
+
: [];
|
|
2184
|
+
|
|
2118
2185
|
// Check for direct returnValue.* sub-properties
|
|
2119
2186
|
const isReturnValueSub =
|
|
2120
2187
|
subPath.startsWith('returnValue.') ||
|
|
@@ -2132,57 +2199,59 @@ export class ScopeDataStructure {
|
|
|
2132
2199
|
(subPath.startsWith(reduceSourceVar + '.') ||
|
|
2133
2200
|
subPath.startsWith(reduceSourceVar + '['));
|
|
2134
2201
|
|
|
2135
|
-
if (
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2202
|
+
if (!isReturnValueSub && !isAliasSub && !isReduceSourceSub) continue;
|
|
2203
|
+
|
|
2204
|
+
for (const subValue of subValues) {
|
|
2205
|
+
if (typeof subValue !== 'string') continue;
|
|
2206
|
+
|
|
2207
|
+
// Convert alias/reduceSource paths to returnValue paths
|
|
2208
|
+
let effectiveSubPath = subPath;
|
|
2209
|
+
if (isAliasSub && !isReturnValueSub) {
|
|
2210
|
+
// Replace the alias prefix with returnValue
|
|
2211
|
+
effectiveSubPath =
|
|
2212
|
+
'returnValue' + subPath.substring(returnValueAlias!.length);
|
|
2213
|
+
} else if (isReduceSourceSub && !isReturnValueSub && !isAliasSub) {
|
|
2214
|
+
// Replace the reduce source prefix with returnValue
|
|
2215
|
+
effectiveSubPath =
|
|
2216
|
+
'returnValue' + subPath.substring(reduceSourceVar!.length);
|
|
2217
|
+
}
|
|
2218
|
+
const subPropertyPath = effectiveSubPath.substring(
|
|
2219
|
+
'returnValue'.length,
|
|
2220
|
+
);
|
|
2221
|
+
const newPath = cleanPath(path + subPropertyPath, allPaths);
|
|
2222
|
+
let newEquivalentValue = cleanPath(
|
|
2223
|
+
subValue.replace(/::cyDuplicateKey\d+::/g, ''),
|
|
2224
|
+
allPaths,
|
|
2225
|
+
);
|
|
2158
2226
|
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2227
|
+
// Resolve variable references through parent scope equivalencies
|
|
2228
|
+
const resolved = this.resolveVariableThroughParentScopes(
|
|
2229
|
+
newEquivalentValue,
|
|
2230
|
+
callbackScope,
|
|
2231
|
+
allPaths,
|
|
2232
|
+
);
|
|
2233
|
+
newEquivalentValue = resolved.resolvedPath;
|
|
2234
|
+
const equivalentScopeName = resolved.scopeName;
|
|
2167
2235
|
|
|
2168
|
-
|
|
2169
|
-
|
|
2236
|
+
if (!newEquivalentValue || !this.isValidPath(newEquivalentValue))
|
|
2237
|
+
continue;
|
|
2170
2238
|
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2239
|
+
this.addEquivalency(
|
|
2240
|
+
newPath,
|
|
2241
|
+
newEquivalentValue,
|
|
2242
|
+
equivalentScopeName,
|
|
2243
|
+
scopeNode,
|
|
2244
|
+
'propagated function call return sub-property equivalency',
|
|
2245
|
+
);
|
|
2178
2246
|
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2247
|
+
// Ensure the database entry has the usage path
|
|
2248
|
+
this.addUsageToEquivalencyDatabaseEntry(
|
|
2249
|
+
newPath,
|
|
2250
|
+
newEquivalentValue,
|
|
2251
|
+
equivalentScopeName,
|
|
2252
|
+
scopeNode.name,
|
|
2253
|
+
);
|
|
2254
|
+
}
|
|
2186
2255
|
}
|
|
2187
2256
|
}
|
|
2188
2257
|
|
|
@@ -2222,8 +2291,15 @@ export class ScopeDataStructure {
|
|
|
2222
2291
|
const parentScope = this.scopeNodes[parentScopeName];
|
|
2223
2292
|
if (!parentScope?.analysis?.isolatedEquivalentVariables) continue;
|
|
2224
2293
|
|
|
2225
|
-
const
|
|
2294
|
+
const rawRootEquiv =
|
|
2226
2295
|
parentScope.analysis.isolatedEquivalentVariables[rootVar];
|
|
2296
|
+
// Normalize to array and use first string value
|
|
2297
|
+
const rootEquivs = Array.isArray(rawRootEquiv)
|
|
2298
|
+
? rawRootEquiv
|
|
2299
|
+
: rawRootEquiv
|
|
2300
|
+
? [rawRootEquiv]
|
|
2301
|
+
: [];
|
|
2302
|
+
const rootEquiv = rootEquivs.find((r) => typeof r === 'string');
|
|
2227
2303
|
if (typeof rootEquiv === 'string') {
|
|
2228
2304
|
return {
|
|
2229
2305
|
resolvedPath: cleanPath(rootEquiv + restOfPath, allPaths),
|
|
@@ -2889,10 +2965,105 @@ export class ScopeDataStructure {
|
|
|
2889
2965
|
this.intermediatesOrderIndex.set(pathId, databaseEntry);
|
|
2890
2966
|
|
|
2891
2967
|
if (intermediateIndex === 0) {
|
|
2892
|
-
|
|
2968
|
+
let isValidSourceCandidate =
|
|
2893
2969
|
pathInfo.schemaPath.startsWith('signature[') ||
|
|
2894
2970
|
pathInfo.schemaPath.includes('functionCallReturnValue');
|
|
2895
|
-
|
|
2971
|
+
|
|
2972
|
+
// Check if path STARTS with a spread pattern like [...var]
|
|
2973
|
+
// This handles cases like [...files][][0] or [...files].sort(...).functionCallReturnValue[][0]
|
|
2974
|
+
// where the spread source variable needs to be resolved to a signature path.
|
|
2975
|
+
// We do this REGARDLESS of isValidSourceCandidate because even paths containing
|
|
2976
|
+
// functionCallReturnValue may need spread resolution to trace back to the signature.
|
|
2977
|
+
const spreadMatch = pathInfo.schemaPath.match(/^\[\.\.\.(\w+)\]/);
|
|
2978
|
+
if (spreadMatch) {
|
|
2979
|
+
const spreadVar = spreadMatch[1];
|
|
2980
|
+
const spreadPattern = spreadMatch[0]; // The full [...var] match
|
|
2981
|
+
const scopeNode = this.scopeNodes[pathInfo.scopeNodeName];
|
|
2982
|
+
|
|
2983
|
+
if (scopeNode?.equivalencies) {
|
|
2984
|
+
// Follow the equivalency chain to find a signature path
|
|
2985
|
+
// e.g., files (cyScope1) → files (root) → signature[0].files
|
|
2986
|
+
const resolveToSignature = (
|
|
2987
|
+
varName: string,
|
|
2988
|
+
currentScopeName: string,
|
|
2989
|
+
visited: Set<string>,
|
|
2990
|
+
): { schemaPath: string; scopeNodeName: string } | null => {
|
|
2991
|
+
const visitKey = `${currentScopeName}::${varName}`;
|
|
2992
|
+
if (visited.has(visitKey)) return null;
|
|
2993
|
+
visited.add(visitKey);
|
|
2994
|
+
|
|
2995
|
+
const currentScope = this.scopeNodes[currentScopeName];
|
|
2996
|
+
if (!currentScope?.equivalencies) return null;
|
|
2997
|
+
|
|
2998
|
+
const varEquivs = currentScope.equivalencies[varName];
|
|
2999
|
+
if (!varEquivs) return null;
|
|
3000
|
+
|
|
3001
|
+
// First check if any equivalency directly points to a signature path
|
|
3002
|
+
const signatureEquiv = varEquivs.find((eq) =>
|
|
3003
|
+
eq.schemaPath.startsWith('signature['),
|
|
3004
|
+
);
|
|
3005
|
+
if (signatureEquiv) {
|
|
3006
|
+
return signatureEquiv;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
// Otherwise, follow the chain to other scopes
|
|
3010
|
+
for (const equiv of varEquivs) {
|
|
3011
|
+
// If the equivalency points to the same variable in a different scope,
|
|
3012
|
+
// follow the chain
|
|
3013
|
+
if (
|
|
3014
|
+
equiv.schemaPath === varName &&
|
|
3015
|
+
equiv.scopeNodeName !== currentScopeName
|
|
3016
|
+
) {
|
|
3017
|
+
const result = resolveToSignature(
|
|
3018
|
+
varName,
|
|
3019
|
+
equiv.scopeNodeName,
|
|
3020
|
+
visited,
|
|
3021
|
+
);
|
|
3022
|
+
if (result) return result;
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
return null;
|
|
3027
|
+
};
|
|
3028
|
+
|
|
3029
|
+
const signatureEquiv = resolveToSignature(
|
|
3030
|
+
spreadVar,
|
|
3031
|
+
pathInfo.scopeNodeName,
|
|
3032
|
+
new Set(),
|
|
3033
|
+
);
|
|
3034
|
+
if (signatureEquiv) {
|
|
3035
|
+
// Replace ONLY the [...var] part with the resolved signature path
|
|
3036
|
+
// This preserves any suffix like .sort(...).functionCallReturnValue[][0]
|
|
3037
|
+
const resolvedPath = pathInfo.schemaPath.replace(
|
|
3038
|
+
spreadPattern,
|
|
3039
|
+
signatureEquiv.schemaPath,
|
|
3040
|
+
);
|
|
3041
|
+
// Add the resolved path as a source candidate
|
|
3042
|
+
if (
|
|
3043
|
+
!databaseEntry.sourceCandidates.some(
|
|
3044
|
+
(sc) =>
|
|
3045
|
+
sc.schemaPath === resolvedPath &&
|
|
3046
|
+
sc.scopeNodeName === pathInfo.scopeNodeName,
|
|
3047
|
+
)
|
|
3048
|
+
) {
|
|
3049
|
+
databaseEntry.sourceCandidates.push({
|
|
3050
|
+
scopeNodeName: pathInfo.scopeNodeName,
|
|
3051
|
+
schemaPath: resolvedPath,
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
3054
|
+
isValidSourceCandidate = true;
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
if (
|
|
3060
|
+
isValidSourceCandidate &&
|
|
3061
|
+
!databaseEntry.sourceCandidates.some(
|
|
3062
|
+
(sc) =>
|
|
3063
|
+
sc.schemaPath === pathInfo.schemaPath &&
|
|
3064
|
+
sc.scopeNodeName === pathInfo.scopeNodeName,
|
|
3065
|
+
)
|
|
3066
|
+
) {
|
|
2896
3067
|
databaseEntry.sourceCandidates.push(pathInfo);
|
|
2897
3068
|
}
|
|
2898
3069
|
} else {
|
|
@@ -3479,18 +3650,171 @@ export class ScopeDataStructure {
|
|
|
3479
3650
|
return {};
|
|
3480
3651
|
}
|
|
3481
3652
|
|
|
3653
|
+
// Collect all descendant scope names (including the scope itself)
|
|
3654
|
+
// This ensures we include external calls from nested scopes like cyScope2
|
|
3655
|
+
const getAllDescendantScopeNames = (
|
|
3656
|
+
node: import('./helpers/ScopeTreeManager').ScopeTreeNode,
|
|
3657
|
+
): Set<string> => {
|
|
3658
|
+
const names = new Set<string>([node.name]);
|
|
3659
|
+
for (const child of node.children) {
|
|
3660
|
+
for (const name of getAllDescendantScopeNames(child)) {
|
|
3661
|
+
names.add(name);
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
return names;
|
|
3665
|
+
};
|
|
3666
|
+
|
|
3667
|
+
const treeNode = this.scopeTreeManager.findNode(scopeNode.name);
|
|
3668
|
+
const descendantScopeNames = treeNode
|
|
3669
|
+
? getAllDescendantScopeNames(treeNode)
|
|
3670
|
+
: new Set<string>([scopeNode.name]);
|
|
3671
|
+
|
|
3672
|
+
// Get all external function calls made from this scope or any descendant scope
|
|
3673
|
+
// This allows us to include prop equivalencies from JSX components
|
|
3674
|
+
// that were rendered in nested scopes (e.g., FileTableRow called from cyScope2)
|
|
3675
|
+
const externalCallsFromScope = this.externalFunctionCalls.filter((efc) =>
|
|
3676
|
+
descendantScopeNames.has(efc.callScope),
|
|
3677
|
+
);
|
|
3678
|
+
const externalCallNames = new Set(
|
|
3679
|
+
externalCallsFromScope.map((efc) => efc.name),
|
|
3680
|
+
);
|
|
3681
|
+
|
|
3682
|
+
// Helper to check if a usage belongs to this scope (directly, via descendant, or via external call)
|
|
3683
|
+
const usageMatchesScope = (usage: { scopeNodeName: string }) =>
|
|
3684
|
+
descendantScopeNames.has(usage.scopeNodeName) ||
|
|
3685
|
+
externalCallNames.has(usage.scopeNodeName);
|
|
3686
|
+
|
|
3482
3687
|
const entries = this.equivalencyDatabase.filter((entry) =>
|
|
3483
|
-
entry.usages.some(
|
|
3688
|
+
entry.usages.some(usageMatchesScope),
|
|
3484
3689
|
);
|
|
3690
|
+
|
|
3691
|
+
// Helper to resolve a source candidate through equivalency chains to find signature paths
|
|
3692
|
+
const resolveToSignature = (
|
|
3693
|
+
source: Pick<ScopeVariable, 'scopeNodeName' | 'schemaPath'>,
|
|
3694
|
+
visited: Set<string>,
|
|
3695
|
+
): Pick<ScopeVariable, 'scopeNodeName' | 'schemaPath'>[] => {
|
|
3696
|
+
const visitKey = `${source.scopeNodeName}::${source.schemaPath}`;
|
|
3697
|
+
if (visited.has(visitKey)) return [];
|
|
3698
|
+
visited.add(visitKey);
|
|
3699
|
+
|
|
3700
|
+
// If already a signature path, return as-is
|
|
3701
|
+
if (source.schemaPath.startsWith('signature[')) {
|
|
3702
|
+
return [source];
|
|
3703
|
+
}
|
|
3704
|
+
|
|
3705
|
+
const currentScope = this.scopeNodes[source.scopeNodeName];
|
|
3706
|
+
if (!currentScope?.equivalencies) return [source];
|
|
3707
|
+
|
|
3708
|
+
// Check for direct equivalencies FIRST (full path match)
|
|
3709
|
+
// This ensures paths like "useMemo(...).functionCallReturnValue" follow to "cyScope1::returnValue"
|
|
3710
|
+
// before prefix matching tries "useMemo(...)" which goes to the useMemo scope
|
|
3711
|
+
const directEquivs = currentScope.equivalencies[source.schemaPath];
|
|
3712
|
+
if (directEquivs?.length > 0) {
|
|
3713
|
+
const results: Pick<ScopeVariable, 'scopeNodeName' | 'schemaPath'>[] =
|
|
3714
|
+
[];
|
|
3715
|
+
for (const equiv of directEquivs) {
|
|
3716
|
+
const resolved = resolveToSignature(
|
|
3717
|
+
{
|
|
3718
|
+
scopeNodeName: equiv.scopeNodeName,
|
|
3719
|
+
schemaPath: equiv.schemaPath,
|
|
3720
|
+
},
|
|
3721
|
+
visited,
|
|
3722
|
+
);
|
|
3723
|
+
results.push(...resolved);
|
|
3724
|
+
}
|
|
3725
|
+
if (results.length > 0) return results;
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3728
|
+
// Handle spread patterns like [...items].sort().functionCallReturnValue
|
|
3729
|
+
// Extract the spread variable and resolve it through the equivalency chain
|
|
3730
|
+
const spreadMatch = source.schemaPath.match(/^\[\.\.\.(\w+)\]/);
|
|
3731
|
+
if (spreadMatch) {
|
|
3732
|
+
const spreadVar = spreadMatch[1];
|
|
3733
|
+
const spreadPattern = spreadMatch[0];
|
|
3734
|
+
const varEquivs = currentScope.equivalencies[spreadVar];
|
|
3735
|
+
|
|
3736
|
+
if (varEquivs?.length > 0) {
|
|
3737
|
+
const results: Pick<ScopeVariable, 'scopeNodeName' | 'schemaPath'>[] =
|
|
3738
|
+
[];
|
|
3739
|
+
for (const equiv of varEquivs) {
|
|
3740
|
+
// Follow the variable equivalency and then resolve from there
|
|
3741
|
+
const resolvedVar = resolveToSignature(
|
|
3742
|
+
{
|
|
3743
|
+
scopeNodeName: equiv.scopeNodeName,
|
|
3744
|
+
schemaPath: equiv.schemaPath,
|
|
3745
|
+
},
|
|
3746
|
+
visited,
|
|
3747
|
+
);
|
|
3748
|
+
// For each resolved variable path, create the full path with array element suffix
|
|
3749
|
+
for (const rv of resolvedVar) {
|
|
3750
|
+
if (rv.schemaPath.startsWith('signature[')) {
|
|
3751
|
+
// Get the suffix after the spread pattern
|
|
3752
|
+
let suffix = source.schemaPath.slice(spreadPattern.length);
|
|
3753
|
+
|
|
3754
|
+
// Clean the suffix: strip array method chains like .sort(...).functionCallReturnValue[]
|
|
3755
|
+
// These don't change the data identity, just transform it.
|
|
3756
|
+
// Keep only the final element access parts like [0], [1], etc.
|
|
3757
|
+
// Pattern: strip everything from a method call up through functionCallReturnValue[]
|
|
3758
|
+
suffix = suffix.replace(
|
|
3759
|
+
/\.\w+\([^)]*\)\.functionCallReturnValue\[\]/g,
|
|
3760
|
+
'',
|
|
3761
|
+
);
|
|
3762
|
+
// Also handle simpler case without nested parens
|
|
3763
|
+
suffix = suffix.replace(
|
|
3764
|
+
/\.sort\(\w*\(\)\)\.functionCallReturnValue\[\]/g,
|
|
3765
|
+
'',
|
|
3766
|
+
);
|
|
3767
|
+
|
|
3768
|
+
// Add [] to indicate array element access from the spread
|
|
3769
|
+
const resolvedPath = rv.schemaPath + '[]' + suffix;
|
|
3770
|
+
results.push({
|
|
3771
|
+
scopeNodeName: rv.scopeNodeName,
|
|
3772
|
+
schemaPath: resolvedPath,
|
|
3773
|
+
});
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
if (results.length > 0) return results;
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
// Try to find prefix equivalencies that can resolve this path
|
|
3782
|
+
// For path like "cyScope3().signature[0][0]", check "cyScope3().signature[0]", etc.
|
|
3783
|
+
const pathParts = this.splitPath(source.schemaPath);
|
|
3784
|
+
for (let i = pathParts.length - 1; i > 0; i--) {
|
|
3785
|
+
const prefix = this.joinPathParts(pathParts.slice(0, i));
|
|
3786
|
+
const suffix = this.joinPathParts(pathParts.slice(i));
|
|
3787
|
+
const prefixEquivs = currentScope.equivalencies[prefix];
|
|
3788
|
+
|
|
3789
|
+
if (prefixEquivs?.length > 0) {
|
|
3790
|
+
const results: Pick<ScopeVariable, 'scopeNodeName' | 'schemaPath'>[] =
|
|
3791
|
+
[];
|
|
3792
|
+
for (const equiv of prefixEquivs) {
|
|
3793
|
+
const newPath = this.joinPathParts([equiv.schemaPath, suffix]);
|
|
3794
|
+
const resolved = resolveToSignature(
|
|
3795
|
+
{ scopeNodeName: equiv.scopeNodeName, schemaPath: newPath },
|
|
3796
|
+
visited,
|
|
3797
|
+
);
|
|
3798
|
+
results.push(...resolved);
|
|
3799
|
+
}
|
|
3800
|
+
if (results.length > 0) return results;
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
|
|
3804
|
+
return [source];
|
|
3805
|
+
};
|
|
3806
|
+
|
|
3485
3807
|
return entries.reduce(
|
|
3486
3808
|
(acc, entry) => {
|
|
3487
3809
|
if (entry.sourceCandidates.length === 0) return acc;
|
|
3488
|
-
const usages = entry.usages.filter(
|
|
3489
|
-
(u) => u.scopeNodeName === scopeNode.name,
|
|
3490
|
-
);
|
|
3810
|
+
const usages = entry.usages.filter(usageMatchesScope);
|
|
3491
3811
|
for (const usage of usages) {
|
|
3492
3812
|
acc[usage.schemaPath] ||= [];
|
|
3493
|
-
|
|
3813
|
+
// Resolve each source candidate through the equivalency chain
|
|
3814
|
+
for (const source of entry.sourceCandidates) {
|
|
3815
|
+
const resolvedSources = resolveToSignature(source, new Set());
|
|
3816
|
+
acc[usage.schemaPath].push(...resolvedSources);
|
|
3817
|
+
}
|
|
3494
3818
|
}
|
|
3495
3819
|
return acc;
|
|
3496
3820
|
},
|
|
@@ -3875,10 +4199,32 @@ export class ScopeDataStructure {
|
|
|
3875
4199
|
return scopeText;
|
|
3876
4200
|
}
|
|
3877
4201
|
|
|
3878
|
-
getEquivalentSignatureVariables() {
|
|
4202
|
+
getEquivalentSignatureVariables(): Record<string, string | string[]> {
|
|
3879
4203
|
const scopeNode = this.scopeNodes[this.scopeTreeManager.getRootName()];
|
|
3880
4204
|
|
|
3881
|
-
const equivalentSignatureVariables: Record<string, string> = {};
|
|
4205
|
+
const equivalentSignatureVariables: Record<string, string | string[]> = {};
|
|
4206
|
+
|
|
4207
|
+
// Helper to add equivalencies - accumulates into array if multiple values for same key
|
|
4208
|
+
// This is critical for OR expressions like `x = a || b` where x should map to both a and b
|
|
4209
|
+
const addEquivalency = (key: string, value: string) => {
|
|
4210
|
+
const existing = equivalentSignatureVariables[key];
|
|
4211
|
+
if (existing === undefined) {
|
|
4212
|
+
// First value - store as string
|
|
4213
|
+
equivalentSignatureVariables[key] = value;
|
|
4214
|
+
} else if (typeof existing === 'string') {
|
|
4215
|
+
if (existing !== value) {
|
|
4216
|
+
// Second different value - convert to array
|
|
4217
|
+
equivalentSignatureVariables[key] = [existing, value];
|
|
4218
|
+
}
|
|
4219
|
+
// Same value - no change needed
|
|
4220
|
+
} else {
|
|
4221
|
+
// Already an array - add if not already present
|
|
4222
|
+
if (!existing.includes(value)) {
|
|
4223
|
+
existing.push(value);
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
};
|
|
4227
|
+
|
|
3882
4228
|
for (const [path, equivalentValues] of Object.entries(
|
|
3883
4229
|
scopeNode.equivalencies,
|
|
3884
4230
|
)) {
|
|
@@ -3887,7 +4233,7 @@ export class ScopeDataStructure {
|
|
|
3887
4233
|
// Maps local variable names to their signature paths
|
|
3888
4234
|
// e.g., "propValue" -> "signature[0].prop"
|
|
3889
4235
|
if (path.startsWith('signature[')) {
|
|
3890
|
-
|
|
4236
|
+
addEquivalency(equivalentValue.schemaPath, path);
|
|
3891
4237
|
}
|
|
3892
4238
|
|
|
3893
4239
|
// Case 2: Hook variable equivalencies (new behavior)
|
|
@@ -3921,7 +4267,7 @@ export class ScopeDataStructure {
|
|
|
3921
4267
|
hookCallPath = dbEntry.sourceCandidates[0].schemaPath;
|
|
3922
4268
|
}
|
|
3923
4269
|
}
|
|
3924
|
-
|
|
4270
|
+
addEquivalency(path, hookCallPath);
|
|
3925
4271
|
}
|
|
3926
4272
|
}
|
|
3927
4273
|
|
|
@@ -3935,10 +4281,8 @@ export class ScopeDataStructure {
|
|
|
3935
4281
|
!equivalentValue.schemaPath.startsWith('signature[') && // not a signature path
|
|
3936
4282
|
!equivalentValue.schemaPath.endsWith('.functionCallReturnValue') // not already handled above
|
|
3937
4283
|
) {
|
|
3938
|
-
//
|
|
3939
|
-
|
|
3940
|
-
equivalentSignatureVariables[path] = equivalentValue.schemaPath;
|
|
3941
|
-
}
|
|
4284
|
+
// Add equivalency (will accumulate if multiple values for OR expressions)
|
|
4285
|
+
addEquivalency(path, equivalentValue.schemaPath);
|
|
3942
4286
|
}
|
|
3943
4287
|
|
|
3944
4288
|
// Case 4: Child component prop mappings (Fix 22)
|
|
@@ -3951,7 +4295,7 @@ export class ScopeDataStructure {
|
|
|
3951
4295
|
path.includes('().signature[') &&
|
|
3952
4296
|
!equivalentValue.schemaPath.includes('()') // schemaPath is a simple variable, not a function call
|
|
3953
4297
|
) {
|
|
3954
|
-
|
|
4298
|
+
addEquivalency(path, equivalentValue.schemaPath);
|
|
3955
4299
|
}
|
|
3956
4300
|
|
|
3957
4301
|
// Case 5: Destructured function parameters (Fix 25)
|
|
@@ -3966,7 +4310,7 @@ export class ScopeDataStructure {
|
|
|
3966
4310
|
!path.includes('.') && // path is a simple identifier (destructured prop name)
|
|
3967
4311
|
equivalentValue.schemaPath.startsWith('signature[') // schemaPath IS a signature path
|
|
3968
4312
|
) {
|
|
3969
|
-
|
|
4313
|
+
addEquivalency(path, equivalentValue.schemaPath);
|
|
3970
4314
|
}
|
|
3971
4315
|
|
|
3972
4316
|
// Case 7: Method calls on variables that result in .functionCallReturnValue (Fix 33)
|
|
@@ -3980,8 +4324,7 @@ export class ScopeDataStructure {
|
|
|
3980
4324
|
if (
|
|
3981
4325
|
!path.includes('.') && // path is a simple identifier
|
|
3982
4326
|
equivalentValue.schemaPath.endsWith('.functionCallReturnValue') && // ends with function return
|
|
3983
|
-
equivalentValue.schemaPath.includes('.')
|
|
3984
|
-
!(path in equivalentSignatureVariables) // not already captured
|
|
4327
|
+
equivalentValue.schemaPath.includes('.') // has property access (method call)
|
|
3985
4328
|
) {
|
|
3986
4329
|
// Check if this looks like a method call on a variable (not a hook call)
|
|
3987
4330
|
// Hook calls look like: hookName() or hookName<T>()
|
|
@@ -3995,7 +4338,7 @@ export class ScopeDataStructure {
|
|
|
3995
4338
|
const parenPos = hookCallPath.indexOf('(');
|
|
3996
4339
|
if (dotBeforeParen !== -1 && dotBeforeParen < parenPos) {
|
|
3997
4340
|
// This is a method call like "splat.split('/')", not a hook call
|
|
3998
|
-
|
|
4341
|
+
addEquivalency(path, equivalentValue.schemaPath);
|
|
3999
4342
|
}
|
|
4000
4343
|
}
|
|
4001
4344
|
}
|
|
@@ -4026,8 +4369,9 @@ export class ScopeDataStructure {
|
|
|
4026
4369
|
!equivalentValue.schemaPath.includes('()') // schemaPath is a simple variable
|
|
4027
4370
|
) {
|
|
4028
4371
|
// Only add if not already present from the root scope
|
|
4372
|
+
// Root scope values take precedence over child scope values
|
|
4029
4373
|
if (!(path in equivalentSignatureVariables)) {
|
|
4030
|
-
|
|
4374
|
+
addEquivalency(path, equivalentValue.schemaPath);
|
|
4031
4375
|
}
|
|
4032
4376
|
}
|
|
4033
4377
|
}
|
|
@@ -4039,12 +4383,83 @@ export class ScopeDataStructure {
|
|
|
4039
4383
|
// We need multiple passes because resolutions can depend on each other
|
|
4040
4384
|
const maxIterations = 5; // Prevent infinite loops
|
|
4041
4385
|
|
|
4386
|
+
// Helper function to resolve a single source path using equivalencies
|
|
4387
|
+
const resolveSourcePath = (
|
|
4388
|
+
sourcePath: string,
|
|
4389
|
+
equivMap: Record<string, string | string[]>,
|
|
4390
|
+
): string | null => {
|
|
4391
|
+
// Extract base variable from the path
|
|
4392
|
+
const dotIndex = sourcePath.indexOf('.');
|
|
4393
|
+
const bracketIndex = sourcePath.indexOf('[');
|
|
4394
|
+
|
|
4395
|
+
let baseVar: string;
|
|
4396
|
+
let rest: string;
|
|
4397
|
+
|
|
4398
|
+
if (dotIndex === -1 && bracketIndex === -1) {
|
|
4399
|
+
baseVar = sourcePath;
|
|
4400
|
+
rest = '';
|
|
4401
|
+
} else if (dotIndex === -1) {
|
|
4402
|
+
baseVar = sourcePath.slice(0, bracketIndex);
|
|
4403
|
+
rest = sourcePath.slice(bracketIndex);
|
|
4404
|
+
} else if (bracketIndex === -1) {
|
|
4405
|
+
baseVar = sourcePath.slice(0, dotIndex);
|
|
4406
|
+
rest = sourcePath.slice(dotIndex);
|
|
4407
|
+
} else {
|
|
4408
|
+
const firstIndex = Math.min(dotIndex, bracketIndex);
|
|
4409
|
+
baseVar = sourcePath.slice(0, firstIndex);
|
|
4410
|
+
rest = sourcePath.slice(firstIndex);
|
|
4411
|
+
}
|
|
4412
|
+
|
|
4413
|
+
// Look up the base variable in equivalencies
|
|
4414
|
+
if (baseVar in equivMap && equivMap[baseVar] !== sourcePath) {
|
|
4415
|
+
const baseResolved = equivMap[baseVar];
|
|
4416
|
+
// Skip if baseResolved is an array (handle later)
|
|
4417
|
+
if (Array.isArray(baseResolved)) return null;
|
|
4418
|
+
// If it resolves to a signature path, build the full resolved path
|
|
4419
|
+
if (
|
|
4420
|
+
baseResolved.startsWith('signature[') ||
|
|
4421
|
+
baseResolved.includes('()')
|
|
4422
|
+
) {
|
|
4423
|
+
if (baseResolved.endsWith('()')) {
|
|
4424
|
+
return baseResolved + '.functionCallReturnValue' + rest;
|
|
4425
|
+
}
|
|
4426
|
+
return baseResolved + rest;
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
return null;
|
|
4430
|
+
};
|
|
4431
|
+
|
|
4042
4432
|
for (let iteration = 0; iteration < maxIterations; iteration++) {
|
|
4043
4433
|
let changed = false;
|
|
4044
4434
|
|
|
4045
|
-
for (const [varName,
|
|
4435
|
+
for (const [varName, sourcePathOrArray] of Object.entries(
|
|
4046
4436
|
equivalentSignatureVariables,
|
|
4047
4437
|
)) {
|
|
4438
|
+
// Handle arrays (OR expressions) by resolving each element
|
|
4439
|
+
if (Array.isArray(sourcePathOrArray)) {
|
|
4440
|
+
const resolvedArray: string[] = [];
|
|
4441
|
+
let arrayChanged = false;
|
|
4442
|
+
for (const sourcePath of sourcePathOrArray) {
|
|
4443
|
+
// Try to resolve this path using transitive resolution
|
|
4444
|
+
const resolved = resolveSourcePath(
|
|
4445
|
+
sourcePath,
|
|
4446
|
+
equivalentSignatureVariables,
|
|
4447
|
+
);
|
|
4448
|
+
if (resolved && resolved !== sourcePath) {
|
|
4449
|
+
resolvedArray.push(resolved);
|
|
4450
|
+
arrayChanged = true;
|
|
4451
|
+
} else {
|
|
4452
|
+
resolvedArray.push(sourcePath);
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
if (arrayChanged) {
|
|
4456
|
+
equivalentSignatureVariables[varName] = resolvedArray;
|
|
4457
|
+
changed = true;
|
|
4458
|
+
}
|
|
4459
|
+
continue;
|
|
4460
|
+
}
|
|
4461
|
+
const sourcePath = sourcePathOrArray;
|
|
4462
|
+
|
|
4048
4463
|
// Skip if already fully resolved (contains function call syntax)
|
|
4049
4464
|
// BUT first check for computed value patterns that need resolution (Fix 28)
|
|
4050
4465
|
// AND method call patterns that need base variable resolution (Fix 33)
|
|
@@ -4106,6 +4521,8 @@ export class ScopeDataStructure {
|
|
|
4106
4521
|
baseVar !== varName
|
|
4107
4522
|
) {
|
|
4108
4523
|
const baseResolved = equivalentSignatureVariables[baseVar];
|
|
4524
|
+
// Skip if baseResolved is an array (OR expression)
|
|
4525
|
+
if (Array.isArray(baseResolved)) continue;
|
|
4109
4526
|
// Only resolve if the base resolved to something useful (contains () or .)
|
|
4110
4527
|
if (baseResolved.includes('()') || baseResolved.includes('.')) {
|
|
4111
4528
|
const newPath = baseResolved + rest;
|
|
@@ -4170,7 +4587,12 @@ export class ScopeDataStructure {
|
|
|
4170
4587
|
}
|
|
4171
4588
|
|
|
4172
4589
|
if (baseVar in equivalentSignatureVariables && baseVar !== varName) {
|
|
4173
|
-
|
|
4590
|
+
// Handle array case (OR expressions) - use first element
|
|
4591
|
+
const rawBaseResolved = equivalentSignatureVariables[baseVar];
|
|
4592
|
+
const baseResolved = Array.isArray(rawBaseResolved)
|
|
4593
|
+
? rawBaseResolved[0]
|
|
4594
|
+
: rawBaseResolved;
|
|
4595
|
+
if (!baseResolved) continue;
|
|
4174
4596
|
// If the base resolves to a hook call, add .functionCallReturnValue
|
|
4175
4597
|
if (baseResolved.endsWith('()')) {
|
|
4176
4598
|
const newPath = baseResolved + '.functionCallReturnValue' + rest;
|
|
@@ -4390,7 +4812,7 @@ export class ScopeDataStructure {
|
|
|
4390
4812
|
path: string;
|
|
4391
4813
|
conditionType: 'truthiness' | 'comparison' | 'switch';
|
|
4392
4814
|
comparedValues?: string[];
|
|
4393
|
-
location: 'if' | 'ternary' | 'logical-and' | 'switch';
|
|
4815
|
+
location: 'if' | 'ternary' | 'logical-and' | 'switch' | 'unconditional';
|
|
4394
4816
|
}>
|
|
4395
4817
|
>,
|
|
4396
4818
|
): void {
|