@rayburst/cli 0.2.1 → 0.2.3
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/README.md +10 -1
- package/dist/{chunk-NIOHEIF6.js → chunk-2VW2AH2U.js} +155 -29
- package/dist/index.js +1 -1
- package/dist/vite-plugin.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -83,7 +83,16 @@ The generated `analysis.json` contains:
|
|
|
83
83
|
- **Branches**: Git branch information
|
|
84
84
|
- **Files**: Modification timestamps
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
## Viewing Your Analysis
|
|
87
|
+
|
|
88
|
+
Once analysis is complete, open the [Rayburst web application](https://rayburst.app) in Chrome or Edge:
|
|
89
|
+
|
|
90
|
+
1. Click "Add Project" button
|
|
91
|
+
2. Select your project folder
|
|
92
|
+
3. The app will load `.rayburst/analysis.json` automatically
|
|
93
|
+
4. Explore your codebase visually with interactive dependency graphs
|
|
94
|
+
|
|
95
|
+
The web app uses the File System Access API (Chrome/Edge only) to read your local analysis data.
|
|
87
96
|
|
|
88
97
|
## Example Output
|
|
89
98
|
|
|
@@ -1,9 +1,129 @@
|
|
|
1
1
|
// src/analysis/analyze-project.ts
|
|
2
2
|
import { Project, SyntaxKind, Node } from "ts-morph";
|
|
3
|
-
import { execSync } from "child_process";
|
|
3
|
+
import { execSync as execSync2 } from "child_process";
|
|
4
4
|
import * as fs from "fs";
|
|
5
|
-
import * as
|
|
5
|
+
import * as path2 from "path";
|
|
6
6
|
import crypto from "crypto";
|
|
7
|
+
|
|
8
|
+
// src/git-utils.ts
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import path from "path";
|
|
11
|
+
function getUncommittedChanges(projectPath) {
|
|
12
|
+
try {
|
|
13
|
+
if (!isGitRepository(projectPath)) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
const command = "git diff --name-only HEAD && git diff --name-only --cached";
|
|
17
|
+
const output = execSync(command, {
|
|
18
|
+
cwd: projectPath,
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
21
|
+
// Ignore stderr
|
|
22
|
+
});
|
|
23
|
+
const files = output.split("\n").filter(Boolean).map((file) => path.resolve(projectPath, file.trim()));
|
|
24
|
+
return [...new Set(files)];
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("Failed to get git diff:", error.message);
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function isGitRepository(projectPath) {
|
|
31
|
+
try {
|
|
32
|
+
execSync("git rev-parse --is-inside-work-tree", {
|
|
33
|
+
cwd: projectPath,
|
|
34
|
+
encoding: "utf8",
|
|
35
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
36
|
+
});
|
|
37
|
+
return true;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function getUntrackedFiles(projectPath) {
|
|
43
|
+
try {
|
|
44
|
+
if (!isGitRepository(projectPath)) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
const output = execSync("git ls-files --others --exclude-standard", {
|
|
48
|
+
cwd: projectPath,
|
|
49
|
+
encoding: "utf8",
|
|
50
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
51
|
+
});
|
|
52
|
+
return output.split("\n").filter(Boolean).map((file) => path.resolve(projectPath, file.trim()));
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error("Failed to get untracked files:", error.message);
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function getAllChangedFiles(projectPath) {
|
|
59
|
+
const uncommitted = getUncommittedChanges(projectPath);
|
|
60
|
+
const untracked = getUntrackedFiles(projectPath);
|
|
61
|
+
return [.../* @__PURE__ */ new Set([...uncommitted, ...untracked])];
|
|
62
|
+
}
|
|
63
|
+
function getChangedFilesVsRemote(projectPath, remoteBranch = void 0) {
|
|
64
|
+
try {
|
|
65
|
+
if (!isGitRepository(projectPath)) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
let currentBranch;
|
|
69
|
+
try {
|
|
70
|
+
currentBranch = execSync("git branch --show-current", {
|
|
71
|
+
cwd: projectPath,
|
|
72
|
+
encoding: "utf8",
|
|
73
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
74
|
+
}).trim();
|
|
75
|
+
} catch {
|
|
76
|
+
const branchOutput = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
77
|
+
cwd: projectPath,
|
|
78
|
+
encoding: "utf8",
|
|
79
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
80
|
+
}).trim();
|
|
81
|
+
currentBranch = branchOutput;
|
|
82
|
+
}
|
|
83
|
+
const remote = remoteBranch || `origin/${currentBranch}`;
|
|
84
|
+
try {
|
|
85
|
+
execSync("git fetch origin --quiet", {
|
|
86
|
+
cwd: projectPath,
|
|
87
|
+
stdio: "ignore",
|
|
88
|
+
timeout: 5e3
|
|
89
|
+
});
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
execSync(`git rev-parse --verify ${remote}`, {
|
|
94
|
+
cwd: projectPath,
|
|
95
|
+
stdio: "ignore"
|
|
96
|
+
});
|
|
97
|
+
} catch {
|
|
98
|
+
if (remote !== "origin/main") {
|
|
99
|
+
try {
|
|
100
|
+
execSync("git rev-parse --verify origin/main", {
|
|
101
|
+
cwd: projectPath,
|
|
102
|
+
stdio: "ignore"
|
|
103
|
+
});
|
|
104
|
+
return getChangedFilesVsRemote(projectPath, "origin/main");
|
|
105
|
+
} catch {
|
|
106
|
+
console.warn(`No remote branch found (tried ${remote} and origin/main)`);
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
const output = execSync(`git diff --name-only ${remote}...HEAD`, {
|
|
113
|
+
cwd: projectPath,
|
|
114
|
+
encoding: "utf8",
|
|
115
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
116
|
+
});
|
|
117
|
+
const committedChanges = output.split("\n").filter(Boolean).map((file) => path.resolve(projectPath, file.trim()));
|
|
118
|
+
const uncommittedChanges = getAllChangedFiles(projectPath);
|
|
119
|
+
return [.../* @__PURE__ */ new Set([...committedChanges, ...uncommittedChanges])];
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.warn("Failed to get changed files vs remote:", error.message);
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/analysis/analyze-project.ts
|
|
7
127
|
async function analyzeProject(projectPath, projectId, onLog) {
|
|
8
128
|
const logs = [];
|
|
9
129
|
const log = (message) => {
|
|
@@ -18,7 +138,7 @@ async function analyzeProject(projectPath, projectId, onLog) {
|
|
|
18
138
|
const gitInfo = getGitInfo(projectPath);
|
|
19
139
|
log(` Git hash: ${gitHash}`);
|
|
20
140
|
log(` Current branch: ${gitInfo.branch}`);
|
|
21
|
-
const tsconfigPath =
|
|
141
|
+
const tsconfigPath = path2.join(projectPath, "tsconfig.json");
|
|
22
142
|
if (!fs.existsSync(tsconfigPath)) {
|
|
23
143
|
log(` Warning: No tsconfig.json found, skipping TypeScript analysis`);
|
|
24
144
|
const emptyAnalysis = createEmptyAnalysis(projectPath, gitInfo);
|
|
@@ -65,15 +185,15 @@ async function analyzeProject(projectPath, projectId, onLog) {
|
|
|
65
185
|
generateEdges(sourceFile, nodeMap, edges);
|
|
66
186
|
}
|
|
67
187
|
log(` Generated ${edges.length} edges`);
|
|
68
|
-
const packageJsonPath =
|
|
69
|
-
let packageJson = { name:
|
|
188
|
+
const packageJsonPath = path2.join(projectPath, "package.json");
|
|
189
|
+
let packageJson = { name: path2.basename(projectPath) };
|
|
70
190
|
if (fs.existsSync(packageJsonPath)) {
|
|
71
191
|
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
72
192
|
}
|
|
73
193
|
const branchId = gitInfo.branch.replace(/[^a-zA-Z0-9]/g, "-");
|
|
74
194
|
const projectMetadata = {
|
|
75
195
|
id: projectId || crypto.createHash("md5").update(projectPath).digest("hex").substring(0, 12),
|
|
76
|
-
title: packageJson.name ||
|
|
196
|
+
title: packageJson.name || path2.basename(projectPath),
|
|
77
197
|
subtitle: (/* @__PURE__ */ new Date()).toLocaleDateString(),
|
|
78
198
|
value: `${nodes.length} nodes`,
|
|
79
199
|
trend: 0,
|
|
@@ -94,6 +214,9 @@ async function analyzeProject(projectPath, projectId, onLog) {
|
|
|
94
214
|
nodes,
|
|
95
215
|
edges
|
|
96
216
|
};
|
|
217
|
+
log(" Checking for changed files vs remote...");
|
|
218
|
+
const changedFiles = getChangedFilesVsRemote(projectPath);
|
|
219
|
+
log(` Found ${changedFiles.length} changed files`);
|
|
97
220
|
const result = {
|
|
98
221
|
project: projectMetadata,
|
|
99
222
|
branches: [branchMetadata],
|
|
@@ -101,7 +224,10 @@ async function analyzeProject(projectPath, projectId, onLog) {
|
|
|
101
224
|
[branchId]: planData
|
|
102
225
|
},
|
|
103
226
|
analyzedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
104
|
-
logs
|
|
227
|
+
logs,
|
|
228
|
+
changedFilesVsRemote: changedFiles.length > 0 ? {
|
|
229
|
+
[branchId]: changedFiles
|
|
230
|
+
} : void 0
|
|
105
231
|
};
|
|
106
232
|
return result;
|
|
107
233
|
} catch (error) {
|
|
@@ -113,8 +239,8 @@ async function analyzeProject(projectPath, projectId, onLog) {
|
|
|
113
239
|
}
|
|
114
240
|
}
|
|
115
241
|
function createEmptyAnalysis(projectPath, gitInfo) {
|
|
116
|
-
const packageJsonPath =
|
|
117
|
-
let packageJson = { name:
|
|
242
|
+
const packageJsonPath = path2.join(projectPath, "package.json");
|
|
243
|
+
let packageJson = { name: path2.basename(projectPath) };
|
|
118
244
|
if (fs.existsSync(packageJsonPath)) {
|
|
119
245
|
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
120
246
|
}
|
|
@@ -122,7 +248,7 @@ function createEmptyAnalysis(projectPath, gitInfo) {
|
|
|
122
248
|
return {
|
|
123
249
|
project: {
|
|
124
250
|
id: crypto.createHash("md5").update(projectPath).digest("hex").substring(0, 12),
|
|
125
|
-
title: packageJson.name ||
|
|
251
|
+
title: packageJson.name || path2.basename(projectPath),
|
|
126
252
|
subtitle: (/* @__PURE__ */ new Date()).toLocaleDateString(),
|
|
127
253
|
value: "0 nodes",
|
|
128
254
|
trend: 0,
|
|
@@ -145,17 +271,17 @@ function createEmptyAnalysis(projectPath, gitInfo) {
|
|
|
145
271
|
}
|
|
146
272
|
function getGitHash(projectPath) {
|
|
147
273
|
try {
|
|
148
|
-
return
|
|
274
|
+
return execSync2("git rev-parse --short HEAD", { cwd: projectPath, encoding: "utf-8" }).trim();
|
|
149
275
|
} catch {
|
|
150
276
|
return "nogit";
|
|
151
277
|
}
|
|
152
278
|
}
|
|
153
279
|
function getGitInfo(projectPath) {
|
|
154
280
|
try {
|
|
155
|
-
const branch =
|
|
156
|
-
const lastCommit =
|
|
157
|
-
const author =
|
|
158
|
-
const commitCount = parseInt(
|
|
281
|
+
const branch = execSync2("git branch --show-current", { cwd: projectPath, encoding: "utf-8" }).trim() || "main";
|
|
282
|
+
const lastCommit = execSync2("git log -1 --pretty=%s", { cwd: projectPath, encoding: "utf-8" }).trim() || "No commits";
|
|
283
|
+
const author = execSync2("git log -1 --pretty=%an", { cwd: projectPath, encoding: "utf-8" }).trim() || "Unknown";
|
|
284
|
+
const commitCount = parseInt(execSync2("git rev-list --count HEAD", { cwd: projectPath, encoding: "utf-8" }).trim() || "0");
|
|
159
285
|
return { branch, lastCommit, author, commitCount };
|
|
160
286
|
} catch {
|
|
161
287
|
return {
|
|
@@ -400,14 +526,14 @@ function generateEdges(sourceFile, nodeMap, edges) {
|
|
|
400
526
|
|
|
401
527
|
// src/local-storage.ts
|
|
402
528
|
import { promises as fs2 } from "fs";
|
|
403
|
-
import
|
|
529
|
+
import path3 from "path";
|
|
404
530
|
async function ensureRayburstDir(projectPath) {
|
|
405
|
-
const rayburstDir =
|
|
531
|
+
const rayburstDir = path3.join(projectPath, ".rayburst");
|
|
406
532
|
await fs2.mkdir(rayburstDir, { recursive: true });
|
|
407
533
|
return rayburstDir;
|
|
408
534
|
}
|
|
409
535
|
async function readLocalAnalysis(projectPath) {
|
|
410
|
-
const analysisPath =
|
|
536
|
+
const analysisPath = path3.join(projectPath, ".rayburst", "analysis.json");
|
|
411
537
|
try {
|
|
412
538
|
const data = await fs2.readFile(analysisPath, "utf-8");
|
|
413
539
|
return JSON.parse(data);
|
|
@@ -417,11 +543,11 @@ async function readLocalAnalysis(projectPath) {
|
|
|
417
543
|
}
|
|
418
544
|
async function writeLocalAnalysis(projectPath, analysis) {
|
|
419
545
|
await ensureRayburstDir(projectPath);
|
|
420
|
-
const analysisPath =
|
|
546
|
+
const analysisPath = path3.join(projectPath, ".rayburst", "analysis.json");
|
|
421
547
|
await fs2.writeFile(analysisPath, JSON.stringify(analysis, null, 2), "utf-8");
|
|
422
548
|
}
|
|
423
549
|
async function readLocalMeta(projectPath) {
|
|
424
|
-
const metaPath =
|
|
550
|
+
const metaPath = path3.join(projectPath, ".rayburst", "meta.json");
|
|
425
551
|
try {
|
|
426
552
|
const data = await fs2.readFile(metaPath, "utf-8");
|
|
427
553
|
return JSON.parse(data);
|
|
@@ -431,11 +557,11 @@ async function readLocalMeta(projectPath) {
|
|
|
431
557
|
}
|
|
432
558
|
async function writeLocalMeta(projectPath, meta) {
|
|
433
559
|
await ensureRayburstDir(projectPath);
|
|
434
|
-
const metaPath =
|
|
560
|
+
const metaPath = path3.join(projectPath, ".rayburst", "meta.json");
|
|
435
561
|
await fs2.writeFile(metaPath, JSON.stringify(meta, null, 2), "utf-8");
|
|
436
562
|
}
|
|
437
563
|
async function addGitignoreEntry(projectPath) {
|
|
438
|
-
const gitignorePath =
|
|
564
|
+
const gitignorePath = path3.join(projectPath, ".gitignore");
|
|
439
565
|
let gitignoreContent = "";
|
|
440
566
|
try {
|
|
441
567
|
gitignoreContent = await fs2.readFile(gitignorePath, "utf-8");
|
|
@@ -449,7 +575,7 @@ async function addGitignoreEntry(projectPath) {
|
|
|
449
575
|
await fs2.writeFile(gitignorePath, gitignoreContent, "utf-8");
|
|
450
576
|
}
|
|
451
577
|
async function isProjectInitialized(projectPath) {
|
|
452
|
-
const rayburstDir =
|
|
578
|
+
const rayburstDir = path3.join(projectPath, ".rayburst");
|
|
453
579
|
try {
|
|
454
580
|
const stats = await fs2.stat(rayburstDir);
|
|
455
581
|
return stats.isDirectory();
|
|
@@ -471,20 +597,20 @@ async function initializeProject(projectPath, projectName, cliVersion) {
|
|
|
471
597
|
return meta;
|
|
472
598
|
}
|
|
473
599
|
function generateProjectId(projectPath) {
|
|
474
|
-
const baseName =
|
|
600
|
+
const baseName = path3.basename(projectPath);
|
|
475
601
|
const timestamp = Date.now().toString(36);
|
|
476
602
|
return `${baseName}-${timestamp}`;
|
|
477
603
|
}
|
|
478
604
|
|
|
479
605
|
// src/registry.ts
|
|
480
|
-
import
|
|
606
|
+
import path4 from "path";
|
|
481
607
|
import { fileURLToPath } from "url";
|
|
482
608
|
import os from "os";
|
|
483
609
|
var __filename = fileURLToPath(import.meta.url);
|
|
484
|
-
var __dirname =
|
|
485
|
-
var RAYBURST_DIR =
|
|
486
|
-
var PROJECTS_FILE =
|
|
487
|
-
var ANALYZED_DIR =
|
|
610
|
+
var __dirname = path4.dirname(__filename);
|
|
611
|
+
var RAYBURST_DIR = path4.join(os.homedir(), ".rayburst");
|
|
612
|
+
var PROJECTS_FILE = path4.join(RAYBURST_DIR, "projects.json");
|
|
613
|
+
var ANALYZED_DIR = path4.join(RAYBURST_DIR, "analyzed");
|
|
488
614
|
|
|
489
615
|
// src/incremental-analyzer.ts
|
|
490
616
|
import chalk from "chalk";
|
package/dist/index.js
CHANGED
package/dist/vite-plugin.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rayburst/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Rayburst - Automatic code analysis for TypeScript/JavaScript projects via Vite plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@rayburst/types": "^0.1.
|
|
45
|
+
"@rayburst/types": "^0.1.3",
|
|
46
46
|
"chalk": "^5.3.0",
|
|
47
47
|
"ts-morph": "^21.0.1",
|
|
48
48
|
"zod": "^4.2.0"
|