1005 lines
40 KiB
JavaScript
1005 lines
40 KiB
JavaScript
#!/usr/bin/env node
|
|
//#region \0rolldown/runtime.js
|
|
var __create = Object.create;
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
key = keys[i];
|
|
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
get: ((k) => from[k]).bind(null, key),
|
|
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
});
|
|
}
|
|
return to;
|
|
};
|
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
value: mod,
|
|
enumerable: true
|
|
}) : target, mod));
|
|
//#endregion
|
|
let node_path = require("node:path");
|
|
let node_fs = require("node:fs");
|
|
let package_manager_detector_detect = require("package-manager-detector/detect");
|
|
let ignore = require("ignore");
|
|
ignore = __toESM(ignore, 1);
|
|
let agent_install_skill = require("agent-install/skill");
|
|
let node_url = require("node:url");
|
|
let tinyexec = require("tinyexec");
|
|
//#region src/utils/react-grab-code.ts
|
|
const REACT_GRAB_SPECIFIER_PATTERN = String.raw`react-grab(?:\/[^"']+)?`;
|
|
const stripComments = (content) => content.replace(/<!--[\s\S]*?-->/g, "").replace(/\{\/\*[\s\S]*?\*\/\}/g, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
|
|
const stripTypeOnlyReactGrabImports = (content) => {
|
|
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"), "");
|
|
};
|
|
const hasReactGrabSetupCode = (content) => {
|
|
const setupCandidateContent = stripTypeOnlyReactGrabImports(stripComments(content));
|
|
return [
|
|
new RegExp(String.raw`import\s*\(\s*["']${REACT_GRAB_SPECIFIER_PATTERN}["']\s*\)`),
|
|
new RegExp(String.raw`import\s+(?!type\b)(?:[^"';]+from\s+)?["']${REACT_GRAB_SPECIFIER_PATTERN}["']`),
|
|
new RegExp(String.raw`require\s*\(\s*["']${REACT_GRAB_SPECIFIER_PATTERN}["']\s*\)`),
|
|
/<Script[\s\S]*?src\s*=\s*(?:["'][^"']*react-grab[^"']*["']|\{(?:["'][^"']*react-grab[^"']*["']|`[^`]*react-grab[^`]*`)\})/i,
|
|
/<script[\s\S]*?src\s*=\s*["'][^"']*react-grab[^"']*["']/i
|
|
].some((pattern) => pattern.test(setupCandidateContent));
|
|
};
|
|
//#endregion
|
|
//#region src/utils/react-grab-setup-files.ts
|
|
const COMPONENT_EXTENSIONS = [
|
|
"tsx",
|
|
"jsx",
|
|
"ts",
|
|
"js"
|
|
];
|
|
const INSTRUMENTATION_EXTENSIONS = [
|
|
"ts",
|
|
"tsx",
|
|
"js",
|
|
"jsx",
|
|
"mts",
|
|
"cts",
|
|
"mjs",
|
|
"cjs"
|
|
];
|
|
const ROUTE_EXTENSIONS = ["tsx", "jsx"];
|
|
const createFileCandidates = (projectRoot, directories, baseName, extensions) => {
|
|
const fileCandidates = [];
|
|
for (const directory of directories) for (const extension of extensions) fileCandidates.push((0, node_path.join)(projectRoot, directory, `${baseName}.${extension}`));
|
|
return fileCandidates;
|
|
};
|
|
const findExistingFile = (fileCandidates) => {
|
|
for (const filePath of fileCandidates) if ((0, node_fs.existsSync)(filePath)) return filePath;
|
|
return null;
|
|
};
|
|
const getLayoutFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["app", "src/app"], "layout", COMPONENT_EXTENSIONS);
|
|
const getDocumentFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["pages", "src/pages"], "_document", COMPONENT_EXTENSIONS);
|
|
const getInstrumentationFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["", "src"], "instrumentation-client", INSTRUMENTATION_EXTENSIONS);
|
|
const getIndexHtmlCandidates = (projectRoot) => [(0, node_path.join)(projectRoot, "index.html"), (0, node_path.join)(projectRoot, "public", "index.html")];
|
|
const getEntryFileCandidates = (projectRoot) => [...createFileCandidates(projectRoot, ["src"], "index", COMPONENT_EXTENSIONS), ...createFileCandidates(projectRoot, ["src"], "main", COMPONENT_EXTENSIONS)];
|
|
const getTanStackRootFileCandidates = (projectRoot) => createFileCandidates(projectRoot, ["src/routes", "app/routes"], "__root", ROUTE_EXTENSIONS);
|
|
const getReactGrabSetupFileCandidates = (projectRoot) => [
|
|
...getLayoutFileCandidates(projectRoot),
|
|
...getDocumentFileCandidates(projectRoot),
|
|
...getInstrumentationFileCandidates(projectRoot),
|
|
...getIndexHtmlCandidates(projectRoot),
|
|
...getEntryFileCandidates(projectRoot),
|
|
...getTanStackRootFileCandidates(projectRoot)
|
|
];
|
|
const findLayoutFile = (projectRoot) => findExistingFile(getLayoutFileCandidates(projectRoot));
|
|
const findDocumentFile = (projectRoot) => findExistingFile(getDocumentFileCandidates(projectRoot));
|
|
const findIndexHtml = (projectRoot) => findExistingFile(getIndexHtmlCandidates(projectRoot));
|
|
const findEntryFile = (projectRoot) => findExistingFile(getEntryFileCandidates(projectRoot));
|
|
const findTanStackRootFile = (projectRoot) => findExistingFile(getTanStackRootFileCandidates(projectRoot));
|
|
const isInstrumentationFile = (filePath) => /(?:^|[/\\])instrumentation-client\.[cm]?[jt]sx?$/.test(filePath);
|
|
//#endregion
|
|
//#region src/utils/detect.ts
|
|
const VALID_PACKAGE_MANAGERS = new Set([
|
|
"npm",
|
|
"yarn",
|
|
"pnpm",
|
|
"bun"
|
|
]);
|
|
const detectPackageManager = async (projectRoot) => {
|
|
const result = await (0, package_manager_detector_detect.detect)({ cwd: projectRoot });
|
|
if (result?.agent) {
|
|
const managerName = result.agent.split("@")[0];
|
|
if (VALID_PACKAGE_MANAGERS.has(managerName)) return managerName;
|
|
}
|
|
return "npm";
|
|
};
|
|
const CONFIG_EXTENSIONS = [
|
|
"ts",
|
|
"mts",
|
|
"cts",
|
|
"js",
|
|
"mjs",
|
|
"cjs"
|
|
];
|
|
const hasConfigFile = (projectRoot, configBaseName) => CONFIG_EXTENSIONS.some((extension) => (0, node_fs.existsSync)((0, node_path.join)(projectRoot, `${configBaseName}.${extension}`)));
|
|
const readMergedDependencies = (projectRoot) => {
|
|
const packageJsonPath = (0, node_path.join)(projectRoot, "package.json");
|
|
if (!(0, node_fs.existsSync)(packageJsonPath)) return null;
|
|
try {
|
|
const packageJson = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8"));
|
|
return {
|
|
...packageJson.dependencies,
|
|
...packageJson.devDependencies
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
const detectFrameworkFromDependencies = (dependencies) => {
|
|
if (!dependencies) return "unknown";
|
|
if (dependencies["next"]) return "next";
|
|
if (dependencies["@tanstack/react-start"]) return "tanstack";
|
|
if (dependencies["vite"]) return "vite";
|
|
if (dependencies["webpack"]) return "webpack";
|
|
return "unknown";
|
|
};
|
|
const detectFrameworkFromConfigFiles = (projectRoot) => {
|
|
if (hasConfigFile(projectRoot, "next.config")) return "next";
|
|
if (hasConfigFile(projectRoot, "app.config")) return "tanstack";
|
|
if (hasConfigFile(projectRoot, "vite.config")) return "vite";
|
|
if (hasConfigFile(projectRoot, "webpack.config")) return "webpack";
|
|
return "unknown";
|
|
};
|
|
const findEnclosingMonorepoRoot = (projectRoot) => {
|
|
let currentDirectory = (0, node_path.dirname)(projectRoot);
|
|
while (currentDirectory !== (0, node_path.dirname)(currentDirectory)) {
|
|
if (detectMonorepo(currentDirectory)) return currentDirectory;
|
|
currentDirectory = (0, node_path.dirname)(currentDirectory);
|
|
}
|
|
return null;
|
|
};
|
|
const detectFramework = (projectRoot) => {
|
|
const localFramework = detectFrameworkFromDependencies(readMergedDependencies(projectRoot));
|
|
if (localFramework !== "unknown") return localFramework;
|
|
return detectFrameworkFromConfigFiles(projectRoot);
|
|
};
|
|
const detectFrameworkFromMonorepoRoot = (projectRoot) => {
|
|
const monorepoRoot = findEnclosingMonorepoRoot(projectRoot);
|
|
if (!monorepoRoot) return "unknown";
|
|
return detectFrameworkFromDependencies(readMergedDependencies(monorepoRoot));
|
|
};
|
|
const detectNextRouterType = (projectRoot) => {
|
|
const hasAppDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "app"));
|
|
const hasSrcAppDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "src", "app"));
|
|
const hasPagesDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "pages"));
|
|
const hasSrcPagesDir = (0, node_fs.existsSync)((0, node_path.join)(projectRoot, "src", "pages"));
|
|
if (hasAppDir || hasSrcAppDir) return "app";
|
|
if (hasPagesDir || hasSrcPagesDir) return "pages";
|
|
return "unknown";
|
|
};
|
|
const detectMonorepo = (projectRoot) => {
|
|
if ((0, node_fs.existsSync)((0, node_path.join)(projectRoot, "pnpm-workspace.yaml"))) return true;
|
|
if ((0, node_fs.existsSync)((0, node_path.join)(projectRoot, "lerna.json"))) return true;
|
|
const packageJsonPath = (0, node_path.join)(projectRoot, "package.json");
|
|
if ((0, node_fs.existsSync)(packageJsonPath)) try {
|
|
if (JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8")).workspaces) return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
return false;
|
|
};
|
|
const getWorkspacePatterns = (projectRoot) => {
|
|
const patterns = [];
|
|
const pnpmWorkspacePath = (0, node_path.join)(projectRoot, "pnpm-workspace.yaml");
|
|
if ((0, node_fs.existsSync)(pnpmWorkspacePath)) {
|
|
const lines = (0, node_fs.readFileSync)(pnpmWorkspacePath, "utf-8").split("\n");
|
|
let inPackages = false;
|
|
for (const line of lines) {
|
|
if (line.match(/^packages:\s*$/)) {
|
|
inPackages = true;
|
|
continue;
|
|
}
|
|
if (inPackages) {
|
|
if (line.match(/^[a-zA-Z]/) || line.trim() === "") {
|
|
if (line.match(/^[a-zA-Z]/)) inPackages = false;
|
|
continue;
|
|
}
|
|
const match = line.match(/^\s*-\s*['"]?([^'"#\n]+?)['"]?\s*$/);
|
|
if (match) patterns.push(match[1].trim());
|
|
}
|
|
}
|
|
}
|
|
const lernaJsonPath = (0, node_path.join)(projectRoot, "lerna.json");
|
|
if ((0, node_fs.existsSync)(lernaJsonPath)) try {
|
|
const lernaJson = JSON.parse((0, node_fs.readFileSync)(lernaJsonPath, "utf-8"));
|
|
if (Array.isArray(lernaJson.packages)) patterns.push(...lernaJson.packages);
|
|
} catch {}
|
|
const packageJsonPath = (0, node_path.join)(projectRoot, "package.json");
|
|
if ((0, node_fs.existsSync)(packageJsonPath)) try {
|
|
const packageJson = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8"));
|
|
if (Array.isArray(packageJson.workspaces)) patterns.push(...packageJson.workspaces);
|
|
else if (packageJson.workspaces?.packages) patterns.push(...packageJson.workspaces.packages);
|
|
} catch {}
|
|
return [...new Set(patterns)];
|
|
};
|
|
const expandWorkspacePattern = (projectRoot, pattern) => {
|
|
const isGlob = pattern.endsWith("/*");
|
|
const basePath = (0, node_path.join)(projectRoot, pattern.replace(/\/\*$/, ""));
|
|
if (!(0, node_fs.existsSync)(basePath)) return [];
|
|
if (!isGlob) return (0, node_fs.existsSync)((0, node_path.join)(basePath, "package.json")) ? [basePath] : [];
|
|
const results = [];
|
|
try {
|
|
const entries = (0, node_fs.readdirSync)(basePath, { withFileTypes: true });
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory()) continue;
|
|
if ((0, node_fs.existsSync)((0, node_path.join)(basePath, entry.name, "package.json"))) results.push((0, node_path.join)(basePath, entry.name));
|
|
}
|
|
} catch {
|
|
return results;
|
|
}
|
|
return results;
|
|
};
|
|
const hasReactDependency = (projectPath) => {
|
|
const dependencies = readMergedDependencies(projectPath);
|
|
if (!dependencies) return false;
|
|
return Boolean(dependencies["react"] || dependencies["react-dom"]);
|
|
};
|
|
const buildReactProject = (projectPath) => {
|
|
const framework = detectFramework(projectPath);
|
|
if (!hasReactDependency(projectPath) && framework === "unknown") return null;
|
|
let name = (0, node_path.basename)(projectPath);
|
|
const packageJsonPath = (0, node_path.join)(projectPath, "package.json");
|
|
try {
|
|
name = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8")).name || name;
|
|
} catch {}
|
|
return {
|
|
name,
|
|
path: projectPath,
|
|
framework
|
|
};
|
|
};
|
|
const findWorkspaceProjects = (projectRoot) => {
|
|
const patterns = getWorkspacePatterns(projectRoot);
|
|
const projects = [];
|
|
for (const pattern of patterns) for (const projectPath of expandWorkspacePattern(projectRoot, pattern)) {
|
|
const project = buildReactProject(projectPath);
|
|
if (project) projects.push(project);
|
|
}
|
|
return projects;
|
|
};
|
|
const ALWAYS_IGNORED_DIRECTORIES = [
|
|
"node_modules",
|
|
".git",
|
|
".next",
|
|
".cache",
|
|
".turbo",
|
|
"dist",
|
|
"build",
|
|
"coverage",
|
|
"test-results"
|
|
];
|
|
const loadGitignore = (projectRoot) => {
|
|
const ignorer = (0, ignore.default)().add(ALWAYS_IGNORED_DIRECTORIES);
|
|
const gitignorePath = (0, node_path.join)(projectRoot, ".gitignore");
|
|
if ((0, node_fs.existsSync)(gitignorePath)) try {
|
|
ignorer.add((0, node_fs.readFileSync)(gitignorePath, "utf-8"));
|
|
} catch {}
|
|
return ignorer;
|
|
};
|
|
const scanDirectoryForProjects = (rootDirectory, ignorer, maxDepth, currentDepth = 0) => {
|
|
if (currentDepth >= maxDepth) return [];
|
|
if (!(0, node_fs.existsSync)(rootDirectory)) return [];
|
|
const projects = [];
|
|
try {
|
|
const entries = (0, node_fs.readdirSync)(rootDirectory, { withFileTypes: true });
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory()) continue;
|
|
if (ignorer.ignores(entry.name)) continue;
|
|
const entryPath = (0, node_path.join)(rootDirectory, entry.name);
|
|
if ((0, node_fs.existsSync)((0, node_path.join)(entryPath, "package.json"))) {
|
|
const project = buildReactProject(entryPath);
|
|
if (project) {
|
|
projects.push(project);
|
|
continue;
|
|
}
|
|
}
|
|
projects.push(...scanDirectoryForProjects(entryPath, ignorer, maxDepth, currentDepth + 1));
|
|
}
|
|
} catch {
|
|
return projects;
|
|
}
|
|
return projects;
|
|
};
|
|
const MAX_SCAN_DEPTH = 2;
|
|
const normalizePathForComparison = (filePath) => filePath.replace(/\\/g, "/");
|
|
const findReactProjects = (projectRoot) => {
|
|
const monorepoRoot = detectMonorepo(projectRoot) ? projectRoot : findEnclosingMonorepoRoot(projectRoot);
|
|
if (monorepoRoot) {
|
|
const workspaceProjects = findWorkspaceProjects(monorepoRoot);
|
|
const localProject = projectRoot === monorepoRoot ? null : buildReactProject(projectRoot);
|
|
const projects = localProject ? [localProject, ...workspaceProjects.filter((project) => normalizePathForComparison(project.path) !== normalizePathForComparison(localProject.path))] : workspaceProjects;
|
|
if (projects.length > 0) return projects;
|
|
}
|
|
const scannedProjects = scanDirectoryForProjects(projectRoot, loadGitignore(projectRoot), MAX_SCAN_DEPTH);
|
|
if (scannedProjects.length > 0) return scannedProjects;
|
|
let currentDirectory = (0, node_path.dirname)(projectRoot);
|
|
while (currentDirectory !== (0, node_path.dirname)(currentDirectory)) {
|
|
const parentProject = buildReactProject(currentDirectory);
|
|
if (parentProject) return [parentProject];
|
|
currentDirectory = (0, node_path.dirname)(currentDirectory);
|
|
}
|
|
return [];
|
|
};
|
|
const hasReactGrabSetupInFile = (filePath) => {
|
|
if (!(0, node_fs.existsSync)(filePath)) return false;
|
|
try {
|
|
return hasReactGrabSetupCode((0, node_fs.readFileSync)(filePath, "utf-8"));
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
const detectReactGrabDependency = (projectRoot) => {
|
|
const dependencies = readMergedDependencies(projectRoot);
|
|
return Boolean(dependencies?.["react-grab"]);
|
|
};
|
|
const detectReactGrabConfigured = (projectRoot) => {
|
|
return getReactGrabSetupFileCandidates(projectRoot).some(hasReactGrabSetupInFile);
|
|
};
|
|
const detectReactGrab = (projectRoot) => detectReactGrabDependency(projectRoot) || detectReactGrabConfigured(projectRoot);
|
|
const detectUnsupportedFramework = (projectRoot) => {
|
|
const dependencies = readMergedDependencies(projectRoot);
|
|
if (!dependencies) return null;
|
|
if (dependencies["@remix-run/react"] || dependencies["remix"]) return "remix";
|
|
if (dependencies["astro"]) return "astro";
|
|
if (dependencies["@sveltejs/kit"]) return "sveltekit";
|
|
if (dependencies["gatsby"]) return "gatsby";
|
|
return null;
|
|
};
|
|
const detectReactGrabVersion = (projectRoot) => {
|
|
const installedPackageJsonPath = (0, node_path.join)(projectRoot, "node_modules", "react-grab", "package.json");
|
|
if ((0, node_fs.existsSync)(installedPackageJsonPath)) try {
|
|
return JSON.parse((0, node_fs.readFileSync)(installedPackageJsonPath, "utf-8")).version ?? null;
|
|
} catch {}
|
|
return null;
|
|
};
|
|
const detectProject = async (projectRoot = process.cwd()) => {
|
|
const localFramework = detectFramework(projectRoot);
|
|
const framework = localFramework === "unknown" ? detectFrameworkFromMonorepoRoot(projectRoot) : localFramework;
|
|
const packageManager = await detectPackageManager(projectRoot);
|
|
const isMonorepo = detectMonorepo(projectRoot) || findEnclosingMonorepoRoot(projectRoot) !== null;
|
|
const isReactGrabConfigured = detectReactGrabConfigured(projectRoot);
|
|
return {
|
|
packageManager,
|
|
framework,
|
|
nextRouterType: framework === "next" ? detectNextRouterType(projectRoot) : "unknown",
|
|
isMonorepo,
|
|
projectRoot,
|
|
hasReactGrab: detectReactGrabDependency(projectRoot) || isReactGrabConfigured,
|
|
isReactGrabConfigured,
|
|
reactGrabVersion: detectReactGrabVersion(projectRoot),
|
|
unsupportedFramework: detectUnsupportedFramework(projectRoot)
|
|
};
|
|
};
|
|
//#endregion
|
|
//#region src/utils/detect-agents.ts
|
|
const PATH_BINARIES = {
|
|
"claude-code": ["claude"],
|
|
codex: ["codex"],
|
|
cursor: ["cursor", "cursor-agent"],
|
|
droid: ["droid"],
|
|
"gemini-cli": ["gemini"],
|
|
"github-copilot": ["copilot"],
|
|
opencode: ["opencode"],
|
|
pi: ["pi", "omegon"]
|
|
};
|
|
const isCommandAvailable = (command) => {
|
|
const pathDirectories = (process.env.PATH ?? "").split(node_path.delimiter).filter(Boolean);
|
|
for (const directory of pathDirectories) {
|
|
const binaryPath = (0, node_path.join)(directory, command);
|
|
try {
|
|
if ((0, node_fs.statSync)(binaryPath).isFile()) {
|
|
(0, node_fs.accessSync)(binaryPath, node_fs.constants.X_OK);
|
|
return true;
|
|
}
|
|
} catch {}
|
|
}
|
|
return false;
|
|
};
|
|
const detectAvailableAgents = async () => {
|
|
const installedAgents = new Set(await (0, agent_install_skill.detectInstalledSkillAgents)());
|
|
return (0, agent_install_skill.getSkillAgentTypes)().filter((agent) => {
|
|
if (agent === "universal") return false;
|
|
if (installedAgents.has(agent)) return true;
|
|
return PATH_BINARIES[agent]?.some(isCommandAvailable) ?? false;
|
|
});
|
|
};
|
|
//#endregion
|
|
//#region src/utils/install-skill.ts
|
|
const SKILL_NAME = "react-grab";
|
|
const SKILL_SOURCE = (0, node_url.fileURLToPath)(new URL("../skills/react-grab", require("url").pathToFileURL(__filename).href));
|
|
const agentLabel = (agent) => (0, agent_install_skill.getSkillAgentConfig)(agent).displayName;
|
|
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, {
|
|
global,
|
|
cwd
|
|
}), SKILL_NAME);
|
|
const installSkill = async ({ agents, global = false, cwd = process.cwd() } = {}) => {
|
|
return (0, agent_install_skill.add)({
|
|
source: SKILL_SOURCE,
|
|
agents: agents ?? await detectAvailableAgents(),
|
|
global,
|
|
cwd,
|
|
mode: "copy"
|
|
});
|
|
};
|
|
const removeSkill = async ({ cwd = process.cwd(), global = false } = {}) => {
|
|
const agents = await detectAvailableAgents();
|
|
const removedAgents = [];
|
|
const dirsToRemove = /* @__PURE__ */ new Set();
|
|
for (const agent of agents) {
|
|
const skillDir = installedSkillDir(agent, global, cwd);
|
|
if (!(0, node_fs.existsSync)(skillDir)) continue;
|
|
removedAgents.push(agent);
|
|
dirsToRemove.add(skillDir);
|
|
}
|
|
for (const skillDir of dirsToRemove) (0, node_fs.rmSync)(skillDir, {
|
|
recursive: true,
|
|
force: true
|
|
});
|
|
return removedAgents;
|
|
};
|
|
//#endregion
|
|
//#region src/utils/templates.ts
|
|
const NEXT_APP_ROUTER_SCRIPT = `{process.env.NODE_ENV === "development" && (
|
|
<Script
|
|
src="//unpkg.com/react-grab/dist/index.global.js"
|
|
crossOrigin="anonymous"
|
|
strategy="beforeInteractive"
|
|
/>
|
|
)}`;
|
|
const VITE_IMPORT = `if (import.meta.env.DEV) {
|
|
import("react-grab");
|
|
}`;
|
|
const WEBPACK_IMPORT = `if (process.env.NODE_ENV === "development") {
|
|
import("react-grab");
|
|
}`;
|
|
const TANSTACK_EFFECT = `useEffect(() => {
|
|
if (import.meta.env.DEV) {
|
|
void import("react-grab");
|
|
}
|
|
}, []);`;
|
|
const SCRIPT_IMPORT = "import Script from \"next/script\";";
|
|
//#endregion
|
|
//#region src/utils/transform.ts
|
|
const hasReactGrabInInstrumentation = (projectRoot) => {
|
|
return findFileWithReactGrabSetup(getInstrumentationFileCandidates(projectRoot)) !== null;
|
|
};
|
|
const findFileWithReactGrabSetup = (fileCandidates) => {
|
|
for (const filePath of fileCandidates) {
|
|
if (!(0, node_fs.existsSync)(filePath)) continue;
|
|
if (hasReactGrabSetupCode((0, node_fs.readFileSync)(filePath, "utf-8"))) return filePath;
|
|
}
|
|
return null;
|
|
};
|
|
const alreadyConfiguredResult = (filePath) => ({
|
|
success: true,
|
|
filePath,
|
|
message: "React Grab is already configured",
|
|
noChanges: true
|
|
});
|
|
const transformNextAppRouter = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
const layoutPath = findLayoutFile(projectRoot);
|
|
if (!layoutPath) return {
|
|
success: false,
|
|
filePath: "",
|
|
message: "Could not find app/layout.tsx, app/layout.jsx, app/layout.ts, or app/layout.js"
|
|
};
|
|
const originalContent = (0, node_fs.readFileSync)(layoutPath, "utf-8");
|
|
let newContent = originalContent;
|
|
const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
|
|
const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
|
|
if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(layoutPath);
|
|
if (hasReactGrabInFile || hasReactGrabInInstrumentationFile) return {
|
|
success: true,
|
|
filePath: layoutPath,
|
|
message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
|
|
noChanges: true
|
|
};
|
|
if (!newContent.includes("import Script from \"next/script\"")) {
|
|
const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
|
if (importMatch) newContent = newContent.replace(importMatch[0], `${importMatch[0]}\n${SCRIPT_IMPORT}`);
|
|
else newContent = `${SCRIPT_IMPORT}\n\n${newContent}`;
|
|
}
|
|
const headMatch = newContent.match(/<head[^>]*>/);
|
|
if (headMatch) newContent = newContent.replace(headMatch[0], `${headMatch[0]}\n ${NEXT_APP_ROUTER_SCRIPT}`);
|
|
else {
|
|
const htmlMatch = newContent.match(/<html[^>]*>/);
|
|
if (htmlMatch) newContent = newContent.replace(htmlMatch[0], `${htmlMatch[0]}\n <head>\n ${NEXT_APP_ROUTER_SCRIPT}\n </head>`);
|
|
}
|
|
return {
|
|
success: true,
|
|
filePath: layoutPath,
|
|
message: "Add React Grab",
|
|
originalContent,
|
|
newContent
|
|
};
|
|
};
|
|
const transformNextPagesRouter = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
const documentPath = findDocumentFile(projectRoot);
|
|
if (!documentPath) return {
|
|
success: false,
|
|
filePath: "",
|
|
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 }"
|
|
};
|
|
const originalContent = (0, node_fs.readFileSync)(documentPath, "utf-8");
|
|
let newContent = originalContent;
|
|
const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
|
|
const hasReactGrabInInstrumentationFile = hasReactGrabInInstrumentation(projectRoot);
|
|
if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(documentPath);
|
|
if (hasReactGrabInFile || hasReactGrabInInstrumentationFile) return {
|
|
success: true,
|
|
filePath: documentPath,
|
|
message: "React Grab is already installed" + (hasReactGrabInInstrumentationFile ? " in instrumentation-client" : " in this file"),
|
|
noChanges: true
|
|
};
|
|
if (!newContent.includes("import Script from \"next/script\"")) {
|
|
const importMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
|
if (importMatch) newContent = newContent.replace(importMatch[0], `${importMatch[0]}\n${SCRIPT_IMPORT}`);
|
|
}
|
|
const headMatch = newContent.match(/<Head[^>]*>/);
|
|
if (headMatch) newContent = newContent.replace(headMatch[0], `${headMatch[0]}\n ${NEXT_APP_ROUTER_SCRIPT}`);
|
|
return {
|
|
success: true,
|
|
filePath: documentPath,
|
|
message: "Add React Grab",
|
|
originalContent,
|
|
newContent
|
|
};
|
|
};
|
|
const checkExistingInstallation = (filePath, reactGrabAlreadyConfigured) => {
|
|
if (!hasReactGrabSetupCode((0, node_fs.readFileSync)(filePath, "utf-8"))) return null;
|
|
return {
|
|
success: true,
|
|
filePath,
|
|
message: reactGrabAlreadyConfigured ? "React Grab is already configured" : "React Grab is already installed in this file",
|
|
noChanges: true
|
|
};
|
|
};
|
|
const transformVite = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
const entryPath = findEntryFile(projectRoot);
|
|
const indexPath = findIndexHtml(projectRoot);
|
|
if (indexPath) {
|
|
const existingResult = checkExistingInstallation(indexPath, reactGrabAlreadyConfigured);
|
|
if (existingResult) return existingResult;
|
|
}
|
|
if (!entryPath) return {
|
|
success: false,
|
|
filePath: "",
|
|
message: "Could not find entry file (src/index.tsx, src/main.tsx, etc.)"
|
|
};
|
|
const existingResult = checkExistingInstallation(entryPath, reactGrabAlreadyConfigured);
|
|
if (existingResult) return existingResult;
|
|
const originalContent = (0, node_fs.readFileSync)(entryPath, "utf-8");
|
|
return {
|
|
success: true,
|
|
filePath: entryPath,
|
|
message: "Add React Grab",
|
|
originalContent,
|
|
newContent: `${VITE_IMPORT}\n\n${originalContent}`
|
|
};
|
|
};
|
|
const transformWebpack = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
const entryPath = findEntryFile(projectRoot);
|
|
if (!entryPath) return {
|
|
success: false,
|
|
filePath: "",
|
|
message: "Could not find entry file (src/index.tsx, src/main.tsx, etc.)"
|
|
};
|
|
const existingResult = checkExistingInstallation(entryPath, reactGrabAlreadyConfigured);
|
|
if (existingResult) return existingResult;
|
|
const originalContent = (0, node_fs.readFileSync)(entryPath, "utf-8");
|
|
return {
|
|
success: true,
|
|
filePath: entryPath,
|
|
message: "Add React Grab",
|
|
originalContent,
|
|
newContent: `${WEBPACK_IMPORT}\n\n${originalContent}`
|
|
};
|
|
};
|
|
const transformTanStack = (projectRoot, reactGrabAlreadyConfigured) => {
|
|
const rootPath = findTanStackRootFile(projectRoot);
|
|
if (!rootPath) return {
|
|
success: false,
|
|
filePath: "",
|
|
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 }, []);"
|
|
};
|
|
const originalContent = (0, node_fs.readFileSync)(rootPath, "utf-8");
|
|
let newContent = originalContent;
|
|
const hasReactGrabInFile = hasReactGrabSetupCode(originalContent);
|
|
if (hasReactGrabInFile && reactGrabAlreadyConfigured) return alreadyConfiguredResult(rootPath);
|
|
if (hasReactGrabInFile) return {
|
|
success: true,
|
|
filePath: rootPath,
|
|
message: "React Grab is already installed in this file",
|
|
noChanges: true
|
|
};
|
|
if (!/import\s+\{[^}]*useEffect[^}]*\}\s+from\s+["']react["']/.test(newContent)) {
|
|
const reactImportMatch = newContent.match(/import\s+\{([^}]*)\}\s+from\s+["']react["'];?/);
|
|
if (reactImportMatch) {
|
|
const existingImports = reactImportMatch[1];
|
|
newContent = newContent.replace(reactImportMatch[0], `import { ${existingImports.trim()}, useEffect } from "react";`);
|
|
} else {
|
|
const firstImportMatch = newContent.match(/^import .+ from ['"].+['"];?\s*$/m);
|
|
if (firstImportMatch) newContent = newContent.replace(firstImportMatch[0], `import { useEffect } from "react";\n${firstImportMatch[0]}`);
|
|
else newContent = `import { useEffect } from "react";\n\n${newContent}`;
|
|
}
|
|
}
|
|
const componentMatch = newContent.match(/function\s+(\w+)\s*\([^)]*\)\s*\{/);
|
|
if (componentMatch) {
|
|
const insertPosition = componentMatch.index + componentMatch[0].length;
|
|
newContent = newContent.slice(0, insertPosition) + `\n ${TANSTACK_EFFECT}\n` + newContent.slice(insertPosition);
|
|
} else return {
|
|
success: false,
|
|
filePath: rootPath,
|
|
message: "Could not find a component function in the root file"
|
|
};
|
|
return {
|
|
success: true,
|
|
filePath: rootPath,
|
|
message: "Add React Grab",
|
|
originalContent,
|
|
newContent
|
|
};
|
|
};
|
|
const hasFrameworkEntryPoint = (projectRoot, framework, nextRouterType) => {
|
|
switch (framework) {
|
|
case "next": return nextRouterType === "app" ? findLayoutFile(projectRoot) !== null : findDocumentFile(projectRoot) !== null;
|
|
case "vite":
|
|
case "webpack": return findEntryFile(projectRoot) !== null;
|
|
case "tanstack": return findTanStackRootFile(projectRoot) !== null;
|
|
default: return false;
|
|
}
|
|
};
|
|
const previewTransform = (projectRoot, framework, nextRouterType, reactGrabAlreadyConfigured = false) => {
|
|
switch (framework) {
|
|
case "next":
|
|
if (nextRouterType === "app") return transformNextAppRouter(projectRoot, reactGrabAlreadyConfigured);
|
|
return transformNextPagesRouter(projectRoot, reactGrabAlreadyConfigured);
|
|
case "vite": return transformVite(projectRoot, reactGrabAlreadyConfigured);
|
|
case "tanstack": return transformTanStack(projectRoot, reactGrabAlreadyConfigured);
|
|
case "webpack": return transformWebpack(projectRoot, reactGrabAlreadyConfigured);
|
|
default: return {
|
|
success: false,
|
|
filePath: "",
|
|
message: `Unknown framework: ${framework}. Please add React Grab manually.`
|
|
};
|
|
}
|
|
};
|
|
const canWriteToFile = (filePath) => {
|
|
try {
|
|
(0, node_fs.accessSync)(filePath, node_fs.constants.W_OK);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
const applyTransform = (result) => {
|
|
if (result.success && result.newContent && result.filePath) {
|
|
if (!canWriteToFile(result.filePath)) return {
|
|
success: false,
|
|
error: `Cannot write to ${result.filePath}. Check file permissions.`
|
|
};
|
|
try {
|
|
(0, node_fs.writeFileSync)(result.filePath, result.newContent);
|
|
return { success: true };
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: `Failed to write to ${result.filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
};
|
|
}
|
|
}
|
|
return { success: true };
|
|
};
|
|
const formatOptionsForNextjs = (options) => {
|
|
const parts = [];
|
|
if (options.activationKey) parts.push(`activationKey: ${JSON.stringify(options.activationKey)}`);
|
|
if (options.activationMode) parts.push(`activationMode: "${options.activationMode}"`);
|
|
if (options.keyHoldDuration !== void 0) parts.push(`keyHoldDuration: ${options.keyHoldDuration}`);
|
|
if (options.allowActivationInsideInput !== void 0) parts.push(`allowActivationInsideInput: ${options.allowActivationInsideInput}`);
|
|
if (options.maxContextLines !== void 0) parts.push(`maxContextLines: ${options.maxContextLines}`);
|
|
return `{ ${parts.join(", ")} }`;
|
|
};
|
|
const formatOptionsAsJson = (options) => {
|
|
const cleanOptions = {};
|
|
if (options.activationKey) cleanOptions.activationKey = options.activationKey;
|
|
if (options.activationMode) cleanOptions.activationMode = options.activationMode;
|
|
if (options.keyHoldDuration !== void 0) cleanOptions.keyHoldDuration = options.keyHoldDuration;
|
|
if (options.allowActivationInsideInput !== void 0) cleanOptions.allowActivationInsideInput = options.allowActivationInsideInput;
|
|
if (options.maxContextLines !== void 0) cleanOptions.maxContextLines = options.maxContextLines;
|
|
return JSON.stringify(cleanOptions);
|
|
};
|
|
const findReactGrabFile = (projectRoot, framework, nextRouterType) => {
|
|
switch (framework) {
|
|
case "next": {
|
|
const primaryFile = nextRouterType === "app" ? findLayoutFile(projectRoot) : findDocumentFile(projectRoot);
|
|
const primarySetupFile = findFileWithReactGrabSetup(nextRouterType === "app" ? getLayoutFileCandidates(projectRoot) : getDocumentFileCandidates(projectRoot));
|
|
if (primarySetupFile) return primarySetupFile;
|
|
const instrumentationFile = findFileWithReactGrabSetup(getInstrumentationFileCandidates(projectRoot));
|
|
if (instrumentationFile) return instrumentationFile;
|
|
return primaryFile;
|
|
}
|
|
case "vite": {
|
|
const entryFile = findEntryFile(projectRoot);
|
|
const entrySetupFile = findFileWithReactGrabSetup(getEntryFileCandidates(projectRoot));
|
|
if (entrySetupFile) return entrySetupFile;
|
|
const indexHtml = findFileWithReactGrabSetup(getIndexHtmlCandidates(projectRoot));
|
|
if (indexHtml) return indexHtml;
|
|
return entryFile;
|
|
}
|
|
case "tanstack": return findFileWithReactGrabSetup(getTanStackRootFileCandidates(projectRoot)) ?? findTanStackRootFile(projectRoot);
|
|
case "webpack": return findFileWithReactGrabSetup(getEntryFileCandidates(projectRoot)) ?? findEntryFile(projectRoot);
|
|
default: return null;
|
|
}
|
|
};
|
|
const addOptionsToNextScript = (originalContent, options, filePath) => {
|
|
const reactGrabScriptMatch = originalContent.match(/(<Script[\s\S]*?react-grab[\s\S]*?)\s*(\/?>)/i);
|
|
if (!reactGrabScriptMatch) return {
|
|
success: false,
|
|
filePath,
|
|
message: "Could not find React Grab Script tag"
|
|
};
|
|
const scriptTag = reactGrabScriptMatch[0];
|
|
const scriptOpening = reactGrabScriptMatch[1];
|
|
const scriptClosing = reactGrabScriptMatch[2];
|
|
const existingDataOptionsMatch = scriptTag.match(/data-options=\{JSON\.stringify\([^)]+\)\}/);
|
|
const dataOptionsAttr = `data-options={JSON.stringify(\n ${formatOptionsForNextjs(options)}\n )}`;
|
|
let newScriptTag;
|
|
if (existingDataOptionsMatch) newScriptTag = scriptTag.replace(existingDataOptionsMatch[0], dataOptionsAttr);
|
|
else newScriptTag = `${scriptOpening}\n ${dataOptionsAttr}\n ${scriptClosing}`;
|
|
return {
|
|
success: true,
|
|
filePath,
|
|
message: "Update React Grab options",
|
|
originalContent,
|
|
newContent: originalContent.replace(scriptTag, newScriptTag)
|
|
};
|
|
};
|
|
const addOptionsToDynamicImport = (originalContent, options, filePath) => {
|
|
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*\))?/);
|
|
if (!reactGrabImportWithInitMatch) return {
|
|
success: false,
|
|
filePath,
|
|
message: "Could not find React Grab import"
|
|
};
|
|
const optionsJson = formatOptionsAsJson(options);
|
|
const newImport = `${reactGrabImportWithInitMatch[1] ?? ""}import("react-grab").then((m) => m.init(${optionsJson}))`;
|
|
return {
|
|
success: true,
|
|
filePath,
|
|
message: "Update React Grab options",
|
|
originalContent,
|
|
newContent: originalContent.replace(reactGrabImportWithInitMatch[0], newImport)
|
|
};
|
|
};
|
|
const addOptionsToTanStackImport = (originalContent, options, filePath) => {
|
|
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*\))/);
|
|
if (!reactGrabImportWithInitMatch) return {
|
|
success: false,
|
|
filePath,
|
|
message: "Could not find React Grab import"
|
|
};
|
|
const optionsJson = formatOptionsAsJson(options);
|
|
const newImport = `${reactGrabImportWithInitMatch[1] ?? reactGrabImportWithInitMatch[2] ?? ""}import("react-grab/core").then(({ init }) => init(${optionsJson}))`;
|
|
return {
|
|
success: true,
|
|
filePath,
|
|
message: "Update React Grab options",
|
|
originalContent,
|
|
newContent: originalContent.replace(reactGrabImportWithInitMatch[0], newImport)
|
|
};
|
|
};
|
|
const addOptionsToAnyImport = (originalContent, options, filePath) => {
|
|
const dynamicImportResult = addOptionsToDynamicImport(originalContent, options, filePath);
|
|
if (dynamicImportResult.success) return dynamicImportResult;
|
|
return addOptionsToTanStackImport(originalContent, options, filePath);
|
|
};
|
|
const previewOptionsTransform = (projectRoot, framework, nextRouterType, options) => {
|
|
const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
|
|
if (!filePath) return {
|
|
success: false,
|
|
filePath: "",
|
|
message: "Could not find file containing React Grab configuration"
|
|
};
|
|
const originalContent = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
if (!hasReactGrabSetupCode(originalContent)) return {
|
|
success: false,
|
|
filePath,
|
|
message: "Could not find React Grab code in the file"
|
|
};
|
|
switch (framework) {
|
|
case "next":
|
|
if (isInstrumentationFile(filePath)) return addOptionsToAnyImport(originalContent, options, filePath);
|
|
return addOptionsToNextScript(originalContent, options, filePath);
|
|
case "vite": return addOptionsToDynamicImport(originalContent, options, filePath);
|
|
case "tanstack": return addOptionsToTanStackImport(originalContent, options, filePath);
|
|
case "webpack": return addOptionsToDynamicImport(originalContent, options, filePath);
|
|
default: return {
|
|
success: false,
|
|
filePath,
|
|
message: `Unknown framework: ${framework}`
|
|
};
|
|
}
|
|
};
|
|
const previewCdnTransform = (projectRoot, framework, nextRouterType, targetCdnDomain) => {
|
|
const filePath = findReactGrabFile(projectRoot, framework, nextRouterType);
|
|
if (!filePath) return {
|
|
success: false,
|
|
filePath: "",
|
|
message: "Could not find React Grab file"
|
|
};
|
|
const originalContent = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
const newContent = originalContent.replace(/(https?:)?\/\/[^/\s"']+(?=\/(?:@?react-grab))/g, `//${targetCdnDomain}`).replace(/(https?:)?\/\/[^/\s"']*react-grab[^/\s"']*\.com(?=\/script\.js)/g, `//${targetCdnDomain}`);
|
|
if (newContent === originalContent) return {
|
|
success: true,
|
|
filePath,
|
|
message: "CDN already set",
|
|
noChanges: true
|
|
};
|
|
return {
|
|
success: true,
|
|
filePath,
|
|
message: "Update CDN",
|
|
originalContent,
|
|
newContent
|
|
};
|
|
};
|
|
//#endregion
|
|
//#region src/utils/install.ts
|
|
const installPackages = async (packages, options = {}) => {
|
|
if (packages.length === 0) return;
|
|
const detectedAgent = options.packageManager ?? await detectPackageManager(options.cwd ?? process.cwd());
|
|
const args = [];
|
|
if (options.preferOffline) args.push("--prefer-offline");
|
|
if (detectedAgent === "pnpm") {
|
|
args.push("--prod=false");
|
|
if ((0, node_fs.existsSync)((0, node_path.resolve)(options.cwd ?? process.cwd(), "pnpm-workspace.yaml"))) args.push("-w");
|
|
}
|
|
if (options.additionalArgs) args.push(...options.additionalArgs);
|
|
await (0, tinyexec.x)(detectedAgent, [
|
|
detectedAgent === "npm" ? "install" : "add",
|
|
...options.isDev !== false ? ["-D"] : [],
|
|
...args,
|
|
...packages
|
|
], {
|
|
nodeOptions: {
|
|
stdio: options.silent ? "ignore" : "inherit",
|
|
cwd: options.cwd,
|
|
env: {
|
|
...process.env,
|
|
REACT_GRAB_INIT: "1"
|
|
}
|
|
},
|
|
throwOnError: true
|
|
});
|
|
};
|
|
const getPackagesToInstall = (includeReactGrab = true) => {
|
|
return includeReactGrab ? ["react-grab"] : [];
|
|
};
|
|
//#endregion
|
|
Object.defineProperty(exports, "__toESM", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return __toESM;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "agentLabel", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return agentLabel;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "applyTransform", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return applyTransform;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "detectAvailableAgents", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return detectAvailableAgents;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "detectFramework", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return detectFramework;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "detectNextRouterType", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return detectNextRouterType;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "detectPackageManager", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return detectPackageManager;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "detectProject", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return detectProject;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "detectReactGrab", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return detectReactGrab;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "detectReactGrabConfigured", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return detectReactGrabConfigured;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "detectUnsupportedFramework", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return detectUnsupportedFramework;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "findReactProjects", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return findReactProjects;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "getPackagesToInstall", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return getPackagesToInstall;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "hasFrameworkEntryPoint", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return hasFrameworkEntryPoint;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "installPackages", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return installPackages;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "installSkill", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return installSkill;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "previewCdnTransform", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return previewCdnTransform;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "previewOptionsTransform", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return previewOptionsTransform;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "previewTransform", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return previewTransform;
|
|
}
|
|
});
|
|
Object.defineProperty(exports, "removeSkill", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return removeSkill;
|
|
}
|
|
});
|