@rayburst/cli 0.2.5 → 0.2.7

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.
@@ -124,6 +124,315 @@ function getChangedFilesVsRemote(projectPath, remoteBranch = void 0) {
124
124
  }
125
125
 
126
126
  // src/analysis/analyze-project.ts
127
+ function isIdentifier(node) {
128
+ return node?.getKind() === SyntaxKind.Identifier;
129
+ }
130
+ function isVariableDeclaration(node) {
131
+ return node?.getKind() === SyntaxKind.VariableDeclaration;
132
+ }
133
+ function isCallExpression(node) {
134
+ return node?.getKind() === SyntaxKind.CallExpression;
135
+ }
136
+ function isObjectLiteralExpression(node) {
137
+ return node?.getKind() === SyntaxKind.ObjectLiteralExpression;
138
+ }
139
+ function isImportDeclaration(node) {
140
+ return node?.getKind() === SyntaxKind.ImportDeclaration;
141
+ }
142
+ function traceIdentifierToDeclaration(node) {
143
+ if (!isIdentifier(node)) {
144
+ return null;
145
+ }
146
+ try {
147
+ const symbol = node.getSymbol();
148
+ if (!symbol) {
149
+ return null;
150
+ }
151
+ const declarations = symbol.getDeclarations();
152
+ if (declarations.length === 0) {
153
+ return null;
154
+ }
155
+ return declarations[0];
156
+ } catch (error) {
157
+ console.error(`[traceIdentifierToDeclaration] Error tracing identifier "${node.getText()}":`, error);
158
+ return null;
159
+ }
160
+ }
161
+ function getImportSourceFile(importDeclaration) {
162
+ if (!isImportDeclaration(importDeclaration)) {
163
+ return null;
164
+ }
165
+ try {
166
+ const moduleSpecifier = importDeclaration.getChildrenOfKind(SyntaxKind.StringLiteral)[0];
167
+ if (!moduleSpecifier) {
168
+ return null;
169
+ }
170
+ const symbol = moduleSpecifier.getSymbol();
171
+ if (!symbol) {
172
+ return null;
173
+ }
174
+ const declarations = symbol.getDeclarations();
175
+ if (declarations.length === 0) {
176
+ return null;
177
+ }
178
+ return declarations[0].getSourceFile();
179
+ } catch (error) {
180
+ console.error(`[getImportSourceFile] Error getting import source:`, error);
181
+ return null;
182
+ }
183
+ }
184
+ function traceVariableDataFlow(variableNode, maxDepth = 5) {
185
+ if (maxDepth <= 0) {
186
+ console.log("[traceVariableDataFlow] Max depth reached, stopping recursion");
187
+ return null;
188
+ }
189
+ if (!isVariableDeclaration(variableNode)) {
190
+ return null;
191
+ }
192
+ try {
193
+ const initializer = variableNode.getChildrenOfKind(SyntaxKind.CallExpression)[0] || variableNode.getChildrenOfKind(SyntaxKind.ObjectLiteralExpression)[0] || variableNode.getChildrenOfKind(SyntaxKind.Identifier)[0];
194
+ if (!initializer) {
195
+ return null;
196
+ }
197
+ if (isCallExpression(initializer) || isObjectLiteralExpression(initializer)) {
198
+ return initializer;
199
+ }
200
+ if (isIdentifier(initializer)) {
201
+ const declaration = traceIdentifierToDeclaration(initializer);
202
+ if (declaration && isVariableDeclaration(declaration)) {
203
+ return traceVariableDataFlow(declaration, maxDepth - 1);
204
+ }
205
+ return declaration;
206
+ }
207
+ return initializer;
208
+ } catch (error) {
209
+ console.error("[traceVariableDataFlow] Error tracing variable:", error);
210
+ return null;
211
+ }
212
+ }
213
+ function extractObjectLiteralProperties(objectLiteral) {
214
+ const properties = /* @__PURE__ */ new Map();
215
+ if (!isObjectLiteralExpression(objectLiteral)) {
216
+ return properties;
217
+ }
218
+ try {
219
+ const propertyAssignments = objectLiteral.getChildrenOfKind(SyntaxKind.PropertyAssignment);
220
+ for (const prop of propertyAssignments) {
221
+ const nameNode = prop.getChildrenOfKind(SyntaxKind.Identifier)[0];
222
+ if (!nameNode) continue;
223
+ const propName = nameNode.getText();
224
+ const children = prop.getChildren();
225
+ const valueNode = children[children.length - 1];
226
+ if (valueNode) {
227
+ properties.set(propName, valueNode);
228
+ }
229
+ }
230
+ const shorthandProperties = objectLiteral.getChildrenOfKind(SyntaxKind.ShorthandPropertyAssignment);
231
+ for (const prop of shorthandProperties) {
232
+ const nameNode = prop.getChildrenOfKind(SyntaxKind.Identifier)[0];
233
+ if (nameNode) {
234
+ const propName = nameNode.getText();
235
+ properties.set(propName, nameNode);
236
+ }
237
+ }
238
+ } catch (error) {
239
+ console.error("[extractObjectLiteralProperties] Error extracting properties:", error);
240
+ }
241
+ return properties;
242
+ }
243
+ function traceConfigProperty(configObject, propertyName) {
244
+ if (!isObjectLiteralExpression(configObject)) {
245
+ return null;
246
+ }
247
+ try {
248
+ const properties = extractObjectLiteralProperties(configObject);
249
+ const propertyValue = properties.get(propertyName);
250
+ if (!propertyValue) {
251
+ return null;
252
+ }
253
+ if (isIdentifier(propertyValue)) {
254
+ const declaration = traceIdentifierToDeclaration(propertyValue);
255
+ if (!declaration) {
256
+ return null;
257
+ }
258
+ if (isVariableDeclaration(declaration)) {
259
+ return traceVariableDataFlow(declaration);
260
+ }
261
+ if (isImportDeclaration(declaration.getParent())) {
262
+ return declaration.getParent();
263
+ }
264
+ return declaration;
265
+ }
266
+ if (isObjectLiteralExpression(propertyValue) || isCallExpression(propertyValue)) {
267
+ return propertyValue;
268
+ }
269
+ return propertyValue;
270
+ } catch (error) {
271
+ console.error(`[traceConfigProperty] Error tracing property "${propertyName}":`, error);
272
+ return null;
273
+ }
274
+ }
275
+ function extractComponentPathsFromImports(sourceFile, projectPath) {
276
+ const componentPaths = [];
277
+ try {
278
+ const importDeclarations = sourceFile.getChildrenOfKind(SyntaxKind.ImportDeclaration);
279
+ for (const importDecl of importDeclarations) {
280
+ try {
281
+ const importedSourceFile = getImportSourceFile(importDecl);
282
+ if (!importedSourceFile) {
283
+ continue;
284
+ }
285
+ const filePath = importedSourceFile.getFilePath();
286
+ const relativePath = filePath.replace(projectPath + "/", "");
287
+ if (relativePath.includes("node_modules")) {
288
+ continue;
289
+ }
290
+ componentPaths.push(relativePath);
291
+ } catch (error) {
292
+ console.error("[extractComponentPathsFromImports] Error processing import:", error);
293
+ }
294
+ }
295
+ } catch (error) {
296
+ console.error("[extractComponentPathsFromImports] Error extracting imports:", error);
297
+ }
298
+ return componentPaths;
299
+ }
300
+ function findNodesInFile(filePath, nodeMap) {
301
+ const nodes = [];
302
+ for (const [nodeId, node] of nodeMap.entries()) {
303
+ const filePathInId = nodeId.split("::")[0];
304
+ if (filePathInId === filePath) {
305
+ nodes.push(node);
306
+ }
307
+ }
308
+ return nodes;
309
+ }
310
+ function createConfigBasedEdges(sourceNode, targetFilePaths, nodeMap, edges, edgeType = "config") {
311
+ let edgesCreated = 0;
312
+ for (const targetFilePath of targetFilePaths) {
313
+ const targetNodes = findNodesInFile(targetFilePath, nodeMap);
314
+ for (const targetNode of targetNodes) {
315
+ if (sourceNode.id === targetNode.id) {
316
+ continue;
317
+ }
318
+ const edgeId = `e-${sourceNode.id}-${targetNode.id}`;
319
+ if (edges.find((e) => e.id === edgeId)) {
320
+ continue;
321
+ }
322
+ edges.push({
323
+ id: edgeId,
324
+ source: sourceNode.id,
325
+ target: targetNode.id,
326
+ type: "floating",
327
+ label: edgeType
328
+ });
329
+ edgesCreated++;
330
+ }
331
+ }
332
+ return edgesCreated;
333
+ }
334
+ function analyzeJsxPropsForComponents(jsxElement, propName, projectPath) {
335
+ const componentPaths = [];
336
+ try {
337
+ const attributes = jsxElement.getChildrenOfKind(SyntaxKind.JsxAttribute);
338
+ for (const attr of attributes) {
339
+ const attrName = attr.getChildrenOfKind(SyntaxKind.Identifier)[0]?.getText();
340
+ if (attrName !== propName) {
341
+ continue;
342
+ }
343
+ const initializer = attr.getChildrenOfKind(SyntaxKind.JsxExpression)[0];
344
+ if (!initializer) continue;
345
+ const identifier = initializer.getChildrenOfKind(SyntaxKind.Identifier)[0];
346
+ if (!identifier) continue;
347
+ const declaration = traceIdentifierToDeclaration(identifier);
348
+ if (!declaration || !isVariableDeclaration(declaration)) continue;
349
+ const dataFlow = traceVariableDataFlow(declaration);
350
+ if (!dataFlow || !isCallExpression(dataFlow)) continue;
351
+ const args = dataFlow.getChildrenOfKind(SyntaxKind.SyntaxList)[0];
352
+ if (!args) continue;
353
+ const configArg = args.getChildrenOfKind(SyntaxKind.ObjectLiteralExpression)[0];
354
+ if (!configArg) continue;
355
+ const commonPropNames = ["routeTree", "routes", "children", "components"];
356
+ for (const commonPropName of commonPropNames) {
357
+ const paths = traceConfigToComponentPaths(configArg, commonPropName, projectPath);
358
+ if (paths.length > 0) {
359
+ return paths;
360
+ }
361
+ }
362
+ }
363
+ } catch (error) {
364
+ console.error("[analyzeJsxPropsForComponents] Error:", error);
365
+ }
366
+ return componentPaths;
367
+ }
368
+ function traceConfigToComponentPaths(configObject, propertyName, projectPath) {
369
+ try {
370
+ const propertyNode = traceConfigProperty(configObject, propertyName);
371
+ if (!propertyNode) {
372
+ return [];
373
+ }
374
+ if (isImportDeclaration(propertyNode)) {
375
+ const sourceFile = getImportSourceFile(propertyNode);
376
+ if (sourceFile) {
377
+ return extractComponentPathsFromImports(sourceFile, projectPath);
378
+ }
379
+ }
380
+ if (isObjectLiteralExpression(propertyNode)) {
381
+ const properties = extractObjectLiteralProperties(propertyNode);
382
+ const allPaths = [];
383
+ for (const [, value] of properties) {
384
+ if (isIdentifier(value)) {
385
+ const decl = traceIdentifierToDeclaration(value);
386
+ if (decl && isImportDeclaration(decl.getParent())) {
387
+ const sourceFile = getImportSourceFile(decl.getParent());
388
+ if (sourceFile) {
389
+ const paths = extractComponentPathsFromImports(sourceFile, projectPath);
390
+ allPaths.push(...paths);
391
+ }
392
+ }
393
+ }
394
+ }
395
+ return allPaths;
396
+ }
397
+ } catch (error) {
398
+ console.error("[traceConfigToComponentPaths] Error:", error);
399
+ }
400
+ return [];
401
+ }
402
+ function detectConfigBasedComponents(sourceFile, nodeMap, edges, projectPath) {
403
+ let totalEdgesCreated = 0;
404
+ try {
405
+ const jsxElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement);
406
+ for (const jsxElement of jsxElements) {
407
+ const containingFunc = jsxElement.getFirstAncestorByKind(SyntaxKind.FunctionDeclaration) || jsxElement.getFirstAncestorByKind(SyntaxKind.ArrowFunction);
408
+ if (!containingFunc) continue;
409
+ const parentName = containingFunc.getName?.() || containingFunc.getParent()?.asKind(SyntaxKind.VariableDeclaration)?.getName() || null;
410
+ if (!parentName) continue;
411
+ const sourceNode = Array.from(nodeMap.values()).find((node) => {
412
+ const nodeName = node.id.split("::")[1];
413
+ return nodeName === parentName;
414
+ });
415
+ if (!sourceNode) continue;
416
+ const commonPropNames = ["router", "config", "routes", "navigation"];
417
+ for (const propName of commonPropNames) {
418
+ const componentPaths = analyzeJsxPropsForComponents(jsxElement, propName, projectPath);
419
+ if (componentPaths.length > 0) {
420
+ const edgesCreated = createConfigBasedEdges(
421
+ sourceNode,
422
+ componentPaths,
423
+ nodeMap,
424
+ edges,
425
+ `config:${propName}`
426
+ );
427
+ totalEdgesCreated += edgesCreated;
428
+ }
429
+ }
430
+ }
431
+ } catch (error) {
432
+ console.error("[detectConfigBasedComponents] Error:", error);
433
+ }
434
+ return totalEdgesCreated;
435
+ }
127
436
  async function analyzeProject(projectPath, projectId, onLog) {
128
437
  const logs = [];
129
438
  const log = (message) => {
@@ -184,13 +493,27 @@ async function analyzeProject(projectPath, projectId, onLog) {
184
493
  for (const sourceFile of sourceFiles) {
185
494
  generateEdges(sourceFile, nodeMap, edges);
186
495
  }
187
- log(` Generated ${edges.length} edges`);
496
+ log(` Generated ${edges.length} edges (syntactic analysis)`);
497
+ log(` Analyzing config-based components...`);
498
+ let totalConfigEdges = 0;
499
+ for (const sourceFile of sourceFiles) {
500
+ const configEdges = detectConfigBasedComponents(sourceFile, nodeMap, edges, projectPath);
501
+ totalConfigEdges += configEdges;
502
+ }
503
+ if (totalConfigEdges > 0) {
504
+ log(` Generated ${totalConfigEdges} additional edges from config analysis`);
505
+ }
506
+ log(` Total edges: ${edges.length}`);
188
507
  const packageJsonPath = path2.join(projectPath, "package.json");
189
508
  let packageJson = { name: path2.basename(projectPath) };
190
509
  if (fs.existsSync(packageJsonPath)) {
191
510
  packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
192
511
  }
193
512
  const branchId = gitInfo.branch.replace(/[^a-zA-Z0-9]/g, "-");
513
+ const entryPoint = detectEntryPoint(projectPath);
514
+ if (entryPoint) {
515
+ log(` Entry point detected: ${entryPoint}`);
516
+ }
194
517
  const projectMetadata = {
195
518
  id: projectId || crypto.createHash("md5").update(projectPath).digest("hex").substring(0, 12),
196
519
  title: packageJson.name || path2.basename(projectPath),
@@ -198,7 +521,8 @@ async function analyzeProject(projectPath, projectId, onLog) {
198
521
  value: `${nodes.length} nodes`,
199
522
  trend: 0,
200
523
  chartData: [30, 40, 35, 50, 45, 60, 55, 70],
201
- chartColor: "#22c55e"
524
+ chartColor: "#22c55e",
525
+ entryPoint
202
526
  };
203
527
  const branchMetadata = {
204
528
  id: branchId,
@@ -245,6 +569,7 @@ function createEmptyAnalysis(projectPath, gitInfo) {
245
569
  packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
246
570
  }
247
571
  const branchId = gitInfo.branch.replace(/[^a-zA-Z0-9]/g, "-");
572
+ const entryPoint = detectEntryPoint(projectPath);
248
573
  return {
249
574
  project: {
250
575
  id: crypto.createHash("md5").update(projectPath).digest("hex").substring(0, 12),
@@ -253,7 +578,8 @@ function createEmptyAnalysis(projectPath, gitInfo) {
253
578
  value: "0 nodes",
254
579
  trend: 0,
255
580
  chartData: [],
256
- chartColor: "#6b7280"
581
+ chartColor: "#6b7280",
582
+ entryPoint
257
583
  },
258
584
  branches: [{
259
585
  id: branchId,
@@ -292,6 +618,64 @@ function getGitInfo(projectPath) {
292
618
  };
293
619
  }
294
620
  }
621
+ function detectEntryPoint(projectPath) {
622
+ const packageJsonPath = path2.join(projectPath, "package.json");
623
+ if (fs.existsSync(packageJsonPath)) {
624
+ try {
625
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
626
+ if (packageJson.main) {
627
+ return packageJson.main;
628
+ }
629
+ } catch (error) {
630
+ console.error("Error reading package.json:", error);
631
+ }
632
+ }
633
+ const viteConfigPaths = [
634
+ "vite.config.ts",
635
+ "vite.config.js",
636
+ "vite.config.mts",
637
+ "vite.config.mjs"
638
+ ];
639
+ for (const configFile of viteConfigPaths) {
640
+ const configPath = path2.join(projectPath, configFile);
641
+ if (fs.existsSync(configPath)) {
642
+ try {
643
+ const configContent = fs.readFileSync(configPath, "utf-8");
644
+ const inputMatch = configContent.match(/input:\s*['"]([\w\/\.-]+)['"]/);
645
+ if (inputMatch) {
646
+ return inputMatch[1];
647
+ }
648
+ const htmlMatch = configContent.match(/['"]\.\/?(index\.html)['"]/);
649
+ if (htmlMatch) {
650
+ const indexHtmlPath = path2.join(projectPath, "index.html");
651
+ if (fs.existsSync(indexHtmlPath)) {
652
+ const htmlContent = fs.readFileSync(indexHtmlPath, "utf-8");
653
+ const scriptMatch = htmlContent.match(/<script[^>]+src=["']\/?([^"']+)["']/);
654
+ if (scriptMatch) {
655
+ return scriptMatch[1];
656
+ }
657
+ }
658
+ }
659
+ } catch (error) {
660
+ console.error(`Error reading ${configFile}:`, error);
661
+ }
662
+ }
663
+ }
664
+ const commonEntryPoints = [
665
+ "src/main.ts",
666
+ "src/main.tsx",
667
+ "src/index.ts",
668
+ "src/index.tsx",
669
+ "src/app.ts",
670
+ "src/app.tsx"
671
+ ];
672
+ for (const entry of commonEntryPoints) {
673
+ if (fs.existsSync(path2.join(projectPath, entry))) {
674
+ return entry;
675
+ }
676
+ }
677
+ return void 0;
678
+ }
295
679
  function createNodeId(filePath, nodeName, gitHash, counter = 0) {
296
680
  const suffix = counter > 0 ? `-${counter}` : "";
297
681
  return `${filePath}::${nodeName}${suffix}::${gitHash}`;
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  readLocalMeta,
10
10
  writeLocalAnalysis,
11
11
  writeLocalMeta
12
- } from "./chunk-JA66HKP7.js";
12
+ } from "./chunk-JMVBDG54.js";
13
13
  export {
14
14
  addGitignoreEntry,
15
15
  analyzeProject,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  rayburstPlugin
3
- } from "./chunk-JA66HKP7.js";
3
+ } from "./chunk-JMVBDG54.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.5",
3
+ "version": "0.2.7",
4
4
  "description": "Rayburst - Automatic code analysis for TypeScript/JavaScript projects via Vite plugin",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -47,7 +47,7 @@
47
47
  "zod": "^4.2.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@rayburst/types": "^0.1.4",
50
+ "@rayburst/types": "^0.1.5",
51
51
  "@types/node": "^25.0.2",
52
52
  "tsup": "^8.5.1",
53
53
  "typescript": "^5.9.3",