UNPKG

@nomadmystic/wordpress-scaffold-cli

Version:

This project is created to speed up WordPress development

454 lines (363 loc) 15.2 kB
// Core Modules import fs from 'fs'; import path from 'path'; // Community modules import fse from "fs-extra"; // Package modules // Utils import StringUtils from '../../utils/string-utils.js'; import PathUtils from '../../utils/path-utils.js'; import MessagingUtils from '../../utils/messaging-utils.js'; import { packageRootDir } from '../../utils/package-root.js'; // Interfaces import ActivePlugins from '../../interfaces/project/interface-active-plugins.js'; import ProjectConfig from '../../interfaces/project/interface-project-config.js'; import { scaffoldInternal } from './scaffold-internal.js'; import DebugUtils from "../../utils/debug-utils.js"; /** * @description Updates our internal JSON with properties for use later * @deprecated Use class ProjectJson * * @param {string} filePath * @param {any} json * @param {boolean} isPlugin * @return {string} */ const updateInternalJson = async (filePath: string, json: any, isPlugin: boolean = false): Promise<string | any> => { try { await scaffoldInternal(); // Setup our arrays for json update logic const dashedValues: Array<string> = [ 'project-name', 'active-theme', ]; const disallowedKeys: Array<string> = [ 'database-setup', 'database-name', 'database-password', 'database-username', 'site-admin-password', 'site-admin-user', 'admin-email', ]; // Get our config file let jsonFile: string = fs.readFileSync(filePath, 'utf-8'); // Baily Early if (!jsonFile || typeof jsonFile === 'undefined' || jsonFile === '') { return ''; } // Create object let jsonFileParsed = JSON.parse(jsonFile); let property: keyof typeof json; for (property in json) { // Sanity Check if (Object.hasOwn(json, property) && property && typeof property !== 'undefined') { // These come through the CLI as camelCase let dashedProperty: string = await StringUtils.camelCaseToDash(property); // What if there value isn't empty? if (json[property] && typeof json[property] === 'undefined' && json[property] !== '') { continue; } // Update the values if (json[property] && typeof json[property] !== 'undefined' && typeof json[property] === 'string' && dashedValues.includes(dashedProperty) ) { jsonFileParsed[`${dashedProperty}`] = await StringUtils.addDashesToString(json[property].trim()); // @todo Pretty specific maybe refactor and abstract this out? if (dashedProperty === 'project-name') { jsonFileParsed['project-namespace'] = await StringUtils.pascalCaseString(jsonFileParsed['project-name']); continue; } continue; } // Some information we don't want to save, so do that here if (typeof json[property] !== 'undefined' && !disallowedKeys.includes(dashedProperty)) { jsonFileParsed[`${dashedProperty}`] = json[property]; } } // End sanity check } // End for // Write our updated values fs.writeFileSync(filePath, JSON.stringify(jsonFileParsed)); return JSON.parse(fs.readFileSync(filePath, 'utf-8')); } catch (err) { console.log('updateInternalJson()'); console.error(err); } }; /** * @classdesc Perform tasks for updating the internal JSON file * @class ProjectJson * @author Keith Murphy | nomadmystics@gmail.com */ export class ProjectJson { /** * @type boolean * @private */ private static isDebugFullMode: boolean = false; /** * @type string * @private */ private static whereAmI: string = ''; /** * @type string * @private */ private static configFilePath: string = ''; // Setup our arrays for json update logic private static dashedValues: Array<string> = [ 'project-name', 'active-theme', ]; /** * @type Array<string> * @private */ private static disallowedKeys: Array<string> = [ 'database-setup', 'database-name', 'database-password', 'database-username', 'site-admin-password', 'site-admin-user', 'admin-email', 'plugin-name', 'plugin-path', 'plugin-description', 'plugin-front-end-framework', ]; /** * @description Starting point for updating the internal config * @public * @author Keith Murphy | nomadmystics@gmail.com * * @param {object} configUpdates * @param {string} type * @return {Promise<object | any>} */ public static update = async (configUpdates: object, type: string = ''): Promise<object | any> => { try { // Gather our location this.whereAmI = await PathUtils.whereAmI(); // Check for debug mode values this.isDebugFullMode = await DebugUtils.isDebugFullMode(); // Make sure we have the Project JSON scaffolded await this.scaffoldInternal(); // Get our config file this.configFilePath = `${this.whereAmI}/internal/project/project-config.json`; let projectConfigObject = await this.getProjectConfigObject(); projectConfigObject = await this.performRootJsonUpdate(projectConfigObject, configUpdates); // Some times need specific updates handel them here if (type === 'plugin') { projectConfigObject = await this.performPluginJsonUpdate(projectConfigObject, configUpdates); } await this.saveFile(projectConfigObject); return projectConfigObject; } catch (err: any) { console.log('ProjectJson.update()'); console.error(err); } }; /** * @description Save our internal JSON file * @private * @author Keith Murphy | nomadmystics@gmail.com * * @param {ProjectConfig} projectConfigObject * @return {Promise<void>} */ private static saveFile = async (projectConfigObject: ProjectConfig): Promise<void> => { try { // Write our updated values fs.writeFileSync(this.configFilePath, JSON.stringify(projectConfigObject)); // Let the user know await MessagingUtils.displayColoredMessage('The internal project config file has been saved.', 'green'); } catch (err: any) { console.log('ProjectJson.saveFile()'); console.error(err); } }; /** * @description Make sure we have our internal folder, if not copy it over * * @return void */ public static scaffoldInternal = async (): Promise<void> => { try { if (!fs.existsSync(`${this.whereAmI}/internal`)) { fse.copySync(`${path.join(packageRootDir + '/scaffolding/internal')}`, `${this.whereAmI}/internal`, { overwrite: false }); } } catch (err: any) { console.log('ProjectJson.scaffoldInternal()'); console.error(err); } }; /** * @description Get the "Global" project config * @public * @author Keith Murphy | nomadmystics@gmail.com * * @return Promise<string | object | any> */ public static getProjectConfigObject = async (): Promise<ProjectConfig | any> => { try { let jsonFile: string = fs.readFileSync(this.configFilePath, 'utf-8'); // Baily Early if (!jsonFile || typeof jsonFile === 'undefined' || jsonFile === '') { return ''; } return JSON.parse(jsonFile); } catch (err: any) { console.log('ProjectJson.getProjectConfigObject'); console.error(err); } }; /** * @description Updates the root level config values * @private * @author Keith Murphy | nomadmystics@gmail.com * * @param {any} projectConfigObject * @param {any} configUpdates * @return {Promise<object | any>} */ private static performRootJsonUpdate = async (projectConfigObject: any, configUpdates: any): Promise<object | any> => { try { let property: keyof typeof configUpdates; for (property in configUpdates) { // Sanity Check if (Object.hasOwn(configUpdates, property) && property && typeof property !== 'undefined') { // These come through the CLI as camelCase let dashedProperty: string = await StringUtils.camelCaseToDash(property); // What if there value isn't empty? if (configUpdates[property] && typeof configUpdates[property] === 'undefined' && configUpdates[property] !== '' ) { continue; } // Update the values if (configUpdates[property] && typeof configUpdates[property] !== 'undefined' && typeof configUpdates[property] === 'string' && this.dashedValues.includes(dashedProperty) ) { projectConfigObject[`${dashedProperty}`] = await StringUtils.addDashesToString(configUpdates[property].trim()); // @todo Pretty specific maybe refactor and abstract this out? if (dashedProperty === 'project-name') { projectConfigObject['project-namespace'] = await StringUtils.pascalCaseString(projectConfigObject['project-name']); continue; } continue; } // Some information we don't want to save, so do that here if (typeof configUpdates[property] !== 'undefined' && !this.disallowedKeys.includes(dashedProperty)) { projectConfigObject[`${dashedProperty}`] = configUpdates[property]; } } // End sanity check } // End for return JSON.parse(fs.readFileSync(this.configFilePath, 'utf-8')); } catch (err: any) { console.log('ProjectJson.performRootJsonUpdate()'); console.error(err); } }; /** * @description Update our internal config with plugin specific values * @private * @author Keith Murphy | nomadmystics@gmail.com * * @param {any} projectConfigObject * @param {any} configUpdates * @return { Promise<object | any>} */ private static performPluginJsonUpdate = async (projectConfigObject: any, configUpdates: any): Promise<object | any> => { try { let activePlugins: Array<ActivePlugins> = projectConfigObject['active-plugins']; const alreadyExists: boolean | undefined = await this.cleanUpPluginArray(activePlugins, configUpdates); // Push our update or let the user know one already exists if (!alreadyExists) { activePlugins.push(configUpdates); } else { console.log(`Plugin ${configUpdates['plugin-name']} already exists in the schema. Please try another name.`) process.exit(1); } projectConfigObject['active-plugins'] = activePlugins; if (this.isDebugFullMode) { console.log('ProjectJson.performPluginJsonUpdate()'); console.log('projectConfigObject'); console.log(projectConfigObject); console.log('configUpdates'); console.log(configUpdates); } return projectConfigObject; } catch (err: any) { console.log('ProjectJson.performPluginJsonUpdate()'); console.error(err); } } /** * @description Do some checks and clean up for the active-plugins object * @private * @author Keith Murphy | nomadmystics@gmail.com * * @param {Array<ActivePlugins>} activePlugins * @param {any} configUpdates * @return {Promise<boolean | undefined>} */ private static cleanUpPluginArray = async (activePlugins: Array<ActivePlugins>, configUpdates: any): Promise<boolean | undefined> => { try { let alreadyExists: boolean = false; // Check if we have an active key by the same name for (let plugin: number = 0; plugin < activePlugins.length; plugin++) { if (activePlugins[plugin] && typeof activePlugins[plugin] !== 'undefined') { const currentPlugin = await this.deleteUnusedPluginProperties(activePlugins[plugin]); // Since this doesn't exist remove it from our array if (!currentPlugin || typeof currentPlugin === 'undefined' || Object.keys(currentPlugin).length === 0) { activePlugins.splice(plugin, 1); } // We have a plugin with the same name if (activePlugins[plugin]?.['plugin-name'] && activePlugins[plugin]?.['plugin-name'] === configUpdates['plugin-name']) { // There is a valid path and plugin with the same name if (fs.existsSync(activePlugins[plugin]?.['plugin-path'])) { alreadyExists = true; } } } } return alreadyExists; } catch (err: any) { console.log('ProjectJson.cleanUpPluginArray()'); console.error(err); } }; /** * @description * @private * @author Keith Murphy | nomadmystics@gmail.com * * @param {ActivePlugins} plugin * @return {Promise<void>} */ private static deleteUnusedPluginProperties = async (plugin: ActivePlugins): Promise<ActivePlugins | any> => { try { let currentPlugin: ActivePlugins = plugin; // Remove the object since the path for the plugin doesn't exist if (!fs.existsSync(currentPlugin?.['plugin-path'])) { delete currentPlugin['plugin-name']; // @ts-ignore delete currentPlugin['plugin-path']; delete currentPlugin['plugin-description']; delete currentPlugin['plugin-front-end-framework']; } return currentPlugin; } catch (err: any) { console.log('ProjectJson.deleteUnusedPluginProperties()'); console.error(err); } }; } export default updateInternalJson;