@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.
Files changed (171) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +2 -2
  4. package/analyzer-template/packages/ai/index.ts +6 -1
  5. package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +39 -17
  6. package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +67 -9
  7. package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +308 -50
  8. package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +15 -6
  9. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +664 -242
  10. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.ts +16 -3
  11. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.ts +6 -4
  12. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +20 -1
  13. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +35 -13
  14. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +160 -0
  15. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.ts +40 -30
  16. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +289 -83
  17. package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +269 -1
  18. package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +9 -5
  19. package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +11 -3
  20. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +1 -1
  21. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +297 -7
  22. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +1 -1
  23. package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +90 -96
  24. package/analyzer-template/packages/ai/src/lib/promptGenerators/gatherAttributesMap.ts +10 -7
  25. package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +25 -13
  26. package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +4 -3
  27. package/analyzer-template/packages/analyze/src/lib/FileAnalyzer.ts +65 -59
  28. package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +113 -26
  29. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.ts +19 -0
  30. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.ts +19 -0
  31. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllExports.ts +11 -0
  32. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.ts +8 -0
  33. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.ts +49 -1
  34. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.ts +2 -1
  35. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +20 -6
  36. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +14 -4
  37. package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +4 -2
  38. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +0 -3
  39. package/analyzer-template/packages/analyze/src/lib/files/analyzeRemixRoute.ts +4 -5
  40. package/analyzer-template/packages/analyze/src/lib/files/getImportedExports.ts +14 -12
  41. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +57 -13
  42. package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +29 -0
  43. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +35 -4
  44. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +117 -9
  45. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +199 -17
  46. package/analyzer-template/packages/analyze/src/lib/files/scenarios/propagateArrayItemSchemas.ts +474 -0
  47. package/analyzer-template/packages/analyze/src/lib/files/setImportedExports.ts +2 -1
  48. package/analyzer-template/packages/analyze/src/lib/utils/getFileByPath.ts +19 -0
  49. package/analyzer-template/packages/aws/package.json +1 -1
  50. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
  51. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  52. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  53. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  54. package/analyzer-template/packages/github/package.json +1 -1
  55. package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +6 -5
  56. package/analyzer-template/packages/types/src/types/ScopeAnalysis.ts +6 -1
  57. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
  58. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  59. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  60. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  61. package/analyzer-template/project/constructMockCode.ts +54 -9
  62. package/analyzer-template/project/writeMockDataTsx.ts +73 -2
  63. package/background/src/lib/virtualized/project/constructMockCode.js +45 -3
  64. package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
  65. package/background/src/lib/virtualized/project/writeMockDataTsx.js +71 -2
  66. package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
  67. package/codeyam-cli/scripts/apply-setup.js +146 -0
  68. package/codeyam-cli/scripts/apply-setup.js.map +1 -1
  69. package/codeyam-cli/src/commands/debug.js +7 -5
  70. package/codeyam-cli/src/commands/debug.js.map +1 -1
  71. package/codeyam-cli/src/utils/install-skills.js +22 -0
  72. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  73. package/codeyam-cli/src/utils/reviewedRules.js +92 -0
  74. package/codeyam-cli/src/utils/reviewedRules.js.map +1 -0
  75. package/codeyam-cli/src/webserver/build/client/assets/globals-CX9f-5xM.css +1 -0
  76. package/codeyam-cli/src/webserver/build/client/assets/{manifest-7522edd4.js → manifest-bba56ec1.js} +1 -1
  77. package/codeyam-cli/src/webserver/build/client/assets/memory-DuTFSyJ2.js +92 -0
  78. package/codeyam-cli/src/webserver/build/client/assets/{root-eVAaavTS.js → root-DTfSQARG.js} +6 -6
  79. package/codeyam-cli/src/webserver/build/server/assets/{index-DVzYx8PN.js → index-TD1f-DHV.js} +1 -1
  80. package/codeyam-cli/src/webserver/build/server/assets/server-build-BQ-1XyEa.js +258 -0
  81. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  82. package/codeyam-cli/src/webserver/build-info.json +5 -5
  83. package/codeyam-cli/templates/codeyam:memory.md +174 -233
  84. package/codeyam-cli/templates/codeyam:new-rule.md +41 -2
  85. package/codeyam-cli/templates/rule-reflection-hook.py +161 -0
  86. package/codeyam-cli/templates/rules-instructions.md +126 -0
  87. package/package.json +1 -1
  88. package/packages/ai/index.js +2 -1
  89. package/packages/ai/index.js.map +1 -1
  90. package/packages/ai/src/lib/analyzeScope.js +29 -12
  91. package/packages/ai/src/lib/analyzeScope.js.map +1 -1
  92. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +54 -8
  93. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
  94. package/packages/ai/src/lib/astScopes/processExpression.js +239 -43
  95. package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
  96. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +503 -165
  97. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  98. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js +13 -3
  99. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js.map +1 -1
  100. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js +6 -4
  101. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js.map +1 -1
  102. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +22 -1
  103. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  104. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +34 -9
  105. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
  106. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +159 -0
  107. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js.map +1 -0
  108. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js +37 -20
  109. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js.map +1 -1
  110. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +237 -73
  111. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
  112. package/packages/ai/src/lib/generateEntityScenarioData.js +195 -1
  113. package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
  114. package/packages/ai/src/lib/generateEntityScenarios.js +7 -1
  115. package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
  116. package/packages/ai/src/lib/generateExecutionFlows.js +10 -2
  117. package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
  118. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +209 -3
  119. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -1
  120. package/packages/ai/src/lib/mergeStatements.js +70 -51
  121. package/packages/ai/src/lib/mergeStatements.js.map +1 -1
  122. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js +10 -4
  123. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js.map +1 -1
  124. package/packages/ai/src/lib/resolvePathToControllable.js +24 -14
  125. package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -1
  126. package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
  127. package/packages/analyze/src/lib/FileAnalyzer.js +60 -36
  128. package/packages/analyze/src/lib/FileAnalyzer.js.map +1 -1
  129. package/packages/analyze/src/lib/ProjectAnalyzer.js +96 -26
  130. package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
  131. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js +14 -0
  132. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js.map +1 -1
  133. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js +14 -0
  134. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js.map +1 -1
  135. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js +6 -0
  136. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js.map +1 -1
  137. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js +6 -0
  138. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js.map +1 -1
  139. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js +39 -1
  140. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js.map +1 -1
  141. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js +2 -1
  142. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js.map +1 -1
  143. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +13 -5
  144. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  145. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +14 -4
  146. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  147. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +2 -1
  148. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  149. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +0 -3
  150. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  151. package/packages/analyze/src/lib/files/analyzeRemixRoute.js +3 -2
  152. package/packages/analyze/src/lib/files/analyzeRemixRoute.js.map +1 -1
  153. package/packages/analyze/src/lib/files/getImportedExports.js +11 -7
  154. package/packages/analyze/src/lib/files/getImportedExports.js.map +1 -1
  155. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +52 -10
  156. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
  157. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +25 -8
  158. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
  159. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +34 -4
  160. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  161. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +56 -8
  162. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
  163. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +168 -9
  164. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  165. package/packages/analyze/src/lib/files/setImportedExports.js +2 -1
  166. package/packages/analyze/src/lib/files/setImportedExports.js.map +1 -1
  167. package/packages/analyze/src/lib/utils/getFileByPath.js +12 -0
  168. package/packages/analyze/src/lib/utils/getFileByPath.js.map +1 -0
  169. package/codeyam-cli/src/webserver/build/client/assets/globals-D3yhhV8x.css +0 -1
  170. package/codeyam-cli/src/webserver/build/client/assets/memory-yxFcrxBX.js +0 -92
  171. package/codeyam-cli/src/webserver/build/server/assets/server-build-4Cr0uToj.js +0 -257
