@react-grab/cli 0.1.46 → 0.1.47-dev.bc3a591
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 +88 -0
- package/dist/api.cjs +70 -0
- package/dist/api.d.cts +121 -0
- package/dist/api.d.cts.map +1 -0
- package/dist/api.d.ts +121 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +53 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.cjs +42 -911
- package/dist/cli.js +19 -865
- package/dist/cli.js.map +1 -1
- package/dist/install-Car5vKPk.js +864 -0
- package/dist/install-Car5vKPk.js.map +1 -0
- package/dist/install-DlD3sLtm.cjs +1004 -0
- package/package.json +6 -1
package/dist/cli.cjs
CHANGED
|
@@ -1,43 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
-
key = keys[i];
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
-
get: ((k) => from[k]).bind(null, key),
|
|
14
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
-
value: mod,
|
|
21
|
-
enumerable: true
|
|
22
|
-
}) : target, mod));
|
|
23
|
-
//#endregion
|
|
2
|
+
const require_install = require("./install-DlD3sLtm.cjs");
|
|
24
3
|
let commander = require("commander");
|
|
25
4
|
let node_path = require("node:path");
|
|
26
|
-
node_path = __toESM(node_path, 1);
|
|
5
|
+
node_path = require_install.__toESM(node_path, 1);
|
|
27
6
|
let picocolors = require("picocolors");
|
|
28
|
-
picocolors = __toESM(picocolors, 1);
|
|
7
|
+
picocolors = require_install.__toESM(picocolors, 1);
|
|
29
8
|
let node_fs = require("node:fs");
|
|
30
|
-
node_fs = __toESM(node_fs, 1);
|
|
31
|
-
let package_manager_detector_detect = require("package-manager-detector/detect");
|
|
32
|
-
let ignore = require("ignore");
|
|
33
|
-
ignore = __toESM(ignore, 1);
|
|
9
|
+
node_fs = require_install.__toESM(node_fs, 1);
|
|
34
10
|
let node_url = require("node:url");
|
|
35
|
-
let agent_install_skill = require("agent-install/skill");
|
|
36
11
|
let prompts = require("prompts");
|
|
37
|
-
prompts = __toESM(prompts, 1);
|
|
12
|
+
prompts = require_install.__toESM(prompts, 1);
|
|
38
13
|
let ora = require("ora");
|
|
39
|
-
ora = __toESM(ora, 1);
|
|
40
|
-
let tinyexec = require("tinyexec");
|
|
14
|
+
ora = require_install.__toESM(ora, 1);
|
|
41
15
|
let node_child_process = require("node:child_process");
|
|
42
16
|
let node_crypto = require("node:crypto");
|
|
43
17
|
//#region src/utils/is-non-interactive.ts
|
|
@@ -53,353 +27,6 @@ const AGENT_ENVIRONMENT_VARIABLES = [
|
|
|
53
27
|
const isEnvironmentVariableSet = (variable) => Boolean(process.env[variable]);
|
|
54
28
|
const detectNonInteractive = (yesFlag) => yesFlag || AGENT_ENVIRONMENT_VARIABLES.some(isEnvironmentVariableSet) || !process.stdin.isTTY;
|
|
55
29
|
//#endregion
|
|
56
|
-
//#region src/utils/react-grab-code.ts
|
|
57
|
-
const REACT_GRAB_SPECIFIER_PATTERN = String.raw`react-grab(?:\/[^"']+)?`;
|
|
58
|
-
const stripComments = (content) => content.replace(/<!--[\s\S]*?-->/g, "").replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
|
|
59
|
-
const stripTypeOnlyReactGrabImports = (content) => {
|
|
60
|
-
return content.replace(new RegExp(String.raw`import\s+type\s+[^;]+from\s+["']${REACT_GRAB_SPECIFIER_PATTERN}["'];?`, "g"), "").replace(new RegExp(String.raw`import\s*\{\s*type\s+[^,}]+(?:\s*,\s*type\s+[^,}]+)*\s*,?\s*\}\s*from\s+["']${REACT_GRAB_SPECIFIER_PATTERN}["'];?`, "g"), "");
|
|
61
|
-
};
|
|
62
|
-
const hasReactGrabSetupCode = (content) => {
|
|
63
|
-
const setupCandidateContent = stripTypeOnlyReactGrabImports(stripComments(content));
|
|
64
|
-
return [
|
|
65
|
-
new RegExp(String.raw`import\s*\(\s*["']${REACT_GRAB_SPECIFIER_PATTERN}["']\s*\)`),
|
|
66
|
-
new RegExp(String.raw`import\s+(?!type\b)(?:[^"';]+from\s+)?["']${REACT_GRAB_SPECIFIER_PATTERN}["']`),
|
|
67
|
-
new RegExp(String.raw`require\s*\(\s*["']${REACT_GRAB_SPECIFIER_PATTERN}["']\s*\)`),
|
|
68
|
-
/<Script[\s\S]*?src\s*=\s*(?:["'][^"']*react-grab[^"']*["']|\{(?:["'][^"']*react-grab[^"']*["']|`[^`]*react-grab[^`]*`)\})/i,
|
|
69
|
-
/<script[\s\S]*?src\s*=\s*["'][^"']*react-grab[^"']*["']/i
|
|
70
|
-
].some((pattern) => pattern.test(setupCandidateContent));
|
|
71
|
-
};
|
|
72
|
-
//#endregion
|
|
73
|
-
//#region src/utils/react-grab-setup-files.ts
|
|
74
|
-
const COMPONENT_EXTENSIONS = [
|
|
75
|
-
"tsx",
|
|
76
|
-
"jsx",
|
|
77
|
-
"ts",
|
|
78
|
-
"js"
|
|
79
|
-
];
|
|
80
|
-
const INSTRUMENTATION_EXTENSIONS = [
|
|
81
|
-
"ts",
|
|
82
|
-
"tsx",
|
|
83
|
-
"js",
|
|
84
|
-
"jsx",
|
|
85
|
-
"mts",
|
|
86
|
-
"cts",
|
|
87
|
-
"mjs",
|
|
88
|
-
"cjs"
|
|
89
|
-
];
|
|
90
|
-
const ROUTE_EXTENSIONS = ["tsx", "jsx"];
|
|
91
|
-
const createFileCandidates = (projectRoot, directories, baseName, extensions) => {
|
|
92
|
-
const fileCandidates = [];
|
|
93
|
-
for (const directory of directories) for (const extension of extensions) fileCandidates.push((0, node_path.join)(projectRoot, directory, `${baseName}.${extension}`));
|
|
94
|
-
return fileCandidates;
|
|
95
|
-
};
|
|
96
|
-
const findExistingFile = (fileCandidates) => {
|
|
97
|
-
for (const filePath of fileCandidates) if ((0, node_fs.existsSync)(filePath)) return filePath;
|
|
98
|
-
return null;
|
|
99
|
-
};
|
|
100
|
-
const getLayoutFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["app", "src/app"], "layout", COMPONENT_EXTENSIONS);
|
|
101
|
-
const getDocumentFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["pages", "src/pages"], "_document", COMPONENT_EXTENSIONS);
|
|
102
|
-
const getInstrumentationFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["", "src"], "instrumentation-client", INSTRUMENTATION_EXTENSIONS);
|
|
103
|
-
const getIndexHtmlCandidates = (projectRoot) => [(0, node_path.join)(projectRoot, "index.html"), (0, node_path.join)(projectRoot, "public", "index.html")];
|
|
104
|
-
const getEntryFileCandidates = (projectRoot) => [...createFileCandidates(projectRoot, ["src"], "index", COMPONENT_EXTENSIONS), ...createFileCandidates(projectRoot, ["src"], "main", COMPONENT_EXTENSIONS)];
|
|
105
|
-
const getTanStackRootFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["src/routes", "app/routes"], "__root", ROUTE_EXTENSIONS);
|
|
106
|
-
const getReactGrabSetupFileCandidates = (projectRoot) => [
|
|
107
|
-
...getLayoutFileCandidates(projectRoot),
|
|
108
|
-
...getDocumentFileCandidates(projectRoot),
|
|
109
|
-
...getInstrumentationFileCandidates(projectRoot),
|
|
110
|
-
...getIndexHtmlCandidates(projectRoot),
|
|
111
|
-
...getEntryFileCandidates(projectRoot),
|
|
112
|
-
...getTanStackRootFileCandidates(projectRoot)
|
|
113
|
-
];
|
|
114
|
-
const findLayoutFile = (projectRoot) => findExistingFile(getLayoutFileCandidates(projectRoot));
|
|
115
|
-
const findDocumentFile = (projectRoot) => findExistingFile(getDocumentFileCandidates(projectRoot));
|
|
116
|
-
const findIndexHtml = (projectRoot) => findExistingFile(getIndexHtmlCandidates(projectRoot));
|
|
117
|
-
const findEntryFile = (projectRoot) => findExistingFile(getEntryFileCandidates(projectRoot));
|
|
118
|
-
const findTanStackRootFile = (projectRoot) => findExistingFile(getTanStackRootFileCandidates(projectRoot));
|
|
119
|
-
const isInstrumentationFile = (filePath) => /(?:^|[/\\])instrumentation-client\.[cm]?[jt]sx?$/.test(filePath);
|
|
120
|
-
//#endregion
|
|
121
|
-
//#region src/utils/detect.ts
|
|
122
|
-
const VALID_PACKAGE_MANAGERS = new Set([
|
|
123
|
-
"npm",
|
|
124
|
-
"yarn",
|
|
125
|
-
"pnpm",
|
|
126
|
-
"bun"
|
|
127
|
-
]);
|
|
128
|
-
const detectPackageManager = async (projectRoot) => {
|
|
129
|
-
const result = await (0, package_manager_detector_detect.detect)({ cwd: projectRoot });
|
|
130
|
-
if (result?.agent) {
|
|
131
|
-
const managerName = result.agent.split("@")[0];
|
|
132
|
-
if (VALID_PACKAGE_MANAGERS.has(managerName)) return managerName;
|
|
133
|
-
}
|
|
134
|
-
return "npm";
|
|
135
|
-
};
|
|
136
|
-
const CONFIG_EXTENSIONS = [
|
|
137
|
-
"ts",
|
|
138
|
-
"mts",
|
|
139
|
-
"cts",
|
|
140
|
-
"js",
|
|
141
|
-
"mjs",
|
|
142
|
-
"cjs"
|
|
143
|
-
];
|
|
144
|
-
const hasConfigFile = (projectRoot, configBaseName) => CONFIG_EXTENSIONS.some((extension) => (0, node_fs.existsSync)((0, node_path.join)(projectRoot, `${configBaseName}.${extension}`)));
|
|
145
|
-
const readMergedDependencies = (projectRoot) => {
|
|
146
|
-
const packageJsonPath = (0, node_path.join)(projectRoot, "package.json");
|
|
147
|
-
if (!(0, node_fs.existsSync)(packageJsonPath)) return null;
|
|
148
|
-
try {
|
|
149
|
-
const packageJson = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8"));
|
|
150
|
-
return {
|
|
151
|
-
...packageJson.dependencies,
|
|
152
|
-
...packageJson.devDependencies
|
|
153
|
-
};
|
|
154
|
-
} catch {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
const detectFrameworkFromDependencies = (dependencies) => {
|
|
159
|
-
if (!dependencies) return "unknown";
|
|
160
|
-
if (dependencies["next"]) return "next";
|
|
161
|
-
if (dependencies["@tanstack/react-start"]) return "tanstack";
|
|
162
|
-
if (dependencies["vite"]) return "vite";
|
|
163
|
-
if (dependencies["webpack"]) return "webpack";
|
|
164
|
-
return "unknown";
|
|
165
|
-
};
|
|
166
|
-
const detectFrameworkFromConfigFiles = (projectRoot) => {
|
|
167
|
-
if (hasConfigFile(projectRoot, "next.config")) return "next";
|
|
168
|
-
if (hasConfigFile(projectRoot, "app.config")) return "tanstack";
|
|
169
|
-
if (hasConfigFile(projectRoot, "vite.config")) return "vite";
|
|
170
|
-
if (hasConfigFile(projectRoot, "webpack.config")) return "webpack";
|
|
171
|
-
return "unknown";
|
|
172
|
-
};
|
|
173
|
-
const findEnclosingMonorepoRoot = (projectRoot) => {
|
|
174
|
-
let currentDirectory = (0, node_path.dirname)(projectRoot);
|
|
175
|
-
while (currentDirectory !== (0, node_path.dirname)(currentDirectory)) {
|
|
176
|
-
if (detectMonorepo(currentDirectory)) return currentDirectory;
|
|
177
|
-
currentDirectory = (0, node_path.dirname)(currentDirectory);
|
|
178
|
-
}
|
|
179
|
-
return null;
|
|
180
|
-
};
|
|
181
|
-
const detectFramework = (projectRoot) => {
|
|
182
|
-
const localFramework = detectFrameworkFromDependencies(readMergedDependencies(projectRoot));
|
|
183
|
-
if (localFramework !== "unknown") return localFramework;
|
|
184
|
-
return detectFrameworkFromConfigFiles(projectRoot);
|
|
185
|
-
};
|
|
186
|
-
const detectFrameworkFromMonorepoRoot = (projectRoot) => {
|
|
187
|
-
const monorepoRoot = findEnclosingMonorepoRoot(projectRoot);
|
|
188
|
-
if (!monorepoRoot) return "unknown";
|
|
189
|
-
return detectFrameworkFromDependencies(readMergedDependencies(monorepoRoot));
|
|
190
|
-
};
|
|
191
|
-
const detectNextRouterType = (projectRoot) => {
|
|
192
|
-
const hasAppDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "app"));
|
|
193
|
-
const hasSrcAppDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "src", "app"));
|
|
194
|
-
const hasPagesDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "pages"));
|
|
195
|
-
const hasSrcPagesDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "src", "pages"));
|
|
196
|
-
if (hasAppDir || hasSrcAppDir) return "app";
|
|
197
|
-
if (hasPagesDir || hasSrcPagesDir) return "pages";
|
|
198
|
-
return "unknown";
|
|
199
|
-
};
|
|
200
|
-
const detectMonorepo = (projectRoot) => {
|
|
201
|
-
if ((0, node_fs.existsSync)((0, node_path.join)(projectRoot, "pnpm-workspace.yaml"))) return true;
|
|
202
|
-
if ((0, node_fs.existsSync)((0, node_path.join)(projectRoot, "lerna.json"))) return true;
|
|
203
|
-
const packageJsonPath = (0, node_path.join)(projectRoot, "package.json");
|
|
204
|
-
if ((0, node_fs.existsSync)(packageJsonPath)) try {
|
|
205
|
-
if (JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8")).workspaces) return true;
|
|
206
|
-
} catch {
|
|
207
|
-
return false;
|
|
208
|
-
}
|
|
209
|
-
return false;
|
|
210
|
-
};
|
|
211
|
-
const getWorkspacePatterns = (projectRoot) => {
|
|
212
|
-
const patterns = [];
|
|
213
|
-
const pnpmWorkspacePath = (0, node_path.join)(projectRoot, "pnpm-workspace.yaml");
|
|
214
|
-
if ((0, node_fs.existsSync)(pnpmWorkspacePath)) {
|
|
215
|
-
const lines = (0, node_fs.readFileSync)(pnpmWorkspacePath, "utf-8").split("\n");
|
|
216
|
-
let inPackages = false;
|
|
217
|
-
for (const line of lines) {
|
|
218
|
-
if (line.match(/^packages:\s*$/)) {
|
|
219
|
-
inPackages = true;
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
if (inPackages) {
|
|
223
|
-
if (line.match(/^[a-zA-Z]/) || line.trim() === "") {
|
|
224
|
-
if (line.match(/^[a-zA-Z]/)) inPackages = false;
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
const match = line.match(/^\s*-\s*['"]?([^'"#\n]+?)['"]?\s*$/);
|
|
228
|
-
if (match) patterns.push(match[1].trim());
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
const lernaJsonPath = (0, node_path.join)(projectRoot, "lerna.json");
|
|
233
|
-
if ((0, node_fs.existsSync)(lernaJsonPath)) try {
|
|
234
|
-
const lernaJson = JSON.parse((0, node_fs.readFileSync)(lernaJsonPath, "utf-8"));
|
|
235
|
-
if (Array.isArray(lernaJson.packages)) patterns.push(...lernaJson.packages);
|
|
236
|
-
} catch {}
|
|
237
|
-
const packageJsonPath = (0, node_path.join)(projectRoot, "package.json");
|
|
238
|
-
if ((0, node_fs.existsSync)(packageJsonPath)) try {
|
|
239
|
-
const packageJson = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8"));
|
|
240
|
-
if (Array.isArray(packageJson.workspaces)) patterns.push(...packageJson.workspaces);
|
|
241
|
-
else if (packageJson.workspaces?.packages) patterns.push(...packageJson.workspaces.packages);
|
|
242
|
-
} catch {}
|
|
243
|
-
return [...new Set(patterns)];
|
|
244
|
-
};
|
|
245
|
-
const expandWorkspacePattern = (projectRoot, pattern) => {
|
|
246
|
-
const isGlob = pattern.endsWith("/*");
|
|
247
|
-
const basePath = (0, node_path.join)(projectRoot, pattern.replace(/\/\*$/, ""));
|
|
248
|
-
if (!(0, node_fs.existsSync)(basePath)) return [];
|
|
249
|
-
if (!isGlob) return (0, node_fs.existsSync)((0, node_path.join)(basePath, "package.json")) ? [basePath] : [];
|
|
250
|
-
const results = [];
|
|
251
|
-
try {
|
|
252
|
-
const entries = (0, node_fs.readdirSync)(basePath, { withFileTypes: true });
|
|
253
|
-
for (const entry of entries) {
|
|
254
|
-
if (!entry.isDirectory()) continue;
|
|
255
|
-
if ((0, node_fs.existsSync)((0, node_path.join)(basePath, entry.name, "package.json"))) results.push((0, node_path.join)(basePath, entry.name));
|
|
256
|
-
}
|
|
257
|
-
} catch {
|
|
258
|
-
return results;
|
|
259
|
-
}
|
|
260
|
-
return results;
|
|
261
|
-
};
|
|
262
|
-
const hasReactDependency = (projectPath) => {
|
|
263
|
-
const dependencies = readMergedDependencies(projectPath);
|
|
264
|
-
if (!dependencies) return false;
|
|
265
|
-
return Boolean(dependencies["react"] || dependencies["react-dom"]);
|
|
266
|
-
};
|
|
267
|
-
const buildReactProject = (projectPath) => {
|
|
268
|
-
const framework = detectFramework(projectPath);
|
|
269
|
-
if (!hasReactDependency(projectPath) && framework === "unknown") return null;
|
|
270
|
-
let name = (0, node_path.basename)(projectPath);
|
|
271
|
-
const packageJsonPath = (0, node_path.join)(projectPath, "package.json");
|
|
272
|
-
try {
|
|
273
|
-
name = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8")).name || name;
|
|
274
|
-
} catch {}
|
|
275
|
-
return {
|
|
276
|
-
name,
|
|
277
|
-
path: projectPath,
|
|
278
|
-
framework
|
|
279
|
-
};
|
|
280
|
-
};
|
|
281
|
-
const findWorkspaceProjects = (projectRoot) => {
|
|
282
|
-
const patterns = getWorkspacePatterns(projectRoot);
|
|
283
|
-
const projects = [];
|
|
284
|
-
for (const pattern of patterns) for (const projectPath of expandWorkspacePattern(projectRoot, pattern)) {
|
|
285
|
-
const project = buildReactProject(projectPath);
|
|
286
|
-
if (project) projects.push(project);
|
|
287
|
-
}
|
|
288
|
-
return projects;
|
|
289
|
-
};
|
|
290
|
-
const ALWAYS_IGNORED_DIRECTORIES = [
|
|
291
|
-
"node_modules",
|
|
292
|
-
".git",
|
|
293
|
-
".next",
|
|
294
|
-
".cache",
|
|
295
|
-
".turbo",
|
|
296
|
-
"dist",
|
|
297
|
-
"build",
|
|
298
|
-
"coverage",
|
|
299
|
-
"test-results"
|
|
300
|
-
];
|
|
301
|
-
const loadGitignore = (projectRoot) => {
|
|
302
|
-
const ignorer = (0, ignore.default)().add(ALWAYS_IGNORED_DIRECTORIES);
|
|
303
|
-
const gitignorePath = (0, node_path.join)(projectRoot, ".gitignore");
|
|
304
|
-
if ((0, node_fs.existsSync)(gitignorePath)) try {
|
|
305
|
-
ignorer.add((0, node_fs.readFileSync)(gitignorePath, "utf-8"));
|
|
306
|
-
} catch {}
|
|
307
|
-
return ignorer;
|
|
308
|
-
};
|
|
309
|
-
const scanDirectoryForProjects = (rootDirectory, ignorer, maxDepth, currentDepth = 0) => {
|
|
310
|
-
if (currentDepth >= maxDepth) return [];
|
|
311
|
-
if (!(0, node_fs.existsSync)(rootDirectory)) return [];
|
|
312
|
-
const projects = [];
|
|
313
|
-
try {
|
|
314
|
-
const entries = (0, node_fs.readdirSync)(rootDirectory, { withFileTypes: true });
|
|
315
|
-
for (const entry of entries) {
|
|
316
|
-
if (!entry.isDirectory()) continue;
|
|
317
|
-
if (ignorer.ignores(entry.name)) continue;
|
|
318
|
-
const entryPath = (0, node_path.join)(rootDirectory, entry.name);
|
|
319
|
-
if ((0, node_fs.existsSync)((0, node_path.join)(entryPath, "package.json"))) {
|
|
320
|
-
const project = buildReactProject(entryPath);
|
|
321
|
-
if (project) {
|
|
322
|
-
projects.push(project);
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
projects.push(...scanDirectoryForProjects(entryPath, ignorer, maxDepth, currentDepth + 1));
|
|
327
|
-
}
|
|
328
|
-
} catch {
|
|
329
|
-
return projects;
|
|
330
|
-
}
|
|
331
|
-
return projects;
|
|
332
|
-
};
|
|
333
|
-
const MAX_SCAN_DEPTH = 2;
|
|
334
|
-
const normalizePathForComparison = (filePath) => filePath.replace(/\\/g, "/");
|
|
335
|
-
const findReactProjects = (projectRoot) => {
|
|
336
|
-
const monorepoRoot = detectMonorepo(projectRoot) ? projectRoot : findEnclosingMonorepoRoot(projectRoot);
|
|
337
|
-
if (monorepoRoot) {
|
|
338
|
-
const workspaceProjects = findWorkspaceProjects(monorepoRoot);
|
|
339
|
-
const localProject = projectRoot === monorepoRoot ? null : buildReactProject(projectRoot);
|
|
340
|
-
const projects = localProject ? [localProject, ...workspaceProjects.filter((project) => normalizePathForComparison(project.path) !== normalizePathForComparison(localProject.path))] : workspaceProjects;
|
|
341
|
-
if (projects.length > 0) return projects;
|
|
342
|
-
}
|
|
343
|
-
const scannedProjects = scanDirectoryForProjects(projectRoot, loadGitignore(projectRoot), MAX_SCAN_DEPTH);
|
|
344
|
-
if (scannedProjects.length > 0) return scannedProjects;
|
|
345
|
-
let currentDirectory = (0, node_path.dirname)(projectRoot);
|
|
346
|
-
while (currentDirectory !== (0, node_path.dirname)(currentDirectory)) {
|
|
347
|
-
const parentProject = buildReactProject(currentDirectory);
|
|
348
|
-
if (parentProject) return [parentProject];
|
|
349
|
-
currentDirectory = (0, node_path.dirname)(currentDirectory);
|
|
350
|
-
}
|
|
351
|
-
return [];
|
|
352
|
-
};
|
|
353
|
-
const hasReactGrabSetupInFile = (filePath) => {
|
|
354
|
-
if (!(0, node_fs.existsSync)(filePath)) return false;
|
|
355
|
-
try {
|
|
356
|
-
return hasReactGrabSetupCode((0, node_fs.readFileSync)(filePath, "utf-8"));
|
|
357
|
-
} catch {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
};
|
|
361
|
-
const detectReactGrabDependency = (projectRoot) => {
|
|
362
|
-
const dependencies = readMergedDependencies(projectRoot);
|
|
363
|
-
return Boolean(dependencies?.["react-grab"]);
|
|
364
|
-
};
|
|
365
|
-
const detectReactGrabConfigured = (projectRoot) => {
|
|
366
|
-
return getReactGrabSetupFileCandidates(projectRoot).some(hasReactGrabSetupInFile);
|
|
367
|
-
};
|
|
368
|
-
const detectUnsupportedFramework = (projectRoot) => {
|
|
369
|
-
const dependencies = readMergedDependencies(projectRoot);
|
|
370
|
-
if (!dependencies) return null;
|
|
371
|
-
if (dependencies["@remix-run/react"] || dependencies["remix"]) return "remix";
|
|
372
|
-
if (dependencies["astro"]) return "astro";
|
|
373
|
-
if (dependencies["@sveltejs/kit"]) return "sveltekit";
|
|
374
|
-
if (dependencies["gatsby"]) return "gatsby";
|
|
375
|
-
return null;
|
|
376
|
-
};
|
|
377
|
-
const detectReactGrabVersion = (projectRoot) => {
|
|
378
|
-
const installedPackageJsonPath = (0, node_path.join)(projectRoot, "node_modules", "react-grab", "package.json");
|
|
379
|
-
if ((0, node_fs.existsSync)(installedPackageJsonPath)) try {
|
|
380
|
-
return JSON.parse((0, node_fs.readFileSync)(installedPackageJsonPath, "utf-8")).version ?? null;
|
|
381
|
-
} catch {}
|
|
382
|
-
return null;
|
|
383
|
-
};
|
|
384
|
-
const detectProject = async (projectRoot = process.cwd()) => {
|
|
385
|
-
const localFramework = detectFramework(projectRoot);
|
|
386
|
-
const framework = localFramework === "unknown" ? detectFrameworkFromMonorepoRoot(projectRoot) : localFramework;
|
|
387
|
-
const packageManager = await detectPackageManager(projectRoot);
|
|
388
|
-
const isMonorepo = detectMonorepo(projectRoot) || findEnclosingMonorepoRoot(projectRoot) !== null;
|
|
389
|
-
const isReactGrabConfigured = detectReactGrabConfigured(projectRoot);
|
|
390
|
-
return {
|
|
391
|
-
packageManager,
|
|
392
|
-
framework,
|
|
393
|
-
nextRouterType: framework === "next" ? detectNextRouterType(projectRoot) : "unknown",
|
|
394
|
-
isMonorepo,
|
|
395
|
-
projectRoot,
|
|
396
|
-
hasReactGrab: detectReactGrabDependency(projectRoot) || isReactGrabConfigured,
|
|
397
|
-
isReactGrabConfigured,
|
|
398
|
-
reactGrabVersion: detectReactGrabVersion(projectRoot),
|
|
399
|
-
unsupportedFramework: detectUnsupportedFramework(projectRoot)
|
|
400
|
-
};
|
|
401
|
-
};
|
|
402
|
-
//#endregion
|
|
403
30
|
//#region src/utils/highlighter.ts
|
|
404
31
|
const highlighter = {
|
|
405
32
|
error: picocolors.default.red,
|
|
@@ -439,39 +66,6 @@ const handleError = (error) => {
|
|
|
439
66
|
process.exit(1);
|
|
440
67
|
};
|
|
441
68
|
//#endregion
|
|
442
|
-
//#region src/utils/detect-agents.ts
|
|
443
|
-
const PATH_BINARIES = {
|
|
444
|
-
"claude-code": ["claude"],
|
|
445
|
-
codex: ["codex"],
|
|
446
|
-
cursor: ["cursor", "cursor-agent"],
|
|
447
|
-
droid: ["droid"],
|
|
448
|
-
"gemini-cli": ["gemini"],
|
|
449
|
-
"github-copilot": ["copilot"],
|
|
450
|
-
opencode: ["opencode"],
|
|
451
|
-
pi: ["pi", "omegon"]
|
|
452
|
-
};
|
|
453
|
-
const isCommandAvailable = (command) => {
|
|
454
|
-
const pathDirectories = (process.env.PATH ?? "").split(node_path.delimiter).filter(Boolean);
|
|
455
|
-
for (const directory of pathDirectories) {
|
|
456
|
-
const binaryPath = (0, node_path.join)(directory, command);
|
|
457
|
-
try {
|
|
458
|
-
if ((0, node_fs.statSync)(binaryPath).isFile()) {
|
|
459
|
-
(0, node_fs.accessSync)(binaryPath, node_fs.constants.X_OK);
|
|
460
|
-
return true;
|
|
461
|
-
}
|
|
462
|
-
} catch {}
|
|
463
|
-
}
|
|
464
|
-
return false;
|
|
465
|
-
};
|
|
466
|
-
const detectAvailableAgents = async () => {
|
|
467
|
-
const installedAgents = new Set(await (0, agent_install_skill.detectInstalledSkillAgents)());
|
|
468
|
-
return (0, agent_install_skill.getSkillAgentTypes)().filter((agent) => {
|
|
469
|
-
if (agent === "universal") return false;
|
|
470
|
-
if (installedAgents.has(agent)) return true;
|
|
471
|
-
return PATH_BINARIES[agent]?.some(isCommandAvailable) ?? false;
|
|
472
|
-
});
|
|
473
|
-
};
|
|
474
|
-
//#endregion
|
|
475
69
|
//#region src/utils/unref-stdin.ts
|
|
476
70
|
const unrefStdin = () => {
|
|
477
71
|
if (process.stdin.isTTY) return;
|
|
@@ -492,16 +86,9 @@ const prompts$1 = (questions) => {
|
|
|
492
86
|
//#region src/utils/spinner.ts
|
|
493
87
|
const spinner = (text) => (0, ora.default)({ text });
|
|
494
88
|
//#endregion
|
|
495
|
-
//#region src/utils/
|
|
496
|
-
const SKILL_NAME = "react-grab";
|
|
497
|
-
const SKILL_SOURCE = (0, node_url.fileURLToPath)(new URL("../skills/react-grab", require("url").pathToFileURL(__filename).href));
|
|
498
|
-
const agentLabel = (agent) => (0, agent_install_skill.getSkillAgentConfig)(agent).displayName;
|
|
499
|
-
const installedSkillDir = (agent, global, cwd) => (0, node_path.join)((0, agent_install_skill.isUniversalSkillAgent)(agent) ? (0, agent_install_skill.getCanonicalSkillsDir)(global, cwd) : (0, agent_install_skill.getSkillAgentDir)(agent, {
|
|
500
|
-
global,
|
|
501
|
-
cwd
|
|
502
|
-
}), SKILL_NAME);
|
|
89
|
+
//#region src/utils/prompt-skill-install.ts
|
|
503
90
|
const promptSkillInstall = async ({ yes = false, global = false, cwd = process.cwd() } = {}) => {
|
|
504
|
-
const detectedAgents = await detectAvailableAgents();
|
|
91
|
+
const detectedAgents = await require_install.detectAvailableAgents();
|
|
505
92
|
if (detectedAgents.length === 0) {
|
|
506
93
|
logger.warn("No supported agents detected.");
|
|
507
94
|
return false;
|
|
@@ -520,7 +107,7 @@ const promptSkillInstall = async ({ yes = false, global = false, cwd = process.c
|
|
|
520
107
|
name: "agents",
|
|
521
108
|
message: `Install the React Grab skill (${global ? "global" : "this project"}) for:`,
|
|
522
109
|
choices: detectedAgents.map((agent) => ({
|
|
523
|
-
title: agentLabel(agent),
|
|
110
|
+
title: require_install.agentLabel(agent),
|
|
524
111
|
value: agent,
|
|
525
112
|
selected: true
|
|
526
113
|
})),
|
|
@@ -531,48 +118,29 @@ const promptSkillInstall = async ({ yes = false, global = false, cwd = process.c
|
|
|
531
118
|
if (selectedAgents.length === 0) return false;
|
|
532
119
|
}
|
|
533
120
|
const installSpinner = spinner("Installing React Grab skill.").start();
|
|
534
|
-
const { installed, failed } = await
|
|
535
|
-
source: SKILL_SOURCE,
|
|
121
|
+
const { installed, failed } = await require_install.installSkill({
|
|
536
122
|
agents: selectedAgents,
|
|
537
123
|
global,
|
|
538
|
-
cwd
|
|
539
|
-
mode: "copy"
|
|
124
|
+
cwd
|
|
540
125
|
});
|
|
541
126
|
if (installed.length === 0) {
|
|
542
127
|
installSpinner.fail("Failed to install React Grab skill.");
|
|
543
128
|
return false;
|
|
544
129
|
}
|
|
545
|
-
installSpinner.succeed(`Installed React Grab skill for ${installed.map((record) => agentLabel(record.agent)).join(", ")}.`);
|
|
546
|
-
for (const record of failed) logger.log(` ${highlighter.error("✗")} ${agentLabel(record.agent)} ${record.error}`);
|
|
130
|
+
installSpinner.succeed(`Installed React Grab skill for ${installed.map((record) => require_install.agentLabel(record.agent)).join(", ")}.`);
|
|
131
|
+
for (const record of failed) logger.log(` ${highlighter.error("✗")} ${require_install.agentLabel(record.agent)} ${record.error}`);
|
|
547
132
|
return true;
|
|
548
133
|
};
|
|
549
|
-
const removeSkill = async ({ cwd = process.cwd(), global = false } = {}) => {
|
|
550
|
-
const agents = await detectAvailableAgents();
|
|
551
|
-
const removedAgents = [];
|
|
552
|
-
const dirsToRemove = /* @__PURE__ */ new Set();
|
|
553
|
-
for (const agent of agents) {
|
|
554
|
-
const skillDir = installedSkillDir(agent, global, cwd);
|
|
555
|
-
if (!(0, node_fs.existsSync)(skillDir)) continue;
|
|
556
|
-
removedAgents.push(agent);
|
|
557
|
-
dirsToRemove.add(skillDir);
|
|
558
|
-
}
|
|
559
|
-
for (const skillDir of dirsToRemove) (0, node_fs.rmSync)(skillDir, {
|
|
560
|
-
recursive: true,
|
|
561
|
-
force: true
|
|
562
|
-
});
|
|
563
|
-
for (const agent of removedAgents) logger.log(` ${highlighter.success("✓")} ${agentLabel(agent)}`);
|
|
564
|
-
return removedAgents.length;
|
|
565
|
-
};
|
|
566
134
|
//#endregion
|
|
567
135
|
//#region src/commands/add.ts
|
|
568
|
-
const VERSION$5 = "0.1.
|
|
136
|
+
const VERSION$5 = "0.1.47-dev.bc3a591";
|
|
569
137
|
const add = new commander.Command().name("add").alias("install").description("install the React Grab skill for your agent").option("-y, --yes", "skip confirmation prompts", false).option("-c, --cwd <cwd>", "working directory (defaults to current directory)", process.cwd()).option("-g, --global", "install the skill globally instead of in the project", false).action(async (opts) => {
|
|
570
138
|
console.log(`${picocolors.default.magenta("✿")} ${picocolors.default.bold("React Grab")} ${picocolors.default.gray(VERSION$5)}`);
|
|
571
139
|
console.log();
|
|
572
140
|
try {
|
|
573
141
|
const isNonInteractive = detectNonInteractive(opts.yes);
|
|
574
142
|
const preflightSpinner = spinner("Preflight checks.").start();
|
|
575
|
-
if (!(await detectProject(opts.cwd)).hasReactGrab) {
|
|
143
|
+
if (!(await require_install.detectProject(opts.cwd)).hasReactGrab) {
|
|
576
144
|
preflightSpinner.fail("React Grab is not installed.");
|
|
577
145
|
logger.break();
|
|
578
146
|
logger.error(`Run ${highlighter.info("react-grab init")} first to install React Grab.`);
|
|
@@ -698,412 +266,6 @@ const printDiff = (filePath, originalContent, newContent) => {
|
|
|
698
266
|
console.log("─".repeat(60));
|
|
699
267
|
};
|
|
700
268
|
//#endregion
|
|
701
|
-
//#region src/utils/templates.ts
|
|
702
|
-
const NEXT_APP_ROUTER_SCRIPT = `{process.env.NODE_ENV === "development" && (
|
|
703
|
-
<Script
|
|
704
|
-
src="//unpkg.com/react-grab/dist/index.global.js"
|
|
705
|
-
crossOrigin="anonymous"
|
|
706
|
-
strategy="beforeInteractive"
|
|
707
|
-
/>
|
|
708
|
-
)}`;
|
|
709
|
-
const VITE_IMPORT = `if (import.meta.env.DEV) {
|
|
710
|
-
import("react-grab");
|
|
711
|
-
}`;
|
|
712
|
-
const WEBPACK_IMPORT = `if (process.env.NODE_ENV === "development") {
|
|
713
|
-
import("react-grab");
|
|
714
|
-
}`;
|
|
715
|
-
const TANSTACK_EFFECT = `useEffect(() => {
|
|
716
|
-
if (import.meta.env.DEV) {
|
|
717
|
-
void import("react-grab");
|
|
718
|
-
}
|
|
719
|
-
}, []);`;
|
|
720
|
-
const SCRIPT_IMPORT = "import Script from \"next/script\";";
|
|
721
|
-
//#endregion
|
|
722
|
-
//#region src/utils/transform.ts
|
|
723
|
-
const hasReactGrabInInstrumentation = (projectRoot) => {
|
|
724
|
-
return findFileWithReactGrabSetup(getInstrumentationFileCandidates(projectRoot)) !== null;
|
|
725
|
-
};
|
|
726
|
-
const findFileWithReactGrabSetup = (fileCandidates) => {
|
|
727
|
-
for (const filePath of fileCandidates) {
|
|
728
|
-
if (!(0, node_fs.existsSync)(filePath)) continue;
|
|
729
|
-
if (hasReactGrabSetupCode((0, node_fs.readFileSync)(filePath, "utf-8"))) return filePath;
|
|
730
|
-
}
|
|
731
|
-
return null;
|
|
732
|
-
};
|
|
733
|
-
const alreadyConfiguredResult = (filePath) => ({
|
|
734
|
-
success: true,
|
|
735
|
-
filePath,
|
|
736
|
-
message: "React Grab is already configured",
|
|
737
|
-
noChanges: true
|
|
738
|
-
});
|
|
739
|
-
const transformNextAppRouter = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
740
|
-
const layoutPath = findLayoutFile(projectRoot);
|
|
741
|
-
if (!layoutPath) return {
|
|
742
|
-
success: false,
|
|
743
|
-
filePath: "",
|
|
744
|
-
message: "Could not find app/layout.tsx, app/layout.jsx, app/layout.ts, or app/layout.js"
|
|
745
|
-
};
|
|
746
|
-
const originalContent = (0, node_fs.readFileSync)(layoutPath, "utf-8");
|
|
747
|
-
let newContent = originalContent;
|
|
748
|
-
const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
|
|
749
|
-
const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
|
|
750
|
-
if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(layoutPath);
|
|
751
|
-
if (hasReactGrabInFile || hasReactGrabInInstrumentationFile) return {
|
|
752
|
-
success: true,
|
|
753
|
-
filePath: layoutPath,
|
|
754
|
-
message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
|
|
755
|
-
noChanges: true
|
|
756
|
-
};
|
|
757
|
-
if (!newContent.includes("import Script from \"next/script\"")) {
|
|
758
|
-
const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
|
759
|
-
if (importMatch) newContent = newContent.replace(importMatch[0], `${importMatch[0]}\n${SCRIPT_IMPORT}`);
|
|
760
|
-
else newContent = `${SCRIPT_IMPORT}\n\n${newContent}`;
|
|
761
|
-
}
|
|
762
|
-
const headMatch = newContent.match(/<head[^>]*>/);
|
|
763
|
-
if (headMatch) newContent = newContent.replace(headMatch[0], `${headMatch[0]}\n ${NEXT_APP_ROUTER_SCRIPT}`);
|
|
764
|
-
else {
|
|
765
|
-
const htmlMatch = newContent.match(/<html[^>]*>/);
|
|
766
|
-
if (htmlMatch) newContent = newContent.replace(htmlMatch[0], `${htmlMatch[0]}\n <head>\n ${NEXT_APP_ROUTER_SCRIPT}\n </head>`);
|
|
767
|
-
}
|
|
768
|
-
return {
|
|
769
|
-
success: true,
|
|
770
|
-
filePath: layoutPath,
|
|
771
|
-
message: "Add React Grab",
|
|
772
|
-
originalContent,
|
|
773
|
-
newContent
|
|
774
|
-
};
|
|
775
|
-
};
|
|
776
|
-
const transformNextPagesRouter = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
777
|
-
const documentPath = findDocumentFile(projectRoot);
|
|
778
|
-
if (!documentPath) return {
|
|
779
|
-
success: false,
|
|
780
|
-
filePath: "",
|
|
781
|
-
message: "Could not find pages/_document.tsx, pages/_document.jsx, pages/_document.ts, or pages/_document.js.\n\nTo set up React Grab with Pages Router, create pages/_document.tsx with:\n\n import { Html, Head, Main, NextScript } from \"next/document\";\n import Script from \"next/script\";\n\n export default function Document() {\n return (\n <Html>\n <Head>\n {process.env.NODE_ENV === \"development\" && (\n <Script src=\"//unpkg.com/react-grab/dist/index.global.js\" strategy=\"beforeInteractive\" />\n )}\n </Head>\n <body>\n <Main />\n <NextScript />\n </body>\n </Html>\n );\n }"
|
|
782
|
-
};
|
|
783
|
-
const originalContent = (0, node_fs.readFileSync)(documentPath, "utf-8");
|
|
784
|
-
let newContent = originalContent;
|
|
785
|
-
const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
|
|
786
|
-
const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
|
|
787
|
-
if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(documentPath);
|
|
788
|
-
if (hasReactGrabInFile || hasReactGrabInInstrumentationFile) return {
|
|
789
|
-
success: true,
|
|
790
|
-
filePath: documentPath,
|
|
791
|
-
message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
|
|
792
|
-
noChanges: true
|
|
793
|
-
};
|
|
794
|
-
if (!newContent.includes("import Script from \"next/script\"")) {
|
|
795
|
-
const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
|
796
|
-
if (importMatch) newContent = newContent.replace(importMatch[0], `${importMatch[0]}\n${SCRIPT_IMPORT}`);
|
|
797
|
-
}
|
|
798
|
-
const headMatch = newContent.match(/<Head[^>]*>/);
|
|
799
|
-
if (headMatch) newContent = newContent.replace(headMatch[0], `${headMatch[0]}\n ${NEXT_APP_ROUTER_SCRIPT}`);
|
|
800
|
-
return {
|
|
801
|
-
success: true,
|
|
802
|
-
filePath: documentPath,
|
|
803
|
-
message: "Add React Grab",
|
|
804
|
-
originalContent,
|
|
805
|
-
newContent
|
|
806
|
-
};
|
|
807
|
-
};
|
|
808
|
-
const checkExistingInstallation = (filePath, reactGrabAlreadyConfigured) => {
|
|
809
|
-
if (!hasReactGrabSetupCode((0, node_fs.readFileSync)(filePath, "utf-8"))) return null;
|
|
810
|
-
return {
|
|
811
|
-
success: true,
|
|
812
|
-
filePath,
|
|
813
|
-
message: reactGrabAlreadyConfigured ? "React Grab is already configured" : "React Grab is already installed in this file",
|
|
814
|
-
noChanges: true
|
|
815
|
-
};
|
|
816
|
-
};
|
|
817
|
-
const transformVite = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
818
|
-
const entryPath = findEntryFile(projectRoot);
|
|
819
|
-
const indexPath = findIndexHtml(projectRoot);
|
|
820
|
-
if (indexPath) {
|
|
821
|
-
const existingResult = checkExistingInstallation(indexPath, reactGrabAlreadyConfigured);
|
|
822
|
-
if (existingResult) return existingResult;
|
|
823
|
-
}
|
|
824
|
-
if (!entryPath) return {
|
|
825
|
-
success: false,
|
|
826
|
-
filePath: "",
|
|
827
|
-
message: "Could not find entry file (src/index.tsx, src/main.tsx, etc.)"
|
|
828
|
-
};
|
|
829
|
-
const existingResult = checkExistingInstallation(entryPath, reactGrabAlreadyConfigured);
|
|
830
|
-
if (existingResult) return existingResult;
|
|
831
|
-
const originalContent = (0, node_fs.readFileSync)(entryPath, "utf-8");
|
|
832
|
-
return {
|
|
833
|
-
success: true,
|
|
834
|
-
filePath: entryPath,
|
|
835
|
-
message: "Add React Grab",
|
|
836
|
-
originalContent,
|
|
837
|
-
newContent: `${VITE_IMPORT}\n\n${originalContent}`
|
|
838
|
-
};
|
|
839
|
-
};
|
|
840
|
-
const transformWebpack = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
841
|
-
const entryPath = findEntryFile(projectRoot);
|
|
842
|
-
if (!entryPath) return {
|
|
843
|
-
success: false,
|
|
844
|
-
filePath: "",
|
|
845
|
-
message: "Could not find entry file (src/index.tsx, src/main.tsx, etc.)"
|
|
846
|
-
};
|
|
847
|
-
const existingResult = checkExistingInstallation(entryPath, reactGrabAlreadyConfigured);
|
|
848
|
-
if (existingResult) return existingResult;
|
|
849
|
-
const originalContent = (0, node_fs.readFileSync)(entryPath, "utf-8");
|
|
850
|
-
return {
|
|
851
|
-
success: true,
|
|
852
|
-
filePath: entryPath,
|
|
853
|
-
message: "Add React Grab",
|
|
854
|
-
originalContent,
|
|
855
|
-
newContent: `${WEBPACK_IMPORT}\n\n${originalContent}`
|
|
856
|
-
};
|
|
857
|
-
};
|
|
858
|
-
const transformTanStack = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
859
|
-
const rootPath = findTanStackRootFile(projectRoot);
|
|
860
|
-
if (!rootPath) return {
|
|
861
|
-
success: false,
|
|
862
|
-
filePath: "",
|
|
863
|
-
message: "Could not find src/routes/__root.tsx or app/routes/__root.tsx.\n\nTo set up React Grab with TanStack Start, add this to your root route component:\n\n import { useEffect } from \"react\";\n\n useEffect(() => {\n if (import.meta.env.DEV) {\n void import(\"react-grab\");\n }\n }, []);"
|
|
864
|
-
};
|
|
865
|
-
const originalContent = (0, node_fs.readFileSync)(rootPath, "utf-8");
|
|
866
|
-
let newContent = originalContent;
|
|
867
|
-
const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
|
|
868
|
-
if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(rootPath);
|
|
869
|
-
if (hasReactGrabInFile) return {
|
|
870
|
-
success: true,
|
|
871
|
-
filePath: rootPath,
|
|
872
|
-
message: "React Grab is already installed in this file",
|
|
873
|
-
noChanges: true
|
|
874
|
-
};
|
|
875
|
-
if (!/import\s+\{[^}]*useEffect[^}]*\}\s+from\s+["']react["']/.test(newContent)) {
|
|
876
|
-
const reactImportMatch = newContent.match(/import\s+\{([^}]*)\}\s+from\s+["']react["'];?/);
|
|
877
|
-
if (reactImportMatch) {
|
|
878
|
-
const existingImports = reactImportMatch[1];
|
|
879
|
-
newContent = newContent.replace(reactImportMatch[0], `import { ${existingImports.trim()}, useEffect } from "react";`);
|
|
880
|
-
} else {
|
|
881
|
-
const firstImportMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
|
882
|
-
if (firstImportMatch) newContent = newContent.replace(firstImportMatch[0], `import { useEffect } from "react";\n${firstImportMatch[0]}`);
|
|
883
|
-
else newContent = `import { useEffect } from "react";\n\n${newContent}`;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
const componentMatch = newContent.match(/function\s+(\w+)\s*\([^)]*\)\s*\{/);
|
|
887
|
-
if (componentMatch) {
|
|
888
|
-
const insertPosition = componentMatch.index + componentMatch[0].length;
|
|
889
|
-
newContent = newContent.slice(0, insertPosition) + `\n ${TANSTACK_EFFECT}\n` + newContent.slice(insertPosition);
|
|
890
|
-
} else return {
|
|
891
|
-
success: false,
|
|
892
|
-
filePath: rootPath,
|
|
893
|
-
message: "Could not find a component function in the root file"
|
|
894
|
-
};
|
|
895
|
-
return {
|
|
896
|
-
success: true,
|
|
897
|
-
filePath: rootPath,
|
|
898
|
-
message: "Add React Grab",
|
|
899
|
-
originalContent,
|
|
900
|
-
newContent
|
|
901
|
-
};
|
|
902
|
-
};
|
|
903
|
-
const hasFrameworkEntryPoint = (projectRoot, framework, nextRouterType) => {
|
|
904
|
-
switch (framework) {
|
|
905
|
-
case "next": return nextRouterType === "app" ? findLayoutFile(projectRoot) !== null : findDocumentFile(projectRoot) !== null;
|
|
906
|
-
case "vite":
|
|
907
|
-
case "webpack": return findEntryFile(projectRoot) !== null;
|
|
908
|
-
case "tanstack": return findTanStackRootFile(projectRoot) !== null;
|
|
909
|
-
default: return false;
|
|
910
|
-
}
|
|
911
|
-
};
|
|
912
|
-
const previewTransform = (projectRoot, framework, nextRouterType, reactGrabAlreadyConfigured = false) => {
|
|
913
|
-
switch (framework) {
|
|
914
|
-
case "next":
|
|
915
|
-
if (nextRouterType === "app") return transformNextAppRouter(projectRoot, reactGrabAlreadyConfigured);
|
|
916
|
-
return transformNextPagesRouter(projectRoot, reactGrabAlreadyConfigured);
|
|
917
|
-
case "vite": return transformVite(projectRoot, reactGrabAlreadyConfigured);
|
|
918
|
-
case "tanstack": return transformTanStack(projectRoot, reactGrabAlreadyConfigured);
|
|
919
|
-
case "webpack": return transformWebpack(projectRoot, reactGrabAlreadyConfigured);
|
|
920
|
-
default: return {
|
|
921
|
-
success: false,
|
|
922
|
-
filePath: "",
|
|
923
|
-
message: `Unknown framework: ${framework}. Please add React Grab manually.`
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
};
|
|
927
|
-
const canWriteToFile = (filePath) => {
|
|
928
|
-
try {
|
|
929
|
-
(0, node_fs.accessSync)(filePath, node_fs.constants.W_OK);
|
|
930
|
-
return true;
|
|
931
|
-
} catch {
|
|
932
|
-
return false;
|
|
933
|
-
}
|
|
934
|
-
};
|
|
935
|
-
const applyTransform = (result) => {
|
|
936
|
-
if (result.success && result.newContent && result.filePath) {
|
|
937
|
-
if (!canWriteToFile(result.filePath)) return {
|
|
938
|
-
success: false,
|
|
939
|
-
error: `Cannot write to ${result.filePath}. Check file permissions.`
|
|
940
|
-
};
|
|
941
|
-
try {
|
|
942
|
-
(0, node_fs.writeFileSync)(result.filePath, result.newContent);
|
|
943
|
-
return { success: true };
|
|
944
|
-
} catch (error) {
|
|
945
|
-
return {
|
|
946
|
-
success: false,
|
|
947
|
-
error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
return { success: true };
|
|
952
|
-
};
|
|
953
|
-
const formatOptionsForNextjs = (options) => {
|
|
954
|
-
const parts = [];
|
|
955
|
-
if (options.activationKey) parts.push(`activationKey: ${JSON.stringify(options.activationKey)}`);
|
|
956
|
-
if (options.activationMode) parts.push(`activationMode: "${options.activationMode}"`);
|
|
957
|
-
if (options.keyHoldDuration !== void 0) parts.push(`keyHoldDuration: ${options.keyHoldDuration}`);
|
|
958
|
-
if (options.allowActivationInsideInput !== void 0) parts.push(`allowActivationInsideInput: ${options.allowActivationInsideInput}`);
|
|
959
|
-
if (options.maxContextLines !== void 0) parts.push(`maxContextLines: ${options.maxContextLines}`);
|
|
960
|
-
return `{ ${parts.join(", ")} }`;
|
|
961
|
-
};
|
|
962
|
-
const formatOptionsAsJson = (options) => {
|
|
963
|
-
const cleanOptions = {};
|
|
964
|
-
if (options.activationKey) cleanOptions.activationKey = options.activationKey;
|
|
965
|
-
if (options.activationMode) cleanOptions.activationMode = options.activationMode;
|
|
966
|
-
if (options.keyHoldDuration !== void 0) cleanOptions.keyHoldDuration = options.keyHoldDuration;
|
|
967
|
-
if (options.allowActivationInsideInput !== void 0) cleanOptions.allowActivationInsideInput = options.allowActivationInsideInput;
|
|
968
|
-
if (options.maxContextLines !== void 0) cleanOptions.maxContextLines = options.maxContextLines;
|
|
969
|
-
return JSON.stringify(cleanOptions);
|
|
970
|
-
};
|
|
971
|
-
const findReactGrabFile = (projectRoot, framework, nextRouterType) => {
|
|
972
|
-
switch (framework) {
|
|
973
|
-
case "next": {
|
|
974
|
-
const primaryFile = nextRouterType === "app" ? findLayoutFile(projectRoot) : findDocumentFile(projectRoot);
|
|
975
|
-
const primarySetupFile = findFileWithReactGrabSetup(nextRouterType === "app" ? getLayoutFileCandidates(projectRoot) : getDocumentFileCandidates(projectRoot));
|
|
976
|
-
if (primarySetupFile) return primarySetupFile;
|
|
977
|
-
const instrumentationFile = findFileWithReactGrabSetup(getInstrumentationFileCandidates(projectRoot));
|
|
978
|
-
if (instrumentationFile) return instrumentationFile;
|
|
979
|
-
return primaryFile;
|
|
980
|
-
}
|
|
981
|
-
case "vite": {
|
|
982
|
-
const entryFile = findEntryFile(projectRoot);
|
|
983
|
-
const entrySetupFile = findFileWithReactGrabSetup(getEntryFileCandidates(projectRoot));
|
|
984
|
-
if (entrySetupFile) return entrySetupFile;
|
|
985
|
-
const indexHtml = findFileWithReactGrabSetup(getIndexHtmlCandidates(projectRoot));
|
|
986
|
-
if (indexHtml) return indexHtml;
|
|
987
|
-
return entryFile;
|
|
988
|
-
}
|
|
989
|
-
case "tanstack": return findFileWithReactGrabSetup(getTanStackRootFileCandidates(projectRoot)) ?? findTanStackRootFile(projectRoot);
|
|
990
|
-
case "webpack": return findFileWithReactGrabSetup(getEntryFileCandidates(projectRoot)) ?? findEntryFile(projectRoot);
|
|
991
|
-
default: return null;
|
|
992
|
-
}
|
|
993
|
-
};
|
|
994
|
-
const addOptionsToNextScript = (originalContent, options, filePath) => {
|
|
995
|
-
const reactGrabScriptMatch = originalContent.match(/(<Script[\s\S]*?react-grab[\s\S]*?)\s*(\/?>)/i);
|
|
996
|
-
if (!reactGrabScriptMatch) return {
|
|
997
|
-
success: false,
|
|
998
|
-
filePath,
|
|
999
|
-
message: "Could not find React Grab Script tag"
|
|
1000
|
-
};
|
|
1001
|
-
const scriptTag = reactGrabScriptMatch[0];
|
|
1002
|
-
const scriptOpening = reactGrabScriptMatch[1];
|
|
1003
|
-
const scriptClosing = reactGrabScriptMatch[2];
|
|
1004
|
-
const existingDataOptionsMatch = scriptTag.match(/data-options=\{JSON\.stringify\([^)]+\)\}/);
|
|
1005
|
-
const dataOptionsAttr = `data-options={JSON.stringify(\n ${formatOptionsForNextjs(options)}\n )}`;
|
|
1006
|
-
let newScriptTag;
|
|
1007
|
-
if (existingDataOptionsMatch) newScriptTag = scriptTag.replace(existingDataOptionsMatch[0], dataOptionsAttr);
|
|
1008
|
-
else newScriptTag = `${scriptOpening}\n ${dataOptionsAttr}\n ${scriptClosing}`;
|
|
1009
|
-
return {
|
|
1010
|
-
success: true,
|
|
1011
|
-
filePath,
|
|
1012
|
-
message: "Update React Grab options",
|
|
1013
|
-
originalContent,
|
|
1014
|
-
newContent: originalContent.replace(scriptTag, newScriptTag)
|
|
1015
|
-
};
|
|
1016
|
-
};
|
|
1017
|
-
const addOptionsToDynamicImport = (originalContent, options, filePath) => {
|
|
1018
|
-
const reactGrabImportWithInitMatch = originalContent.match(/(void\s+)?import\s*\(\s*["']react-grab(?:\/[^"']+)?["']\s*\)(?:\.then\s*\(\s*(?:\(m\)\s*=>\s*m\.init\s*\([^)]*\)|\(\{\s*init\s*\}\)\s*=>\s*init\s*\([^)]*\))\s*\))?/);
|
|
1019
|
-
if (!reactGrabImportWithInitMatch) return {
|
|
1020
|
-
success: false,
|
|
1021
|
-
filePath,
|
|
1022
|
-
message: "Could not find React Grab import"
|
|
1023
|
-
};
|
|
1024
|
-
const optionsJson = formatOptionsAsJson(options);
|
|
1025
|
-
const newImport = `${reactGrabImportWithInitMatch[1] ?? ""}import("react-grab").then((m) => m.init(${optionsJson}))`;
|
|
1026
|
-
return {
|
|
1027
|
-
success: true,
|
|
1028
|
-
filePath,
|
|
1029
|
-
message: "Update React Grab options",
|
|
1030
|
-
originalContent,
|
|
1031
|
-
newContent: originalContent.replace(reactGrabImportWithInitMatch[0], newImport)
|
|
1032
|
-
};
|
|
1033
|
-
};
|
|
1034
|
-
const addOptionsToTanStackImport = (originalContent, options, filePath) => {
|
|
1035
|
-
const reactGrabImportWithInitMatch = originalContent.match(/(?:(void\s+)?import\s*\(\s*["']react-grab\/core["']\s*\)\.then\s*\(\s*(?:\(\s*\{\s*init\s*\}\s*\)\s*=>\s*init\s*\([^)]*\)|\(m\)\s*=>\s*m\.init\s*\([^)]*\))\s*\)|(void\s+)?import\s*\(\s*["']react-grab(?!\/core)(?:\/[^"']+)?["']\s*\))/);
|
|
1036
|
-
if (!reactGrabImportWithInitMatch) return {
|
|
1037
|
-
success: false,
|
|
1038
|
-
filePath,
|
|
1039
|
-
message: "Could not find React Grab import"
|
|
1040
|
-
};
|
|
1041
|
-
const optionsJson = formatOptionsAsJson(options);
|
|
1042
|
-
const newImport = `${reactGrabImportWithInitMatch[1] ?? reactGrabImportWithInitMatch[2] ?? ""}import("react-grab/core").then(({ init }) => init(${optionsJson}))`;
|
|
1043
|
-
return {
|
|
1044
|
-
success: true,
|
|
1045
|
-
filePath,
|
|
1046
|
-
message: "Update React Grab options",
|
|
1047
|
-
originalContent,
|
|
1048
|
-
newContent: originalContent.replace(reactGrabImportWithInitMatch[0], newImport)
|
|
1049
|
-
};
|
|
1050
|
-
};
|
|
1051
|
-
const addOptionsToAnyImport = (originalContent, options, filePath) => {
|
|
1052
|
-
const dynamicImportResult = addOptionsToDynamicImport(originalContent, options, filePath);
|
|
1053
|
-
if (dynamicImportResult.success) return dynamicImportResult;
|
|
1054
|
-
return addOptionsToTanStackImport(originalContent, options, filePath);
|
|
1055
|
-
};
|
|
1056
|
-
const previewOptionsTransform = (projectRoot, framework, nextRouterType, options) => {
|
|
1057
|
-
const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
|
|
1058
|
-
if (!filePath) return {
|
|
1059
|
-
success: false,
|
|
1060
|
-
filePath: "",
|
|
1061
|
-
message: "Could not find file containing React Grab configuration"
|
|
1062
|
-
};
|
|
1063
|
-
const originalContent = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
1064
|
-
if (!hasReactGrabSetupCode(originalContent)) return {
|
|
1065
|
-
success: false,
|
|
1066
|
-
filePath,
|
|
1067
|
-
message: "Could not find React Grab code in the file"
|
|
1068
|
-
};
|
|
1069
|
-
switch (framework) {
|
|
1070
|
-
case "next":
|
|
1071
|
-
if (isInstrumentationFile(filePath)) return addOptionsToAnyImport(originalContent, options, filePath);
|
|
1072
|
-
return addOptionsToNextScript(originalContent, options, filePath);
|
|
1073
|
-
case "vite": return addOptionsToDynamicImport(originalContent, options, filePath);
|
|
1074
|
-
case "tanstack": return addOptionsToTanStackImport(originalContent, options, filePath);
|
|
1075
|
-
case "webpack": return addOptionsToDynamicImport(originalContent, options, filePath);
|
|
1076
|
-
default: return {
|
|
1077
|
-
success: false,
|
|
1078
|
-
filePath,
|
|
1079
|
-
message: `Unknown framework: ${framework}`
|
|
1080
|
-
};
|
|
1081
|
-
}
|
|
1082
|
-
};
|
|
1083
|
-
const previewCdnTransform = (projectRoot, framework, nextRouterType, targetCdnDomain) => {
|
|
1084
|
-
const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
|
|
1085
|
-
if (!filePath) return {
|
|
1086
|
-
success: false,
|
|
1087
|
-
filePath: "",
|
|
1088
|
-
message: "Could not find React Grab file"
|
|
1089
|
-
};
|
|
1090
|
-
const originalContent = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
1091
|
-
const newContent = originalContent.replace(/(https?:)?\/\/[^/\s"']+(?=\/(?:@?react-grab))/g, `//${targetCdnDomain}`).replace(/(https?:)?\/\/[^/\s"']*react-grab[^/\s"']*\.com(?=\/script\.js)/g, `//${targetCdnDomain}`);
|
|
1092
|
-
if (newContent === originalContent) return {
|
|
1093
|
-
success: true,
|
|
1094
|
-
filePath,
|
|
1095
|
-
message: "CDN already set",
|
|
1096
|
-
noChanges: true
|
|
1097
|
-
};
|
|
1098
|
-
return {
|
|
1099
|
-
success: true,
|
|
1100
|
-
filePath,
|
|
1101
|
-
message: "Update CDN",
|
|
1102
|
-
originalContent,
|
|
1103
|
-
newContent
|
|
1104
|
-
};
|
|
1105
|
-
};
|
|
1106
|
-
//#endregion
|
|
1107
269
|
//#region src/utils/constants.ts
|
|
1108
270
|
const MAX_KEY_HOLD_DURATION_MS = 2e3;
|
|
1109
271
|
const DEFAULT_WATCH_DIR = ".react-grab";
|
|
@@ -1127,7 +289,7 @@ const formatActivationKeyDisplay = (activationKey) => {
|
|
|
1127
289
|
};
|
|
1128
290
|
//#endregion
|
|
1129
291
|
//#region src/commands/configure.ts
|
|
1130
|
-
const VERSION$4 = "0.1.
|
|
292
|
+
const VERSION$4 = "0.1.47-dev.bc3a591";
|
|
1131
293
|
const isMac = process.platform === "darwin";
|
|
1132
294
|
const META_LABEL = isMac ? "Cmd" : "Win";
|
|
1133
295
|
const ALT_LABEL = isMac ? "Option" : "Alt";
|
|
@@ -1418,7 +580,7 @@ const CONFIG_OPTIONS = [
|
|
|
1418
580
|
{
|
|
1419
581
|
id: "maxContextLines",
|
|
1420
582
|
title: "Max Context Lines",
|
|
1421
|
-
description: "
|
|
583
|
+
description: "Max source-location lines in copied context (raise for large apps)"
|
|
1422
584
|
}
|
|
1423
585
|
];
|
|
1424
586
|
const comboToString = (combo) => {
|
|
@@ -1439,7 +601,7 @@ const configure = new commander.Command().name("configure").alias("config").desc
|
|
|
1439
601
|
try {
|
|
1440
602
|
const cwd = opts.cwd;
|
|
1441
603
|
const preflightSpinner = spinner("Preflight checks.").start();
|
|
1442
|
-
const projectInfo = await detectProject(cwd);
|
|
604
|
+
const projectInfo = await require_install.detectProject(cwd);
|
|
1443
605
|
if (!projectInfo.hasReactGrab) {
|
|
1444
606
|
preflightSpinner.fail("React Grab is not installed.");
|
|
1445
607
|
logger.break();
|
|
@@ -1456,7 +618,7 @@ const configure = new commander.Command().name("configure").alias("config").desc
|
|
|
1456
618
|
}
|
|
1457
619
|
preflightSpinner.succeed();
|
|
1458
620
|
if (opts.cdn) {
|
|
1459
|
-
const result = previewCdnTransform(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType, opts.cdn);
|
|
621
|
+
const result = require_install.previewCdnTransform(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType, opts.cdn);
|
|
1460
622
|
if (!result.success) {
|
|
1461
623
|
logger.break();
|
|
1462
624
|
logger.error(result.message);
|
|
@@ -1487,7 +649,7 @@ const configure = new commander.Command().name("configure").alias("config").desc
|
|
|
1487
649
|
}
|
|
1488
650
|
}
|
|
1489
651
|
const writeSpinner = spinner(`Applying changes to ${result.filePath}.`).start();
|
|
1490
|
-
const writeResult = applyTransform(result);
|
|
652
|
+
const writeResult = require_install.applyTransform(result);
|
|
1491
653
|
if (!writeResult.success) {
|
|
1492
654
|
writeSpinner.fail();
|
|
1493
655
|
logger.break();
|
|
@@ -1640,7 +802,7 @@ const configure = new commander.Command().name("configure").alias("config").desc
|
|
|
1640
802
|
collectedOptions.maxContextLines = maxContextLines;
|
|
1641
803
|
}
|
|
1642
804
|
}
|
|
1643
|
-
const result = previewOptionsTransform(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType, collectedOptions);
|
|
805
|
+
const result = require_install.previewOptionsTransform(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType, collectedOptions);
|
|
1644
806
|
if (!result.success) {
|
|
1645
807
|
logger.break();
|
|
1646
808
|
logger.warn(result.message);
|
|
@@ -1671,7 +833,7 @@ const configure = new commander.Command().name("configure").alias("config").desc
|
|
|
1671
833
|
}
|
|
1672
834
|
}
|
|
1673
835
|
const writeSpinner = spinner(`Applying changes to ${result.filePath}.`).start();
|
|
1674
|
-
const writeResult = applyTransform(result);
|
|
836
|
+
const writeResult = require_install.applyTransform(result);
|
|
1675
837
|
if (!writeResult.success) {
|
|
1676
838
|
writeSpinner.fail();
|
|
1677
839
|
logger.break();
|
|
@@ -1692,42 +854,10 @@ const configure = new commander.Command().name("configure").alias("config").desc
|
|
|
1692
854
|
}
|
|
1693
855
|
});
|
|
1694
856
|
//#endregion
|
|
1695
|
-
//#region src/utils/install.ts
|
|
1696
|
-
const installPackages = async (packages, options = {}) => {
|
|
1697
|
-
if (packages.length === 0) return;
|
|
1698
|
-
const detectedAgent = options.packageManager ?? await detectPackageManager(options.cwd ?? process.cwd());
|
|
1699
|
-
const args = [];
|
|
1700
|
-
if (options.preferOffline) args.push("--prefer-offline");
|
|
1701
|
-
if (detectedAgent === "pnpm") {
|
|
1702
|
-
args.push("--prod=false");
|
|
1703
|
-
if ((0, node_fs.existsSync)((0, node_path.resolve)(options.cwd ?? process.cwd(), "pnpm-workspace.yaml"))) args.push("-w");
|
|
1704
|
-
}
|
|
1705
|
-
if (options.additionalArgs) args.push(...options.additionalArgs);
|
|
1706
|
-
await (0, tinyexec.x)(detectedAgent, [
|
|
1707
|
-
detectedAgent === "npm" ? "install" : "add",
|
|
1708
|
-
...options.isDev !== false ? ["-D"] : [],
|
|
1709
|
-
...args,
|
|
1710
|
-
...packages
|
|
1711
|
-
], {
|
|
1712
|
-
nodeOptions: {
|
|
1713
|
-
stdio: options.silent ? "ignore" : "inherit",
|
|
1714
|
-
cwd: options.cwd,
|
|
1715
|
-
env: {
|
|
1716
|
-
...process.env,
|
|
1717
|
-
REACT_GRAB_INIT: "1"
|
|
1718
|
-
}
|
|
1719
|
-
},
|
|
1720
|
-
throwOnError: true
|
|
1721
|
-
});
|
|
1722
|
-
};
|
|
1723
|
-
const getPackagesToInstall = (includeReactGrab = true) => {
|
|
1724
|
-
return includeReactGrab ? ["react-grab"] : [];
|
|
1725
|
-
};
|
|
1726
|
-
//#endregion
|
|
1727
857
|
//#region src/utils/cli-helpers.ts
|
|
1728
858
|
const applyTransformWithFeedback = (result, message) => {
|
|
1729
859
|
const writeSpinner = spinner(message ?? `Applying changes to ${result.filePath}.`).start();
|
|
1730
|
-
const writeResult = applyTransform(result);
|
|
860
|
+
const writeResult = require_install.applyTransform(result);
|
|
1731
861
|
if (!writeResult.success) {
|
|
1732
862
|
writeSpinner.fail();
|
|
1733
863
|
logger.break();
|
|
@@ -1741,7 +871,7 @@ const installPackagesWithFeedback = async (packages, packageManager, projectRoot
|
|
|
1741
871
|
if (packages.length === 0) return;
|
|
1742
872
|
const installSpinner = spinner(`Installing ${packages.join(", ")}.`).start();
|
|
1743
873
|
try {
|
|
1744
|
-
await installPackages(packages, {
|
|
874
|
+
await require_install.installPackages(packages, {
|
|
1745
875
|
packageManager,
|
|
1746
876
|
cwd: projectRoot
|
|
1747
877
|
});
|
|
@@ -1759,7 +889,7 @@ const isTelemetryEnabled = () => {
|
|
|
1759
889
|
};
|
|
1760
890
|
//#endregion
|
|
1761
891
|
//#region src/commands/init.ts
|
|
1762
|
-
const VERSION$3 = "0.1.
|
|
892
|
+
const VERSION$3 = "0.1.47-dev.bc3a591";
|
|
1763
893
|
const REPORT_URL = "https://react-grab.com/api/report-cli";
|
|
1764
894
|
const DOCS_URL = "https://github.com/aidenybai/react-grab";
|
|
1765
895
|
const reportToCli = (type, config, error) => {
|
|
@@ -1840,7 +970,7 @@ const init = new commander.Command().name("init").alias("setup").description("in
|
|
|
1840
970
|
process.exit(1);
|
|
1841
971
|
}
|
|
1842
972
|
const preflightSpinner = spinner("Preflight checks.").start();
|
|
1843
|
-
const projectInfo = await detectProject(cwd);
|
|
973
|
+
const projectInfo = await require_install.detectProject(cwd);
|
|
1844
974
|
if (projectInfo.isReactGrabConfigured && !opts.force) {
|
|
1845
975
|
preflightSpinner.succeed();
|
|
1846
976
|
if (isNonInteractive) {
|
|
@@ -1954,7 +1084,7 @@ const init = new commander.Command().name("init").alias("setup").description("in
|
|
|
1954
1084
|
process.exit(1);
|
|
1955
1085
|
}
|
|
1956
1086
|
collectedOptions.maxContextLines = maxContextLines;
|
|
1957
|
-
const optionsResult = previewOptionsTransform(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType, collectedOptions);
|
|
1087
|
+
const optionsResult = require_install.previewOptionsTransform(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType, collectedOptions);
|
|
1958
1088
|
if (!optionsResult.success) {
|
|
1959
1089
|
logger.break();
|
|
1960
1090
|
logger.error(optionsResult.message);
|
|
@@ -2004,12 +1134,12 @@ const init = new commander.Command().name("init").alias("setup").description("in
|
|
|
2004
1134
|
logger.break();
|
|
2005
1135
|
process.exit(1);
|
|
2006
1136
|
}
|
|
2007
|
-
if (projectInfo.framework === "unknown" || projectInfo.isMonorepo && !hasFrameworkEntryPoint(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType)) {
|
|
1137
|
+
if (projectInfo.framework === "unknown" || projectInfo.isMonorepo && !require_install.hasFrameworkEntryPoint(projectInfo.projectRoot, projectInfo.framework, projectInfo.nextRouterType)) {
|
|
2008
1138
|
let searchRoot = cwd;
|
|
2009
|
-
let reactProjects = findReactProjects(searchRoot);
|
|
1139
|
+
let reactProjects = require_install.findReactProjects(searchRoot);
|
|
2010
1140
|
if (reactProjects.length === 0 && cwd !== process.cwd()) {
|
|
2011
1141
|
searchRoot = process.cwd();
|
|
2012
|
-
reactProjects = findReactProjects(searchRoot);
|
|
1142
|
+
reactProjects = require_install.findReactProjects(searchRoot);
|
|
2013
1143
|
}
|
|
2014
1144
|
if (reactProjects.length > 0) {
|
|
2015
1145
|
frameworkSpinner.info(`Verifying framework. Found ${reactProjects.length} project${reactProjects.length === 1 ? "" : "s"}.`);
|
|
@@ -2039,7 +1169,7 @@ const init = new commander.Command().name("init").alias("setup").description("in
|
|
|
2039
1169
|
process.exit(0);
|
|
2040
1170
|
}
|
|
2041
1171
|
process.chdir(selectedProject);
|
|
2042
|
-
const newProjectInfo = await detectProject(selectedProject);
|
|
1172
|
+
const newProjectInfo = await require_install.detectProject(selectedProject);
|
|
2043
1173
|
Object.assign(projectInfo, newProjectInfo);
|
|
2044
1174
|
const newFrameworkSpinner = spinner("Verifying framework.").start();
|
|
2045
1175
|
if (newProjectInfo.framework === "unknown") failWithManualSetup(newFrameworkSpinner, "Could not detect a supported framework in this project.", { listSupportedFrameworks: true });
|
|
@@ -2061,7 +1191,7 @@ const init = new commander.Command().name("init").alias("setup").description("in
|
|
|
2061
1191
|
cwd
|
|
2062
1192
|
});
|
|
2063
1193
|
}
|
|
2064
|
-
const result = previewTransform(projectInfo.projectRoot, finalFramework, finalNextRouterType, projectInfo.isReactGrabConfigured);
|
|
1194
|
+
const result = require_install.previewTransform(projectInfo.projectRoot, finalFramework, finalNextRouterType, projectInfo.isReactGrabConfigured);
|
|
2065
1195
|
if (!result.success) {
|
|
2066
1196
|
logger.break();
|
|
2067
1197
|
logger.error(result.message);
|
|
@@ -2093,7 +1223,7 @@ const init = new commander.Command().name("init").alias("setup").description("in
|
|
|
2093
1223
|
}
|
|
2094
1224
|
}
|
|
2095
1225
|
const shouldInstallReactGrab = !projectInfo.hasReactGrab;
|
|
2096
|
-
if (!opts.skipInstall && shouldInstallReactGrab) await installPackagesWithFeedback(getPackagesToInstall(shouldInstallReactGrab), finalPackageManager, projectInfo.projectRoot);
|
|
1226
|
+
if (!opts.skipInstall && shouldInstallReactGrab) await installPackagesWithFeedback(require_install.getPackagesToInstall(shouldInstallReactGrab), finalPackageManager, projectInfo.projectRoot);
|
|
2097
1227
|
if (hasLayoutChanges) applyTransformWithFeedback(result);
|
|
2098
1228
|
logger.break();
|
|
2099
1229
|
if (hasLayoutChanges) logger.log(`${highlighter.success("Success!")} React Grab has been installed.`);
|
|
@@ -2710,19 +1840,20 @@ const pull = new commander.Command().name("pull").description("start the watcher
|
|
|
2710
1840
|
});
|
|
2711
1841
|
//#endregion
|
|
2712
1842
|
//#region src/commands/remove.ts
|
|
2713
|
-
const VERSION$2 = "0.1.
|
|
1843
|
+
const VERSION$2 = "0.1.47-dev.bc3a591";
|
|
2714
1844
|
const remove = new commander.Command().name("remove").description("uninstall the React Grab skill from your agent").option("-c, --cwd <cwd>", "working directory (defaults to current directory)", process.cwd()).option("-g, --global", "remove the globally-installed skill instead of the project's", false).action(async (opts) => {
|
|
2715
1845
|
console.log(`${picocolors.default.magenta("✿")} ${picocolors.default.bold("React Grab")} ${picocolors.default.gray(VERSION$2)}`);
|
|
2716
1846
|
console.log();
|
|
2717
1847
|
try {
|
|
2718
1848
|
logger.break();
|
|
2719
|
-
const
|
|
1849
|
+
const removedAgents = await require_install.removeSkill({
|
|
2720
1850
|
cwd: (0, node_path.resolve)(opts.cwd),
|
|
2721
1851
|
global: opts.global
|
|
2722
1852
|
});
|
|
1853
|
+
for (const agent of removedAgents) logger.log(` ${highlighter.success("✓")} ${require_install.agentLabel(agent)}`);
|
|
2723
1854
|
logger.break();
|
|
2724
|
-
if (
|
|
2725
|
-
else logger.log(`${highlighter.success("Removed")} the React Grab skill from ${
|
|
1855
|
+
if (removedAgents.length === 0) logger.log("React Grab skill is not installed in any detected agent.");
|
|
1856
|
+
else logger.log(`${highlighter.success("Removed")} the React Grab skill from ${removedAgents.length} agent${removedAgents.length === 1 ? "" : "s"}.`);
|
|
2726
1857
|
logger.break();
|
|
2727
1858
|
} catch (error) {
|
|
2728
1859
|
handleError(error);
|
|
@@ -2738,7 +1869,7 @@ const stop = new commander.Command().name("stop").description("stop the React Gr
|
|
|
2738
1869
|
});
|
|
2739
1870
|
//#endregion
|
|
2740
1871
|
//#region src/commands/upgrade.ts
|
|
2741
|
-
const VERSION$1 = "0.1.
|
|
1872
|
+
const VERSION$1 = "0.1.47-dev.bc3a591";
|
|
2742
1873
|
const NPM_REGISTRY_URL = "https://registry.npmjs.org/react-grab/latest";
|
|
2743
1874
|
const fetchLatestVersion = async () => {
|
|
2744
1875
|
try {
|
|
@@ -2763,7 +1894,7 @@ const upgrade = new commander.Command().name("upgrade").alias("update").descript
|
|
|
2763
1894
|
try {
|
|
2764
1895
|
const cwd = (0, node_path.resolve)(opts.cwd);
|
|
2765
1896
|
const detectSpinner = spinner("Detecting project.").start();
|
|
2766
|
-
const projectInfo = await detectProject(cwd);
|
|
1897
|
+
const projectInfo = await require_install.detectProject(cwd);
|
|
2767
1898
|
if (!projectInfo.hasReactGrab) {
|
|
2768
1899
|
detectSpinner.fail("React Grab is not installed.");
|
|
2769
1900
|
logger.break();
|
|
@@ -2791,7 +1922,7 @@ const upgrade = new commander.Command().name("upgrade").alias("update").descript
|
|
|
2791
1922
|
versionSpinner.succeed(`Update available: ${highlighter.dim(fromLabel)} → ${highlighter.info(`v${latestVersion}`)}.`);
|
|
2792
1923
|
const upgradeSpinner = spinner("Upgrading react-grab.").start();
|
|
2793
1924
|
try {
|
|
2794
|
-
await installPackages(["react-grab@latest"], {
|
|
1925
|
+
await require_install.installPackages(["react-grab@latest"], {
|
|
2795
1926
|
packageManager: projectInfo.packageManager,
|
|
2796
1927
|
cwd: projectInfo.projectRoot,
|
|
2797
1928
|
isDev: isDevDependency(projectInfo.projectRoot)
|
|
@@ -2850,7 +1981,7 @@ const watch = new commander.Command().name("watch").description("run the React G
|
|
|
2850
1981
|
});
|
|
2851
1982
|
//#endregion
|
|
2852
1983
|
//#region src/cli.ts
|
|
2853
|
-
const VERSION = "0.1.
|
|
1984
|
+
const VERSION = "0.1.47-dev.bc3a591";
|
|
2854
1985
|
const VERSION_API_URL = "https://www.react-grab.com/api/version";
|
|
2855
1986
|
process.on("SIGINT", () => process.exit(0));
|
|
2856
1987
|
process.on("SIGTERM", () => process.exit(0));
|