@rayburst/cli 0.2.8 → 0.3.0

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.
@@ -297,6 +297,109 @@ function extractComponentPathsFromImports(sourceFile, projectPath) {
297
297
  }
298
298
  return componentPaths;
299
299
  }
300
+ function extractComponentsFromRouteFile(sourceFile, projectPath) {
301
+ const componentPaths = [];
302
+ try {
303
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
304
+ for (const callExpr of callExpressions) {
305
+ const expr = callExpr.getExpression();
306
+ const functionName = expr.getText();
307
+ if (!functionName.includes("createFileRoute") && !functionName.includes("createRootRoute")) {
308
+ continue;
309
+ }
310
+ const args = callExpr.getArguments();
311
+ if (args.length === 0) continue;
312
+ let configObject;
313
+ const parent = callExpr.getParent();
314
+ if (Node.isCallExpression(parent)) {
315
+ const parentArgs = parent.getArguments();
316
+ if (parentArgs.length > 0 && Node.isObjectLiteralExpression(parentArgs[0])) {
317
+ configObject = parentArgs[0];
318
+ }
319
+ }
320
+ if (!configObject && Node.isObjectLiteralExpression(args[0])) {
321
+ configObject = args[0];
322
+ }
323
+ if (!configObject) continue;
324
+ const properties = extractObjectLiteralProperties(configObject);
325
+ const componentValue = properties.get("component");
326
+ if (!componentValue) continue;
327
+ if (isIdentifier(componentValue)) {
328
+ const componentName = componentValue.getText();
329
+ const decl = traceIdentifierToDeclaration(componentValue);
330
+ if (decl) {
331
+ const importDecl = decl.getFirstAncestorByKind(SyntaxKind.ImportDeclaration);
332
+ if (importDecl) {
333
+ const importedSourceFile = getImportSourceFile(importDecl);
334
+ if (importedSourceFile) {
335
+ const filePath = importedSourceFile.getFilePath();
336
+ const relativePath = filePath.replace(projectPath + "/", "");
337
+ if (!relativePath.includes("node_modules")) {
338
+ componentPaths.push(relativePath);
339
+ }
340
+ }
341
+ } else {
342
+ const localComponentPaths = findNodesInFile(
343
+ sourceFile.getFilePath().replace(projectPath + "/", ""),
344
+ /* @__PURE__ */ new Map()
345
+ // This won't work without nodeMap, but we'll handle it differently
346
+ );
347
+ }
348
+ }
349
+ } else if (Node.isArrowFunction(componentValue) || Node.isFunctionExpression(componentValue)) {
350
+ const jsxElements = componentValue.getDescendantsOfKind(SyntaxKind.JsxElement);
351
+ const jsxSelfClosing = componentValue.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement);
352
+ for (const jsxElement of jsxElements) {
353
+ const tagName = jsxElement.getOpeningElement().getTagNameNode().getText();
354
+ const importSource = resolveImportSource(sourceFile, tagName);
355
+ if (importSource && !importSource.startsWith(".") && !importSource.startsWith("/")) {
356
+ continue;
357
+ }
358
+ const importDecls = sourceFile.getImportDeclarations();
359
+ for (const importDecl of importDecls) {
360
+ const namedImports = importDecl.getNamedImports();
361
+ const defaultImport = importDecl.getDefaultImport();
362
+ if (namedImports.some((ni) => ni.getName() === tagName) || defaultImport?.getText() === tagName) {
363
+ const importedSourceFile = getImportSourceFile(importDecl);
364
+ if (importedSourceFile) {
365
+ const filePath = importedSourceFile.getFilePath();
366
+ const relativePath = filePath.replace(projectPath + "/", "");
367
+ if (!relativePath.includes("node_modules")) {
368
+ componentPaths.push(relativePath);
369
+ }
370
+ }
371
+ }
372
+ }
373
+ }
374
+ for (const jsxElement of jsxSelfClosing) {
375
+ const tagName = jsxElement.getTagNameNode().getText();
376
+ const importSource = resolveImportSource(sourceFile, tagName);
377
+ if (importSource && !importSource.startsWith(".") && !importSource.startsWith("/")) {
378
+ continue;
379
+ }
380
+ const importDecls = sourceFile.getImportDeclarations();
381
+ for (const importDecl of importDecls) {
382
+ const namedImports = importDecl.getNamedImports();
383
+ const defaultImport = importDecl.getDefaultImport();
384
+ if (namedImports.some((ni) => ni.getName() === tagName) || defaultImport?.getText() === tagName) {
385
+ const importedSourceFile = getImportSourceFile(importDecl);
386
+ if (importedSourceFile) {
387
+ const filePath = importedSourceFile.getFilePath();
388
+ const relativePath = filePath.replace(projectPath + "/", "");
389
+ if (!relativePath.includes("node_modules")) {
390
+ componentPaths.push(relativePath);
391
+ }
392
+ }
393
+ }
394
+ }
395
+ }
396
+ }
397
+ }
398
+ } catch (error) {
399
+ console.error("[extractComponentsFromRouteFile] Error:", error);
400
+ }
401
+ return componentPaths;
402
+ }
300
403
  function findNodesInFile(filePath, nodeMap) {
301
404
  const nodes = [];
302
405
  for (const [nodeId, node] of nodeMap.entries()) {
@@ -331,30 +434,46 @@ function createConfigBasedEdges(sourceNode, targetFilePaths, nodeMap, edges, edg
331
434
  }
332
435
  return edgesCreated;
333
436
  }
334
- function analyzeJsxPropsForComponents(jsxElement, propName, projectPath) {
437
+ function analyzeJsxPropsForComponents(jsxElement, propName, projectPath, project) {
335
438
  const componentPaths = [];
336
439
  try {
337
- const attributes = jsxElement.getChildrenOfKind(SyntaxKind.JsxAttribute);
440
+ const jsxAttributes = jsxElement.getChildrenOfKind(SyntaxKind.JsxAttributes)[0];
441
+ if (!jsxAttributes) {
442
+ console.log(`[DEBUG analyzeJsxProps] No JsxAttributes container found`);
443
+ return componentPaths;
444
+ }
445
+ const attributes = jsxAttributes.getChildrenOfKind(SyntaxKind.JsxAttribute);
446
+ console.log(`[DEBUG analyzeJsxProps] Attributes found: ${attributes.length}, looking for: ${propName}`);
338
447
  for (const attr of attributes) {
339
448
  const attrName = attr.getChildrenOfKind(SyntaxKind.Identifier)[0]?.getText();
449
+ console.log(`[DEBUG analyzeJsxProps] Attribute name: ${attrName}`);
340
450
  if (attrName !== propName) {
341
451
  continue;
342
452
  }
453
+ console.log(`[DEBUG analyzeJsxProps] Found matching prop: ${propName}`);
343
454
  const initializer = attr.getChildrenOfKind(SyntaxKind.JsxExpression)[0];
455
+ console.log(`[DEBUG analyzeJsxProps] Initializer exists: ${!!initializer}`);
344
456
  if (!initializer) continue;
345
457
  const identifier = initializer.getChildrenOfKind(SyntaxKind.Identifier)[0];
458
+ console.log(`[DEBUG analyzeJsxProps] Identifier: ${identifier?.getText()}`);
346
459
  if (!identifier) continue;
347
460
  const declaration = traceIdentifierToDeclaration(identifier);
461
+ console.log(`[DEBUG analyzeJsxProps] Declaration found: ${!!declaration}, is VariableDeclaration: ${declaration ? isVariableDeclaration(declaration) : false}`);
348
462
  if (!declaration || !isVariableDeclaration(declaration)) continue;
349
463
  const dataFlow = traceVariableDataFlow(declaration);
464
+ console.log(`[DEBUG analyzeJsxProps] DataFlow found: ${!!dataFlow}, is CallExpression: ${dataFlow ? isCallExpression(dataFlow) : false}`);
350
465
  if (!dataFlow || !isCallExpression(dataFlow)) continue;
351
466
  const args = dataFlow.getChildrenOfKind(SyntaxKind.SyntaxList)[0];
467
+ console.log(`[DEBUG analyzeJsxProps] Args found: ${!!args}`);
352
468
  if (!args) continue;
353
469
  const configArg = args.getChildrenOfKind(SyntaxKind.ObjectLiteralExpression)[0];
470
+ console.log(`[DEBUG analyzeJsxProps] ConfigArg found: ${!!configArg}`);
354
471
  if (!configArg) continue;
355
472
  const commonPropNames = ["routeTree", "routes", "children", "components"];
356
473
  for (const commonPropName of commonPropNames) {
357
- const paths = traceConfigToComponentPaths(configArg, commonPropName, projectPath);
474
+ console.log(`[DEBUG analyzeJsxProps] Checking config property: ${commonPropName}`);
475
+ const paths = traceConfigToComponentPaths(configArg, commonPropName, projectPath, project);
476
+ console.log(`[DEBUG analyzeJsxProps] Paths found for ${commonPropName}: ${paths.length}`);
358
477
  if (paths.length > 0) {
359
478
  return paths;
360
479
  }
@@ -365,16 +484,34 @@ function analyzeJsxPropsForComponents(jsxElement, propName, projectPath) {
365
484
  }
366
485
  return componentPaths;
367
486
  }
368
- function traceConfigToComponentPaths(configObject, propertyName, projectPath) {
487
+ function traceConfigToComponentPaths(configObject, propertyName, projectPath, project) {
369
488
  try {
370
489
  const propertyNode = traceConfigProperty(configObject, propertyName);
490
+ console.log(`[DEBUG traceConfigToComponentPaths] Property node for "${propertyName}": ${propertyNode ? propertyNode.getKindName() : "NULL"}`);
371
491
  if (!propertyNode) {
372
492
  return [];
373
493
  }
374
494
  if (isImportDeclaration(propertyNode)) {
375
495
  const sourceFile = getImportSourceFile(propertyNode);
376
496
  if (sourceFile) {
377
- return extractComponentPathsFromImports(sourceFile, projectPath);
497
+ const filePath = sourceFile.getFilePath();
498
+ const relativePath = filePath.replace(projectPath + "/", "");
499
+ console.log(`[DEBUG traceConfigToComponentPaths] Found routeTree file: ${relativePath}`);
500
+ const routeFilePaths = extractComponentPathsFromImports(sourceFile, projectPath);
501
+ console.log(`[DEBUG traceConfigToComponentPaths] Found ${routeFilePaths.length} route files`);
502
+ const allComponentPaths = [];
503
+ for (const routePath of routeFilePaths) {
504
+ const routeSourceFile = project.getSourceFile(projectPath + "/" + routePath);
505
+ if (!routeSourceFile) {
506
+ console.log(`[DEBUG traceConfigToComponentPaths] Could not load route file: ${routePath}`);
507
+ continue;
508
+ }
509
+ console.log(`[DEBUG traceConfigToComponentPaths] Extracting components from route: ${routePath}`);
510
+ const componentPaths = extractComponentsFromRouteFile(routeSourceFile, projectPath);
511
+ console.log(`[DEBUG traceConfigToComponentPaths] Found ${componentPaths.length} components in ${routePath}`);
512
+ allComponentPaths.push(...componentPaths);
513
+ }
514
+ return [...routeFilePaths, ...allComponentPaths];
378
515
  }
379
516
  }
380
517
  if (isObjectLiteralExpression(propertyNode)) {
@@ -399,13 +536,19 @@ function traceConfigToComponentPaths(configObject, propertyName, projectPath) {
399
536
  }
400
537
  return [];
401
538
  }
402
- function detectConfigBasedComponents(sourceFile, nodeMap, edges, projectPath) {
539
+ function detectConfigBasedComponents(sourceFile, nodeMap, edges, projectPath, project) {
403
540
  let totalEdgesCreated = 0;
404
541
  try {
405
542
  const jsxElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement);
543
+ console.log(`[DEBUG] File: ${sourceFile.getFilePath()}, JSX elements: ${jsxElements.length}`);
406
544
  for (const jsxElement of jsxElements) {
545
+ console.log(`[DEBUG] Processing JSX element: ${jsxElement.getText().substring(0, 50)}...`);
407
546
  const containingFunc = jsxElement.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration) || jsxElement.getFirstAncestorByKind(SyntaxKind.ArrowFunction);
408
- if (!containingFunc) continue;
547
+ if (!containingFunc) {
548
+ console.log("[DEBUG] No containing function found, skipping");
549
+ continue;
550
+ }
551
+ console.log(`[DEBUG] Containing function kind: ${containingFunc.getKindName()}`);
409
552
  let parentName = null;
410
553
  if (containingFunc.getKind() === SyntaxKind.FunctionDeclaration) {
411
554
  parentName = containingFunc.getName?.();
@@ -415,16 +558,27 @@ function detectConfigBasedComponents(sourceFile, nodeMap, edges, projectPath) {
415
558
  parentName = varDecl.getName?.();
416
559
  }
417
560
  }
418
- if (!parentName) continue;
561
+ console.log(`[DEBUG] Parent name: ${parentName}`);
562
+ if (!parentName) {
563
+ console.log("[DEBUG] No parent name found, skipping");
564
+ continue;
565
+ }
419
566
  const sourceNode = Array.from(nodeMap.values()).find((node) => {
420
567
  const nodeName = node.id.split("::")[1];
421
568
  return nodeName === parentName;
422
569
  });
423
- if (!sourceNode) continue;
570
+ console.log(`[DEBUG] Source node found: ${sourceNode ? sourceNode.id : "NO"}`);
571
+ if (!sourceNode) {
572
+ console.log("[DEBUG] No source node in nodeMap, skipping");
573
+ continue;
574
+ }
424
575
  const commonPropNames = ["router", "config", "routes", "navigation"];
425
576
  for (const propName of commonPropNames) {
426
- const componentPaths = analyzeJsxPropsForComponents(jsxElement, propName, projectPath);
577
+ console.log(`[DEBUG] Checking prop: ${propName}`);
578
+ const componentPaths = analyzeJsxPropsForComponents(jsxElement, propName, projectPath, project);
579
+ console.log(`[DEBUG] Component paths found: ${componentPaths.length}`);
427
580
  if (componentPaths.length > 0) {
581
+ console.log(`[DEBUG] Creating edges for ${componentPaths.length} components`);
428
582
  const edgesCreated = createConfigBasedEdges(
429
583
  sourceNode,
430
584
  componentPaths,
@@ -432,6 +586,7 @@ function detectConfigBasedComponents(sourceFile, nodeMap, edges, projectPath) {
432
586
  edges,
433
587
  `config:${propName}`
434
588
  );
589
+ console.log(`[DEBUG] Edges created: ${edgesCreated}`);
435
590
  totalEdgesCreated += edgesCreated;
436
591
  }
437
592
  }
@@ -469,6 +624,7 @@ async function analyzeProject(projectPath, projectId, onLog) {
469
624
  const nodes = [];
470
625
  const edges = [];
471
626
  const nodeMap = /* @__PURE__ */ new Map();
627
+ const externalNodeMap = /* @__PURE__ */ new Map();
472
628
  const usedIds = /* @__PURE__ */ new Set();
473
629
  const idCounters = /* @__PURE__ */ new Map();
474
630
  let fileIndex = 0;
@@ -499,13 +655,13 @@ async function analyzeProject(projectPath, projectId, onLog) {
499
655
  }
500
656
  log(` Extracted ${nodes.length} nodes`);
501
657
  for (const sourceFile of sourceFiles) {
502
- generateEdges(sourceFile, nodeMap, edges);
658
+ generateEdges(sourceFile, nodeMap, edges, nodes, externalNodeMap);
503
659
  }
504
660
  log(` Generated ${edges.length} edges (syntactic analysis)`);
505
661
  log(` Analyzing config-based components...`);
506
662
  let totalConfigEdges = 0;
507
663
  for (const sourceFile of sourceFiles) {
508
- const configEdges = detectConfigBasedComponents(sourceFile, nodeMap, edges, projectPath);
664
+ const configEdges = detectConfigBasedComponents(sourceFile, nodeMap, edges, projectPath, project);
509
665
  totalConfigEdges += configEdges;
510
666
  }
511
667
  if (totalConfigEdges > 0) {
@@ -986,25 +1142,86 @@ function extractState(sourceFile, relativePath, gitHash, baseX, startY, nodes, n
986
1142
  });
987
1143
  return count;
988
1144
  }
989
- function generateEdges(sourceFile, nodeMap, edges) {
1145
+ function resolveImportSource(sourceFile, identifierName) {
1146
+ const importDecls = sourceFile.getImportDeclarations();
1147
+ for (const importDecl of importDecls) {
1148
+ const namedImports = importDecl.getNamedImports();
1149
+ const defaultImport = importDecl.getDefaultImport();
1150
+ const namespaceImport = importDecl.getNamespaceImport();
1151
+ if (namedImports.some((ni) => ni.getName() === identifierName)) {
1152
+ return importDecl.getModuleSpecifierValue();
1153
+ }
1154
+ if (defaultImport?.getText() === identifierName) {
1155
+ return importDecl.getModuleSpecifierValue();
1156
+ }
1157
+ if (namespaceImport && identifierName.startsWith(namespaceImport.getText() + ".")) {
1158
+ return importDecl.getModuleSpecifierValue();
1159
+ }
1160
+ }
1161
+ return null;
1162
+ }
1163
+ function createExternalComponentNode(componentName, packageName, sourceFilePath, nodes, nodeMap, externalNodeMap) {
1164
+ const externalId = `external::${packageName}::${componentName}`;
1165
+ if (externalNodeMap.has(externalId)) {
1166
+ return externalNodeMap.get(externalId);
1167
+ }
1168
+ const node = {
1169
+ id: externalId,
1170
+ type: "component",
1171
+ position: { x: 1200, y: nodes.length * 150 },
1172
+ // Position externals to the right
1173
+ data: {
1174
+ componentName,
1175
+ props: "external",
1176
+ label: componentName,
1177
+ description: `External: ${packageName}`,
1178
+ returnValueSummary: `<${componentName} />`,
1179
+ returnDescription: `External component from ${packageName}`,
1180
+ hasJsxReturn: true,
1181
+ hasNullReturn: false,
1182
+ primaryReturnType: "jsx"
1183
+ }
1184
+ };
1185
+ externalNodeMap.set(externalId, node);
1186
+ nodeMap.set(componentName, node);
1187
+ nodes.push(node);
1188
+ return node;
1189
+ }
1190
+ function generateEdges(sourceFile, nodeMap, edges, nodes, externalNodeMap) {
990
1191
  sourceFile.getDescendantsOfKind(SyntaxKind.JsxElement).forEach((jsxElement) => {
991
1192
  const openingElement = jsxElement.getOpeningElement();
992
1193
  const tagName = openingElement.getTagNameNode().getText();
993
1194
  const containingFunc = jsxElement.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration) || jsxElement.getFirstAncestorByKind(SyntaxKind.ArrowFunction);
994
1195
  if (containingFunc) {
995
1196
  const parentName = containingFunc.getName?.() || containingFunc.getParent()?.asKind(SyntaxKind.VariableDeclaration)?.getName() || null;
996
- if (parentName && nodeMap.has(parentName) && nodeMap.has(tagName)) {
997
- const sourceNode = nodeMap.get(parentName);
998
- const targetNode = nodeMap.get(tagName);
999
- if (sourceNode && targetNode) {
1000
- const edgeId = `e-${sourceNode.id}-${targetNode.id}`;
1001
- if (!edges.find((e) => e.id === edgeId)) {
1002
- edges.push({
1003
- id: edgeId,
1004
- source: sourceNode.id,
1005
- target: targetNode.id,
1006
- type: "floating"
1007
- });
1197
+ if (parentName && nodeMap.has(parentName)) {
1198
+ let targetNode = nodeMap.get(tagName);
1199
+ if (!targetNode) {
1200
+ const importSource = resolveImportSource(sourceFile, tagName);
1201
+ if (importSource && !importSource.startsWith(".") && !importSource.startsWith("/")) {
1202
+ const sourceFilePath = sourceFile.getFilePath();
1203
+ targetNode = createExternalComponentNode(
1204
+ tagName,
1205
+ importSource,
1206
+ sourceFilePath,
1207
+ nodes,
1208
+ nodeMap,
1209
+ externalNodeMap
1210
+ );
1211
+ }
1212
+ }
1213
+ if (targetNode) {
1214
+ const sourceNode = nodeMap.get(parentName);
1215
+ if (sourceNode) {
1216
+ const edgeId = `e-${sourceNode.id}-${targetNode.id}`;
1217
+ if (!edges.find((e) => e.id === edgeId)) {
1218
+ edges.push({
1219
+ id: edgeId,
1220
+ source: sourceNode.id,
1221
+ target: targetNode.id,
1222
+ type: "floating"
1223
+ });
1224
+ }
1008
1225
  }
1009
1226
  }
1010
1227
  }
@@ -1015,18 +1232,34 @@ function generateEdges(sourceFile, nodeMap, edges) {
1015
1232
  const containingFunc = jsxElement.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration) || jsxElement.getFirstAncestorByKind(SyntaxKind.ArrowFunction);
1016
1233
  if (containingFunc) {
1017
1234
  const parentName = containingFunc.getName?.() || containingFunc.getParent()?.asKind(SyntaxKind.VariableDeclaration)?.getName() || null;
1018
- if (parentName && nodeMap.has(parentName) && nodeMap.has(tagName)) {
1019
- const sourceNode = nodeMap.get(parentName);
1020
- const targetNode = nodeMap.get(tagName);
1021
- if (sourceNode && targetNode) {
1022
- const edgeId = `e-${sourceNode.id}-${targetNode.id}`;
1023
- if (!edges.find((e) => e.id === edgeId)) {
1024
- edges.push({
1025
- id: edgeId,
1026
- source: sourceNode.id,
1027
- target: targetNode.id,
1028
- type: "floating"
1029
- });
1235
+ if (parentName && nodeMap.has(parentName)) {
1236
+ let targetNode = nodeMap.get(tagName);
1237
+ if (!targetNode) {
1238
+ const importSource = resolveImportSource(sourceFile, tagName);
1239
+ if (importSource && !importSource.startsWith(".") && !importSource.startsWith("/")) {
1240
+ const sourceFilePath = sourceFile.getFilePath();
1241
+ targetNode = createExternalComponentNode(
1242
+ tagName,
1243
+ importSource,
1244
+ sourceFilePath,
1245
+ nodes,
1246
+ nodeMap,
1247
+ externalNodeMap
1248
+ );
1249
+ }
1250
+ }
1251
+ if (targetNode) {
1252
+ const sourceNode = nodeMap.get(parentName);
1253
+ if (sourceNode) {
1254
+ const edgeId = `e-${sourceNode.id}-${targetNode.id}`;
1255
+ if (!edges.find((e) => e.id === edgeId)) {
1256
+ edges.push({
1257
+ id: edgeId,
1258
+ source: sourceNode.id,
1259
+ target: targetNode.id,
1260
+ type: "floating"
1261
+ });
1262
+ }
1030
1263
  }
1031
1264
  }
1032
1265
  }
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  readLocalMeta,
10
10
  writeLocalAnalysis,
11
11
  writeLocalMeta
12
- } from "./chunk-K4NZXXX4.js";
12
+ } from "./chunk-L6EQILHI.js";
13
13
  export {
14
14
  addGitignoreEntry,
15
15
  analyzeProject,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  rayburstPlugin
3
- } from "./chunk-K4NZXXX4.js";
3
+ } from "./chunk-L6EQILHI.js";
4
4
  export {
5
5
  rayburstPlugin
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rayburst/cli",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "description": "Rayburst - Automatic code analysis for TypeScript/JavaScript projects via Vite plugin",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",