@react-grab/cli 0.0.69 → 0.0.71
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/dist/cli.cjs +453 -76
- package/dist/cli.js +455 -78
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,8 @@ import { select, confirm } from '@inquirer/prompts';
|
|
|
3
3
|
import pc from 'picocolors';
|
|
4
4
|
import yargs from 'yargs';
|
|
5
5
|
import { hideBin } from 'yargs/helpers';
|
|
6
|
-
import {
|
|
7
|
-
import { join } from 'path';
|
|
6
|
+
import { readFileSync, existsSync, writeFileSync, readdirSync, accessSync, constants } from 'fs';
|
|
7
|
+
import { join, basename } from 'path';
|
|
8
8
|
import { detect } from '@antfu/ni';
|
|
9
9
|
import { execSync } from 'child_process';
|
|
10
10
|
|
|
@@ -73,22 +73,165 @@ var detectMonorepo = (projectRoot) => {
|
|
|
73
73
|
}
|
|
74
74
|
return false;
|
|
75
75
|
};
|
|
76
|
-
var
|
|
76
|
+
var getWorkspacePatterns = (projectRoot) => {
|
|
77
|
+
const patterns = [];
|
|
78
|
+
const pnpmWorkspacePath = join(projectRoot, "pnpm-workspace.yaml");
|
|
79
|
+
if (existsSync(pnpmWorkspacePath)) {
|
|
80
|
+
const content = readFileSync(pnpmWorkspacePath, "utf-8");
|
|
81
|
+
const lines = content.split("\n");
|
|
82
|
+
let inPackages = false;
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
if (line.match(/^packages:\s*$/)) {
|
|
85
|
+
inPackages = true;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (inPackages) {
|
|
89
|
+
if (line.match(/^[a-zA-Z]/) || line.trim() === "") {
|
|
90
|
+
if (line.match(/^[a-zA-Z]/)) inPackages = false;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const match = line.match(/^\s*-\s*['"]?([^'"#\n]+?)['"]?\s*$/);
|
|
94
|
+
if (match) {
|
|
95
|
+
patterns.push(match[1].trim());
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const lernaJsonPath = join(projectRoot, "lerna.json");
|
|
101
|
+
if (existsSync(lernaJsonPath)) {
|
|
102
|
+
try {
|
|
103
|
+
const lernaJson = JSON.parse(readFileSync(lernaJsonPath, "utf-8"));
|
|
104
|
+
if (Array.isArray(lernaJson.packages)) {
|
|
105
|
+
patterns.push(...lernaJson.packages);
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
77
110
|
const packageJsonPath = join(projectRoot, "package.json");
|
|
78
|
-
if (
|
|
79
|
-
|
|
111
|
+
if (existsSync(packageJsonPath)) {
|
|
112
|
+
try {
|
|
113
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
114
|
+
if (Array.isArray(packageJson.workspaces)) {
|
|
115
|
+
patterns.push(...packageJson.workspaces);
|
|
116
|
+
} else if (packageJson.workspaces?.packages) {
|
|
117
|
+
patterns.push(...packageJson.workspaces.packages);
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
80
121
|
}
|
|
122
|
+
return [...new Set(patterns)];
|
|
123
|
+
};
|
|
124
|
+
var expandWorkspacePattern = (projectRoot, pattern) => {
|
|
125
|
+
const results = [];
|
|
126
|
+
const cleanPattern = pattern.replace(/\/\*$/, "");
|
|
127
|
+
const basePath = join(projectRoot, cleanPattern);
|
|
128
|
+
if (!existsSync(basePath)) return results;
|
|
129
|
+
try {
|
|
130
|
+
const entries = readdirSync(basePath, { withFileTypes: true });
|
|
131
|
+
for (const entry of entries) {
|
|
132
|
+
if (entry.isDirectory()) {
|
|
133
|
+
const packageJsonPath = join(basePath, entry.name, "package.json");
|
|
134
|
+
if (existsSync(packageJsonPath)) {
|
|
135
|
+
results.push(join(basePath, entry.name));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
return results;
|
|
141
|
+
}
|
|
142
|
+
return results;
|
|
143
|
+
};
|
|
144
|
+
var hasReactDependency = (projectPath) => {
|
|
145
|
+
const packageJsonPath = join(projectPath, "package.json");
|
|
146
|
+
if (!existsSync(packageJsonPath)) return false;
|
|
81
147
|
try {
|
|
82
148
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
...packageJson.devDependencies
|
|
86
|
-
};
|
|
87
|
-
return Boolean(allDependencies["react-grab"]);
|
|
149
|
+
const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
150
|
+
return Boolean(allDeps["react"] || allDeps["react-dom"]);
|
|
88
151
|
} catch {
|
|
89
152
|
return false;
|
|
90
153
|
}
|
|
91
154
|
};
|
|
155
|
+
var findWorkspaceProjects = (projectRoot) => {
|
|
156
|
+
const patterns = getWorkspacePatterns(projectRoot);
|
|
157
|
+
const projects = [];
|
|
158
|
+
for (const pattern of patterns) {
|
|
159
|
+
const projectPaths = expandWorkspacePattern(projectRoot, pattern);
|
|
160
|
+
for (const projectPath of projectPaths) {
|
|
161
|
+
const framework = detectFramework(projectPath);
|
|
162
|
+
const hasReact = hasReactDependency(projectPath);
|
|
163
|
+
if (hasReact || framework !== "unknown") {
|
|
164
|
+
const packageJsonPath = join(projectPath, "package.json");
|
|
165
|
+
let name = basename(projectPath);
|
|
166
|
+
try {
|
|
167
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
168
|
+
name = packageJson.name || name;
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
projects.push({
|
|
172
|
+
name,
|
|
173
|
+
path: projectPath,
|
|
174
|
+
framework,
|
|
175
|
+
hasReact
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return projects;
|
|
181
|
+
};
|
|
182
|
+
var hasReactGrabInFile = (filePath) => {
|
|
183
|
+
if (!existsSync(filePath)) return false;
|
|
184
|
+
try {
|
|
185
|
+
const content = readFileSync(filePath, "utf-8");
|
|
186
|
+
const fuzzyPatterns = [
|
|
187
|
+
/["'`][^"'`]*react-grab/,
|
|
188
|
+
/react-grab[^"'`]*["'`]/,
|
|
189
|
+
/<[^>]*react-grab/i,
|
|
190
|
+
/import[^;]*react-grab/i,
|
|
191
|
+
/require[^)]*react-grab/i,
|
|
192
|
+
/from\s+[^;]*react-grab/i,
|
|
193
|
+
/src[^>]*react-grab/i
|
|
194
|
+
];
|
|
195
|
+
return fuzzyPatterns.some((pattern) => pattern.test(content));
|
|
196
|
+
} catch {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
var detectReactGrab = (projectRoot) => {
|
|
201
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
202
|
+
if (existsSync(packageJsonPath)) {
|
|
203
|
+
try {
|
|
204
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
205
|
+
const allDependencies = {
|
|
206
|
+
...packageJson.dependencies,
|
|
207
|
+
...packageJson.devDependencies
|
|
208
|
+
};
|
|
209
|
+
if (allDependencies["react-grab"]) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const filesToCheck = [
|
|
216
|
+
join(projectRoot, "app", "layout.tsx"),
|
|
217
|
+
join(projectRoot, "app", "layout.jsx"),
|
|
218
|
+
join(projectRoot, "src", "app", "layout.tsx"),
|
|
219
|
+
join(projectRoot, "src", "app", "layout.jsx"),
|
|
220
|
+
join(projectRoot, "pages", "_document.tsx"),
|
|
221
|
+
join(projectRoot, "pages", "_document.jsx"),
|
|
222
|
+
join(projectRoot, "instrumentation-client.ts"),
|
|
223
|
+
join(projectRoot, "instrumentation-client.js"),
|
|
224
|
+
join(projectRoot, "src", "instrumentation-client.ts"),
|
|
225
|
+
join(projectRoot, "src", "instrumentation-client.js"),
|
|
226
|
+
join(projectRoot, "index.html"),
|
|
227
|
+
join(projectRoot, "public", "index.html"),
|
|
228
|
+
join(projectRoot, "src", "index.tsx"),
|
|
229
|
+
join(projectRoot, "src", "index.ts"),
|
|
230
|
+
join(projectRoot, "src", "main.tsx"),
|
|
231
|
+
join(projectRoot, "src", "main.ts")
|
|
232
|
+
];
|
|
233
|
+
return filesToCheck.some(hasReactGrabInFile);
|
|
234
|
+
};
|
|
92
235
|
var AGENT_PACKAGES = ["@react-grab/claude-code", "@react-grab/cursor", "@react-grab/opencode"];
|
|
93
236
|
var detectUnsupportedFramework = (projectRoot) => {
|
|
94
237
|
const packageJsonPath = join(projectRoot, "package.json");
|
|
@@ -343,6 +486,19 @@ var WEBPACK_IMPORT_WITH_AGENT = (agent) => {
|
|
|
343
486
|
var SCRIPT_IMPORT = 'import Script from "next/script";';
|
|
344
487
|
|
|
345
488
|
// src/transform.ts
|
|
489
|
+
var hasReactGrabCode = (content) => {
|
|
490
|
+
const fuzzyPatterns = [
|
|
491
|
+
/["'`][^"'`]*react-grab/,
|
|
492
|
+
/react-grab[^"'`]*["'`]/,
|
|
493
|
+
/<[^>]*react-grab/i,
|
|
494
|
+
/import[^;]*react-grab/i,
|
|
495
|
+
/require[^)]*react-grab/i,
|
|
496
|
+
/from\s+[^;]*react-grab/i,
|
|
497
|
+
/src[^>]*react-grab/i,
|
|
498
|
+
/href[^>]*react-grab/i
|
|
499
|
+
];
|
|
500
|
+
return fuzzyPatterns.some((pattern) => pattern.test(content));
|
|
501
|
+
};
|
|
346
502
|
var findLayoutFile = (projectRoot) => {
|
|
347
503
|
const possiblePaths = [
|
|
348
504
|
join(projectRoot, "app", "layout.tsx"),
|
|
@@ -357,6 +513,26 @@ var findLayoutFile = (projectRoot) => {
|
|
|
357
513
|
}
|
|
358
514
|
return null;
|
|
359
515
|
};
|
|
516
|
+
var findInstrumentationFile = (projectRoot) => {
|
|
517
|
+
const possiblePaths = [
|
|
518
|
+
join(projectRoot, "instrumentation-client.ts"),
|
|
519
|
+
join(projectRoot, "instrumentation-client.js"),
|
|
520
|
+
join(projectRoot, "src", "instrumentation-client.ts"),
|
|
521
|
+
join(projectRoot, "src", "instrumentation-client.js")
|
|
522
|
+
];
|
|
523
|
+
for (const filePath of possiblePaths) {
|
|
524
|
+
if (existsSync(filePath)) {
|
|
525
|
+
return filePath;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return null;
|
|
529
|
+
};
|
|
530
|
+
var hasReactGrabInInstrumentation = (projectRoot) => {
|
|
531
|
+
const instrumentationPath = findInstrumentationFile(projectRoot);
|
|
532
|
+
if (!instrumentationPath) return false;
|
|
533
|
+
const content = readFileSync(instrumentationPath, "utf-8");
|
|
534
|
+
return hasReactGrabCode(content);
|
|
535
|
+
};
|
|
360
536
|
var findDocumentFile = (projectRoot) => {
|
|
361
537
|
const possiblePaths = [
|
|
362
538
|
join(projectRoot, "pages", "_document.tsx"),
|
|
@@ -424,7 +600,7 @@ var addAgentToExistingNextApp = (originalContent, agent, filePath) => {
|
|
|
424
600
|
strategy="lazyOnload"
|
|
425
601
|
/>`;
|
|
426
602
|
const reactGrabScriptMatch = originalContent.match(
|
|
427
|
-
/<Script[^>]*
|
|
603
|
+
/<(?:Script|script|NextScript)[^>]*react-grab[^>]*\/?>/is
|
|
428
604
|
);
|
|
429
605
|
if (reactGrabScriptMatch) {
|
|
430
606
|
const newContent = originalContent.replace(
|
|
@@ -537,15 +713,16 @@ var transformNextAppRouter = (projectRoot, agent, reactGrabAlreadyConfigured) =>
|
|
|
537
713
|
}
|
|
538
714
|
const originalContent = readFileSync(layoutPath, "utf-8");
|
|
539
715
|
let newContent = originalContent;
|
|
540
|
-
const
|
|
541
|
-
|
|
716
|
+
const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
|
|
717
|
+
const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
|
|
718
|
+
if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
|
|
542
719
|
return addAgentToExistingNextApp(originalContent, agent, layoutPath);
|
|
543
720
|
}
|
|
544
|
-
if (
|
|
721
|
+
if (hasReactGrabInFile2 || hasReactGrabInInstrumentationFile) {
|
|
545
722
|
return {
|
|
546
723
|
success: true,
|
|
547
724
|
filePath: layoutPath,
|
|
548
|
-
message: "React Grab is already installed in this file",
|
|
725
|
+
message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
|
|
549
726
|
noChanges: true
|
|
550
727
|
};
|
|
551
728
|
}
|
|
@@ -596,15 +773,16 @@ var transformNextPagesRouter = (projectRoot, agent, reactGrabAlreadyConfigured)
|
|
|
596
773
|
}
|
|
597
774
|
const originalContent = readFileSync(documentPath, "utf-8");
|
|
598
775
|
let newContent = originalContent;
|
|
599
|
-
const
|
|
600
|
-
|
|
776
|
+
const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
|
|
777
|
+
const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
|
|
778
|
+
if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
|
|
601
779
|
return addAgentToExistingNextApp(originalContent, agent, documentPath);
|
|
602
780
|
}
|
|
603
|
-
if (
|
|
781
|
+
if (hasReactGrabInFile2 || hasReactGrabInInstrumentationFile) {
|
|
604
782
|
return {
|
|
605
783
|
success: true,
|
|
606
784
|
filePath: documentPath,
|
|
607
|
-
message: "React Grab is already installed in this file",
|
|
785
|
+
message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
|
|
608
786
|
noChanges: true
|
|
609
787
|
};
|
|
610
788
|
}
|
|
@@ -640,11 +818,11 @@ var transformVite = (projectRoot, agent, reactGrabAlreadyConfigured) => {
|
|
|
640
818
|
}
|
|
641
819
|
const originalContent = readFileSync(indexPath, "utf-8");
|
|
642
820
|
let newContent = originalContent;
|
|
643
|
-
const
|
|
644
|
-
if (
|
|
821
|
+
const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
|
|
822
|
+
if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
|
|
645
823
|
return addAgentToExistingVite(originalContent, agent, indexPath);
|
|
646
824
|
}
|
|
647
|
-
if (
|
|
825
|
+
if (hasReactGrabInFile2) {
|
|
648
826
|
return {
|
|
649
827
|
success: true,
|
|
650
828
|
filePath: indexPath,
|
|
@@ -676,11 +854,11 @@ var transformWebpack = (projectRoot, agent, reactGrabAlreadyConfigured) => {
|
|
|
676
854
|
};
|
|
677
855
|
}
|
|
678
856
|
const originalContent = readFileSync(entryPath, "utf-8");
|
|
679
|
-
const
|
|
680
|
-
if (
|
|
857
|
+
const hasReactGrabInFile2 = hasReactGrabCode(originalContent);
|
|
858
|
+
if (hasReactGrabInFile2 && reactGrabAlreadyConfigured) {
|
|
681
859
|
return addAgentToExistingWebpack(originalContent, agent, entryPath);
|
|
682
860
|
}
|
|
683
|
-
if (
|
|
861
|
+
if (hasReactGrabInFile2) {
|
|
684
862
|
return {
|
|
685
863
|
success: true,
|
|
686
864
|
filePath: entryPath,
|
|
@@ -846,7 +1024,25 @@ var applyPackageJsonTransform = (result) => {
|
|
|
846
1024
|
};
|
|
847
1025
|
|
|
848
1026
|
// src/cli.ts
|
|
849
|
-
var VERSION = "0.0.
|
|
1027
|
+
var VERSION = "0.0.71";
|
|
1028
|
+
var REPORT_URL = "https://reactgrab.com/api/report-cli";
|
|
1029
|
+
var reportToCli = async (type, config, error) => {
|
|
1030
|
+
try {
|
|
1031
|
+
await fetch(REPORT_URL, {
|
|
1032
|
+
method: "POST",
|
|
1033
|
+
headers: { "Content-Type": "application/json" },
|
|
1034
|
+
body: JSON.stringify({
|
|
1035
|
+
type,
|
|
1036
|
+
version: VERSION,
|
|
1037
|
+
config,
|
|
1038
|
+
error: error ? { message: error.message, stack: error.stack } : void 0,
|
|
1039
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1040
|
+
})
|
|
1041
|
+
}).catch(() => {
|
|
1042
|
+
});
|
|
1043
|
+
} catch {
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
850
1046
|
var FRAMEWORK_NAMES = {
|
|
851
1047
|
next: "Next.js",
|
|
852
1048
|
vite: "Vite",
|
|
@@ -877,49 +1073,122 @@ var showDocsLink = () => {
|
|
|
877
1073
|
`);
|
|
878
1074
|
};
|
|
879
1075
|
var showManualInstructions = (framework, nextRouterType) => {
|
|
880
|
-
console.log(
|
|
1076
|
+
console.log(
|
|
1077
|
+
`
|
|
881
1078
|
${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Manual Setup Instructions:")}
|
|
882
|
-
`
|
|
1079
|
+
`
|
|
1080
|
+
);
|
|
883
1081
|
if (framework === "next" && nextRouterType === "app") {
|
|
884
1082
|
console.log(`${pc.bold("Next.js App Router:")}`);
|
|
885
1083
|
console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
|
|
886
|
-
console.log(` 2. Add to ${pc.cyan("app/layout.tsx")}
|
|
1084
|
+
console.log(` 2. Add to ${pc.cyan("app/layout.tsx")} inside <head>:`);
|
|
887
1085
|
console.log(` ${pc.gray('import Script from "next/script";')}`);
|
|
888
|
-
console.log(
|
|
889
|
-
|
|
1086
|
+
console.log(
|
|
1087
|
+
` ${pc.gray('{process.env.NODE_ENV === "development" && (')}`
|
|
1088
|
+
);
|
|
1089
|
+
console.log(` ${pc.gray(" <Script")}`);
|
|
1090
|
+
console.log(
|
|
1091
|
+
` ${pc.gray(' src="//unpkg.com/react-grab/dist/index.global.js"')}`
|
|
1092
|
+
);
|
|
1093
|
+
console.log(` ${pc.gray(' crossOrigin="anonymous"')}`);
|
|
1094
|
+
console.log(` ${pc.gray(' strategy="beforeInteractive"')}`);
|
|
1095
|
+
console.log(` ${pc.gray(" />")}`);
|
|
1096
|
+
console.log(` ${pc.gray(")}")}`);
|
|
890
1097
|
} else if (framework === "next" && nextRouterType === "pages") {
|
|
891
1098
|
console.log(`${pc.bold("Next.js Pages Router:")}`);
|
|
892
1099
|
console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
|
|
893
|
-
console.log(` 2.
|
|
1100
|
+
console.log(` 2. Add to ${pc.cyan("pages/_document.tsx")} inside <Head>:`);
|
|
894
1101
|
console.log(` ${pc.gray('import Script from "next/script";')}`);
|
|
895
|
-
console.log(
|
|
896
|
-
|
|
1102
|
+
console.log(
|
|
1103
|
+
` ${pc.gray('{process.env.NODE_ENV === "development" && (')}`
|
|
1104
|
+
);
|
|
1105
|
+
console.log(` ${pc.gray(" <Script")}`);
|
|
1106
|
+
console.log(
|
|
1107
|
+
` ${pc.gray(' src="//unpkg.com/react-grab/dist/index.global.js"')}`
|
|
1108
|
+
);
|
|
1109
|
+
console.log(` ${pc.gray(' crossOrigin="anonymous"')}`);
|
|
1110
|
+
console.log(` ${pc.gray(' strategy="beforeInteractive"')}`);
|
|
1111
|
+
console.log(` ${pc.gray(" />")}`);
|
|
1112
|
+
console.log(` ${pc.gray(")}")}`);
|
|
897
1113
|
} else if (framework === "vite") {
|
|
898
1114
|
console.log(`${pc.bold("Vite:")}`);
|
|
899
1115
|
console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
|
|
900
1116
|
console.log(` 2. Add to ${pc.cyan("index.html")} inside <head>:`);
|
|
901
1117
|
console.log(` ${pc.gray('<script type="module">')}`);
|
|
902
|
-
console.log(
|
|
1118
|
+
console.log(
|
|
1119
|
+
` ${pc.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`
|
|
1120
|
+
);
|
|
903
1121
|
console.log(` ${pc.gray("</script>")}`);
|
|
904
1122
|
} else if (framework === "webpack") {
|
|
905
1123
|
console.log(`${pc.bold("Webpack:")}`);
|
|
906
1124
|
console.log(` 1. Install: ${pc.cyan("npm install -D react-grab")}`);
|
|
907
|
-
console.log(
|
|
908
|
-
|
|
1125
|
+
console.log(
|
|
1126
|
+
` 2. Add to your entry file (e.g., ${pc.cyan("src/index.tsx")}):`
|
|
1127
|
+
);
|
|
1128
|
+
console.log(
|
|
1129
|
+
` ${pc.gray('if (process.env.NODE_ENV === "development") {')}`
|
|
1130
|
+
);
|
|
909
1131
|
console.log(` ${pc.gray(' import("react-grab");')}`);
|
|
910
1132
|
console.log(` ${pc.gray("}")}`);
|
|
911
1133
|
} else {
|
|
912
|
-
console.log(
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
console.log(`
|
|
1134
|
+
console.log(
|
|
1135
|
+
`${pc.bold("Next.js App Router:")} Add to ${pc.cyan("app/layout.tsx")} inside <head>:`
|
|
1136
|
+
);
|
|
1137
|
+
console.log(` ${pc.gray('import Script from "next/script";')}`);
|
|
1138
|
+
console.log(
|
|
1139
|
+
` ${pc.gray('{process.env.NODE_ENV === "development" && (')}`
|
|
1140
|
+
);
|
|
1141
|
+
console.log(
|
|
1142
|
+
` ${pc.gray(' <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`
|
|
1143
|
+
);
|
|
1144
|
+
console.log(` ${pc.gray(")}")}`);
|
|
1145
|
+
console.log("");
|
|
1146
|
+
console.log(
|
|
1147
|
+
`${pc.bold("Next.js Pages Router:")} Add to ${pc.cyan("pages/_document.tsx")} inside <Head>:`
|
|
1148
|
+
);
|
|
1149
|
+
console.log(` ${pc.gray('import Script from "next/script";')}`);
|
|
1150
|
+
console.log(
|
|
1151
|
+
` ${pc.gray('{process.env.NODE_ENV === "development" && (')}`
|
|
1152
|
+
);
|
|
1153
|
+
console.log(
|
|
1154
|
+
` ${pc.gray(' <Script src="//unpkg.com/react-grab/dist/index.global.js" strategy="beforeInteractive" />')}`
|
|
1155
|
+
);
|
|
1156
|
+
console.log(` ${pc.gray(")}")}`);
|
|
1157
|
+
console.log("");
|
|
1158
|
+
console.log(
|
|
1159
|
+
`${pc.bold("Vite:")} Add to ${pc.cyan("index.html")} inside <head>:`
|
|
1160
|
+
);
|
|
1161
|
+
console.log(` ${pc.gray('<script type="module">')}`);
|
|
1162
|
+
console.log(
|
|
1163
|
+
` ${pc.gray(' if (import.meta.env.DEV) { import("react-grab"); }')}`
|
|
1164
|
+
);
|
|
1165
|
+
console.log(` ${pc.gray("</script>")}`);
|
|
1166
|
+
console.log("");
|
|
1167
|
+
console.log(
|
|
1168
|
+
`${pc.bold("Webpack:")} Add to entry file (e.g., ${pc.cyan("src/index.tsx")}):`
|
|
1169
|
+
);
|
|
1170
|
+
console.log(
|
|
1171
|
+
` ${pc.gray('if (process.env.NODE_ENV === "development") {')}`
|
|
1172
|
+
);
|
|
1173
|
+
console.log(` ${pc.gray(' import("react-grab");')}`);
|
|
1174
|
+
console.log(` ${pc.gray("}")}`);
|
|
1175
|
+
console.log("");
|
|
1176
|
+
console.log(`For full instructions, visit:`);
|
|
1177
|
+
console.log(
|
|
1178
|
+
` ${pc.cyan("https://github.com/aidenybai/react-grab#readme")}`
|
|
1179
|
+
);
|
|
1180
|
+
return;
|
|
916
1181
|
}
|
|
917
1182
|
showDocsLink();
|
|
918
1183
|
};
|
|
919
1184
|
var showAccuracyWarning = () => {
|
|
920
|
-
console.log(
|
|
921
|
-
|
|
922
|
-
|
|
1185
|
+
console.log(
|
|
1186
|
+
`
|
|
1187
|
+
${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Auto-detection may not be 100% accurate.")}`
|
|
1188
|
+
);
|
|
1189
|
+
console.log(
|
|
1190
|
+
`${pc.yellow(" Please verify the changes in your file before committing.")}`
|
|
1191
|
+
);
|
|
923
1192
|
};
|
|
924
1193
|
var parseArgs = async () => {
|
|
925
1194
|
const argv = await yargs(hideBin(process.argv)).scriptName("react-grab").usage(
|
|
@@ -961,7 +1230,13 @@ router type) and installs React Grab with optional agent integrations.`
|
|
|
961
1230
|
type: "boolean",
|
|
962
1231
|
default: false,
|
|
963
1232
|
description: "Only modify config files, skip npm/yarn/pnpm install"
|
|
964
|
-
}).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup with prompts").example("$0 -y", "Auto-detect everything and install without prompts").example("$0 -f next -r app", "Configure for Next.js App Router").example(
|
|
1233
|
+
}).help().alias("help", "h").version(VERSION).alias("version", "v").example("$0", "Run interactive setup with prompts").example("$0 -y", "Auto-detect everything and install without prompts").example("$0 -f next -r app", "Configure for Next.js App Router").example(
|
|
1234
|
+
"$0 -a cursor -y",
|
|
1235
|
+
"Add Cursor agent integration non-interactively"
|
|
1236
|
+
).example("$0 -p pnpm -a claude-code", "Use pnpm and add Claude Code agent").example(
|
|
1237
|
+
"$0 --skip-install",
|
|
1238
|
+
"Only modify files, install packages manually"
|
|
1239
|
+
).epilog(
|
|
965
1240
|
`${pc.bold("Agent Integrations:")}
|
|
966
1241
|
${pc.cyan("claude-code")} Connect React Grab to Claude Code
|
|
967
1242
|
${pc.cyan("cursor")} Connect React Grab to Cursor IDE
|
|
@@ -986,33 +1261,104 @@ ${pc.bold("Documentation:")} ${pc.underline(DOCS_URL)}`
|
|
|
986
1261
|
var main = async () => {
|
|
987
1262
|
const args = await parseArgs();
|
|
988
1263
|
const isNonInteractive = args.yes;
|
|
989
|
-
console.log(
|
|
990
|
-
|
|
1264
|
+
console.log(
|
|
1265
|
+
`
|
|
1266
|
+
${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`
|
|
1267
|
+
);
|
|
991
1268
|
const projectInfo = await detectProject(process.cwd());
|
|
992
|
-
console.log(
|
|
993
|
-
|
|
1269
|
+
console.log(
|
|
1270
|
+
`- Framework: ${pc.cyan(FRAMEWORK_NAMES[projectInfo.framework])}`
|
|
1271
|
+
);
|
|
1272
|
+
console.log(
|
|
1273
|
+
`- Package Manager: ${pc.cyan(PACKAGE_MANAGER_NAMES[projectInfo.packageManager])}`
|
|
1274
|
+
);
|
|
994
1275
|
if (projectInfo.framework === "next") {
|
|
995
|
-
console.log(
|
|
1276
|
+
console.log(
|
|
1277
|
+
`- Router Type: ${pc.cyan(projectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}`
|
|
1278
|
+
);
|
|
996
1279
|
}
|
|
997
|
-
console.log(
|
|
998
|
-
|
|
1280
|
+
console.log(
|
|
1281
|
+
`- Monorepo: ${pc.cyan(projectInfo.isMonorepo ? "Yes" : "No")}`
|
|
1282
|
+
);
|
|
1283
|
+
console.log(
|
|
1284
|
+
`- React Grab: ${projectInfo.hasReactGrab ? pc.green("Installed") : pc.yellow("Not installed")}`
|
|
1285
|
+
);
|
|
999
1286
|
if (projectInfo.installedAgents.length > 0) {
|
|
1000
|
-
console.log(
|
|
1287
|
+
console.log(
|
|
1288
|
+
`- Agents: ${pc.cyan(projectInfo.installedAgents.map((agent) => AGENT_NAMES[agent] || agent).join(", "))}`
|
|
1289
|
+
);
|
|
1001
1290
|
}
|
|
1002
1291
|
console.log("");
|
|
1003
1292
|
if (projectInfo.unsupportedFramework) {
|
|
1004
1293
|
const frameworkName = UNSUPPORTED_FRAMEWORK_NAMES[projectInfo.unsupportedFramework];
|
|
1005
|
-
console.log(
|
|
1006
|
-
|
|
1007
|
-
|
|
1294
|
+
console.log(
|
|
1295
|
+
`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow(`Detected ${frameworkName} - this framework requires manual setup.`)}`
|
|
1296
|
+
);
|
|
1297
|
+
console.log(
|
|
1298
|
+
`${pc.yellow(" React Grab may not work correctly with auto-configuration.")}
|
|
1299
|
+
`
|
|
1300
|
+
);
|
|
1008
1301
|
showManualInstructions(projectInfo.framework, projectInfo.nextRouterType);
|
|
1009
1302
|
process.exit(0);
|
|
1010
1303
|
}
|
|
1011
1304
|
if (projectInfo.framework === "unknown") {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1305
|
+
if (projectInfo.isMonorepo && !isNonInteractive) {
|
|
1306
|
+
const workspaceProjects = findWorkspaceProjects(projectInfo.projectRoot);
|
|
1307
|
+
const reactProjects = workspaceProjects.filter(
|
|
1308
|
+
(p) => p.hasReact || p.framework !== "unknown"
|
|
1309
|
+
);
|
|
1310
|
+
if (reactProjects.length > 0) {
|
|
1311
|
+
console.log(
|
|
1312
|
+
`${pc.cyan("\u2139")} ${pc.cyan("Found React projects in this monorepo:")}
|
|
1313
|
+
`
|
|
1314
|
+
);
|
|
1315
|
+
const selectedProject = await select({
|
|
1316
|
+
message: "Select a project to install React Grab:",
|
|
1317
|
+
choices: reactProjects.map((project) => ({
|
|
1318
|
+
name: `${project.name} ${pc.gray(`(${FRAMEWORK_NAMES[project.framework] || "React"})`)}`,
|
|
1319
|
+
value: project.path
|
|
1320
|
+
}))
|
|
1321
|
+
});
|
|
1322
|
+
console.log(
|
|
1323
|
+
`
|
|
1324
|
+
${pc.magenta("\u269B")} Switching to ${pc.cyan(selectedProject)}...
|
|
1325
|
+
`
|
|
1326
|
+
);
|
|
1327
|
+
process.chdir(selectedProject);
|
|
1328
|
+
const newProjectInfo = await detectProject(selectedProject);
|
|
1329
|
+
projectInfo.framework = newProjectInfo.framework;
|
|
1330
|
+
projectInfo.nextRouterType = newProjectInfo.nextRouterType;
|
|
1331
|
+
projectInfo.hasReactGrab = newProjectInfo.hasReactGrab;
|
|
1332
|
+
projectInfo.installedAgents = newProjectInfo.installedAgents;
|
|
1333
|
+
projectInfo.projectRoot = newProjectInfo.projectRoot;
|
|
1334
|
+
console.log(
|
|
1335
|
+
`- Framework: ${pc.cyan(FRAMEWORK_NAMES[newProjectInfo.framework])}`
|
|
1336
|
+
);
|
|
1337
|
+
if (newProjectInfo.framework === "next") {
|
|
1338
|
+
console.log(
|
|
1339
|
+
`- Router Type: ${pc.cyan(newProjectInfo.nextRouterType === "app" ? "App Router" : "Pages Router")}`
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
console.log(
|
|
1343
|
+
`- React Grab: ${newProjectInfo.hasReactGrab ? pc.green("Installed") : pc.yellow("Not installed")}`
|
|
1344
|
+
);
|
|
1345
|
+
console.log("");
|
|
1346
|
+
} else {
|
|
1347
|
+
console.log(
|
|
1348
|
+
`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Could not detect framework automatically.")}
|
|
1349
|
+
`
|
|
1350
|
+
);
|
|
1351
|
+
showManualInstructions("unknown");
|
|
1352
|
+
process.exit(0);
|
|
1353
|
+
}
|
|
1354
|
+
} else {
|
|
1355
|
+
console.log(
|
|
1356
|
+
`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("Could not detect framework automatically.")}
|
|
1357
|
+
`
|
|
1358
|
+
);
|
|
1359
|
+
showManualInstructions("unknown");
|
|
1360
|
+
process.exit(0);
|
|
1361
|
+
}
|
|
1016
1362
|
}
|
|
1017
1363
|
let action = "install-all";
|
|
1018
1364
|
if (projectInfo.hasReactGrab && !isNonInteractive) {
|
|
@@ -1027,9 +1373,13 @@ ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
|
|
|
1027
1373
|
} else if (projectInfo.hasReactGrab && args.agent && args.agent !== "none") {
|
|
1028
1374
|
action = "add-agent";
|
|
1029
1375
|
} else if (projectInfo.hasReactGrab && isNonInteractive && !args.agent) {
|
|
1030
|
-
console.log(
|
|
1031
|
-
|
|
1032
|
-
|
|
1376
|
+
console.log(
|
|
1377
|
+
`${pc.yellow("\u26A0\uFE0F")} ${pc.yellow("React Grab is already installed.")}`
|
|
1378
|
+
);
|
|
1379
|
+
console.log(
|
|
1380
|
+
`${pc.yellow(" Use --agent to add an agent, or run without -y for interactive mode.")}
|
|
1381
|
+
`
|
|
1382
|
+
);
|
|
1033
1383
|
action = "reconfigure";
|
|
1034
1384
|
}
|
|
1035
1385
|
let finalFramework = args.framework || projectInfo.framework;
|
|
@@ -1081,9 +1431,11 @@ ${pc.magenta("\u269B")} ${pc.bold("React Grab")} ${pc.gray(VERSION)}`);
|
|
|
1081
1431
|
{ name: "Opencode", value: "opencode" }
|
|
1082
1432
|
].filter((agent) => !projectInfo.installedAgents.includes(agent.value));
|
|
1083
1433
|
if (availableAgents.length === 0) {
|
|
1084
|
-
console.log(
|
|
1434
|
+
console.log(
|
|
1435
|
+
`
|
|
1085
1436
|
${pc.green("All agent integrations are already installed.")}
|
|
1086
|
-
`
|
|
1437
|
+
`
|
|
1438
|
+
);
|
|
1087
1439
|
} else if (action === "add-agent") {
|
|
1088
1440
|
agentIntegration = await select({
|
|
1089
1441
|
message: "Select an agent integration to add:",
|
|
@@ -1139,7 +1491,11 @@ ${pc.magenta("\u269B")} Previewing changes...
|
|
|
1139
1491
|
if (hasLayoutChanges) {
|
|
1140
1492
|
console.log("");
|
|
1141
1493
|
}
|
|
1142
|
-
printDiff(
|
|
1494
|
+
printDiff(
|
|
1495
|
+
packageJsonResult.filePath,
|
|
1496
|
+
packageJsonResult.originalContent,
|
|
1497
|
+
packageJsonResult.newContent
|
|
1498
|
+
);
|
|
1143
1499
|
}
|
|
1144
1500
|
if (hasLayoutChanges || hasPackageJsonChanges) {
|
|
1145
1501
|
showAccuracyWarning();
|
|
@@ -1156,16 +1512,25 @@ ${pc.yellow("Changes cancelled.")}
|
|
|
1156
1512
|
}
|
|
1157
1513
|
}
|
|
1158
1514
|
}
|
|
1159
|
-
const shouldInstallReactGrab = action === "install-all" && !projectInfo.hasReactGrab;
|
|
1515
|
+
const shouldInstallReactGrab = (action === "install-all" || action === "add-agent") && !projectInfo.hasReactGrab;
|
|
1160
1516
|
const shouldInstallAgent = agentIntegration !== "none" && !projectInfo.installedAgents.includes(agentIntegration);
|
|
1161
1517
|
if (!args.skipInstall && (shouldInstallReactGrab || shouldInstallAgent)) {
|
|
1162
|
-
const packages = getPackagesToInstall(
|
|
1518
|
+
const packages = getPackagesToInstall(
|
|
1519
|
+
agentIntegration,
|
|
1520
|
+
shouldInstallReactGrab
|
|
1521
|
+
);
|
|
1163
1522
|
if (packages.length > 0) {
|
|
1164
|
-
console.log(
|
|
1523
|
+
console.log(
|
|
1524
|
+
`
|
|
1165
1525
|
${pc.magenta("\u269B")} Installing: ${pc.cyan(packages.join(", "))}
|
|
1166
|
-
`
|
|
1526
|
+
`
|
|
1527
|
+
);
|
|
1167
1528
|
try {
|
|
1168
|
-
installPackages(
|
|
1529
|
+
installPackages(
|
|
1530
|
+
packages,
|
|
1531
|
+
finalPackageManager,
|
|
1532
|
+
projectInfo.projectRoot
|
|
1533
|
+
);
|
|
1169
1534
|
console.log(`
|
|
1170
1535
|
${pc.green("Packages installed successfully!")}
|
|
1171
1536
|
`);
|
|
@@ -1191,8 +1556,10 @@ ${pc.green("Applied:")} ${result.filePath}`);
|
|
|
1191
1556
|
if (hasPackageJsonChanges) {
|
|
1192
1557
|
const packageJsonWriteResult = applyPackageJsonTransform(packageJsonResult);
|
|
1193
1558
|
if (!packageJsonWriteResult.success) {
|
|
1194
|
-
console.error(
|
|
1195
|
-
|
|
1559
|
+
console.error(
|
|
1560
|
+
`
|
|
1561
|
+
${pc.red("Error:")} ${packageJsonWriteResult.error}`
|
|
1562
|
+
);
|
|
1196
1563
|
showDocsLink();
|
|
1197
1564
|
process.exit(1);
|
|
1198
1565
|
}
|
|
@@ -1204,20 +1571,30 @@ ${pc.red("Error:")} ${packageJsonWriteResult.error}`);
|
|
|
1204
1571
|
${pc.green("Done!")}`);
|
|
1205
1572
|
console.log(`
|
|
1206
1573
|
Next steps:`);
|
|
1207
|
-
console.log(
|
|
1208
|
-
console.log(
|
|
1209
|
-
console.log(
|
|
1574
|
+
console.log(`- Start your development server`);
|
|
1575
|
+
console.log(`- Select an element to copy its context`);
|
|
1576
|
+
console.log(`- Learn more at ${pc.cyan(DOCS_URL)}
|
|
1210
1577
|
`);
|
|
1211
1578
|
if (agentIntegration !== "none") {
|
|
1212
|
-
console.log(
|
|
1579
|
+
console.log(
|
|
1580
|
+
`${pc.magenta("\u269B")} Agent: ${pc.cyan(AGENT_NAMES[agentIntegration])}`
|
|
1581
|
+
);
|
|
1213
1582
|
console.log(` Make sure to start the agent server before using it.
|
|
1214
1583
|
`);
|
|
1215
1584
|
}
|
|
1585
|
+
await reportToCli("completed", {
|
|
1586
|
+
framework: finalFramework,
|
|
1587
|
+
packageManager: finalPackageManager,
|
|
1588
|
+
router: finalNextRouterType,
|
|
1589
|
+
agent: agentIntegration !== "none" ? agentIntegration : void 0,
|
|
1590
|
+
isMonorepo: projectInfo.isMonorepo
|
|
1591
|
+
});
|
|
1216
1592
|
};
|
|
1217
|
-
main().catch((error) => {
|
|
1593
|
+
main().catch(async (error) => {
|
|
1218
1594
|
console.error(`${pc.red("Error:")}`, error);
|
|
1219
1595
|
console.log("\nFor manual installation instructions, visit:");
|
|
1220
1596
|
console.log(` ${DOCS_URL}
|
|
1221
1597
|
`);
|
|
1598
|
+
await reportToCli("error", void 0, error);
|
|
1222
1599
|
process.exit(1);
|
|
1223
1600
|
});
|