react-doctor-cli-dev 1.0.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.
Files changed (82) hide show
  1. package/backend/.env +3 -0
  2. package/backend/dist/index.js +43 -0
  3. package/backend/dist/middleware/auth.js +16 -0
  4. package/backend/dist/routes/reports.js +93 -0
  5. package/backend/package-lock.json +2000 -0
  6. package/backend/package.json +30 -0
  7. package/backend/src/db.ts +24 -0
  8. package/backend/src/index.ts +49 -0
  9. package/backend/src/middleware/auth.ts +21 -0
  10. package/backend/src/routes/reports.ts +110 -0
  11. package/backend/tsconfig.json +12 -0
  12. package/cli/bin/react-doctor.js +29 -0
  13. package/cli/dist/commands/analyze.js +125 -0
  14. package/cli/dist/commands/full.js +366 -0
  15. package/cli/dist/commands/install.js +138 -0
  16. package/cli/dist/commands/profile.js +166 -0
  17. package/cli/dist/index.js +78 -0
  18. package/cli/dist/ui.js +113 -0
  19. package/cli/package-lock.json +936 -0
  20. package/cli/package.json +34 -0
  21. package/cli/src/commands/analyze.ts +162 -0
  22. package/cli/src/commands/full.ts +574 -0
  23. package/cli/src/commands/install.ts +163 -0
  24. package/cli/src/commands/profile.ts +246 -0
  25. package/cli/src/index.ts +84 -0
  26. package/cli/src/ui.ts +120 -0
  27. package/cli/tsconfig.json +16 -0
  28. package/core/report-compiler/index.ts +359 -0
  29. package/core/report-compiler/test-report-compiler.ts +126 -0
  30. package/core/rule-engine/context-builder.ts +146 -0
  31. package/core/rule-engine/evaluator.ts +131 -0
  32. package/core/rule-engine/index.ts +222 -0
  33. package/core/rule-engine/rules.json +304 -0
  34. package/core/rule-engine/suggestion-builder.ts +209 -0
  35. package/core/rule-engine/test-rule-engine.ts +144 -0
  36. package/core/rule-engine/types.ts +202 -0
  37. package/core/runtime/profiler/browser.ts +121 -0
  38. package/core/runtime/profiler/collectors.ts +216 -0
  39. package/core/runtime/profiler/index.ts +311 -0
  40. package/core/runtime/profiler/porfiler.ts +967 -0
  41. package/core/runtime/profiler/route-scanner.ts +76 -0
  42. package/core/runtime/profiler/score.ts +59 -0
  43. package/core/runtime/profiler/server.ts +115 -0
  44. package/core/runtime/profiler/types.ts +65 -0
  45. package/core/runtime/test-runtime-profiler.ts +226 -0
  46. package/core/static-ana/static/analyzer.ts +145 -0
  47. package/core/static-ana/static/ast-parser.ts +31 -0
  48. package/core/static-ana/static/detectors/console-log.ts +49 -0
  49. package/core/static-ana/static/detectors/dead-code.ts +51 -0
  50. package/core/static-ana/static/detectors/effect-loop.ts +45 -0
  51. package/core/static-ana/static/detectors/index.ts +16 -0
  52. package/core/static-ana/static/detectors/inline-function.ts +59 -0
  53. package/core/static-ana/static/detectors/inline-style.ts +52 -0
  54. package/core/static-ana/static/detectors/large-component.ts +79 -0
  55. package/core/static-ana/static/detectors/missing-key.ts +56 -0
  56. package/core/static-ana/static/detectors/missing-memo.ts +59 -0
  57. package/core/static-ana/static/detectors/prop-drilling.ts +66 -0
  58. package/core/static-ana/static/helpers.ts +81 -0
  59. package/core/static-ana/static/scanner.ts +93 -0
  60. package/core/static-ana/test-analyzer.ts +115 -0
  61. package/core/static-ana/types.ts +25 -0
  62. package/core/tests/mock-react-project/src/app.tsx +22 -0
  63. package/core/tests/mock-react-project/src/components/Button.tsx +9 -0
  64. package/core/tests/mock-react-project/src/components/Header.tsx +3 -0
  65. package/core/tests/mock-react-project/src/components/ListTesting.tsx +51 -0
  66. package/core/tests/mock-react-project/src/components/UserDashboard.tsx +66 -0
  67. package/core/tests/mock-react-project/src/utils.ts +4 -0
  68. package/package.json +55 -0
  69. package/react-doctor-cli-dev-1.0.0.tgz +0 -0
  70. package/shared/dist/index.d.ts +2 -0
  71. package/shared/dist/index.js +19 -0
  72. package/shared/dist/schemas.d.ts +91 -0
  73. package/shared/dist/schemas.js +82 -0
  74. package/shared/dist/types.d.ts +44 -0
  75. package/shared/dist/types.js +2 -0
  76. package/shared/package-lock.json +47 -0
  77. package/shared/package.json +21 -0
  78. package/shared/src/index.ts +4 -0
  79. package/shared/src/schemas.ts +136 -0
  80. package/shared/src/types.ts +137 -0
  81. package/shared/tsconfig.json +15 -0
  82. package/tsconfig.json +25 -0
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "react-doctor-cli-dev",
3
+ "version": "1.0.0",
4
+ "description": "React performance analyzer — static analysis + runtime profiling + smart suggestions",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "react-doctor": "./bin/react-doctor.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "prepare": "tsc"
12
+ },
13
+ "keywords": [
14
+ "react",
15
+ "performance",
16
+ "profiler",
17
+ "cli",
18
+ "analyzer"
19
+ ],
20
+ "author": "Ozma",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "axios": "^1.6.0",
24
+ "chalk": "^4.1.2",
25
+ "commander": "^12.0.0",
26
+ "ora": "^5.4.1",
27
+ "ts-node": "^10.9.0",
28
+ "typescript": "^5.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/babel__traverse": "^7.28.0",
32
+ "@types/node": "^20.0.0"
33
+ }
34
+ }
@@ -0,0 +1,162 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // cli/src/commands/analyze.ts
3
+ //
4
+ // react-doctor analyze <projectPath>
5
+ //
6
+ // Runs the Static Analyzer only — no browser needed, fast.
7
+ // Reads JSX/TSX source files, runs all 9 detectors, and
8
+ // saves staticreport.json to .react-doctor/
9
+ //
10
+ // Use this when:
11
+ // - You want a quick code quality check
12
+ // - You don't have Chrome installed
13
+ // - You only care about code patterns, not runtime metrics
14
+ //
15
+ // Use "react-doctor full" when you want everything.
16
+ // ─────────────────────────────────────────────────────────────
17
+
18
+ import { Command } from "commander";
19
+ import path from "path";
20
+ import fs from "fs";
21
+ import chalk from "chalk";
22
+ import {
23
+ printBanner, printSection, printResult,
24
+ printDone, printFail, printInfo,
25
+ severityIcon, spinner,
26
+ } from "../ui";
27
+ import { runFullCommand } from "./full";
28
+
29
+ function getCoreModule(relativePath: string) {
30
+ return require(path.resolve(__dirname, "..", "..", "..", "core", relativePath));
31
+ }
32
+
33
+ // ─────────────────────────────────────────────────────────────
34
+ // REGISTER COMMAND
35
+ // ─────────────────────────────────────────────────────────────
36
+
37
+ export function registerAnalyzeCommand(program: Command): void {
38
+ program
39
+ .command("analyze")
40
+ .description("Run static code analysis only (no browser required)")
41
+ .argument(
42
+ "[projectPath]",
43
+ "Path to the React project (defaults to current directory)",
44
+ process.cwd(),
45
+ )
46
+ .option(
47
+ "--full",
48
+ "After static analysis, also run the runtime profiler and rule engine",
49
+ false,
50
+ )
51
+ .option("--no-banner", "Skip the banner")
52
+ .action(async (projectPath: string, options) => {
53
+
54
+ // If --full flag is passed, delegate to the full command
55
+ // which runs the complete pipeline
56
+ if (options.full) {
57
+ await runFullCommand(projectPath, { noBanner: options.noBanner });
58
+ return;
59
+ }
60
+
61
+ const resolvedPath = path.resolve(projectPath);
62
+
63
+ if (!options.noBanner) printBanner();
64
+
65
+ // ── Validate project ────────────────────────────────────
66
+ if (!fs.existsSync(path.join(resolvedPath, "package.json"))) {
67
+ printFail(
68
+ `No package.json found at: ${resolvedPath}\n\n` +
69
+ ` Pass the path to your React project:\n` +
70
+ ` react-doctor analyze ./my-react-app`,
71
+ );
72
+ process.exit(1);
73
+ }
74
+
75
+ printSection("Static Analysis");
76
+ printInfo("Project", resolvedPath);
77
+ console.log();
78
+
79
+ // ── Run the static analyzer ─────────────────────────────
80
+ const spin = spinner("Scanning JSX/TSX source files...");
81
+
82
+ try {
83
+ const { FileScanner } = getCoreModule("static-ana/static/scanner");
84
+ const { StaticAnalyzer } = getCoreModule("static-ana/static/analyzer");
85
+
86
+ const scanner = new FileScanner();
87
+ const analyzer = new StaticAnalyzer();
88
+
89
+ const files = await scanner.findFiles(resolvedPath);
90
+ spin.text = ` Analyzing ${files.length} file(s)...`;
91
+
92
+ const report = await analyzer.analyze(files);
93
+
94
+ // Save to .react-doctor/
95
+ const outputDir = path.join(resolvedPath, ".react-doctor");
96
+ fs.mkdirSync(outputDir, { recursive: true });
97
+ fs.writeFileSync(
98
+ path.join(outputDir, "staticreport.json"),
99
+ JSON.stringify(report, null, 2),
100
+ );
101
+
102
+ spin.succeed(chalk.green(`Analysis complete — ${files.length} file(s) scanned`));
103
+
104
+ // ── Results summary ─────────────────────────────────────
105
+ printSection("Results");
106
+
107
+ const total = report.issues?.length ?? 0;
108
+ const critical = report.issues?.filter((i: any) => i.severity === "critical").length ?? 0;
109
+ const warnings = report.issues?.filter((i: any) => i.severity === "warning").length ?? 0;
110
+ const infos = report.issues?.filter((i: any) => i.severity === "info").length ?? 0;
111
+
112
+ printResult("Files analyzed", String(report.filesAnalyzed ?? 0), "info");
113
+ printResult("Components", String(report.componentCount ?? 0), "info");
114
+ printResult("Total issues", String(total), total > 0 ? "warn" : "good");
115
+ printResult("Critical", String(critical), critical > 0 ? "poor" : "good");
116
+ printResult("Warnings", String(warnings), warnings > 0 ? "warn" : "good");
117
+ printResult("Info", String(infos), "info");
118
+ printResult("Health grade", report.grade ?? "N/A", "info");
119
+
120
+ // ── Detailed issues (top 10) ────────────────────────────
121
+ if (total > 0) {
122
+ printSection("Issues Found");
123
+
124
+ const sorted = [...(report.issues ?? [])].sort((a: any, b: any) => {
125
+ const order: Record<string, number> = { critical: 0, warning: 1, info: 2 };
126
+ return (order[a.severity] ?? 3) - (order[b.severity] ?? 3);
127
+ });
128
+
129
+ sorted.slice(0, 10).forEach((issue: any) => {
130
+ const icon = severityIcon(issue.severity);
131
+ console.log(` ${icon} ${chalk.bold(issue.component ?? "Unknown")}`);
132
+ console.log(` ${chalk.gray(issue.file + ":" + issue.line)}`);
133
+ console.log(` ${issue.message}`);
134
+ console.log(` ${chalk.cyan("→")} ${issue.suggestion}`);
135
+ console.log();
136
+ });
137
+
138
+ if (total > 10) {
139
+ console.log(chalk.gray(` ... and ${total - 10} more. See the full report:\n`));
140
+ console.log(chalk.cyan(` ${path.join(resolvedPath, ".react-doctor", "staticreport.json")}\n`));
141
+ }
142
+ } else {
143
+ console.log(chalk.green(" ✅ No issues found — your code looks great!\n"));
144
+ }
145
+
146
+ printResult("Report saved", path.join(resolvedPath, ".react-doctor", "staticreport.json"), "info");
147
+ console.log();
148
+
149
+ printDone("Static analysis finished.");
150
+ console.log(
151
+ chalk.gray(" Tip: run ") +
152
+ chalk.cyan("react-doctor full ./") +
153
+ chalk.gray(" to also measure runtime performance.\n"),
154
+ );
155
+
156
+ } catch (err: any) {
157
+ spin.fail(chalk.red("Static analysis failed"));
158
+ console.log(chalk.red(`\n ${err.message}\n`));
159
+ process.exit(1);
160
+ }
161
+ });
162
+ }