react-doctor-cli-dev 1.0.1 → 1.0.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.
@@ -0,0 +1,179 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // cli/src/commands/dashboard.ts
3
+ //
4
+ // react-doctor dashboard
5
+ //
6
+ // Opens the React Doctor dashboard in the browser.
7
+ //
8
+ // WHAT IT DOES:
9
+ // 1. Checks if the backend is already running on the port
10
+ // 2. If not — starts it automatically (same logic as --upload)
11
+ // 3. Opens http://localhost:PORT in the default browser
12
+ //
13
+ // This command is the natural companion to --upload.
14
+ // Workflow:
15
+ // react-doctor full ./my-app --upload ← runs analysis + saves report
16
+ // react-doctor dashboard ← opens the dashboard to view it
17
+ //
18
+ // Or in one shot:
19
+ // react-doctor full ./my-app --upload && react-doctor dashboard
20
+ // ─────────────────────────────────────────────────────────────
21
+
22
+ import { Command } from "commander";
23
+ import path from "path";
24
+ import fs from "fs";
25
+ import axios from "axios";
26
+ import { spawn } from "child_process";
27
+ import chalk from "chalk";
28
+ import {
29
+ printBanner, printSection,
30
+ printDone, printFail, printInfo, spinner,
31
+ } from "../ui";
32
+
33
+ export function registerDashboardCommand(program: Command): void {
34
+ program
35
+ .command("dashboard")
36
+ .description("Open the React Doctor dashboard (auto-starts backend if needed)")
37
+ .option(
38
+ "--port <port>",
39
+ "Port the backend runs on",
40
+ "3000",
41
+ )
42
+ .option(
43
+ "--api-key <key>",
44
+ "API key for the backend",
45
+ process.env.REACT_DOCTOR_API_KEY || "react-doctor-secret-key-change-this",
46
+ )
47
+ .option("--no-banner", "Skip the banner")
48
+ .action(async (options) => {
49
+
50
+ if (!options.noBanner) printBanner();
51
+
52
+ const port = options.port;
53
+ const apiUrl = `http://localhost:${port}`;
54
+
55
+ printSection("Dashboard");
56
+ printInfo("Backend URL", apiUrl);
57
+ console.log();
58
+
59
+ const spin = spinner("Checking backend status...");
60
+
61
+ try {
62
+ // ── 1. Check if backend is already up ─────────────────
63
+ let backendRunning = false;
64
+ try {
65
+ await axios.get(`${apiUrl}/health`, { timeout: 2000 });
66
+ backendRunning = true;
67
+ } catch {
68
+ backendRunning = false;
69
+ }
70
+
71
+ // ── 2. Start backend if not running ───────────────────
72
+ if (!backendRunning) {
73
+ spin.text = " Backend not running — starting automatically...";
74
+
75
+ // Locate backend folder (sibling of cli/)
76
+ const projectRoot = path.resolve(__dirname, "..", "..", "..");
77
+ const backendRoot = path.resolve(projectRoot, "backend");
78
+ const backendDist = path.join(backendRoot, "dist", "index.js");
79
+ const backendSrc = path.join(backendRoot, "src", "index.ts");
80
+
81
+ let command: string;
82
+ let args: string[];
83
+
84
+ if (fs.existsSync(backendDist)) {
85
+ command = "node";
86
+ args = [backendDist];
87
+ } else if (fs.existsSync(backendSrc)) {
88
+ command = "npx";
89
+ args = ["ts-node", backendSrc];
90
+ } else {
91
+ spin.fail(chalk.red("Backend not found"));
92
+ printFail(
93
+ `Could not find backend at: ${backendRoot}\n\n` +
94
+ ` Make sure the 'backend/' folder exists next to 'cli/'.`,
95
+ );
96
+ process.exit(1);
97
+ }
98
+
99
+ // Create data dir inside npm global cache for the backend DB
100
+ const dataDir = path.join(backendRoot, "data");
101
+ fs.mkdirSync(dataDir, { recursive: true });
102
+
103
+ spawn(command, args, {
104
+ stdio: "ignore",
105
+ detached: true,
106
+ env: {
107
+ ...process.env,
108
+ API_KEY: options.apiKey,
109
+ PORT: port,
110
+ DB_PATH: path.join(dataDir, "reports.db"),
111
+ },
112
+ cwd: backendRoot,
113
+ }).unref(); // let CLI exit without killing the server
114
+
115
+ // Wait for backend to be ready (up to 15 seconds)
116
+ let ready = false;
117
+ let retries = 0;
118
+ while (!ready && retries < 15) {
119
+ try {
120
+ await axios.get(`${apiUrl}/health`, { timeout: 1000 });
121
+ ready = true;
122
+ } catch {
123
+ await new Promise(r => setTimeout(r, 1000));
124
+ retries++;
125
+ }
126
+ }
127
+
128
+ if (!ready) {
129
+ spin.fail(chalk.red("Backend failed to start"));
130
+ printFail("Backend did not respond after 15 seconds.");
131
+ process.exit(1);
132
+ }
133
+
134
+ spin.succeed(chalk.green("Backend started successfully"));
135
+ } else {
136
+ spin.succeed(chalk.green("Backend already running"));
137
+ }
138
+
139
+ // ── 3. Open dashboard in default browser ──────────────
140
+ const dashboardUrl = apiUrl;
141
+
142
+ console.log();
143
+ printInfo("Opening", dashboardUrl);
144
+ console.log();
145
+
146
+ // Cross-platform browser open
147
+ const openCmd =
148
+ process.platform === "win32" ? ["cmd", ["/c", "start", dashboardUrl]] :
149
+ process.platform === "darwin" ? ["open", [dashboardUrl]] :
150
+ ["xdg-open", [dashboardUrl]];
151
+
152
+ spawn(openCmd[0] as string, openCmd[1] as string[], {
153
+ stdio: "ignore",
154
+ detached: true,
155
+ }).unref();
156
+
157
+ printDone(`Dashboard opened at ${chalk.cyan(dashboardUrl)}`);
158
+
159
+ // ── 4. Show quick API reference ───────────────────────
160
+ console.log(chalk.gray(" Available endpoints:"));
161
+ console.log(chalk.cyan(` GET ${apiUrl}/health`));
162
+ console.log(chalk.cyan(` GET ${apiUrl}/api/reports`));
163
+ console.log(chalk.cyan(` GET ${apiUrl}/api/reports/:id`));
164
+ console.log(chalk.cyan(` GET ${apiUrl}/api/reports/project/:name`));
165
+ console.log(chalk.cyan(` POST ${apiUrl}/api/reports/upload`));
166
+ console.log();
167
+ console.log(
168
+ chalk.gray(" Tip: run ") +
169
+ chalk.cyan("react-doctor full ./ --upload") +
170
+ chalk.gray(" to add a new report.\n"),
171
+ );
172
+
173
+ } catch (err: any) {
174
+ spin.fail(chalk.red("Dashboard failed to open"));
175
+ console.log(chalk.red(`\n ${err.message}\n`));
176
+ process.exit(1);
177
+ }
178
+ });
179
+ }
package/cli/src/index.ts CHANGED
@@ -1,35 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  // ─────────────────────────────────────────────────────────────
3
- // cli/src/index.ts
4
- //
5
- // The CLI entry point. This is the file that runs when the
6
- // user types "react-doctor" in their terminal.
7
- //
8
- // HOW IT WORKS:
9
- // 1. Commander.js parses the command and flags from argv
10
- // 2. The matching command handler is called
11
- // 3. The handler imports core modules and runs the pipeline
12
- //
13
- // HOW THE BINARY REGISTRATION WORKS:
14
- // package.json has a "bin" field:
15
- // "bin": { "react-doctor": "./dist/index.js" }
16
- //
17
- // After "npm link" (dev) or "npm install" (production),
18
- // npm creates a symlink from the system's bin directory
19
- // to this file. That's what makes "react-doctor" a real
20
- // terminal command available anywhere.
21
- //
22
- // THE SHEBANG (#!/usr/bin/env node) on line 1:
23
- // This tells the OS to run this file with Node.js when
24
- // called directly as a script. Without it, the OS doesn't
25
- // know which interpreter to use.
3
+ // cli/src/index.ts — CLI entry point
26
4
  // ─────────────────────────────────────────────────────────────
