rwsdk-tools
Version:
A collection of utility tools for working with the RWSDK (Redwood SDK)
876 lines (752 loc) • 27.9 kB
JavaScript
#!/usr/bin/env node
/**
* RWSDK Tools CLI
*
* A command-line tool for installing and managing RWSDK utility tools.
*/
const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");
// Configuration
const config = {
toolsDir: path.join(__dirname, "tools"),
defaultInstallPath: process.cwd(),
};
// Ensure tools directory exists
if (!fs.existsSync(config.toolsDir)) {
fs.mkdirSync(config.toolsDir, { recursive: true });
}
/**
* Main function to process command line arguments
*/
function main() {
const args = process.argv.slice(2);
const command = args[0];
if (!command) {
// Default behavior: install all tools
installAllTools();
return;
}
// Process specific commands
switch (command) {
case "routes":
installGenerateRoutesTool();
break;
case "component":
installComponentGeneratorTool();
break;
case "tailwind":
installTailwindSetup();
break;
case "shadcn":
installShadcnSetup();
break;
case "seedtosql":
installSeedToSqlTool();
break;
case "merge":
installMergePrismaTool();
break;
case "email":
installEmailTool();
break;
case "windsurf":
installWindsurfTool();
break;
case "addon":
const addonSubcommand = args[1];
if (addonSubcommand === "generate") {
installAddonGenerateTool();
} else if (addonSubcommand === "install") {
installAddonInstallTool();
} else {
console.error(`Unknown addon subcommand: ${addonSubcommand}`);
showHelp();
}
break;
case "help":
showHelp();
break;
default:
console.error(`Unknown command: ${command}`);
showHelp();
process.exit(1);
}
}
/**
* Show help information
*/
function showHelp() {
console.log("\n\x1b[1;36mRWSDK Tools - Utility tools for Redwood SDK\x1b[0m");
console.log("\nUsage:");
console.log(" npx rwsdk-tools Install all tools");
console.log(" npx rwsdk-tools routes Install routes generator");
console.log(" npx rwsdk-tools component Install component generator");
console.log(
" npx rwsdk-tools tailwind Set up Tailwind CSS for your project"
);
console.log(
" npx rwsdk-tools shadcn Set up shadcn UI components for your project"
);
console.log(" npx rwsdk-tools seedtosql Install Seed to SQL converter");
console.log(" npx rwsdk-tools merge Install Prisma schema merger");
console.log(
" npx rwsdk-tools email Set up email functionality with Resend"
);
console.log(" npx rwsdk-tools windsurf Set up Windsurf configuration");
console.log(" npx rwsdk-tools addon generate Generate addon configuration");
console.log(" npx rwsdk-tools addon install Install RedwoodSDK addons");
console.log(" npx rwsdk-tools help Show this help message");
}
/**
* Install all available tools
*/
function installAllTools() {
console.log("\x1b[36mInstalling all GraftThis...\x1b[0m");
// Install all available tools
installGenerateRoutesTool();
installComponentGeneratorTool();
installTailwindSetup();
installShadcnSetup();
installSeedToSqlTool();
installMergePrismaTool();
installEmailTool();
installWindsurfTool();
installAddonGenerateTool();
installAddonInstallTool();
console.log("\n\x1b[32mAll tools installed successfully!\x1b[0m");
}
/**
* Install the generateRoutes tool to the current project
*/
function installGenerateRoutesTool() {
const targetPath = config.defaultInstallPath;
const toolPath = path.join(config.toolsDir, "generateRoutes");
console.log("\x1b[36mInstalling generateRoutes tool...\x1b[0m");
try {
// Create src/scripts directory if it doesn't exist
const scriptsDir = path.join(targetPath, "src", "scripts");
fs.mkdirSync(scriptsDir, { recursive: true });
// Copy generateRoutes.ts to src/scripts directory
const sourcePath = path.join(toolPath, "generateRoutes.ts");
const destPath = path.join(scriptsDir, "generateRoutes.ts");
if (!fs.existsSync(sourcePath)) {
console.error(`Error: generateRoutes.ts not found at ${sourcePath}`);
process.exit(1);
}
fs.copyFileSync(sourcePath, destPath);
console.log(`\x1b[32m✓ Copied generateRoutes.ts to ${destPath}\x1b[0m`);
// Add script to package.json
addScriptToPackageJson(
targetPath,
"routes",
"npx tsx src/scripts/generateRoutes.ts"
);
console.log("\x1b[32m✓ generateRoutes tool installed successfully!\x1b[0m");
console.log("\n\n👉 \x1b[1mNext steps:\x1b[0m");
console.log(" pnpm routes\n\n");
} catch (error) {
console.error(
`\x1b[31mError installing generateRoutes tool: ${error.message}\x1b[0m`
);
process.exit(1);
}
}
/**
* Add a script to the target project's package.json
* @param {string} projectPath - Path to the project
* @param {string} scriptName - Name of the script to add
* @param {string} scriptCommand - Command to run for the script
*/
function addScriptToPackageJson(projectPath, scriptName, scriptCommand) {
const packageJsonPath = path.join(projectPath, "package.json");
try {
if (!fs.existsSync(packageJsonPath)) {
console.error(`Error: package.json not found at ${packageJsonPath}`);
return;
}
// Read and parse the project's package.json
const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8");
const packageJson = JSON.parse(packageJsonContent);
// Initialize scripts section if it doesn't exist
if (!packageJson.scripts) {
packageJson.scripts = {};
}
// Add or update the script
packageJson.scripts[scriptName] = scriptCommand;
// Write the updated package.json back to the file
// Preserve formatting by using the same spacing as the original file
const spacing = packageJsonContent.includes(' "')
? 2
: packageJsonContent.includes(' "')
? 4
: 2;
fs.writeFileSync(
packageJsonPath,
JSON.stringify(packageJson, null, spacing)
);
console.log(
`\x1b[32m✓ Added '${scriptName}' script to package.json: '${scriptCommand}'\x1b[0m`
);
} catch (error) {
console.error(`Error adding script to package.json: ${error.message}`);
}
}
/**
* Install the component generator tool to the current project
*/
function installComponentGeneratorTool() {
const targetPath = config.defaultInstallPath;
const toolPath = path.join(config.toolsDir, "componentGenerator");
console.log("\x1b[36mInstalling component generator tool...\x1b[0m");
try {
// Check if plopfile.mjs exists in the project root
const plopfilePath = path.join(targetPath, "plopfile.mjs");
const sourcePlopfilePath = path.join(toolPath, "plopfile.mjs");
if (!fs.existsSync(sourcePlopfilePath)) {
console.error(`Error: plopfile.mjs not found at ${sourcePlopfilePath}`);
process.exit(1);
}
// Copy plopfile.mjs to project root
fs.copyFileSync(sourcePlopfilePath, plopfilePath);
console.log(`\x1b[32m\u2713 Copied plopfile.mjs to ${plopfilePath}\x1b[0m`);
// Create plop-templates directory and copy templates
const templateSourceDir = path.join(toolPath, "plop-templates");
const templateTargetDir = path.join(targetPath, "plop-templates");
if (fs.existsSync(templateSourceDir)) {
// Create the target directory if it doesn't exist
if (!fs.existsSync(templateTargetDir)) {
fs.mkdirSync(templateTargetDir, { recursive: true });
}
// Copy the component templates directory
const componentSourceDir = path.join(templateSourceDir, "components");
const componentTargetDir = path.join(templateTargetDir, "component");
if (fs.existsSync(componentSourceDir)) {
if (!fs.existsSync(componentTargetDir)) {
fs.mkdirSync(componentTargetDir, { recursive: true });
}
// Copy all template files
const templateFiles = fs.readdirSync(componentSourceDir);
templateFiles.forEach((file) => {
const sourcePath = path.join(componentSourceDir, file);
const targetPath = path.join(componentTargetDir, file);
fs.copyFileSync(sourcePath, targetPath);
console.log(
`\x1b[32m\u2713 Copied template ${file} to ${targetPath}\x1b[0m`
);
});
}
}
// Add scripts to package.json
addScriptToPackageJson(targetPath, "plop", "plop");
addScriptToPackageJson(targetPath, "component", "plop component");
addScriptToPackageJson(targetPath, "restructure", "plop restructure");
addScriptToPackageJson(
targetPath,
"restructure-all",
"plop restructure-all"
);
// Check if plop is installed and install it if needed
try {
// Check if plop is installed
const plopInstalled = checkPlopInstalled(targetPath);
if (!plopInstalled) {
console.log(
"\n\x1b[33m\u26A0\uFE0F Plop is not installed in this project. Installing plop..."
);
try {
// Run the pnpm install command to install plop
const { execSync } = require("child_process");
execSync("pnpm install -D plop", {
cwd: targetPath,
stdio: "inherit", // Show the output to the user
});
console.log("\n\x1b[32m\u2705 Plop installed successfully!\x1b[0m\n");
} catch (error) {
console.error(
`\n\x1b[31m\u274C Error installing plop: ${error.message}\x1b[0m`
);
console.log(
"\n\x1b[33m\u26A0\uFE0F Please install plop manually by running:\x1b[0m"
);
console.log("\n pnpm install -D plop\n");
}
} else {
console.log(
"\n\x1b[32m\u2713 Plop is already installed. You're all set!\x1b[0m\n"
);
}
} catch (error) {
// Ignore errors when checking for plop
console.error(`Error checking for plop: ${error.message}`);
}
console.log(
"\x1b[32m\u2713 Component generator tool installed successfully!\x1b[0m"
);
console.log("\n\n👉 \x1b[1mNext steps:\x1b[0m");
console.log(" pnpm run component\n\n");
} catch (error) {
console.error(
`\x1b[31mError installing component generator tool: ${error.message}\x1b[0m`
);
process.exit(1);
}
}
/**
* Check if plop is installed in the project
* @param {string} projectPath - Path to the project
* @returns {boolean} - Whether plop is installed
*/
function checkPlopInstalled(projectPath) {
const packageJsonPath = path.join(projectPath, "package.json");
try {
if (fs.existsSync(packageJsonPath)) {
const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8");
const packageJson = JSON.parse(packageJsonContent);
// Check if plop is in dependencies or devDependencies
const hasPlopDep =
packageJson.dependencies && packageJson.dependencies.plop;
const hasPlopDevDep =
packageJson.devDependencies && packageJson.devDependencies.plop;
return hasPlopDep || hasPlopDevDep;
}
} catch (error) {
// Ignore errors when checking for plop
}
return false;
}
/**
* Check if Tailwind CSS dependencies are installed in the project
* @param {string} projectPath - Path to the project
* @returns {boolean} - Whether Tailwind dependencies are installed
*/
function checkTailwindInstalled(projectPath) {
const packageJsonPath = path.join(projectPath, "package.json");
try {
if (fs.existsSync(packageJsonPath)) {
const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8");
const packageJson = JSON.parse(packageJsonContent);
// Check if tailwindcss and @tailwindcss/vite are in dependencies or devDependencies
const hasTailwindDep =
packageJson.dependencies && packageJson.dependencies.tailwindcss;
const hasTailwindDevDep =
packageJson.devDependencies && packageJson.devDependencies.tailwindcss;
const hasTailwindViteDep =
packageJson.dependencies &&
packageJson.dependencies["@tailwindcss/vite"];
const hasTailwindViteDevDep =
packageJson.devDependencies &&
packageJson.devDependencies["@tailwindcss/vite"];
// Return true if both packages are installed (in either dependencies or devDependencies)
return (
(hasTailwindDep || hasTailwindDevDep) &&
(hasTailwindViteDep || hasTailwindViteDevDep)
);
}
} catch (error) {
// Ignore errors when checking for tailwind
}
return false;
}
/**
* Install and set up Tailwind CSS for an RWSDK project
*/
function installTailwindSetup() {
const targetPath = config.defaultInstallPath;
console.log(
"\x1b[36mSetting up Tailwind CSS for your RWSDK project...\x1b[0m"
);
try {
// Step 1: Check if the project has the required files
const viteConfigPath = path.join(targetPath, "vite.config.mts");
const documentPath = path.join(targetPath, "src", "app", "Document.tsx");
if (!fs.existsSync(viteConfigPath)) {
console.error(
`\x1b[31mError: vite.config.mts not found at ${viteConfigPath}\x1b[0m`
);
console.error("Make sure you are in an RWSDK project directory.");
process.exit(1);
}
if (!fs.existsSync(documentPath)) {
console.error(
`\x1b[31mError: Document.tsx not found at ${documentPath}\x1b[0m`
);
console.error("Make sure you are in an RWSDK project directory.");
process.exit(1);
}
// Step 2: Create or update the styles.css file
const stylesDir = path.join(targetPath, "src", "app");
const stylesPath = path.join(stylesDir, "styles.css");
if (!fs.existsSync(stylesDir)) {
fs.mkdirSync(stylesDir, { recursive: true });
}
if (fs.existsSync(stylesPath)) {
// File exists, check if it already has the tailwind import
let stylesContent = fs.readFileSync(stylesPath, "utf8");
if (!stylesContent.includes('@import "tailwindcss"')) {
// Add the import at the top of the file
stylesContent = '@import "tailwindcss";\n' + stylesContent;
fs.writeFileSync(stylesPath, stylesContent);
console.log(
`\x1b[32m\u2713 Added Tailwind import to existing styles.css file at ${stylesPath}\x1b[0m`
);
} else {
console.log(
`\x1b[32m\u2713 Tailwind import already exists in styles.css\x1b[0m`
);
}
} else {
// File doesn't exist, create it with just the tailwind import
fs.writeFileSync(stylesPath, '@import "tailwindcss";');
console.log(
`\x1b[32m\u2713 Created styles.css file at ${stylesPath}\x1b[0m`
);
}
// Step 3: Update the vite.config.mts file
let viteConfig = fs.readFileSync(viteConfigPath, "utf8");
// Check if tailwindcss is already imported
if (!viteConfig.includes("import tailwindcss from '@tailwindcss/vite'")) {
// Add the import statement at the top of the file
viteConfig = "import tailwindcss from '@tailwindcss/vite'\n" + viteConfig;
console.log(
"\x1b[32m\u2713 Added tailwindcss import to vite.config.mts\x1b[0m"
);
}
// Check if the environments config exists
if (!viteConfig.includes("environments:")) {
// Add the environments config
viteConfig = viteConfig.replace(
"export default defineConfig({",
"export default defineConfig({\n environments: {\n ssr: {},\n },"
);
console.log(
"\x1b[32m\u2713 Added environments config to vite.config.mts\x1b[0m"
);
}
// Check if tailwindcss is already in the plugins array
if (!viteConfig.includes("tailwindcss()")) {
// Add tailwindcss to the plugins array
viteConfig = viteConfig.replace(
/plugins:\s*\[([^\]]*)\]/,
(match, plugins) => {
if (plugins.trim().endsWith(",")) {
return `plugins: [${plugins} tailwindcss()]`;
} else if (plugins.trim()) {
return `plugins: [${plugins}, tailwindcss()]`;
} else {
return `plugins: [tailwindcss()]`;
}
}
);
console.log(
"\x1b[32m\u2713 Added tailwindcss to plugins array in vite.config.mts\x1b[0m"
);
}
// Write the updated vite.config.mts file
fs.writeFileSync(viteConfigPath, viteConfig);
// Step 4: Update the Document.tsx file
let documentContent = fs.readFileSync(documentPath, "utf8");
// Check if styles are already imported
if (!documentContent.includes("import styles from './styles.css?url'")) {
// Always add the import statement at the very top of the file
documentContent = `import styles from './styles.css?url';\n\n${documentContent}`;
console.log("\x1b[32m\u2713 Added styles import to Document.tsx\x1b[0m");
// Double-check that the import was added
if (!documentContent.includes("import styles from './styles.css?url'")) {
console.log(
"\x1b[33m\u26A0\uFE0F Warning: Import may not have been added correctly. Trying alternative method...\x1b[0m"
);
// Try a more direct approach by splitting into lines
const lines = documentContent.split("\n");
lines.unshift(`import styles from './styles.css?url';`);
documentContent = lines.join("\n");
console.log(
"\x1b[32m\u2713 Added styles import using alternative method\x1b[0m"
);
}
}
// Check if the link tag is already in the head
if (!documentContent.includes('<link rel="stylesheet" href={styles}')) {
// Add the link tag to the head
documentContent = documentContent.replace(
/<head>(\s*)/,
'<head>$1<link rel="stylesheet" href={styles} />$1'
);
console.log(
"\x1b[32m\u2713 Added stylesheet link to Document.tsx\x1b[0m"
);
}
// Write the updated Document.tsx file
fs.writeFileSync(documentPath, documentContent);
// Step 5: Check if dependencies are installed and install them if needed
const tailwindInstalled = checkTailwindInstalled(targetPath);
console.log("\n\x1b[32m\u2713 Tailwind CSS setup complete!\x1b[0m");
if (!tailwindInstalled) {
console.log(
"\n\x1b[33m\u26A0\uFE0F Installing required dependencies...\x1b[0m"
);
try {
// Run the pnpm install command as regular dependencies (not dev dependencies)
const { execSync } = require("child_process");
execSync("pnpm install tailwindcss @tailwindcss/vite", {
cwd: targetPath,
stdio: "inherit", // Show the output to the user
});
console.log(
"\n\x1b[32m\u2705 Tailwind dependencies installed successfully!\x1b[0m\n"
);
} catch (error) {
console.error(
`\n\x1b[31m\u274C Error installing dependencies: ${error.message}\x1b[0m`
);
console.log(
"\n\x1b[33m\u26A0\uFE0F Please install the dependencies manually by running:\x1b[0m"
);
console.log("\n pnpm install tailwindcss @tailwindcss/vite\n");
}
} else {
console.log(
"\n\x1b[32m\u2705 Tailwind dependencies are already installed. You're all set!\x1b[0m\n"
);
}
} catch (error) {
console.error(
`\x1b[31mError setting up Tailwind CSS: ${error.message}\x1b[0m`
);
process.exit(1);
}
}
/**
* Run the shadcn setup directly on the current project
*/
function installShadcnSetup() {
const targetPath = config.defaultInstallPath;
const toolPath = path.join(config.toolsDir, "shadcnSetup");
console.log("\x1b[36mSetting up shadcn for your project...\x1b[0m");
try {
// Require the shadcnSetup module
const shadcnSetup = require(toolPath);
// Run the setup directly
shadcnSetup.install();
} catch (error) {
console.error(`\x1b[31mError setting up shadcn: ${error.message}\x1b[0m`);
process.exit(1);
}
}
/**
* Install the Seed to SQL converter tool to the current project
*/
function installSeedToSqlTool() {
const targetPath = config.defaultInstallPath;
const toolPath = path.join(config.toolsDir, "seedToSql");
console.log("\x1b[36mInstalling Seed to SQL converter tool...\x1b[0m");
try {
// Create src/scripts directory if it doesn't exist
const scriptsDir = path.join(targetPath, "src", "scripts");
fs.mkdirSync(scriptsDir, { recursive: true });
// Copy seedToSql.mjs to src/scripts directory
const sourcePath = path.join(toolPath, "seedToSql.mjs");
const destPath = path.join(scriptsDir, "seedToSql.mjs");
if (!fs.existsSync(sourcePath)) {
console.error(
`\x1b[31mError: seedToSql.mjs not found at ${sourcePath}\x1b[0m`
);
process.exit(1);
}
fs.copyFileSync(sourcePath, destPath);
console.log(`\x1b[32m\u2713 Copied seedToSql.mjs to ${destPath}\x1b[0m`);
// Make the script executable
try {
fs.chmodSync(destPath, "755");
console.log(`\x1b[32m\u2713 Made seedToSql.mjs executable\x1b[0m`);
} catch (error) {
console.warn(
`\x1b[33mWarning: Could not make seedToSql.mjs executable: ${error.message}\x1b[0m`
);
}
// Add script to package.json
addScriptToPackageJson(
targetPath,
"seedtosql",
"node src/scripts/seedToSql.mjs"
);
console.log(
"\x1b[32m\u2713 Seed to SQL converter tool installed successfully!\x1b[0m"
);
console.log("\n\n👉 \x1b[1mNext Steps:\x1b[0m");
console.log(" pnpm run seedtosql\n\n");
} catch (error) {
console.error(
`\x1b[31mError installing Seed to SQL converter tool: ${error.message}\x1b[0m`
);
process.exit(1);
}
}
/**
* Install the Prisma schema merger tool to the current project
*/
function installMergePrismaTool() {
console.log("\x1b[36mInstalling Prisma schema merger tool...\x1b[0m");
const projectPath = config.defaultInstallPath;
const scriptsDir = path.join(projectPath, "src", "scripts");
const sourceFile = path.join(
__dirname,
"tools",
"mergePrisma",
"mergePrismaSchema.mjs"
);
const targetFile = path.join(scriptsDir, "mergePrismaSchema.mjs");
try {
// Create scripts directory if it doesn't exist
if (!fs.existsSync(scriptsDir)) {
fs.mkdirSync(scriptsDir, { recursive: true });
console.log(`\x1b[32m✓ Created scripts directory: ${scriptsDir}\x1b[0m`);
}
// Copy the mergePrismaSchema.mjs file to the scripts directory
fs.copyFileSync(sourceFile, targetFile);
console.log(
`\x1b[32m✓ Copied mergePrismaSchema.mjs to ${targetFile}\x1b[0m`
);
// Add the script to package.json
addScriptToPackageJson(
projectPath,
"merge",
"node src/scripts/mergePrismaSchema.mjs"
);
console.log(
"\n\x1b[32mPrisma schema merger tool installed successfully!\x1b[0m"
);
console.log("\n\n👉 \x1b[1mNext steps:\x1b[0m");
console.log(" pnpm merge\n\n");
} catch (error) {
console.error(
`Error installing Prisma schema merger tool: ${error.message}`
);
}
}
/**
* Install the email tool and set up email functionality with Resend
*/
function installEmailTool() {
const toolPath = path.join(config.toolsDir, "email");
const emailScriptPath = path.join(toolPath, "index.js");
console.log("\x1b[36mSetting up email functionality...\x1b[0m");
try {
// Make sure the email script is executable
fs.chmodSync(emailScriptPath, "755");
// Execute the email setup script
execSync(`node ${emailScriptPath}`, { stdio: "inherit" });
console.log("\x1b[32m✓ Email functionality set up successfully!\x1b[0m");
} catch (error) {
console.error(
`\x1b[31mError setting up email functionality: ${error.message}\x1b[0m`
);
process.exit(1);
}
}
/**
* Install the windsurf tool and set up windsurf configuration
*/
function installWindsurfTool() {
const toolPath = path.join(config.toolsDir, "windsurf");
const windsurfScriptPath = path.join(toolPath, "index.js");
console.log("\x1b[36mSetting up Windsurf configuration...\x1b[0m");
try {
// Make sure the windsurf script is executable
fs.chmodSync(windsurfScriptPath, "755");
// Execute the windsurf setup script
execSync(`node ${windsurfScriptPath}`, { stdio: "inherit" });
console.log("\x1b[32m✓ Windsurf configuration set up successfully!\x1b[0m");
} catch (error) {
console.error(
`\x1b[31mError setting up Windsurf configuration: ${error.message}\x1b[0m`
);
process.exit(1);
}
}
/**
* Install the addonGenerate tool to the current project
*/
function installAddonGenerateTool() {
const targetPath = config.defaultInstallPath;
const toolPath = path.join(config.toolsDir, "addonGenerate");
console.log("\x1b[36mInstalling addonGenerate tool...\x1b[0m");
try {
// Create src/scripts directory if it doesn't exist
const scriptsDir = path.join(targetPath, "src", "scripts");
fs.mkdirSync(scriptsDir, { recursive: true });
// Copy generateAddonConfig.mjs to src/scripts directory
const sourcePath = path.join(toolPath, "generateAddonConfig.mjs");
const destPath = path.join(scriptsDir, "generateAddonConfig.mjs");
if (!fs.existsSync(sourcePath)) {
console.error(
`Error: generateAddonConfig.mjs not found at ${sourcePath}`
);
process.exit(1);
}
fs.copyFileSync(sourcePath, destPath);
console.log(
`\x1b[32m✓ Copied generateAddonConfig.mjs to ${destPath}\x1b[0m`
);
// Add script to package.json
addScriptToPackageJson(
targetPath,
"addon:generate",
"node src/scripts/generateAddonConfig.mjs"
);
console.log("\x1b[32m✓ addonGenerate tool installed successfully!\x1b[0m");
console.log("\n\n👉 \x1b[1mNext steps:\x1b[0m");
console.log(" pnpm addon:generate <addonName>\n\n");
} catch (error) {
console.error(
`\x1b[31mError installing addonGenerate tool: ${error.message}\x1b[0m`
);
process.exit(1);
}
}
/**
* Install the addonInstall tool to the current project
*/
function installAddonInstallTool() {
const targetPath = config.defaultInstallPath;
const toolPath = path.join(config.toolsDir, "addonInstall");
console.log("\x1b[36mInstalling addonInstall tool...\x1b[0m");
try {
// Create src/scripts directory if it doesn't exist
const scriptsDir = path.join(targetPath, "src", "scripts");
fs.mkdirSync(scriptsDir, { recursive: true });
// Copy installAddon.mjs to src/scripts directory
const sourcePath = path.join(toolPath, "installAddon.mjs");
const destPath = path.join(scriptsDir, "installAddon.mjs");
if (!fs.existsSync(sourcePath)) {
console.error(
`Error: installAddon.mjs not found at ${sourcePath}`
);
process.exit(1);
}
fs.copyFileSync(sourcePath, destPath);
console.log(
`\x1b[32m✓ Copied installAddon.mjs to ${destPath}\x1b[0m`
);
// Add script to package.json
addScriptToPackageJson(
targetPath,
"addon:install",
"node src/scripts/installAddon.mjs"
);
console.log("\x1b[32m✓ addonInstall tool installed successfully!\x1b[0m");
console.log("\n\n👉 \x1b[1mNext steps:\x1b[0m");
console.log(" pnpm addon:install install <addonName> [options]\n\n");
console.log(" Options:\n");
console.log(" --repo <url> Install from a GitHub repository");
console.log(" --source <path> Full path to the addon directory");
console.log(" --dest <path> Destination directory (defaults to src/app/addons)\n");
} catch (error) {
console.error(
`\x1b[31mError installing addonInstall tool: ${error.message}\x1b[0m`
);
process.exit(1);
}
}
// Start the CLI
main();