UNPKG

km-web-plugin

Version:

ICE Web Plugin Initializer

335 lines (283 loc) 9.48 kB
#!/usr/bin/env node import { fileURLToPath } from "url"; import { dirname, join } from "path"; import inquirer from "inquirer"; import chalk from "chalk"; import ora from "ora"; import fs from "fs-extra"; import { execSync } from "child_process"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); console.log( chalk.cyan(` ╔═══════════════════════════════════════╗ ║ ICE Web Plugin Generator ║ ║ Create Vue.js plugins for ║ ║ Encompass ║ ╚═══════════════════════════════════════╝ `), ); function toPascalCase(str) { return str .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => { return index === 0 ? word.toUpperCase() : word.toUpperCase(); }) .replace(/\s+/g, ""); } function toKebabCase(str) { return str .replace(/([a-z])([A-Z])/g, "$1-$2") .replace(/[\s_]+/g, "-") .toLowerCase(); } function toCamelCase(str) { const pascal = toPascalCase(str); return pascal.charAt(0).toLowerCase() + pascal.slice(1); } function sanitizeProjectName(name) { return toKebabCase(name).replace(/[^a-z0-9-]/g, ""); } async function promptUser() { const answers = await inquirer.prompt([ { type: "input", name: "projectName", message: "Project name:", default: "my-web-plugin", validate: (input) => { if (!input.trim()) return "Project name is required"; if (!/^[a-zA-Z0-9-_\s]+$/.test(input)) { return "Project name can only contain letters, numbers, spaces, hyphens, and underscores"; } return true; }, }, { type: "input", name: "appName", message: "Application display name:", default: "My Plugin", validate: (input) => { if (!input.trim()) return "Application name is required"; return true; }, }, { type: "confirm", name: "includePopup", message: "Include Popup support?", default: false, }, { type: "confirm", name: "includeGlobalTool", message: "Include Global Tool support?", default: false, }, { type: "confirm", name: "installDeps", message: "Install dependencies?", default: true, }, ]); answers.projectNameSanitized = sanitizeProjectName(answers.projectName); answers.projectPath = join(process.cwd(), answers.projectNameSanitized); answers.projectNameKebab = toKebabCase(answers.projectName); answers.projectNameCamel = toCamelCase(answers.projectName); answers.projectNamePascal = toPascalCase(answers.projectName); answers.appNameNoSpaces = answers.appName.replace(/\s+/g, ""); answers.appNameKebab = toKebabCase(answers.appName); answers.appNameCamel = toCamelCase(answers.appName); answers.appNamePascal = toPascalCase(answers.appName); answers.includePlugin = true; answers.initGit = false; answers.includeExampleForm = false; return answers; } async function createProject(options) { const spinner = ora("Creating project structure...").start(); try { await fs.ensureDir(options.projectPath); const baseTemplatePath = join(__dirname, "..", "templates", "base"); await copyTemplateFiles(baseTemplatePath, options.projectPath, options); await fs.ensureDir(join(options.projectPath, "server")); await fs.ensureDir(join(options.projectPath, "src", "stores")); await fs.writeFile( join(options.projectPath, "src", "lib", "formCode.js"), "// Form code will go here\n", ); if (options.includePopup) { const popupTemplatePath = join(__dirname, "..", "templates", "popup"); await copyPluginSpecificFiles( popupTemplatePath, options.projectPath, options, "Popup", ); } if (options.includeGlobalTool) { const globalToolTemplatePath = join( __dirname, "..", "templates", "globaltool", ); await copyPluginSpecificFiles( globalToolTemplatePath, options.projectPath, options, "GlobalTool", ); } if (!options.includeExampleForm) { await removeExampleFiles(options.projectPath); } spinner.succeed("Project structure created!"); // Initialize git (maybe used in future) if (options.initGit) { spinner.start("Initializing git repository..."); execSync("git init", { cwd: options.projectPath, stdio: "ignore" }); spinner.succeed("Git repository initialized!"); } // Install dependencies if (options.installDeps) { spinner.start("Installing dependencies..."); execSync("npm install", { cwd: options.projectPath, stdio: "inherit" }); spinner.succeed("Dependencies installed!"); } console.log(chalk.green("\n✨ Project created successfully!")); console.log(chalk.cyan(`\nNext steps:`)); console.log(` ${chalk.gray("$")} cd ${options.projectNameSanitized}`); if (!options.installDeps) { console.log(` ${chalk.gray("$")} npm install`); } console.log(` ${chalk.gray("$")} npm run dev`); console.log(chalk.cyan(`\nAvailable scripts:`)); console.log( ` ${chalk.gray("npm run dev")} - Start development server`, ); console.log( ` ${chalk.gray("npm run dev-plugin")} - Start plugin development`, ); console.log(` ${chalk.gray("npm run build-plugin")} - Build plugin`); if (options.includePopup) { console.log( ` ${chalk.gray("npm run dev-popup")} - Start popup development`, ); console.log(` ${chalk.gray("npm run build-popup")} - Build popup`); } if (options.includeGlobalTool) { console.log( ` ${chalk.gray("npm run dev-globaltool")} - Start global tool development`, ); console.log( ` ${chalk.gray("npm run build-globaltool")} - Build global tool`, ); } } catch (error) { spinner.fail("Failed to create project"); console.error(chalk.red(error.message)); process.exit(1); } } async function copyTemplateFiles(src, dest, options, merge = false) { const templateEngine = await import("./template-engine.js"); if (!(await fs.pathExists(src))) { return; } const files = await fs.readdir(src); for (const file of files) { const srcPath = join(src, file); const processedFilename = templateEngine.processFilename(file, options); const destPath = join(dest, processedFilename); const stat = await fs.stat(srcPath); if (stat.isDirectory()) { await fs.ensureDir(destPath); await copyTemplateFiles(srcPath, destPath, options, merge); } else { const content = await fs.readFile(srcPath, "utf-8"); let processed = templateEngine.processTemplate(content, options); processed = templateEngine.processSpecialFiles( srcPath, processed, options, ); if (!(await fs.pathExists(destPath)) || merge) { await fs.writeFile(destPath, processed); } } } } async function copyPluginSpecificFiles(src, dest, options, pluginType) { const templateEngine = await import("./template-engine.js"); if (!(await fs.pathExists(src))) { return; } const files = await fs.readdir(src); for (const file of files) { const srcPath = join(src, file); const stat = await fs.stat(srcPath); const processedFilename = templateEngine.processFilename(file, options); let destPath; if (file.includes("vite") && file.endsWith(".ts")) { destPath = join(dest, processedFilename); } else { const libPath = join(dest, "src", "lib", pluginType); await fs.ensureDir(libPath); destPath = join(libPath, processedFilename); } if (stat.isDirectory()) { await fs.ensureDir(destPath); await copyTemplateFiles(srcPath, destPath, options, true); } else { const content = await fs.readFile(srcPath, "utf-8"); let processed = templateEngine.processTemplate(content, options); processed = templateEngine.processSpecialFiles( srcPath, processed, options, ); await fs.writeFile(destPath, processed); } } } async function removeExampleFiles(projectPath) { const filesToRemove = [ "src/components/ExampleForm.vue", "src/composables/useExample.ts", "src/services/ExampleService.ts", "src/types/example.ts", ]; for (const file of filesToRemove) { const filePath = join(projectPath, file); if (await fs.pathExists(filePath)) { await fs.remove(filePath); } } } async function main() { try { const options = await promptUser(); if (await fs.pathExists(options.projectPath)) { const { overwrite } = await inquirer.prompt([ { type: "confirm", name: "overwrite", message: `Directory ${options.projectNameSanitized} already exists. Overwrite?`, default: false, }, ]); if (!overwrite) { console.log(chalk.yellow("Operation cancelled.")); process.exit(0); } await fs.remove(options.projectPath); } await createProject(options); } catch (error) { console.error(chalk.red("Error:"), error.message); process.exit(1); } } main();