27
5
 
28
6
  import { Command } from "commander";
29
- import { registerAnalyzeCommand } from "./commands/analyze";
30
- import { registerProfileCommand } from "./commands/profile";
31
- import { registerFullCommand } from "./commands/full";
32
- import { registerInstallCommand } from "./commands/install";
7
+ import { registerAnalyzeCommand } from "./commands/analyze";
8
+ import { registerProfileCommand } from "./commands/profile";
9
+ import { registerFullCommand } from "./commands/full";
10
+ import { registerDashboardCommand } from "./commands/dashboard";
33
11
 
34
12
  const program = new Command();
35
13
 
@@ -37,46 +15,41 @@ const program = new Command();
37
15
  program
38
16
  .name("react-doctor")
39
17
  .description("React performance analyzer — static analysis + runtime profiling + smart suggestions")
40
- .version("1.0.0");
18
+ .version("1.0.2");
41
19
 
42
- // ── Register all commands ─────────────────────────────────────
43
- // Each function adds one command to the program.
44
- // The order here is the order they appear in --help output.
20
+ // ── Register commands ─────────────────────────────────────────
21
+ registerFullCommand(program); // react-doctor full
22
+ registerAnalyzeCommand(program); // react-doctor analyze
23
+ registerProfileCommand(program); // react-doctor profile
24
+ registerDashboardCommand(program); // react-doctor dashboard
45
25
 
