code-graph-context 2.0.0 → 2.0.1

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.
@@ -84,6 +84,38 @@ function extractInjectionToken(node) {
84
84
  }
85
85
  return null;
86
86
  }
87
+ /**
88
+ * Extract constructor parameter types and @Inject tokens for edge detection.
89
+ * This allows INJECTS detection to work without AST access (for cross-chunk detection).
90
+ */
91
+ function extractConstructorParamTypes(node) {
92
+ const types = [];
93
+ const injectTokens = new Map();
94
+ const constructors = node.getConstructors();
95
+ if (constructors.length === 0)
96
+ return { types, injectTokens };
97
+ const constructor = constructors[0];
98
+ const parameters = constructor.getParameters();
99
+ for (const param of parameters) {
100
+ const typeNode = param.getTypeNode();
101
+ if (typeNode) {
102
+ const typeName = typeNode.getText();
103
+ types.push(typeName);
104
+ // Check for @Inject decorator
105
+ const decorators = param.getDecorators();
106
+ for (const decorator of decorators) {
107
+ if (decorator.getName() === 'Inject') {
108
+ const args = decorator.getArguments();
109
+ if (args.length > 0) {
110
+ const token = args[0].getText().replace(/['"]/g, '');
111
+ injectTokens.set(typeName, token);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
117
+ return { types, injectTokens };
118
+ }
87
119
  function hasDynamicMethods(node) {
88
120
  const methods = node.getMethods();
89
121
  const dynamicMethods = ['forRoot', 'forFeature', 'forRootAsync', 'forFeatureAsync'];
@@ -262,59 +294,39 @@ function detectDependencyInjection(sourceNode, targetNode) {
262
294
  return false;
263
295
  if (targetNode.properties?.coreType !== CoreNodeType.CLASS_DECLARATION)
264
296
  return false;
265
- const constructors = sourceNode.sourceNode?.getConstructors();
266
- if (!constructors || constructors.length === 0)
267
- return false;
268
- const constructor = constructors[0];
269
- const parameters = constructor.getParameters();
270
297
  const targetName = targetNode.properties?.name;
271
- return parameters.some((param) => {
272
- const paramType = param.getTypeNode()?.getText();
273
- if (paramType === targetName)
274
- return true;
275
- const decorators = param.getDecorators();
276
- return decorators.some((decorator) => {
277
- if (decorator.getName() === 'Inject') {
278
- const args = decorator.getArguments();
279
- if (args.length > 0) {
280
- const token = args[0].getText().replace(/['"]/g, '');
281
- return token === targetName;
282
- }
283
- }
284
- return false;
285
- });
286
- });
298
+ if (!targetName)
299
+ return false;
300
+ // Use pre-extracted constructor params from context (works after AST cleanup)
301
+ const constructorParamTypes = sourceNode.properties?.context?.constructorParamTypes ?? [];
302
+ const injectTokens = sourceNode.properties?.context?.injectTokens ?? {};
303
+ // Check if target is in constructor params by type
304
+ if (constructorParamTypes.includes(targetName))
305
+ return true;
306
+ // Check if target is referenced via @Inject token
307
+ return Object.values(injectTokens).includes(targetName);
287
308
  }
288
309
  function extractInjectionTokenFromRelation(sourceNode, targetNode) {
289
- const constructors = sourceNode.sourceNode?.getConstructors();
290
- if (!constructors || constructors.length === 0)
310
+ const targetName = targetNode.properties?.name;
311
+ if (!targetName)
291
312
  return null;
292
- const constructor = constructors[0];
293
- const parameters = constructor.getParameters();
294
- for (const param of parameters) {
295
- const decorators = param.getDecorators();
296
- for (const decorator of decorators) {
297
- if (decorator.getName() === 'Inject') {
298
- const args = decorator.getArguments();
299
- if (args.length > 0) {
300
- return args[0].getText().replace(/['"]/g, '');
301
- }
302
- }
313
+ // Use pre-extracted inject tokens from context (works after AST cleanup)
314
+ const injectTokens = sourceNode.properties?.context?.injectTokens ?? {};
315
+ // Find the token that maps to the target
316
+ for (const [typeName, token] of Object.entries(injectTokens)) {
317
+ if (token === targetName || typeName === targetName) {
318
+ return token;
303
319
  }
304
320
  }
305
321
  return null;
306
322
  }
307
323
  function findParameterIndex(sourceNode, targetNode) {
308
- const constructors = sourceNode.sourceNode?.getConstructors();
309
- if (!constructors || constructors.length === 0)
310
- return 0;
311
- const constructor = constructors[0];
312
- const parameters = constructor.getParameters();
313
324
  const targetName = targetNode.properties?.name;
314
- return parameters.findIndex((param) => {
315
- const paramType = param.getTypeNode()?.getText();
316
- return paramType === targetName;
317
- });
325
+ if (!targetName)
326
+ return -1;
327
+ // Use pre-extracted constructor params from context (works after AST cleanup)
328
+ const constructorParamTypes = sourceNode.properties?.context?.constructorParamTypes ?? [];
329
+ return constructorParamTypes.indexOf(targetName);
318
330
  }
319
331
  function computeFullPathFromNodes(sourceNode, targetNode) {
320
332
  const basePath = sourceNode.properties?.context?.basePath ?? '';
@@ -791,6 +803,8 @@ export const NESTJS_FRAMEWORK_SCHEMA = {
791
803
  const node = parsedNode.sourceNode;
792
804
  if (!node)
793
805
  return {};
806
+ // Extract constructor param types for INJECTS edge detection
807
+ const { types, injectTokens } = extractConstructorParamTypes(node);
794
808
  return {
795
809
  isAbstract: node.getAbstractKeyword() != null,
796
810
  isDefaultExport: node.isDefaultExport(),
@@ -800,6 +814,9 @@ export const NESTJS_FRAMEWORK_SCHEMA = {
800
814
  methodCount: node.getMethods().length,
801
815
  propertyCount: node.getProperties().length,
802
816
  constructorParameterCount: countConstructorParameters(node),
817
+ // Pre-extracted for cross-chunk edge detection
818
+ constructorParamTypes: types,
819
+ injectTokens: Object.fromEntries(injectTokens),
803
820
  };
804
821
  },
805
822
  priority: 1,
@@ -876,13 +876,12 @@ export class TypeScriptParser {
876
876
  }
877
877
  async applyEdgeEnhancement(edgeEnhancement) {
878
878
  try {
879
- // Combine parsed nodes and existing nodes for target matching
880
- // Sources must be parsed (have AST), targets can be either
879
+ // Combine parsed nodes and existing nodes for edge matching
880
+ // Detection patterns should use pre-extracted context, not raw AST
881
881
  const allTargetNodes = new Map([...this.parsedNodes, ...this.existingNodes]);
882
882
  for (const [sourceId, sourceNode] of this.parsedNodes) {
883
- // Skip if source doesn't have AST (shouldn't happen for parsedNodes, but be safe)
884
- if (!sourceNode.sourceNode)
885
- continue;
883
+ // Note: sourceNode.sourceNode may be undefined after AST cleanup
884
+ // Detection patterns should use pre-extracted context from node.properties.context
886
885
  for (const [targetId, targetNode] of allTargetNodes) {
887
886
  if (sourceId === targetId)
888
887
  continue;
@@ -174,10 +174,11 @@ export class WorkspaceParser {
174
174
  for (const [nodeId, parsedNode] of innerParsedNodes) {
175
175
  this.accumulatedParsedNodes.set(nodeId, {
176
176
  id: parsedNode.id,
177
+ coreType: parsedNode.coreType, // Needed for detection patterns
177
178
  semanticType: parsedNode.semanticType,
178
179
  properties: {
179
180
  name: parsedNode.properties.name,
180
- context: parsedNode.properties.context, // Contains propertyTypes
181
+ context: parsedNode.properties.context, // Contains propertyTypes, dependencies
181
182
  },
182
183
  });
183
184
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-graph-context",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "MCP server that builds code graphs to provide rich context to LLMs",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/drewdrewH/code-graph-context#readme",