@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
|
@@ -6,7 +6,14 @@ type ImportsAnalysis = {
|
|
|
6
6
|
[importedFilePath: string]: ExportInfo[];
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
const importsAnalysisCache = new WeakMap<ts.SourceFile, ImportsAnalysis>();
|
|
10
|
+
|
|
9
11
|
export function getImportsAnalysis(sourceFile: ts.SourceFile): ImportsAnalysis {
|
|
12
|
+
const cached = importsAnalysisCache.get(sourceFile);
|
|
13
|
+
if (cached) {
|
|
14
|
+
return cached;
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
const importsMapEntries: [string, ExportInfo[]][] = [];
|
|
11
18
|
|
|
12
19
|
const importsVisitor = (node: ts.Node) => {
|
|
@@ -35,6 +42,7 @@ export function getImportsAnalysis(sourceFile: ts.SourceFile): ImportsAnalysis {
|
|
|
35
42
|
{} as ImportsAnalysis,
|
|
36
43
|
);
|
|
37
44
|
|
|
45
|
+
importsAnalysisCache.set(sourceFile, consolidatedImportsMapEntries);
|
|
38
46
|
return consolidatedImportsMapEntries;
|
|
39
47
|
}
|
|
40
48
|
|
|
@@ -2,21 +2,44 @@ import ts from 'typescript';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Cache for nearest tsconfig lookups (keyed by directory).
|
|
7
|
+
* Speeds up repeated lookups during module resolution.
|
|
8
|
+
*/
|
|
9
|
+
const nearestTsConfigCache = new Map<string, string | undefined>();
|
|
10
|
+
|
|
5
11
|
/**
|
|
6
12
|
* Find the nearest tsconfig.json by walking up the directory tree from the source file
|
|
7
13
|
*/
|
|
8
14
|
function findNearestTsConfig(sourceFileName: string): string | undefined {
|
|
9
15
|
let currentDir = path.dirname(sourceFileName);
|
|
10
16
|
const root = path.parse(currentDir).root;
|
|
17
|
+
const visited: string[] = [];
|
|
11
18
|
|
|
12
19
|
while (currentDir !== root) {
|
|
20
|
+
const cached = nearestTsConfigCache.get(currentDir);
|
|
21
|
+
if (cached !== undefined || nearestTsConfigCache.has(currentDir)) {
|
|
22
|
+
for (const dir of visited) {
|
|
23
|
+
nearestTsConfigCache.set(dir, cached);
|
|
24
|
+
}
|
|
25
|
+
return cached;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
visited.push(currentDir);
|
|
13
29
|
const tsConfigPath = path.join(currentDir, 'tsconfig.json');
|
|
14
30
|
if (fs.existsSync(tsConfigPath)) {
|
|
31
|
+
for (const dir of visited) {
|
|
32
|
+
nearestTsConfigCache.set(dir, tsConfigPath);
|
|
33
|
+
}
|
|
15
34
|
return tsConfigPath;
|
|
16
35
|
}
|
|
17
36
|
currentDir = path.dirname(currentDir);
|
|
18
37
|
}
|
|
19
38
|
|
|
39
|
+
for (const dir of visited) {
|
|
40
|
+
nearestTsConfigCache.set(dir, undefined);
|
|
41
|
+
}
|
|
42
|
+
|
|
20
43
|
return undefined;
|
|
21
44
|
}
|
|
22
45
|
|
|
@@ -25,6 +48,14 @@ function findNearestTsConfig(sourceFileName: string): string | undefined {
|
|
|
25
48
|
*/
|
|
26
49
|
const tsConfigCache = new Map<string, ts.CompilerOptions>();
|
|
27
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Cache resolved module lookups by program for fast repeat access.
|
|
53
|
+
*/
|
|
54
|
+
const resolvedModuleCacheByProgram = new WeakMap<
|
|
55
|
+
ts.Program,
|
|
56
|
+
Map<string, ts.ResolvedModuleFull | null>
|
|
57
|
+
>();
|
|
58
|
+
|
|
28
59
|
/**
|
|
29
60
|
* Get compiler options from a tsconfig file
|
|
30
61
|
*/
|
|
@@ -62,6 +93,19 @@ export function getResolvedModule(
|
|
|
62
93
|
// Try to find the nearest tsconfig for this source file
|
|
63
94
|
const nearestTsConfig = findNearestTsConfig(sourceFile.fileName);
|
|
64
95
|
|
|
96
|
+
const cacheForProgram =
|
|
97
|
+
resolvedModuleCacheByProgram.get(program) ??
|
|
98
|
+
new Map<string, ts.ResolvedModuleFull | null>();
|
|
99
|
+
if (!resolvedModuleCacheByProgram.has(program)) {
|
|
100
|
+
resolvedModuleCacheByProgram.set(program, cacheForProgram);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const cacheKey = `${nearestTsConfig ?? ''}|${sourceFile.fileName}|${moduleName}`;
|
|
104
|
+
if (cacheForProgram.has(cacheKey)) {
|
|
105
|
+
const cached = cacheForProgram.get(cacheKey);
|
|
106
|
+
return cached ?? undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
65
109
|
// Start with the program's compiler options
|
|
66
110
|
let compilerOptions = program.getCompilerOptions();
|
|
67
111
|
|
|
@@ -90,6 +134,7 @@ export function getResolvedModule(
|
|
|
90
134
|
|
|
91
135
|
// If TypeScript's resolution succeeded, return it
|
|
92
136
|
if (result.resolvedModule) {
|
|
137
|
+
cacheForProgram.set(cacheKey, result.resolvedModule);
|
|
93
138
|
return result.resolvedModule;
|
|
94
139
|
}
|
|
95
140
|
|
|
@@ -105,14 +150,17 @@ export function getResolvedModule(
|
|
|
105
150
|
for (const ext of extensions) {
|
|
106
151
|
const testPath = absolutePath + ext;
|
|
107
152
|
if (fs.existsSync(testPath)) {
|
|
108
|
-
|
|
153
|
+
const resolved = {
|
|
109
154
|
resolvedFileName: testPath,
|
|
110
155
|
extension: ext.includes('/') ? path.extname(ext) : ext,
|
|
111
156
|
isExternalLibraryImport: false,
|
|
112
157
|
} as ts.ResolvedModuleFull;
|
|
158
|
+
cacheForProgram.set(cacheKey, resolved);
|
|
159
|
+
return resolved;
|
|
113
160
|
}
|
|
114
161
|
}
|
|
115
162
|
}
|
|
116
163
|
|
|
164
|
+
cacheForProgram.set(cacheKey, null);
|
|
117
165
|
return undefined;
|
|
118
166
|
}
|
package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.ts
CHANGED
|
@@ -21,5 +21,6 @@ export function getSourceFilesForAllImports(
|
|
|
21
21
|
|
|
22
22
|
visit(sourceFile); // Start visiting nodes from the root source file
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const uniqueModuleNames = Array.from(new Set(moduleNames));
|
|
25
|
+
return getSourceFilesForImports(uniqueModuleNames, sourceFile, program);
|
|
25
26
|
}
|
|
@@ -336,11 +336,23 @@ async function getEntityDataStructureAndFunctionCalls({
|
|
|
336
336
|
functionName: importedExport.name,
|
|
337
337
|
});
|
|
338
338
|
|
|
339
|
-
// If neither signature nor return value found, this function
|
|
340
|
-
//
|
|
341
|
-
//
|
|
339
|
+
// If neither signature nor return value found, check if this function has matching
|
|
340
|
+
// external function calls. External functions (like useLoaderData from react-router)
|
|
341
|
+
// don't have entries in functionResults, so getReturnValue returns null for them.
|
|
342
|
+
// But their return values ARE traced in externalFunctionCalls via perVariableSchemas.
|
|
343
|
+
// We need to check for matching calls before skipping.
|
|
342
344
|
if (!signatureSchema && !returnValueSchema) {
|
|
343
|
-
|
|
345
|
+
const hasMatchingExternalCall =
|
|
346
|
+
dataStructure.externalFunctionCalls?.some(
|
|
347
|
+
(fc) =>
|
|
348
|
+
fc.name === importedExport.name ||
|
|
349
|
+
fc.name.startsWith(importedExport.name + '<'),
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
// Only skip if there are no matching external function calls
|
|
353
|
+
if (!hasMatchingExternalCall) {
|
|
354
|
+
return acc;
|
|
355
|
+
}
|
|
344
356
|
}
|
|
345
357
|
|
|
346
358
|
// Clone schemas before deduplication to avoid mutating shared references
|
|
@@ -570,11 +582,13 @@ async function getEntityDataStructureAndFunctionCalls({
|
|
|
570
582
|
// Get JSX rendering usages for array size and text length flow generation
|
|
571
583
|
const jsxRenderingUsages = getJsxRenderingUsages(dataStructure);
|
|
572
584
|
|
|
585
|
+
const equivalentSignatureVariables =
|
|
586
|
+
getEquivalentSignatureVariables(dataStructure);
|
|
587
|
+
|
|
573
588
|
const isolatedDataStructure = {
|
|
574
589
|
signatureSchema: deduplicateFunctionSchemas(clonedRawSignature),
|
|
575
590
|
returnValueSchema: deduplicateFunctionSchemas(clonedRawReturnValue),
|
|
576
|
-
equivalentSignatureVariables
|
|
577
|
-
getEquivalentSignatureVariables(dataStructure),
|
|
591
|
+
equivalentSignatureVariables,
|
|
578
592
|
dependencySchemas,
|
|
579
593
|
environmentVariables: dataStructure.environmentVariables,
|
|
580
594
|
conditionalUsages:
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
measureAndReportExecutionTime,
|
|
6
6
|
ProjectAnalyzer,
|
|
7
7
|
} from '~codeyam/analyze';
|
|
8
|
+
import { getFileByPathSafe } from '../../utils/getFileByPath';
|
|
8
9
|
import trackEntityCircularDependencies from './trackEntityCircularDependencies';
|
|
9
10
|
import validateDependencyAnalyses from './validateDependencyAnalyses';
|
|
10
11
|
import extractClassMethods from './analyzeEntities/extractClassMethods';
|
|
@@ -388,10 +389,19 @@ export default async function analyzeEntities({
|
|
|
388
389
|
// Circular dependency detected in path - skip
|
|
389
390
|
continue;
|
|
390
391
|
}
|
|
391
|
-
// If already visited via another traversal path,
|
|
392
|
-
//
|
|
393
|
-
//
|
|
392
|
+
// If already visited via another traversal path, check if it's a leaf.
|
|
393
|
+
// If it's a leaf, we should still wait for it to complete.
|
|
394
|
+
// Only skip if we've already explored it and it's NOT a leaf (meaning it's
|
|
395
|
+
// blocked on something else - continuing to traverse would cause a deadlock).
|
|
394
396
|
if (visited.has(dependentEntityKey)) {
|
|
397
|
+
// If the visited entity is a leaf, we should wait for it to complete
|
|
398
|
+
// before processing this entity. Don't skip it!
|
|
399
|
+
if (leafKeys.has(dependentEntityKey)) {
|
|
400
|
+
// It's a leaf that hasn't completed yet - wait for it
|
|
401
|
+
isLeaf = false;
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
// If it's not a leaf, it's blocked on something else - skip to avoid deadlock
|
|
395
405
|
continue;
|
|
396
406
|
}
|
|
397
407
|
isLeaf = false;
|
|
@@ -426,7 +436,7 @@ export default async function analyzeEntities({
|
|
|
426
436
|
}
|
|
427
437
|
|
|
428
438
|
const file =
|
|
429
|
-
entity.file ||
|
|
439
|
+
entity.file || getFileByPathSafe(projectAnalyzer, entity.filePath);
|
|
430
440
|
if (!file) {
|
|
431
441
|
awsLog(
|
|
432
442
|
`CodeYam: analyzeEntities: File not found for entity: ${entityKey}`,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Entity } from '~codeyam/types';
|
|
2
2
|
import findOrCreateEntity from './findOrCreateEntity';
|
|
3
3
|
import { ProjectAnalyzer } from '../../ProjectAnalyzer';
|
|
4
|
+
import { getFileByPathSafe } from '../../utils/getFileByPath';
|
|
4
5
|
import { AnalysisContext } from '../../analysisContext';
|
|
5
6
|
|
|
6
7
|
// Track files we've already warned about to avoid duplicate warnings
|
|
@@ -71,8 +72,9 @@ export default async function gatherEntityMap({
|
|
|
71
72
|
// ` → imported export ${importedExportFilePath}:${importedExportName}`,
|
|
72
73
|
// );
|
|
73
74
|
|
|
74
|
-
const importedFile =
|
|
75
|
-
|
|
75
|
+
const importedFile = getFileByPathSafe(
|
|
76
|
+
projectAnalyzer,
|
|
77
|
+
importedExportFilePath,
|
|
76
78
|
);
|
|
77
79
|
|
|
78
80
|
if (!importedFile) {
|
package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts
CHANGED
|
@@ -69,9 +69,6 @@ export default function validateDependencyAnalyses({
|
|
|
69
69
|
|
|
70
70
|
// if the entity has duplicate dependencies, handle gracefully
|
|
71
71
|
if (dependentAnalyses[dependencyFilePath]?.[dependencyName]) {
|
|
72
|
-
// console.log(
|
|
73
|
-
// `CodeYam: validateDependencyAnalyses: Entity ${entity.filePath}:${entity.name} has duplicate dependency ${dependencyFilePath}:${dependencyName}. Skipping...`,
|
|
74
|
-
// );
|
|
75
72
|
continue;
|
|
76
73
|
}
|
|
77
74
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Entity, EntityType, File, ProjectFramework } from '~codeyam/types';
|
|
2
2
|
import { ProjectAnalyzer } from '../ProjectAnalyzer';
|
|
3
|
+
import { getFileByPathSafe } from '../utils/getFileByPath';
|
|
3
4
|
import { generateSha } from '~codeyam/database';
|
|
4
5
|
import { isRemixRoute } from '~codeyam/utils';
|
|
5
6
|
|
|
@@ -88,11 +89,9 @@ export default function analyzeRemixRoute(
|
|
|
88
89
|
// Find root.tsx for this route's webapp and add as implicit dependency
|
|
89
90
|
// This ensures root.tsx dependencies (like useLoaderData in AppContent) get analyzed for mocking
|
|
90
91
|
const routeAppDir = file.path.split('/routes/')[0];
|
|
91
|
-
const rootFile =
|
|
92
|
-
(
|
|
93
|
-
|
|
94
|
-
f.path === `${routeAppDir}/root.jsx`,
|
|
95
|
-
);
|
|
92
|
+
const rootFile =
|
|
93
|
+
getFileByPathSafe(projectAnalyzer, `${routeAppDir}/root.tsx`) ??
|
|
94
|
+
getFileByPathSafe(projectAnalyzer, `${routeAppDir}/root.jsx`);
|
|
96
95
|
|
|
97
96
|
const rootDependencies: Entity['metadata']['importedExports'] = [];
|
|
98
97
|
if (rootFile) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Entity, File, Project } from '~codeyam/types';
|
|
2
2
|
import { FileAnalyzer } from '../FileAnalyzer';
|
|
3
3
|
import { ProjectAnalyzer } from '../ProjectAnalyzer';
|
|
4
|
+
import { getFileByPathSafe } from '../utils/getFileByPath';
|
|
4
5
|
import type { FunctionDependenciesMap } from '../asts/sourceFiles/getPseudoFile';
|
|
5
6
|
// import { generateSha } from '~codeyam/database';
|
|
6
7
|
|
|
@@ -63,9 +64,7 @@ export default function getImportedExports(
|
|
|
63
64
|
imports[filePath].resolvedPath ??
|
|
64
65
|
filePath;
|
|
65
66
|
|
|
66
|
-
let importedExportFile =
|
|
67
|
-
(f) => f.path === searchPath,
|
|
68
|
-
);
|
|
67
|
+
let importedExportFile = getFileByPathSafe(projectAnalyzer, searchPath);
|
|
69
68
|
|
|
70
69
|
// Track the actual path we found (may differ from searchPath after dist->src mapping)
|
|
71
70
|
let actualResolvedPath = imports[filePath].resolvedPath;
|
|
@@ -84,7 +83,7 @@ export default function getImportedExports(
|
|
|
84
83
|
const srcPath = `${packagePath}/src/${relativePath}`
|
|
85
84
|
.replace(/\.d\.ts$/, '.ts')
|
|
86
85
|
.replace(/\.js$/, '.ts');
|
|
87
|
-
importedExportFile =
|
|
86
|
+
importedExportFile = getFileByPathSafe(projectAnalyzer, srcPath);
|
|
88
87
|
if (importedExportFile) {
|
|
89
88
|
actualResolvedPath = srcPath;
|
|
90
89
|
console.log(
|
|
@@ -97,7 +96,7 @@ export default function getImportedExports(
|
|
|
97
96
|
if (!importedExportFile) {
|
|
98
97
|
const srcPath = searchPath.replace('/dist/', '/src/');
|
|
99
98
|
// Try exact match first
|
|
100
|
-
importedExportFile =
|
|
99
|
+
importedExportFile = getFileByPathSafe(projectAnalyzer, srcPath);
|
|
101
100
|
if (importedExportFile) {
|
|
102
101
|
actualResolvedPath = srcPath;
|
|
103
102
|
console.log(
|
|
@@ -114,13 +113,16 @@ export default function getImportedExports(
|
|
|
114
113
|
const basePath = srcPath
|
|
115
114
|
.replace(/\.d\.ts$/, '')
|
|
116
115
|
.replace(/\.js$/, '');
|
|
117
|
-
importedExportFile =
|
|
118
|
-
(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
importedExportFile =
|
|
117
|
+
getFileByPathSafe(projectAnalyzer, basePath + '.ts') ??
|
|
118
|
+
getFileByPathSafe(projectAnalyzer, basePath + '.tsx');
|
|
119
|
+
if (!importedExportFile) {
|
|
120
|
+
importedExportFile = project.files.find(
|
|
121
|
+
(f) =>
|
|
122
|
+
f.path.startsWith(basePath.replace('/index', '/')) &&
|
|
123
|
+
!f.path.includes('/dist/'),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
124
126
|
if (importedExportFile) {
|
|
125
127
|
actualResolvedPath = importedExportFile.path;
|
|
126
128
|
console.log(
|
|
@@ -101,27 +101,47 @@ export default function enrichArrayTypesFromChildSignatures(
|
|
|
101
101
|
): void => {
|
|
102
102
|
// Group by prop name (e.g., signature[0].survey.id, signature[0].survey.name -> "survey")
|
|
103
103
|
const propGroups: Record<string, Record<string, string>> = {};
|
|
104
|
+
// Also collect direct fields on signature[0] (e.g., signature[0].filePath -> "filePath")
|
|
105
|
+
const directFields: Record<string, string> = {};
|
|
104
106
|
|
|
105
107
|
for (const [path, type] of Object.entries(schema)) {
|
|
106
108
|
// Match signature[0].propName.field or signature[0].propName[].field patterns
|
|
107
109
|
// The (?:\[\])? makes array props optional, handling both:
|
|
108
110
|
// - signature[0].survey.updatedAt (object prop)
|
|
109
111
|
// - signature[0].questionAnswers[].question.questionText (array prop)
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
const [, propName, fieldPath] =
|
|
112
|
+
const nestedMatch = path.match(/^signature\[0\]\.(\w+)(?:\[\])?\.(.+)$/);
|
|
113
|
+
if (nestedMatch) {
|
|
114
|
+
const [, propName, fieldPath] = nestedMatch;
|
|
113
115
|
propGroups[propName] ||= {};
|
|
114
116
|
propGroups[propName][fieldPath] = type;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Also match direct fields: signature[0].fieldName (single level, no nested path)
|
|
121
|
+
// This handles cases like TruncatedFilePath where filePath is passed directly
|
|
122
|
+
const directMatch = path.match(/^signature\[0\]\.(\w+)$/);
|
|
123
|
+
if (directMatch) {
|
|
124
|
+
const [, fieldName] = directMatch;
|
|
125
|
+
directFields[fieldName] = type;
|
|
115
126
|
}
|
|
116
127
|
}
|
|
117
128
|
|
|
118
|
-
// Store with the full signature path as key
|
|
129
|
+
// Store nested prop groups with the full signature path as key
|
|
119
130
|
for (const [propName, fields] of Object.entries(propGroups)) {
|
|
120
131
|
if (Object.keys(fields).length > 0) {
|
|
121
132
|
const signaturePath = `${entityName}().signature[0].${propName}`;
|
|
122
133
|
childSignatureFieldsByPath.set(signaturePath, { propName, fields });
|
|
123
134
|
}
|
|
124
135
|
}
|
|
136
|
+
|
|
137
|
+
// Store direct fields with signature[0] as key (for when entire object is passed)
|
|
138
|
+
if (Object.keys(directFields).length > 0) {
|
|
139
|
+
const signaturePath = `${entityName}().signature[0]`;
|
|
140
|
+
childSignatureFieldsByPath.set(signaturePath, {
|
|
141
|
+
propName: '',
|
|
142
|
+
fields: directFields,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
125
145
|
};
|
|
126
146
|
|
|
127
147
|
// Process all schemas in dependencySchemas
|
|
@@ -187,15 +207,20 @@ export default function enrichArrayTypesFromChildSignatures(
|
|
|
187
207
|
for (const { filePath, entityName, arrayPath } of mockedArrayPaths) {
|
|
188
208
|
const schema = dependencySchemas[filePath][entityName].returnValueSchema;
|
|
189
209
|
|
|
190
|
-
// Check if this array already has element type definitions
|
|
210
|
+
// Check if this array already has INDEXED element type definitions (e.g., [0]: null)
|
|
191
211
|
// If so, skip it - it's likely a specialized array like [null] for refs
|
|
212
|
+
// But DON'T skip if it only has general [] element types - those can be enriched
|
|
192
213
|
const elementPathPrefix = `${arrayPath}[`;
|
|
193
|
-
const
|
|
194
|
-
path.startsWith(elementPathPrefix)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
214
|
+
const hasIndexedElementType = Object.keys(schema).some((path) => {
|
|
215
|
+
if (!path.startsWith(elementPathPrefix)) return false;
|
|
216
|
+
// Check if it's an indexed type like [0], [1], etc. vs general []
|
|
217
|
+
const afterPrefix = path.slice(elementPathPrefix.length);
|
|
218
|
+
// Indexed types start with a digit: [0], [1], etc.
|
|
219
|
+
return /^\d/.test(afterPrefix);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (hasIndexedElementType) {
|
|
223
|
+
// Array already has indexed element type info (e.g., useAutoAnimate()[0]: null)
|
|
199
224
|
// Don't override it with child signature fields
|
|
200
225
|
continue;
|
|
201
226
|
}
|
|
@@ -218,7 +243,13 @@ export default function enrichArrayTypesFromChildSignatures(
|
|
|
218
243
|
|
|
219
244
|
// Check if this local variable could have come from this mocked function
|
|
220
245
|
// Use heuristics: name matching (getSurveys -> surveys, getUsers -> users)
|
|
221
|
-
if
|
|
246
|
+
// Also check if the array path ends with the local variable name
|
|
247
|
+
// e.g., useLoaderData().functionCallReturnValue.entities ends with .entities
|
|
248
|
+
const arrayEndsWithLocalVar = arrayPath.endsWith(`.${localVarName}`);
|
|
249
|
+
if (
|
|
250
|
+
!couldBeDerivedFrom(localVarName, entityName) &&
|
|
251
|
+
!arrayEndsWithLocalVar
|
|
252
|
+
) {
|
|
222
253
|
continue;
|
|
223
254
|
}
|
|
224
255
|
|
|
@@ -257,8 +288,21 @@ export default function enrichArrayTypesFromChildSignatures(
|
|
|
257
288
|
|
|
258
289
|
for (const [fieldPath, type] of Object.entries(matchedFields)) {
|
|
259
290
|
const fullPath = `${elementPath}.${fieldPath}`;
|
|
260
|
-
|
|
291
|
+
const existingType = schema[fullPath];
|
|
292
|
+
if (!existingType) {
|
|
261
293
|
schema[fullPath] = type;
|
|
294
|
+
} else {
|
|
295
|
+
// Prefer the more specific (narrower) type
|
|
296
|
+
const existingIsOptional =
|
|
297
|
+
existingType.includes('| undefined') ||
|
|
298
|
+
existingType.includes('| null');
|
|
299
|
+
const newIsOptional =
|
|
300
|
+
type.includes('| undefined') || type.includes('| null');
|
|
301
|
+
if (existingIsOptional && !newIsOptional) {
|
|
302
|
+
// Use the new type - it's more specific
|
|
303
|
+
schema[fullPath] = type;
|
|
304
|
+
}
|
|
305
|
+
// Otherwise keep existing (same specificity or existing is more specific)
|
|
262
306
|
}
|
|
263
307
|
}
|
|
264
308
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
convertDotNotation,
|
|
3
3
|
fillInDirectSchemaGapsAndUnknowns,
|
|
4
|
+
buildSchemaIndexes,
|
|
5
|
+
type SchemaIndexes,
|
|
4
6
|
joinParenthesesAndArrays,
|
|
5
7
|
mergeJsonTypeDefinitions,
|
|
6
8
|
splitOutsideParenthesesAndArrays,
|
|
@@ -165,6 +167,7 @@ function processCall(
|
|
|
165
167
|
variableNameOccurrence: number | undefined,
|
|
166
168
|
options: GatherDataForMocksOptions | undefined,
|
|
167
169
|
hookCallIndex: number,
|
|
170
|
+
prebuiltIndexes?: SchemaIndexes,
|
|
168
171
|
): { key: string; value: JsonTypeDefinition } | null {
|
|
169
172
|
const callName = importedExport.calls?.[callIndex] ?? importedExport.name;
|
|
170
173
|
|
|
@@ -465,6 +468,7 @@ function processCall(
|
|
|
465
468
|
const filledSchema = fillInDirectSchemaGapsAndUnknowns({
|
|
466
469
|
scopeName: importedExport.name,
|
|
467
470
|
schema: relevantMergedDependencySchema,
|
|
471
|
+
prebuiltIndexes,
|
|
468
472
|
});
|
|
469
473
|
|
|
470
474
|
const schema = convertDotNotation(filledSchema);
|
|
@@ -568,6 +572,25 @@ export default function gatherDataForMocks(
|
|
|
568
572
|
dependencySchemas: DataStructure['dependencySchemas'],
|
|
569
573
|
options?: GatherDataForMocksOptions,
|
|
570
574
|
) {
|
|
575
|
+
// Build schema indexes ONCE from all dependency schemas combined.
|
|
576
|
+
// This avoids rebuilding indexes (~1-2 seconds for 18k keys) for each processCall.
|
|
577
|
+
const allSchemaKeys: Record<string, string> = {};
|
|
578
|
+
if (dependencySchemas) {
|
|
579
|
+
for (const filePath in dependencySchemas) {
|
|
580
|
+
for (const entityName in dependencySchemas[filePath]) {
|
|
581
|
+
const schema =
|
|
582
|
+
dependencySchemas[filePath][entityName]?.returnValueSchema;
|
|
583
|
+
if (schema) {
|
|
584
|
+
Object.assign(allSchemaKeys, schema);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const prebuiltIndexes =
|
|
590
|
+
Object.keys(allSchemaKeys).length > 0
|
|
591
|
+
? buildSchemaIndexes(allSchemaKeys)
|
|
592
|
+
: undefined;
|
|
593
|
+
|
|
571
594
|
// Track the global index for each hook name across all imports
|
|
572
595
|
// This enables canonical keys like EntityName::useLoaderData::0, EntityName::useFetcher::0, etc.
|
|
573
596
|
const hookCallIndices: Record<string, number> = {};
|
|
@@ -653,6 +676,7 @@ export default function gatherDataForMocks(
|
|
|
653
676
|
undefined,
|
|
654
677
|
options,
|
|
655
678
|
hookCallIndex,
|
|
679
|
+
prebuiltIndexes,
|
|
656
680
|
);
|
|
657
681
|
|
|
658
682
|
if (processResult) {
|
|
@@ -717,6 +741,7 @@ export default function gatherDataForMocks(
|
|
|
717
741
|
occurrence,
|
|
718
742
|
options,
|
|
719
743
|
hookCallIndex,
|
|
744
|
+
prebuiltIndexes,
|
|
720
745
|
);
|
|
721
746
|
if (processResult) {
|
|
722
747
|
addResultToAccumulator(acc, processResult.key, processResult.value);
|
|
@@ -749,6 +774,7 @@ export default function gatherDataForMocks(
|
|
|
749
774
|
undefined,
|
|
750
775
|
options,
|
|
751
776
|
hookCallIndex,
|
|
777
|
+
prebuiltIndexes,
|
|
752
778
|
);
|
|
753
779
|
|
|
754
780
|
if (processResult) {
|
|
@@ -769,6 +795,7 @@ export default function gatherDataForMocks(
|
|
|
769
795
|
undefined,
|
|
770
796
|
options,
|
|
771
797
|
hookCallIndex,
|
|
798
|
+
prebuiltIndexes,
|
|
772
799
|
);
|
|
773
800
|
|
|
774
801
|
// Check if we got a meaningful result (not empty object or null)
|
|
@@ -813,6 +840,7 @@ export default function gatherDataForMocks(
|
|
|
813
840
|
0, // Use 0 to trigger variable-qualified lookup
|
|
814
841
|
options,
|
|
815
842
|
hookCallIndex,
|
|
843
|
+
prebuiltIndexes,
|
|
816
844
|
);
|
|
817
845
|
if (varQualifiedResult && varQualifiedResult.value) {
|
|
818
846
|
mergedValue = mergeJsonTypeDefinitions(
|
|
@@ -849,6 +877,7 @@ export default function gatherDataForMocks(
|
|
|
849
877
|
0, // Use 0 to trigger variable-qualified lookup
|
|
850
878
|
options,
|
|
851
879
|
hookCallIndex,
|
|
880
|
+
prebuiltIndexes,
|
|
852
881
|
);
|
|
853
882
|
if (varQualifiedResult && varQualifiedResult.value) {
|
|
854
883
|
// Deep merge the value into the accumulated result
|
|
@@ -16,6 +16,7 @@ import { awsLog } from '~codeyam/utils';
|
|
|
16
16
|
import gatherDataForMocks from './gatherDataForMocks';
|
|
17
17
|
import enrichArrayTypesFromChildSignatures from './enrichArrayTypesFromChildSignatures';
|
|
18
18
|
import enrichUnknownTypesFromSourceEquivalencies from './enrichUnknownTypesFromSourceEquivalencies';
|
|
19
|
+
// import propagateArrayItemSchemas from './propagateArrayItemSchemas';
|
|
19
20
|
export interface GenerateDataStructureArgs {
|
|
20
21
|
entity: Entity;
|
|
21
22
|
dependentAnalyses: ReadonlyAnalysisMap;
|
|
@@ -469,11 +470,34 @@ export default function generateDataStructure({
|
|
|
469
470
|
|
|
470
471
|
if (existingSchema) {
|
|
471
472
|
// Merge schemas - child schemas add to parent schemas
|
|
473
|
+
// But prefer more specific types (without | undefined) when both exist
|
|
474
|
+
const mergedSignatureSchema = { ...existingSchema.signatureSchema };
|
|
475
|
+
for (const path in childSchema.signatureSchema) {
|
|
476
|
+
const existingType = mergedSignatureSchema[path];
|
|
477
|
+
const childType = childSchema.signatureSchema[path];
|
|
478
|
+
if (!existingType) {
|
|
479
|
+
mergedSignatureSchema[path] = childType;
|
|
480
|
+
} else {
|
|
481
|
+
// Prefer the more specific (narrower) type
|
|
482
|
+
const existingIsOptional =
|
|
483
|
+
existingType.includes('| undefined') ||
|
|
484
|
+
existingType.includes('| null');
|
|
485
|
+
const childIsOptional =
|
|
486
|
+
childType.includes('| undefined') ||
|
|
487
|
+
childType.includes('| null');
|
|
488
|
+
if (childIsOptional && !existingIsOptional) {
|
|
489
|
+
// Keep existing - it's more specific
|
|
490
|
+
} else if (!childIsOptional && existingIsOptional) {
|
|
491
|
+
// Use child - it's more specific
|
|
492
|
+
mergedSignatureSchema[path] = childType;
|
|
493
|
+
} else {
|
|
494
|
+
// Same specificity - use child (original behavior)
|
|
495
|
+
mergedSignatureSchema[path] = childType;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
472
499
|
mergedDataStructure.dependencySchemas[schemaFilePath][schemaName] = {
|
|
473
|
-
signatureSchema:
|
|
474
|
-
...existingSchema.signatureSchema,
|
|
475
|
-
...childSchema.signatureSchema,
|
|
476
|
-
},
|
|
500
|
+
signatureSchema: mergedSignatureSchema,
|
|
477
501
|
returnValueSchema: mergeJsonTypeDefinitions(
|
|
478
502
|
existingSchema.returnValueSchema as Record<string, unknown>,
|
|
479
503
|
childSchema.returnValueSchema as Record<string, unknown>,
|
|
@@ -560,6 +584,13 @@ export default function generateDataStructure({
|
|
|
560
584
|
allImportedExports,
|
|
561
585
|
);
|
|
562
586
|
|
|
587
|
+
// DISABLED: Testing if ternary fix eliminates the need for this heuristic
|
|
588
|
+
// Propagate array item schemas between arrays that likely have the same type.
|
|
589
|
+
// When one array (e.g., entities) has item schema traced but another similar array
|
|
590
|
+
// (e.g., currentEntities) doesn't (because it's only used in an OR expression fallback),
|
|
591
|
+
// copy the item schema from the rich array to the empty one.
|
|
592
|
+
// propagateArrayItemSchemas(mergedDataStructure.dependencySchemas);
|
|
593
|
+
|
|
563
594
|
let dataForMocks = gatherDataForMocks(
|
|
564
595
|
allImportedExports,
|
|
565
596
|
mergedDataStructure.dependencySchemas,
|