46
- registerFullCommand(program); // react-doctor full
47
- registerAnalyzeCommand(program); // react-doctor analyze
48
- registerProfileCommand(program); // react-doctor profile
49
- registerInstallCommand(program); // react-doctor install
50
-
51
- // ── Usage examples shown at bottom of --help ─────────────────
26
+ // ── Usage examples ────────────────────────────────────────────
52
27
  program.addHelpText("after", `
53
28
  Examples:
54
- $ react-doctor full ./my-app Desktop only (default)
55
- $ react-doctor full ./my-app --mobile Mobile only
56
- $ react-doctor full ./my-app --desktop --mobile Both desktop and mobile
57
- $ react-doctor full ./my-app --cpu 4 Simulate slow Android device
58
- $ react-doctor full ./my-app --throttle slow4g Simulate slow 4G network
59
- $ react-doctor full ./my-app --throttle 3g Simulate 3G network
60
- $ react-doctor full ./my-app --cpu 4 --throttle 3g Slow device + slow network
61
- $ react-doctor full ./my-app --upload Upload results to dashboard
62
-
63
- $ react-doctor analyze ./my-app Static code analysis only
64
- $ react-doctor analyze ./my-app --full Static + runtime + rules
65
-
66
- $ react-doctor profile ./my-app Desktop only (default)
67
- $ react-doctor profile ./my-app --mobile Mobile only
68
- $ react-doctor profile ./my-app --desktop --mobile Both devices
69
- $ react-doctor profile ./my-app --cpu 4 4x CPU slowdown simulation
70
- $ react-doctor profile ./my-app --throttle slow4g Simulate slow 4G network
71
- $ react-doctor profile ./my-app --throttle 3g Simulate 3G network
72
-
73
- $ react-doctor install Install from GitHub into a project
74
- $ react-doctor install --path ./my-app Install into a specific folder
29
+ $ react-doctor full ./my-app Run full diagnostic (desktop)
30
+ $ react-doctor full ./my-app --mobile Include mobile viewport
31
+ $ react-doctor full ./my-app --desktop --mobile Both desktop and mobile
32
+ $ react-doctor full ./my-app --cpu 4 Simulate slow Android device
33
+ $ react-doctor full ./my-app --throttle slow4g Simulate slow 4G network
34
+ $ react-doctor full ./my-app --throttle 3g Simulate 3G network
35
+ $ react-doctor full ./my-app --cpu 4 --throttle 3g Slow device + slow network
36
+ $ react-doctor full ./my-app --upload Run + save report to dashboard
37
+
38
+ $ react-doctor analyze ./my-app Static code analysis only
39
+ $ react-doctor analyze ./my-app --full Static + runtime + rules
40
+
41
+ $ react-doctor profile ./my-app Runtime profiling only (desktop)
42
+ $ react-doctor profile ./my-app --mobile Mobile viewport
43
+ $ react-doctor profile ./my-app --desktop --mobile Both devices
44
+ $ react-doctor profile ./my-app --cpu 4 4x CPU slowdown simulation
45
+ $ react-doctor profile ./my-app --throttle slow4g Simulate slow 4G network
46
+ $ react-doctor profile ./my-app --throttle 3g Simulate 3G network
47
+
48
+ $ react-doctor dashboard Open dashboard (auto-starts backend)
49
+ $ react-doctor dashboard --port 4000 Use custom port
75
50
  `);
76
51
 
77
52
  // ── Show help if called with no arguments ─────────────────────
78
- // Without this, calling "react-doctor" with no command just
79
- // exits silently, which is confusing. This prints help instead.
80
53
  if (process.argv.length < 3) {
81
54
  program.help();
82
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor-cli-dev",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "React performance analyzer with static analysis, runtime profiling, rule engine, and dashboard upload",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
Binary file