UNPKG

@nomadmystic/wordpress-scaffold-cli

Version:

This project is created to speed up WordPress development

355 lines (298 loc) 13.1 kB
#!/usr/bin/env node // Community modules import 'dotenv/config'; import fse from "fs-extra"; // Package modules // Classes import InquirerCli from '../cli/inquirer-cli.js'; import AbstractScaffold from '../abstract/AbstractScaffold.js'; import updateInternalJson, { ProjectJson } from '../scaffold/common/update-internal-json.js'; // Utils import PathUtils from '../utils/path-utils.js'; import StringUtils from '../utils/string-utils.js'; // Interfaces import PluginAnswers from '../interfaces/plugin/interface-plugin-anwsers.js'; import PluginAnswerValues from '../interfaces/plugin/interface-plugin-answer-values.js'; import PluginConfig from '../interfaces/plugin/interface-plugin-config.js'; import ScaffoldCopyFolders from '../interfaces/common/interface-scaffold-copy-folders.js'; import ScaffoldJsonUpdates from '../interfaces/common/interface-scaffold-json-updates.js'; // Functions import getPluginOptions from '../config/plugin-options.js'; import UpdateTypeFiles from '../scaffold/common/update-type-files.js'; import MessagingUtils from '../utils/messaging-utils.js'; import CreateObjectArrays from '../scaffold/common/create-object-arrays.js'; import DebugUtils from "../utils/debug-utils.js"; /** * @classdesc Scaffold a new theme based on user's inputs * @class ScaffoldTheme * @extends AbstractScaffold * @author Keith Murphy | nomadmystics@gmail.com */ class ScaffoldPlugin extends AbstractScaffold { /** * @type boolean * @private */ private static isDebugFullMode: boolean = false; /** * @type boolean * @private */ private static composerAlreadyExists: boolean | any = false; /** * @type boolean * @private */ private static packageAlreadyExists: boolean | any = false; /** * @type PluginAnswerValues * @private */ private static pluginValues: PluginAnswerValues; /** * {@inheritDoc AbstractScaffold} */ public static initializeScaffolding = async (): Promise<void> => { try { // Bail early await PathUtils.checkForWordPressInstall(); // Check for debug mode values this.isDebugFullMode = await DebugUtils.isDebugFullMode(); // Get our answers const answers: PluginAnswers | void = await InquirerCli.performPromptsTasks(await getPluginOptions()).catch((err) => console.error(err)); // Start the scaffolding process await this.scaffoldFiles(answers); } catch (err: any) { console.log('ScaffoldTheme.performScaffolding()'); console.error(err); } }; /** * {@inheritDoc AbstractScaffold} */ protected static scaffoldFiles = async (answers: PluginAnswers | any): Promise<void> => { try { // Build the values we need this.pluginValues = await this.buildValueObject(answers); if (this.isDebugFullMode) { console.log(this.pluginValues); } // Validate we aren't overwriting another plugin with the same name await PathUtils.validateIsPathWithDisplay(this.pluginValues.finalPath, 'There is already a plugin with that name. Please use another name.', true); // Update the internal JSON files let pluginConfig = await this.updateProjectConfig(this.pluginValues); // Update the individual files we need to scaffold await this.performScaffold(this.pluginValues, pluginConfig); await MessagingUtils.displayEndingMessages(this.pluginValues, this.composerAlreadyExists, this.packageAlreadyExists); } catch (err: any) { console.log('ScaffoldTheme.scaffoldFiles()'); console.error(err); } }; /** * @description Build an object of our needed values for scaffolding the plugin * @private * @author Keith Murphy | nomadmystics@gmail.com * @todo refactor into Abstract class method in theme and project * * @param {PluginAnswers | any} answers * @return {Promise<PluginAnswerValues | any>} */ private static buildValueObject = async (answers: PluginAnswers | any): Promise<PluginAnswerValues | any> => { try { // Absolute path of the themes folder const pluginsPath: string | undefined = await PathUtils.getPluginsFolderPath(); // User inputs const projectName: string = answers.projectName ? answers.projectName : ''; const name: string = answers.name ? answers.name.trim() : ''; const description: string = answers.description ? answers.description.trim() : ''; const frontEndFramework: string = answers.frontEndFramework ? answers.frontEndFramework : ''; const siteUrl: string = answers.siteUrl; const devSiteUrl: string = answers.devSiteUrl; // Make folder "safe" if there are spaces const safeName: string = await StringUtils.addDashesToString(name); // Create the finalized path for the scaffolded theme const finalPluginPath: string = `${pluginsPath}/${safeName}`; // Create our string modification const capAndSnakeCasePlugin: string = await StringUtils.capAndSnakeCaseString(safeName); const pluginNamespace: string = await StringUtils.pascalCaseString(safeName); return { type: 'plugin', projectName: projectName, name: name, safeName: safeName, pluginsPath: pluginsPath, finalPath: finalPluginPath, description: description, frontEndFramework: frontEndFramework, siteUrl: siteUrl, devSiteUrl: devSiteUrl, capAndSnakeCasePlugin: capAndSnakeCasePlugin, namespace: pluginNamespace, }; } catch (err: any) { console.log('ScaffoldTheme.buildValueObject()'); console.error(err); } }; /** * @description Update our project config object based on user inputs * @public * @author Keith Murphy | nomadmystics@gmail.com * @todo refactor into Abstract class method in theme and project * * @param {PluginAnswerValues} pluginValues * @return {Promise<PluginConfig | any>} */ private static updateProjectConfig = async (pluginValues: PluginAnswerValues): Promise<PluginConfig | any> => { try { let { projectName, name, finalPath, description, frontEndFramework, } = pluginValues; let configUpdates: PluginConfig = { 'plugin-name': name, 'plugin-path': finalPath, 'plugin-description': description, 'plugin-front-end-framework': frontEndFramework, }; if (projectName && typeof projectName !== 'undefined') { configUpdates['project-name'] = projectName; configUpdates['project-namespace'] = await StringUtils.pascalCaseString(projectName); } // Update our config before we scaffold plugin, so we can use it in our scaffold functions configUpdates = await ProjectJson.update(configUpdates, 'plugin'); return configUpdates; } catch (err: any) { console.log('ScaffoldPlugin.updateProjectConfig()'); console.error(err); } }; /** * @description * @private * @author Keith Murphy | nomadmystics@gmail.com * @todo refactor into Abstract class method in theme and project * * @param {PluginAnswerValues} pluginValues * @param {PluginConfig} pluginConfig * @return {Promise<void>} */ private static performScaffold = async (pluginValues: PluginAnswerValues, pluginConfig: PluginConfig): Promise<void> => { try { const updateObjectsArray: Array<ScaffoldJsonUpdates> = await this.buildUpdateObjectArray(pluginValues); const foldersToCopy: Array<ScaffoldCopyFolders> = await this.buildFoldersToCopy(pluginValues); await UpdateTypeFiles.copyFiles(foldersToCopy); await UpdateTypeFiles.updateFiles(pluginValues, updateObjectsArray); await UpdateTypeFiles.updateClassFiles(pluginValues); await UpdateTypeFiles.updateWebpack(pluginValues, 'plugin'); } catch (err: any) { console.log('ScaffoldPlugin.performScaffold()'); console.error(err); } }; /** * @description * @private * @author Keith Murphy | nomadmystics@gmail.com * * @param {PluginAnswerValues} pluginValues * @return {Promise<Array<ScaffoldCopyFolders> | any>} */ private static buildFoldersToCopy = async (pluginValues: PluginAnswerValues): Promise<Array<ScaffoldCopyFolders> | any> => { try { const foldersToCopy: Array<ScaffoldCopyFolders> = [ { source: 'scaffolding/plugin', destination: `${pluginValues.finalPath}`, }, { source: 'scaffolding/common/classes', destination: `${pluginValues.finalPath}/classes`, }, { source: `scaffolding/common/front-end-scaffolding/${pluginValues.frontEndFramework?.toLowerCase()}/js`, destination: `${pluginValues.finalPath}/src/js`, }, { source: `scaffolding/common/front-end-scaffolding/${pluginValues.frontEndFramework?.toLowerCase()}/scss`, destination: `${pluginValues.finalPath}/src/scss`, }, { source: `scaffolding/common/front-end-scaffolding/${pluginValues.frontEndFramework?.toLowerCase()}/project-root`, destination: `${pluginValues.finalPath}`, }, { source: 'scaffolding/common/project-root', destination: `${pluginValues.finalPath}`, }, ]; return foldersToCopy; } catch (err: any) { console.log('ScaffoldPlugin.buildFoldersToCopy()'); console.error(err); } }; /** * @description * @private * @author Keith Murphy | nomadmystics@gmail.com * * @param {PluginAnswerValues} pluginValues * @return {Promise<Array<ScaffoldJsonUpdates> | any>} */ private static buildUpdateObjectArray = async (pluginValues: PluginAnswerValues): Promise<Array<ScaffoldJsonUpdates> | any> => { try { const updateObjectsArray: Array<ScaffoldJsonUpdates> = [ { fileName: 'plugin-name.php', stringToUpdate: 'SCAFFOLD_NAME', updateString: pluginValues.name, }, { fileName: 'plugin-name.php', stringToUpdate: 'SCAFFOLD_DESCRIPTION', updateString: pluginValues.description, }, { fileName: 'plugin-name.php', stringToUpdate: 'SCAFFOLD_DESCRIPTION', updateString: pluginValues.description, }, { fileName: 'plugin-name.php', stringToUpdate: 'CAPS_AND_SNAKE_NAME', updateString: pluginValues.capAndSnakeCasePlugin, }, { fileName: 'webpack.config.js', stringToUpdate: 'SCAFFOLD_NAME', updateString: pluginValues.name, }, { fileName: 'classes/BootstrapClasses.php', stringToUpdate: 'CAPS_AND_SNAKE_NAME', updateString: pluginValues.capAndSnakeCasePlugin, }, ]; // Perform composer updates const composerObjects: Array<ScaffoldJsonUpdates> | any = await CreateObjectArrays.readComposerObjects(pluginValues, this.composerAlreadyExists); if (!this.composerAlreadyExists) { updateObjectsArray.push(...composerObjects); } // Perform package.json updates const packageObjects: Array<ScaffoldJsonUpdates> | any = await CreateObjectArrays.readPackageObjects(pluginValues, this.packageAlreadyExists); if (!this.packageAlreadyExists) { updateObjectsArray.push(...packageObjects); } return updateObjectsArray; } catch (err: any) { console.log('ScaffoldPlugin.buildUpdateObjectArray()'); console.error(err); } }; } ScaffoldPlugin.initializeScaffolding().catch(err => console.error(err));