@@ -525,7 +525,7 @@ function findControllableBaseForDerivedPath(
525
525
  export default function resolvePathToControllable(
526
526
  localPath: string,
527
527
  attributesMap: Record<string, string>,
528
- equivalentSignatureVariables: Record<string, string>,
528
+ equivalentSignatureVariables: Record<string, string | string[]>,
529
529
  fullToShortPathMap: Record<string, string>,
530
530
  ): PathResolutionResult {
531
531
  const chain: string[] = [localPath];
@@ -600,7 +600,11 @@ export default function resolvePathToControllable(
600
600
 
601
601
  // 4. Equivalent variable resolution
602
602
  const localVarName = extractLocalVariableName(localPath);
603
- const dataSourceBase = equivalentSignatureVariables[localVarName];
603
+ // Handle array case (OR expressions) - use first element if array
604
+ const rawDataSourceBase = equivalentSignatureVariables[localVarName];
605
+ const dataSourceBase = Array.isArray(rawDataSourceBase)
606
+ ? rawDataSourceBase[0]
607
+ : rawDataSourceBase;
604
608
 
605
609
  if (dataSourceBase) {
606
610
  chain.push(`equivalent var: ${localVarName} → ${dataSourceBase}`);
@@ -626,18 +630,26 @@ export default function resolvePathToControllable(
626
630
  baseVarName in equivalentSignatureVariables &&
627
631
  baseVarName !== localVarName
628
632
  ) {
629
- const baseDataSource = equivalentSignatureVariables[baseVarName];
630
- chain.push(`transitive resolution: ${baseVarName} → ${baseDataSource}`);
631
- // Append the array access suffix to the resolved base
632
- if (baseDataSource.endsWith('()')) {
633
- fullResolvedPath =
634
- baseDataSource + '.functionCallReturnValue' + accessSuffix;
635
- } else if (baseDataSource.endsWith('.functionCallReturnValue')) {
636
- fullResolvedPath = baseDataSource + accessSuffix;
637
- } else {
638
- fullResolvedPath = baseDataSource + accessSuffix;
633
+ // Handle array case (OR expressions) - use first element if array
634
+ const rawBaseDataSource = equivalentSignatureVariables[baseVarName];
635
+ const baseDataSource = Array.isArray(rawBaseDataSource)
636
+ ? rawBaseDataSource[0]
637
+ : rawBaseDataSource;
638
+ if (baseDataSource) {
639
+ chain.push(
640
+ `transitive resolution: ${baseVarName} ${baseDataSource}`,
641
+ );
642
+ // Append the array access suffix to the resolved base
643
+ if (baseDataSource.endsWith('()')) {
644
+ fullResolvedPath =
645
+ baseDataSource + '.functionCallReturnValue' + accessSuffix;
646
+ } else if (baseDataSource.endsWith('.functionCallReturnValue')) {
647
+ fullResolvedPath = baseDataSource + accessSuffix;
648
+ } else {
649
+ fullResolvedPath = baseDataSource + accessSuffix;
650
+ }
651
+ chain.push(`transitively resolved: ${fullResolvedPath}`);
639
652
  }
640
- chain.push(`transitively resolved: ${fullResolvedPath}`);
641
653
  }
642
654
  }
643
655
 
@@ -63,7 +63,7 @@ export interface EnrichedConditionalUsage {
63
63
  /** For comparison conditions, the literal values being compared against */
64
64
  comparedValues?: string[];
65
65
  /** Where this conditional usage occurs */
66
- location: 'if' | 'ternary' | 'logical-and' | 'switch';
66
+ location: 'if' | 'ternary' | 'logical-and' | 'switch' | 'unconditional';
67
67
  /**
68
68
  * The traced source data path in the format "scopeName.path"
69
69
  * e.g., "useParams().functionCallReturnValue['*']"
@@ -120,7 +120,8 @@ export interface SerializableDataStructure {
120
120
  functionResults: Record<string, SerializableFunctionResult>;
121
121
 
122
122
  // Equivalent signature variables for root scope
123
- equivalentSignatureVariables: Record<string, string>;
123
+ // Values can be arrays for OR expressions where a variable maps to multiple sources
124
+ equivalentSignatureVariables: Record<string, string | string[]>;
124
125
 
125
126
  environmentVariables: string[];
126
127
 
@@ -296,7 +297,7 @@ export function getSourceEquivalencies(
296
297
 
297
298
  export function getEquivalentSignatureVariables(
298
299
  dataStructure: SerializableDataStructure,
299
- ): Record<string, string> {
300
+ ): Record<string, string | string[]> {
300
301
  return dataStructure.equivalentSignatureVariables;
301
302
  }
302
303
 
@@ -9,6 +9,7 @@ import {
9
9
  import { FunctionNode } from './types';
10
10
  import getEntityType from './files/getEntityType';
11
11
  import getEntityCode from './files/getEntityCode';
12
+ import { getFileByPathSafe } from './utils/getFileByPath';
12
13
 
13
14
  export class FileAnalyzer {
14
15
  public projectAnalyzer: lib.ProjectAnalyzer;
@@ -19,6 +20,7 @@ export class FileAnalyzer {
19
20
  // Current per-file caching helps when multiple entities in the same file call getResolvedImportsRelevantToFunctionName,
20
21
  // but a project-level cache would also benefit the recursive import chain traversals that cross file boundaries.
21
22
  private resolvedImportsCache: Map<string, FunctionDependenciesMap>;
23
+ private allEntityNodesOnlyAliasesCache?: Record<string, ts.Node>;
22
24
 
23
25
  constructor({
24
26
  projectAnalyzer,
@@ -62,18 +64,23 @@ export class FileAnalyzer {
62
64
 
63
65
  getAllEntityNodesOnlyAliases() {
64
66
  if (!this.sourceFile) return {};
67
+ if (this.allEntityNodesOnlyAliasesCache) {
68
+ return this.allEntityNodesOnlyAliasesCache;
69
+ }
65
70
  try {
66
71
  const allEntityNodes = lib.asts.sourceFiles.getAllEntityNodes({
67
72
  sourceFile: this.sourceFile,
68
73
  });
69
- if (allEntityNodes.default) {
70
- const namedDefault = Object.entries(allEntityNodes).find(
74
+ const aliasesOnly = { ...allEntityNodes };
75
+ if (aliasesOnly.default) {
76
+ const namedDefault = Object.entries(aliasesOnly).find(
71
77
  ([otherEntityName, node]) =>
72
- otherEntityName !== 'default' && node === allEntityNodes.default,
78
+ otherEntityName !== 'default' && node === aliasesOnly.default,
73
79
  );
74
- if (namedDefault) delete allEntityNodes.default;
80
+ if (namedDefault) delete aliasesOnly.default;
75
81
  }
76
- return allEntityNodes;
82
+ this.allEntityNodesOnlyAliasesCache = aliasesOnly;
83
+ return aliasesOnly;
77
84
  } catch (error) {
78
85
  console.log('CodeYam ERROR: getAllEntityNodesOnlyAliases failed:', {
79
86
  filePath: this.file?.path,
@@ -223,15 +230,11 @@ export class FileAnalyzer {
223
230
 
224
231
  // Handle dist -> src mapping for workspace packages
225
232
  let targetPath = resolvedPath;
226
- let targetFile = this.projectAnalyzer.project.files.find(
227
- (f) => f.path === targetPath,
228
- );
233
+ let targetFile = getFileByPathSafe(this.projectAnalyzer, targetPath);
229
234
 
230
235
  if (!targetFile && targetPath.includes('/dist/')) {
231
236
  const srcPath = targetPath.replace('/dist/', '/src/');
232
- targetFile = this.projectAnalyzer.project.files.find(
233
- (f) => f.path === srcPath,
234
- );
237
+ targetFile = getFileByPathSafe(this.projectAnalyzer, srcPath);
235
238
  if (targetFile) {
236
239
  targetPath = srcPath;
237
240
  }
@@ -239,9 +242,9 @@ export class FileAnalyzer {
239
242
  // Try with different extensions if still not found
240
243
  if (!targetFile) {
241
244
  const basePath = srcPath.replace(/\.d\.ts$/, '').replace(/\.js$/, '');
242
- targetFile = this.projectAnalyzer.project.files.find(
243
- (f) => f.path === basePath + '.ts' || f.path === basePath + '.tsx',
244
- );
245
+ targetFile =
246
+ getFileByPathSafe(this.projectAnalyzer, basePath + '.ts') ??
247
+ getFileByPathSafe(this.projectAnalyzer, basePath + '.tsx');
245
248
  if (targetFile) {
246
249
  targetPath = targetFile.path;
247
250
  }
@@ -301,24 +304,25 @@ export class FileAnalyzer {
301
304
 
302
305
  getLocalEntitiesUsedInFunction(functionName: string) {
303
306
  if (!this.sourceFile) return [];
304
- const entityNodes = lib.asts.sourceFiles.getAllEntityNodes({
305
- sourceFile: this.sourceFile,
306
- });
307
+ const entityNodes = this.getAllEntityNodes();
307
308
 
308
309
  // Defensive check: ensure the function exists in entityNodes
309
- if (!entityNodes[functionName]) {
310
+ const functionNode = entityNodes[functionName];
311
+ if (!functionNode) {
310
312
  return [];
311
313
  }
312
314
 
313
315
  try {
314
- const identifiers = this.getIdentifiersInNode(entityNodes[functionName]);
316
+ const identifiers = this.getIdentifiersInNode(functionNode);
315
317
  return identifiers
316
- .filter(
317
- (identifier, index, self) =>
318
- entityNodes[identifier] !== entityNodes[functionName] &&
319
- Object.keys(entityNodes).includes(identifier) &&
320
- self.indexOf(identifier) === index,
321
- )
318
+ .filter((identifier) => {
319
+ const node = entityNodes[identifier];
320
+ return (
321
+ !!node &&
322
+ node !== functionNode &&
323
+ typeof (node as ts.Node).kind === 'number'
324
+ );
325
+ })
322
326
  .map((identifier) => ({
323
327
  name: identifier,
324
328
  node: entityNodes[identifier],
@@ -348,14 +352,13 @@ export class FileAnalyzer {
348
352
  sourceFile: this.sourceFile,
349
353
  });
350
354
  const identifiersInRelevantNode = this.getIdentifiersInNode(relevantNode);
355
+ const identifiersSet = new Set(identifiersInRelevantNode);
351
356
 
352
357
  const entries = Object.entries(importsAnalysis)
353
358
  .map(([importedFilePath, importedExpressions]) => {
354
359
  const relevantExpressions = importedExpressions.filter(
355
360
  (importedExpression) => {
356
- return identifiersInRelevantNode.includes(
357
- importedExpression.importAlias,
358
- );
361
+ return identifiersSet.has(importedExpression.importAlias);
359
362
  },
360
363
  );
361
364
 
@@ -439,8 +442,9 @@ export class FileAnalyzer {
439
442
  resolvedImport.resolvedFileName,
440
443
  );
441
444
 
442
- let importedFile = this.projectAnalyzer.project.files.find(
443
- (f) => f.path === projectPath,
445
+ let importedFile = getFileByPathSafe(
446
+ this.projectAnalyzer,
447
+ projectPath,
444
448
  );
445
449
 
446
450
  // Workspace packages often resolve to dist/ paths (from package.json "types" or "main"),
@@ -448,9 +452,7 @@ export class FileAnalyzer {
448
452
  if (!importedFile && projectPath.includes('/dist/')) {
449
453
  const srcPath = projectPath.replace('/dist/', '/src/');
450
454
  // Try exact match first (e.g., dist/index.d.ts -> src/index.ts)
451
- importedFile = this.projectAnalyzer.project.files.find(
452
- (f) => f.path === srcPath,
453
- );
455
+ importedFile = getFileByPathSafe(this.projectAnalyzer, srcPath);
454
456
  if (importedFile) {
455
457
  projectPath = srcPath;
456
458
  }
@@ -460,10 +462,9 @@ export class FileAnalyzer {
460
462
  const basePath = srcPath
461
463
  .replace(/\.d\.ts$/, '')
462
464
  .replace(/\.js$/, '');
463
- importedFile = this.projectAnalyzer.project.files.find(
464
- (f) =>
465
- f.path === basePath + '.ts' || f.path === basePath + '.tsx',
466
- );
465
+ importedFile =
466
+ getFileByPathSafe(this.projectAnalyzer, basePath + '.ts') ??
467
+ getFileByPathSafe(this.projectAnalyzer, basePath + '.tsx');
467
468
  if (importedFile) {
468
469
  projectPath = importedFile.path;
469
470
  }
@@ -591,24 +592,35 @@ export class FileAnalyzer {
591
592
 
592
593
  getNamespaceIdentifiersInNode(node: ts.Node, namespaceIdentifier: string) {
593
594
  const identifiers: string[] = [];
595
+ const seen = new Set<string>();
594
596
 
595
597
  const identifierVisitor = (child: ts.Node) => {
596
598
  if (ts.isPropertyAccessExpression(child)) {
597
599
  if (child.expression.getText() === namespaceIdentifier) {
598
- identifiers.push(child.name.getText());
600
+ const name = child.name.getText();
601
+ if (!seen.has(name)) {
602
+ seen.add(name);
603
+ identifiers.push(name);
604
+ }
599
605
  }
600
606
  }
601
607
  ts.forEachChild(child, identifierVisitor);
602
608
  };
603
609
 
604
610
  ts.forEachChild(node, identifierVisitor);
605
- return identifiers.filter(
606
- (identifier, index, self) => self.indexOf(identifier) === index,
607
- );
611
+ return identifiers;
608
612
  }
609
613
 
610
614
  getIdentifiersInNode(node: ts.Node): string[] {
611
615
  const identifiers: string[] = [];
616
+ const seen = new Set<string>();
617
+
618
+ const addIdentifier = (text: string, allowDefault = false) => {
619
+ if (!allowDefault && text === 'default') return;
620
+ if (seen.has(text)) return;
621
+ seen.add(text);
622
+ identifiers.push(text);
623
+ };
612
624
 
613
625
  // Helper to safely get text from an identifier or string literal
614
626
  const getNodeText = (node: ts.Identifier | ts.StringLiteral): string => {
@@ -637,63 +649,57 @@ export class FileAnalyzer {
637
649
  };
638
650
 
639
651
  if (ts.isIdentifier(node)) {
640
- identifiers.push(getNodeText(node));
652
+ addIdentifier(getNodeText(node));
641
653
  }
642
654
 
643
655
  const identifierVisitor = (child: ts.Node) => {
644
656
  if (ts.isIdentifier(child)) {
645
- const text = getNodeText(child);
646
- if (text === 'default') return;
647
- identifiers.push(text);
657
+ addIdentifier(getNodeText(child));
648
658
  } else if (ts.isExportDeclaration(child)) {
649
659
  if (child.exportClause && ts.isNamedExports(child.exportClause)) {
650
660
  child.exportClause.elements.forEach((element) => {
651
- identifiers.push(getNodeText(element.name));
661
+ addIdentifier(getNodeText(element.name), true);
652
662
  });
653
663
  } else if (
654
664
  child.exportClause &&
655
665
  ts.isNamespaceExport(child.exportClause)
656
666
  ) {
657
- identifiers.push(getNodeText(child.exportClause.name));
667
+ addIdentifier(getNodeText(child.exportClause.name));
658
668
  }
659
669
  } else if (ts.isExportAssignment(child)) {
660
670
  if (ts.isIdentifier(child.expression)) {
661
- identifiers.push(getNodeText(child.expression));
671
+ addIdentifier(getNodeText(child.expression));
662
672
  }
663
673
  } else if (ts.isExportSpecifier(child)) {
664
- identifiers.push(getNodeText(child.name));
674
+ addIdentifier(getNodeText(child.name), true);
665
675
  return;
666
676
  } else if (ts.isFunctionDeclaration(child) && child.name) {
667
- identifiers.push(getNodeText(child.name));
677
+ addIdentifier(getNodeText(child.name));
668
678
  } else if (ts.isVariableStatement(child)) {
669
679
  child.declarationList.declarations.forEach((declaration) => {
670
680
  if (ts.isIdentifier(declaration.name)) {
671
- identifiers.push(getNodeText(declaration.name));
681
+ addIdentifier(getNodeText(declaration.name));
672
682
  }
673
683
  });
674
684
  } else if (ts.isImportDeclaration(child)) {
675
685
  if (child.importClause && child.importClause.namedBindings) {
676
686
  if (ts.isNamespaceImport(child.importClause.namedBindings)) {
677
- identifiers.push(
678
- getNodeText(child.importClause.namedBindings.name),
679
- );
687
+ addIdentifier(getNodeText(child.importClause.namedBindings.name));
680
688
  } else if (ts.isNamedImports(child.importClause.namedBindings)) {
681
689
  child.importClause.namedBindings.elements.forEach((element) => {
682
- identifiers.push(getNodeText(element.name));
690
+ addIdentifier(getNodeText(element.name));
683
691
  });
684
692
  }
685
693
  }
686
694
  } else if (ts.isImportSpecifier(child)) {
687
- identifiers.push(getNodeText(child.name));
695
+ addIdentifier(getNodeText(child.name), true);
688
696
  return;
689
697
  }
690
698
  ts.forEachChild(child, identifierVisitor);
691
699
  };
692
700
 
693
701
  ts.forEachChild(node, identifierVisitor);
694
- return identifiers.filter(
695
- (identifier, index, self) => self.indexOf(identifier) === index,
696
- );
702
+ return identifiers;
697
703
  }
698
704
 
699
705
  resolveNamespaceImport(
@@ -13,6 +13,8 @@ export class ProjectAnalyzer {
13
13
  programs: ts.Program[]; // All programs from all tsconfigs
14
14
  typeChecker: ts.TypeChecker;
15
15
  private filesByPath: Map<string, File>; // O(1) lookup cache for files
16
+ private sourceFileByPath: Map<string, ts.SourceFile>; // O(1) lookup cache for source files
17
+ private typeCheckerByPath: Map<string, ts.TypeChecker>; // O(1) lookup cache for type checkers by file
16
18
 
17
19
  private constructor({
18
20
  project,
@@ -47,6 +49,11 @@ export class ProjectAnalyzer {
47
49
  // Primary program is the first one (usually root) for backward compatibility
48
50
  this.program = this.programs[0];
49
51
  this.typeChecker = this.program.getTypeChecker();
52
+
53
+ // Build source file and type checker indexes for fast lookups
54
+ this.sourceFileByPath = new Map();
55
+ this.typeCheckerByPath = new Map();
56
+ this.buildSourceFileIndex();
50
57
  }
51
58
 
52
59
  static from({
@@ -73,32 +80,7 @@ export class ProjectAnalyzer {
73
80
  }
74
81
 
75
82
  getFileAnalyzerByPath(filePath: string) {
76
- if (this.project.files?.length !== this.filesByPath.size) {
77
- // Rebuild the cache if project.files has changed
78
- this.filesByPath.clear();
79
- for (const file of this.project.files) {
80
- this.filesByPath.set(file.path, file);
81
- }
82
- }
83
-
84
- // Convert absolute path to relative if needed
85
- let relativePath = filePath;
86
- if (filePath.startsWith(this.dirPath + '/')) {
87
- relativePath = this.getRelativePath(filePath);
88
- } else if (filePath.startsWith('/')) {
89
- // Might be an absolute path with a different prefix (e.g., symlink)
90
- // Try to normalize it to match our dirPath
91
- try {
92
- const normalizedPath = realpathSync(filePath);
93
- if (normalizedPath.startsWith(this.dirPath + '/')) {
94
- relativePath = normalizedPath.replace(this.dirPath + '/', '');
95
- }
96
- } catch (error) {
97
- // If normalization fails, keep the original path
98
- }
99
- }
100
-
101
- const file = this.filesByPath.get(relativePath);
83
+ const file = this.getFileByPath(filePath);
102
84
  if (!file) {
103
85
  // Technically FileAnalyzer was built to accept null file, but most of
104
86
  // the code we've written since assumes fileAnalyzer.sourceFile exists,
@@ -109,6 +91,33 @@ export class ProjectAnalyzer {
109
91
  return new lib.FileAnalyzer({ projectAnalyzer: this, file });
110
92
  }
111
93
 
94
+ getFileByPath(filePath: string): File | undefined {
95
+ this.ensureFileCache();
96
+
97
+ const candidates = new Set<string>();
98
+ if (filePath) {
99
+ candidates.add(filePath);
100
+ }
101
+
102
+ if (!filePath.startsWith('/')) {
103
+ candidates.add(filePath);
104
+ } else if (filePath.startsWith(this.dirPath + '/')) {
105
+ candidates.add(filePath.slice(this.dirPath.length + 1));
106
+ }
107
+
108
+ const relativePath = this.toProjectRelativePath(filePath);
109
+ if (relativePath) {
110
+ candidates.add(relativePath);
111
+ }
112
+
113
+ for (const candidate of candidates) {
114
+ const file = this.filesByPath.get(candidate);
115
+ if (file) return file;
116
+ }
117
+
118
+ return undefined;
119
+ }
120
+
112
121
  getFileAnalyzer(file: File) {
113
122
  this.filesByPath.set(file.path, file);
114
123
  return new lib.FileAnalyzer({ projectAnalyzer: this, file });
@@ -142,6 +151,16 @@ export class ProjectAnalyzer {
142
151
 
143
152
  getSourceFile(filePath: string): ts.SourceFile | undefined {
144
153
  const absolutePath = this.getAbsolutePath(filePath);
154
+ const relativePath = filePath.startsWith('/')
155
+ ? this.toProjectRelativePath(filePath)
156
+ : filePath;
157
+
158
+ if (relativePath) {
159
+ const cached = this.sourceFileByPath.get(relativePath);
160
+ if (cached) {
161
+ return cached;
162
+ }
163
+ }
145
164
 
146
165
  // Search across all programs
147
166
  for (const program of this.programs) {
@@ -241,6 +260,16 @@ export class ProjectAnalyzer {
241
260
  */
242
261
  getTypeCheckerForFile(filePath: string): ts.TypeChecker {
243
262
  const absolutePath = this.getAbsolutePath(filePath);
263
+ const relativePath = filePath.startsWith('/')
264
+ ? this.toProjectRelativePath(filePath)
265
+ : filePath;
266
+
267
+ if (relativePath) {
268
+ const cachedChecker = this.typeCheckerByPath.get(relativePath);
269
+ if (cachedChecker) {
270
+ return cachedChecker;
271
+ }
272
+ }
244
273
 
245
274
  // Search for the file in each program and return that program's typeChecker
246
275
  for (const program of this.programs) {
@@ -320,5 +349,63 @@ export class ProjectAnalyzer {
320
349
  // Update primary program for backward compatibility
321
350
  this.program = this.programs[0];
322
351
  this.typeChecker = this.program.getTypeChecker();
352
+
353
+ this.buildSourceFileIndex();
354
+ }
355
+
356
+ private ensureFileCache(): void {
357
+ if (this.project.files?.length !== this.filesByPath.size) {
358
+ // Rebuild the cache if project.files has changed
359
+ this.filesByPath.clear();
360
+ for (const file of this.project.files) {
361
+ this.filesByPath.set(file.path, file);
362
+ }
363
+ }
364
+ }
365
+
366
+ private toProjectRelativePath(filePath: string): string | null {
367
+ if (!filePath) return null;
368
+
369
+ if (filePath.startsWith(this.dirPath + '/')) {
370
+ return filePath.replace(this.dirPath + '/', '');
371
+ }
372
+
373
+ if (!filePath.startsWith('/')) {
374
+ return null;
375
+ }
376
+
377
+ try {
378
+ const normalizedPath = realpathSync(filePath);
379
+ if (normalizedPath.startsWith(this.dirPath + '/')) {
380
+ return normalizedPath.replace(this.dirPath + '/', '');
381
+ }
382
+ } catch (error) {
383
+ // If normalization fails, return null
384
+ }
385
+
386
+ return null;
387
+ }
388
+
389
+ private buildSourceFileIndex(): void {
390
+ this.sourceFileByPath.clear();
391
+ this.typeCheckerByPath.clear();
392
+
393
+ for (const program of this.programs) {
394
+ const checker = program.getTypeChecker();
395
+ for (const sf of program.getSourceFiles()) {
396
+ // Skip node_modules files to keep the index small
397
+ if (sf.fileName.includes('node_modules')) {
398
+ continue;
399
+ }
400
+
401
+ const relativePath = this.toProjectRelativePath(sf.fileName);
402
+ if (!relativePath) continue;
403
+
404
+ if (!this.sourceFileByPath.has(relativePath)) {
405
+ this.sourceFileByPath.set(relativePath, sf);
406
+ this.typeCheckerByPath.set(relativePath, checker);
407
+ }
408
+ }
409
+ }
323
410
  }
324
411
  }
@@ -7,6 +7,11 @@ interface GetAllDeclaredEntityNodesArgs {
7
7
  entities?: { [key: string]: ts.Node };
8
8
  }
9
9
 
10
+ const allDeclaredEntityNodesCache = new WeakMap<
11
+ ts.SourceFile,
12
+ { [key: string]: ts.Node }
13
+ >();
14
+
10
15
  /**
11
16
  * Returns all declared entity nodes in a source file.
12
17
  *
@@ -24,6 +29,14 @@ export function getAllDeclaredEntityNodes({
24
29
  entities = {},
25
30
  }: GetAllDeclaredEntityNodesArgs): { [key: string]: ts.Node } {
26
31
  try {
32
+ const isRootCall = !possibleIdentifierNode && !entityNode;
33
+ if (isRootCall) {
34
+ const cached = allDeclaredEntityNodesCache.get(sourceFile);
35
+ if (cached) {
36
+ return cached;
37
+ }
38
+ }
39
+
27
40
  if (!possibleIdentifierNode) {
28
41
  possibleIdentifierNode = sourceFile;
29
42
  }
@@ -34,6 +47,9 @@ export function getAllDeclaredEntityNodes({
34
47
  entities[possibleIdentifierNode.text] =
35
48
  entityNode ?? possibleIdentifierNode;
36
49
  }
50
+ if (isRootCall) {
51
+ allDeclaredEntityNodesCache.set(sourceFile, entities);
52
+ }
37
53
  return entities;
38
54
  }
39
55
 
@@ -225,6 +241,9 @@ export function getAllDeclaredEntityNodes({
225
241
  }
226
242
  });
227
243
 
244
+ if (isRootCall) {
245
+ allDeclaredEntityNodesCache.set(sourceFile, entities);
246
+ }
228
247
  return entities;
229
248
  } catch (e) {
230
249
  console.log(
@@ -7,6 +7,11 @@ interface GetAllEntityNodesArgs {
7
7
  entities?: { [key: string]: ts.Node };
8
8
  }
9
9
 
10
+ const allEntityNodesCache = new WeakMap<
11
+ ts.SourceFile,
12
+ { [key: string]: ts.Node }
13
+ >();
14
+
10
15
  export function getAllEntityNodes({
11
16
  sourceFile,
12
17
  possibleIdentifierNode,
@@ -14,6 +19,14 @@ export function getAllEntityNodes({
14
19
  entities = {},
15
20
  }: GetAllEntityNodesArgs) {
16
21
  try {
22
+ const isRootCall = !possibleIdentifierNode && !entityNode;
23
+ if (isRootCall) {
24
+ const cached = allEntityNodesCache.get(sourceFile);
25
+ if (cached) {
26
+ return cached;
27
+ }
28
+ }
29
+
17
30
  if (!possibleIdentifierNode) {
18
31
  possibleIdentifierNode = sourceFile;
19
32
  }
@@ -39,6 +52,9 @@ export function getAllEntityNodes({
39
52
  }),
40
53
  };
41
54
  }
55
+ if (isRootCall) {
56
+ allEntityNodesCache.set(sourceFile, entities);
57
+ }
42
58
  return entities;
43
59
  }
44
60
 
@@ -223,6 +239,9 @@ export function getAllEntityNodes({
223
239
  }
224
240
  });
225
241
 
242
+ if (isRootCall) {
243
+ allEntityNodesCache.set(sourceFile, entities);
244
+ }
226
245
  return entities;
227
246
  } catch (e) {
228
247
  console.log(
@@ -10,9 +10,19 @@ export interface ExportedFunction {
10
10
  directImportName?: string;
11
11
  }
12
12
 
13
+ const exportsCache = new WeakMap<
14
+ ts.SourceFile,
15
+ Record<string, ExportedFunction>
16
+ >();
17
+
13
18
  export function getAllExports(
14
19
  sourceFile: ts.SourceFile,
15
20
  ): Record<string, ExportedFunction> {
21
+ const cached = exportsCache.get(sourceFile);
22
+ if (cached) {
23
+ return cached;
24
+ }
25
+
16
26
  const exportsCodeMap: Record<string, ExportedFunction> = {};
17
27
 
18
28
  function addExport(
@@ -239,5 +249,6 @@ export function getAllExports(
239
249
  }
240
250
  });
241
251
 
252
+ exportsCache.set(sourceFile, exportsCodeMap);
242
253
  return exportsCodeMap;
243
254
  }