UNPKG

wp-plugin-init

Version:

CLI to scaffold a PHP plugin boilerplate structure

155 lines (122 loc) 6.4 kB
#!/usr/bin/env node import fs from 'fs-extra'; import path from 'path'; import chalk from 'chalk'; import { fileURLToPath } from 'url'; import { glob } from 'glob'; import { exec } from 'child_process'; import util from 'util'; const execPromise = util.promisify(exec); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const pluginName = process.argv[2]; if (!pluginName) { console.log(chalk.red("❌ Please provide a plugin name.")); process.exit(1); } const readablePluginName = pluginName .split(/[-_]/) .map(w => w.charAt(0).toUpperCase() + w.slice(1)) .join(' '); // e.g., "Test One" const classPrefix = readablePluginName.replace(/\s+/g, ''); // e.g., "TestOne" const constantPrefix = pluginName.toUpperCase().replace(/[-\s]/g, '_'); // e.g., TEST_ONE const targetDir = path.join(process.cwd(), pluginName); const templateDir = path.join(__dirname, 'template'); (async () => { try { if (await fs.pathExists(targetDir)) { console.log(chalk.red(`❌ Folder '${pluginName}' already exists.`)); return; } await fs.copy(templateDir, targetDir); // Rename main plugin file const oldFile = path.join(targetDir, 'wp-plugins-boilerplate.php'); const newFile = path.join(targetDir, `${pluginName}.php`); if (await fs.pathExists(oldFile)) { await fs.rename(oldFile, newFile); console.log(chalk.blue(`✅ plugin file name updated with plugin name.`)); } // Update contents in main plugin file let content = await fs.readFile(newFile, 'utf-8'); content = content .replace(/__PLUGIN_NAME__/g, pluginName) .replace(/__PLUGIN_TITLE__/g, readablePluginName); // ✅ added replacement await fs.writeFile(newFile, content); // Update README.md const readmePath = path.join(targetDir, 'README.md'); if (await fs.pathExists(readmePath)) { let readme = await fs.readFile(readmePath, 'utf-8'); readme = readme .replace(/__PLUGIN_NAME__/g, pluginName) .replace(/__PLUGIN_TITLE__/g, readablePluginName); await fs.writeFile(readmePath, readme); console.log(chalk.blue(`✅ readme.md updated with plugin name.`)); } // Update package.json const pkgPath = path.join(targetDir, 'package.json'); if (await fs.pathExists(pkgPath)) { const pkg = await fs.readJson(pkgPath); pkg.name = pluginName; pkg.description = `Boilerplate for ${pluginName}`; await fs.writeJson(pkgPath, pkg, { spaces: 2 }); console.log(chalk.blue(`✅ package.json updated with plugin name.`)); } // Update composer.json const composerPath = path.join(targetDir, 'composer.json'); if (await fs.pathExists(composerPath)) { const composer = await fs.readJson(composerPath); composer.name = `your-vendor/${pluginName}`; composer.description = `Boilerplate for ${pluginName}`; if (composer.autoload?.['psr-4']) { delete composer.autoload['psr-4']['PluginsNameSpaces\\']; composer.autoload['psr-4'][`${classPrefix}\\`] = 'app/'; } await fs.writeJson(composerPath, composer, { spaces: 2 }); console.log(chalk.blue(`✅ composer.json namespace set to "${classPrefix}\\\\"`)); try { await execPromise('composer dump-autoload', { cwd: targetDir }); console.log(chalk.green('✅ composer dump-autoload ran successfully.')); } catch (error) { console.error(chalk.red('❌ Error running composer dump-autoload:', error)); } } // Replace namespaces, class names, constants in all PHP files const phpFiles = glob.sync(path.join(targetDir, '**/*.php')); const menuSlug = `${pluginName}-dashboard-menu`; const subMenuSlug = `${pluginName}-dashboard-submenu`; for (const file of phpFiles) { let content = await fs.readFile(file, 'utf8'); // 🆕 Menu and submenu slugs and labels content = content.replace(/__\('Plugin Name', 'text-domain'\)/g, `__('${readablePluginName}', '${pluginName}')`); content = content.replace(/'plugin-dashboard-menu'/g, `'${menuSlug}'`); content = content.replace(/"plugin-dashboard-menu"/g, `"${menuSlug}"`); content = content.replace(/"plugin-dashboard-submenu"/g, `"${subMenuSlug}"`); content = content.replace(/'plugin-dashboard-submenu'/g, `'${subMenuSlug}'`); // Namespace and class references content = content.replace(/namespace\s+PluginsNameSpaces/g, `namespace ${classPrefix}`); content = content.replace(/use\s+PluginsNameSpaces\\/g, `use ${classPrefix}\\`); content = content.replace(/(['"])PluginsNameSpaces\\/g, `$1${classPrefix}\\`); content = content.replace(/new\s+PluginsNameSpaces\\/g, `new ${classPrefix}\\`); content = content.replace(/PluginsNameSpaces\\/g, `${classPrefix}\\`); // Constant replacement content = content.replace(/PLUGINS_NAMES_MIGRATOR/g, constantPrefix); await fs.writeFile(file, content); } console.log(chalk.blue(`✅ Updated namespace and constants`)); console.log(chalk.green(`✅ Plugin '${pluginName}' created successfully!`)); // Build the plugin automatically try { console.log(chalk.yellow('📦 Installing NPM packages...')); await execPromise('npm install', { cwd: targetDir }); // console.log(chalk.yellow('🚧 Running development build...')); // await execPromise('npm run dev', { cwd: targetDir }); console.log(chalk.yellow('🏗️ Running production build...')); await execPromise('npm run build', { cwd: targetDir }); console.log(chalk.green('✅ Build completed! Your plugin is ready.')); } catch (err) { console.error(chalk.red('❌ Failed to run build process:'), err.stderr || err); } } catch (err) { console.error(chalk.red("❌ Error creating plugin:"), err); } })();