From 3791575efa1cf3df1598efa634c0de8bcb2e51d5 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 18:43:46 +0100 Subject: [PATCH 01/70] Rename class and file 'install' to 'Installer' The class 'install' and corresponding file have been renamed to 'Installer'. Adjustments have been made to reflect that the class is a constructor, improving both readability and comprehensibility of the code. Additionally, module exports have been updated accordingly. --- lib/{dump.js => Dump.js} | 6 +++--- lib/{install.js => Installer.js} | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) rename lib/{dump.js => Dump.js} (98%) rename lib/{install.js => Installer.js} (95%) diff --git a/lib/dump.js b/lib/Dump.js similarity index 98% rename from lib/dump.js rename to lib/Dump.js index 89140e6..21dcd0e 100644 --- a/lib/dump.js +++ b/lib/Dump.js @@ -2,8 +2,8 @@ const fs = require('fs'); const path = require('path'); const { geVarFromPHPFile } = require('./utils'); -class dump { - constructor () { +class Dump { + constructor () {W this.wpFolder = process.cwd(); this.themeFolder = path.join(this.wpFolder, 'wp-content', 'themes'); this.pluginsFolder = path.join(this.wpFolder, 'wp-content', 'plugins'); @@ -89,4 +89,4 @@ class dump { } } -module.exports = dump; +module.exports = Dump; diff --git a/lib/install.js b/lib/Installer.js similarity index 95% rename from lib/install.js rename to lib/Installer.js index 73cb93c..877cb61 100644 --- a/lib/install.js +++ b/lib/Installer.js @@ -1,12 +1,12 @@ const path = require('path'); -const { isWPCLIAvailable } = require('./utils'); -const { cleanup, makeDir } = require('./fs'); +const { isWPCLIAvailable } = require('./utils/index.js'); +const { cleanup, makeDir } = require('./utils/fs.js'); const { WordPressPackage, PluginPackage, ThemePackage } = require('./package'); /** * The wpmm class represents the WordPress installer and provides methods for installing WordPress and its dependencies. */ -class WordPressInstaller { +class Installer { /** * Initializes a new instance of the constructor. * @@ -102,4 +102,4 @@ class WordPressInstaller { } } -module.exports = WordPressInstaller; +module.exports = Installer; From 45ec2e0b3e5f4ffde0e918e7cf0f3679c3bfa247 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 18:44:04 +0100 Subject: [PATCH 02/70] Add Database.js file with upload and dump methods A new file 'Database.js' has been added featuring a Database class. This class includes two methods, 'uploadDatabase' and 'dumpDatabase', meant to handle respective operations on a MySQL database. The 'getConnectionSettings' utility function is used for obtaining database connection configurations. The file utilizes 'mysql2/promise' and 'mysqldump' packages for database operations. --- lib/Database.js | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 lib/Database.js diff --git a/lib/Database.js b/lib/Database.js new file mode 100644 index 0000000..4418743 --- /dev/null +++ b/lib/Database.js @@ -0,0 +1,73 @@ +const mysql = require('mysql2/promise'); +const { getConnectionSettings } = require('./utils'); +const mysqldump = require('mysqldump'); +const fs = require('fs').promises; + +class Database { + constructor (config) { + // Load configuration from wp-package.json + this.config = config; + } + + async uploadDatabase (sqlFilePath) { + try { + console.log('Uploading database...'); + + if (!fs.existsSync(sqlFilePath)) { + return new Error('SQL file not found'); + } + + if (!this.config) { + return new Error('Database configuration not found'); + } + + const databaseConnectionConfig = getConnectionSettings(this.config); + + const connection = await mysql.createConnection(databaseConnectionConfig); + + const sqlFileContent = await fs.readFile(sqlFilePath, 'utf8'); + const queries = sqlFileContent.split(';'); + + for (const query of queries) { + if (query.trim() !== '') { + await connection.query(query); + } + } + + console.log('Database uploaded successfully'); + return connection.end(); + } catch (error) { + console.error('Error uploading database:', error.message); + } + } + + /** + * Asynchronously dumps the database to the specified output file. + * + * @param {string} outputFilePath - The path of the file to dump the database to. + * @return {Promise} - A promise that resolves if the database is dumped successfully, or rejects with an error message if an error occurs. + */ + async dumpDatabase (outputFilePath) { + try { + console.log('Dumping database...'); + + if (!this.config) { + return new Error('Database configuration not found'); + } + + const databaseConnectionConfig = getConnectionSettings(this.config); + + await mysqldump({ + databaseConnectionConfig, + dumpToFile: outputFilePath, + compressFile: true + }); + + console.log('Database dumped successfully'); + } catch (error) { + console.error('Error dumping database:', error.message); + } + } +} + +module.exports = Database; From daa727a4d58f504b4116da1ebb5ab70a97e0230e Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 18:44:21 +0100 Subject: [PATCH 03/70] Refactor package installation classes into separate files Separated existing installation package classes (WordPressPackage, PluginPackage, ThemePackage) from `package.js` into their own files(`WpPackage.js`, `PluginPackage.js`, `ThemePackage.js`). This refactoring improves code organization, clarity, and separation of concerns. The classes are now directly exported from their files, making it easier to import them separately where necessary. --- lib/{package.js => Package.js} | 123 ++------------------------------- lib/PluginPackage.js | 16 +++++ lib/ThemePackage.js | 16 +++++ lib/WpPackage.js | 89 ++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 119 deletions(-) rename lib/{package.js => Package.js} (52%) create mode 100644 lib/PluginPackage.js create mode 100644 lib/ThemePackage.js create mode 100644 lib/WpPackage.js diff --git a/lib/package.js b/lib/Package.js similarity index 52% rename from lib/package.js rename to lib/Package.js index 91649f7..4cfd6ed 100644 --- a/lib/package.js +++ b/lib/Package.js @@ -3,17 +3,15 @@ const path = require('path'); const { exec } = require('child_process'); const { getDownloadUrl, - getWordPressDownloadUrl, installNpmPackages, - replaceDbConstant, - replaceEmptySalts, installComposer, replaceDbConstantBool + installComposer } = require('./utils'); const { downloadFile, extractZip, renameFolder -} = require('./fs'); +} = require('./utils/fs'); class Package { /** @@ -25,7 +23,7 @@ class Package { * @param {Object} paths - the object containing the paths * @param {string} paths.rootFolder - the root folder of the application * @param {string} paths.tempDir - the temporary directory - * @param {string} paths.baseFolder - the temporary directory + * @param {string} paths.baseFolder - the script base directory * @param {string} paths.destFolder - The destination folder for package installation. */ constructor (config, paths) { @@ -81,7 +79,6 @@ class Package { * * @param {string} packageUrl - The URL of the package to download. * @param {string} packageName - The name of the package. - * @param {string} targetDirectory - The directory where the package will be saved. * @return {Promise} A promise that resolves when the package is successfully downloaded and saved, or rejects with an error. */ async downloadPackage (packageUrl, packageName) { @@ -134,116 +131,4 @@ class Package { } } -class WordPressPackage extends Package { - /** - * Installs WordPress with the specified version and language. - * - * @param {string} version - The version of WordPress to install. - * @param {string} language - The language of WordPress to install. - */ - async installWordPress (version, language) { - const downloadUrl = getWordPressDownloadUrl(version, language); - - try { - const destinationPath = path.join(this.rootFolder, this.config.name); - - if (fs.existsSync(destinationPath)) { - console.log('πŸ”„οΈ WordPress folder already exists. Skipping download.'); - } else { - // Download WordPress - return await this.execDownload(`wordpress-${version}.zip`, downloadUrl).then(() => { - // Copy WordPress folder to destination path - renameFolder(path.join(this.tempDir, 'wordpress'), destinationPath); - console.log(`πŸ†— WordPress installed successfully in ${destinationPath}`); - }); - } - } catch (error) { - console.error('πŸ”΄ Error downloading or installing WordPress:', error); - } - } - - /** - * Sets up the WordPress configuration by copying the sample config file, - * replacing the placeholder values with the actual configuration values, - * and saving the updated config file. - * - * @return {void} This function does not return anything. - */ - async setupWordPressConfig () { - const configPath = path.join(this.baseFolder, 'wp-config.php'); - - try { - if (fs.existsSync(configPath)) { - console.log('πŸ†— WordPress configuration already set up. updating...'); - } else { - const sampleConfigPath = path.join(this.baseFolder, 'wp-config-sample.php'); - // Copy wp-config-sample.php to wp-config.php - fs.copyFileSync(sampleConfigPath, configPath); - } - - // Read the content of wp-config.php - let configContent = fs.readFileSync(configPath, 'utf8'); - - // Update database name, username, password, and other settings based on user-defined config - configContent = replaceDbConstant(configContent, 'DB_NAME', this.config.wordpress.config.DB_NAME); - configContent = replaceDbConstant(configContent, 'DB_USER', this.config.wordpress.config.DB_USER); - configContent = replaceDbConstant(configContent, 'DB_PASSWORD', this.config.wordpress.config.DB_PASSWORD); - configContent = replaceDbConstant(configContent, 'DB_HOST', this.config.wordpress.config.DB_HOST); - configContent = replaceDbConstant(configContent, 'DB_CHARSET', this.config.wordpress.config.DB_CHARSET); - - configContent = replaceDbConstantBool(configContent, 'WP_DEBUG', this.config.wordpress.config.WP_DEBUG); - - configContent = replaceEmptySalts(configContent); - - // Write the updated content back to wp-config.php - fs.writeFileSync(configPath, configContent, 'utf8'); - - console.log('πŸ†— WordPress configuration set up successfully.'); - } catch (error) { - console.error('πŸ”΄ Error setting up WordPress configuration:', error); - } - } - - /** - * Installs WordPress and sets up the WordPress configuration. - * - * @returns {Promise} A Promise that resolves when the installation and configuration are complete. - */ - async install () { - const { version, language } = this.config.wordpress; - await this.installWordPress(version, language); - await this.setupWordPressConfig(); - } -} - -class PluginPackage extends Package { - /** - * Asynchronously installs a plugin. - * - * @return {Promise} A Promise that resolves when the plugin is installed successfully. - */ - async install () { - return await this.installPackage(this.config, 'plugin').then(() => { - console.log(`πŸ†— Plugin ${this.config.name} installed successfully.`); - }); - } -} - -class ThemePackage extends Package { - /** - * Installs a package with the specified name and version as a theme. - * - * @return {Promise} A promise that resolves once the package is installed. - */ - async install () { - return await this.installPackage(this.config, 'theme').then(() => { - console.log(`πŸ†— Theme ${this.config.name} installed successfully.`); - }); - } -} - -module.exports = { - WordPressPackage, - PluginPackage, - ThemePackage -}; +module.exports = Package; diff --git a/lib/PluginPackage.js b/lib/PluginPackage.js new file mode 100644 index 0000000..b358bdd --- /dev/null +++ b/lib/PluginPackage.js @@ -0,0 +1,16 @@ +const Package = require('./package'); + +export class PluginPackage extends Package { + /** + * Asynchronously installs a plugin. + * + * @return {Promise} A Promise that resolves when the plugin is installed successfully. + */ + async install () { + return await this.installPackage(this.config, 'plugin').then(() => { + console.log(`πŸ†— Plugin ${this.config.name} installed successfully.`); + }); + } +} + +module.exports = PluginPackage; diff --git a/lib/ThemePackage.js b/lib/ThemePackage.js new file mode 100644 index 0000000..b0aca70 --- /dev/null +++ b/lib/ThemePackage.js @@ -0,0 +1,16 @@ +const Package = require('./package'); + +export class ThemePackage extends Package { + /** + * Installs a package with the specified name and version as a theme. + * + * @return {Promise} A promise that resolves once the package is installed. + */ + async install () { + return await this.installPackage(this.config, 'theme').then(() => { + console.log(`πŸ†— Theme ${this.config.name} installed successfully.`); + }); + } +} + +module.exports = ThemePackage; diff --git a/lib/WpPackage.js b/lib/WpPackage.js new file mode 100644 index 0000000..c17723e --- /dev/null +++ b/lib/WpPackage.js @@ -0,0 +1,89 @@ +const { getWordPressDownloadUrl, replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/index.js'); +const path = require('path'); +const fs = require('lib/utils/fs'); +const { renameFolder } = require('./utils/fs'); +const Package = require('./package'); + +export class WpPackage extends Package { + /** + * Installs WordPress with the specified version and language. + * + * @param {string} version - The version of WordPress to install. + * @param {string} language - The language of WordPress to install. + */ + async installWordPress (version, language) { + const downloadUrl = getWordPressDownloadUrl(version, language); + + try { + const destinationPath = path.join(this.rootFolder, this.config.name); + + if (fs.existsSync(destinationPath)) { + console.log('πŸ”„οΈ WordPress folder already exists. Skipping download.'); + } else { + // Download WordPress + return await this.execDownload(`wordpress-${version}.zip`, downloadUrl).then(() => { + // Copy WordPress folder to destination path + renameFolder(path.join(this.tempDir, 'wordpress'), destinationPath); + console.log(`πŸ†— WordPress installed successfully in ${destinationPath}`); + }); + } + } catch (error) { + console.error('πŸ”΄ Error downloading or installing WordPress:', error); + } + } + + /** + * Sets up the WordPress configuration by copying the sample config file, + * replacing the placeholder values with the actual configuration values, + * and saving the updated config file. + * + * @return {void} This function does not return anything. + */ + async setupWordPressConfig () { + const configPath = path.join(this.baseFolder, 'wp-config.php'); + + try { + if (fs.existsSync(configPath)) { + console.log('πŸ†— WordPress configuration already set up. updating...'); + } else { + const sampleConfigPath = path.join(this.baseFolder, 'wp-config-sample.php'); + // Copy wp-config-sample.php to wp-config.php + fs.copyFileSync(sampleConfigPath, configPath); + } + + // Read the content of wp-config.php + let configContent = fs.readFileSync(configPath, 'utf8'); + + // Update database name, username, password, and other settings based on user-defined config + configContent = replaceDbConstant(configContent, 'DB_NAME', this.config.wordpress.config.DB_NAME); + configContent = replaceDbConstant(configContent, 'DB_USER', this.config.wordpress.config.DB_USER); + configContent = replaceDbConstant(configContent, 'DB_PASSWORD', this.config.wordpress.config.DB_PASSWORD); + configContent = replaceDbConstant(configContent, 'DB_HOST', this.config.wordpress.config.DB_HOST); + configContent = replaceDbConstant(configContent, 'DB_CHARSET', this.config.wordpress.config.DB_CHARSET); + + configContent = replaceDbConstantBool(configContent, 'WP_DEBUG', this.config.wordpress.config.WP_DEBUG); + + configContent = replaceEmptySalts(configContent); + + // Write the updated content back to wp-config.php + fs.writeFileSync(configPath, configContent, 'utf8'); + + console.log('πŸ†— WordPress configuration set up successfully.'); + } catch (error) { + console.error('πŸ”΄ Error setting up WordPress configuration:', error); + } + } + + /** + * Installs WordPress and sets up the WordPress configuration. + * + * @returns {Promise} A Promise that resolves when the installation and configuration are complete. + */ + async install () { + const { version, language } = this.config.wordpress; + await this.installWordPress(version, language); + await this.setupWordPressConfig(); + } +} + +module.exports = WpPackage; From ac8af1d1a9867d4b1aea4307ce31e5cf28dfad44 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 18:44:34 +0100 Subject: [PATCH 04/70] Add Updater.js with update methods for plugins, themes and WordPress Added a new file `Updater.js` including methods to update plugins, themes, and WordPress core using the configurations. This class improves the structured and guided update process by providing dedicated methods to perform the updates separately, handling configuration validation and logging errors effectively during the update process. --- lib/Updater.js | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 lib/Updater.js diff --git a/lib/Updater.js b/lib/Updater.js new file mode 100644 index 0000000..bacc73c --- /dev/null +++ b/lib/Updater.js @@ -0,0 +1,76 @@ +const { exec } = require('child_process'); + +class Updater { + constructor (config) { + // Load configuration from wp-package.json + this.config = config; + } + + async updatePlugins () { + try { + const plugins = this.config.plugins; + + if (!plugins || !Array.isArray(plugins)) { + console.error('Invalid or missing plugins configuration'); + return; + } + + for (const plugin of plugins) { + const command = `wp plugin install ${plugin} --activate`; + + exec(command, (error, stdout, stderr) => { + if (error) { + console.error(`Error updating plugin ${plugin}:`, error.message); + } else { + console.log(`Plugin ${plugin} updated successfully`); + } + }); + } + } catch (error) { + console.error('Error updating plugins:', error.message); + } + } + + async updateThemes () { + try { + const themes = this.config.themes; + + if (!themes || !Array.isArray(themes)) { + console.error('Invalid or missing themes configuration'); + return; + } + + for (const theme of themes) { + const command = `wp theme install ${theme} --activate`; + + exec(command, (error, stdout, stderr) => { + if (error) { + console.error(`Error updating theme ${theme}:`, error.message); + } else { + console.log(`Theme ${theme} updated successfully`); + } + }); + } + } catch (error) { + console.error('Error updating themes:', error.message); + } + } + + async updateWordPress () { + try { + const command = 'wp core update'; + + exec(command, (error, stdout, stderr) => { + if (error) { + console.error('Error updating WordPress:', error.message); + } else { + console.log('WordPress updated successfully'); + } + }); + } catch (error) { + console.error('Error updating WordPress:', error.message); + } + } +} + +module.exports = Updater; From ec94d275961412ef25c0520d7e7f63bd385ca01b Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 18:44:49 +0100 Subject: [PATCH 05/70] Refactor utils.js, moving functions into newly created utils/index.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved all functions in utils.js to a newly created index.js file within the utils directory. This refactoring effort improves code organization by defining utility functions in their respective context, under the utils directory. Meanwhile, β€˜fs.js’ was renamed and moved to β€˜utils/fs.js’ to maintain consistency. These changes do not alter any functionality. --- lib/{ => utils}/fs.js | 2 +- lib/{utils.js => utils/index.js} | 42 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) rename lib/{ => utils}/fs.js (98%) rename lib/{utils.js => utils/index.js} (83%) diff --git a/lib/fs.js b/lib/utils/fs.js similarity index 98% rename from lib/fs.js rename to lib/utils/fs.js index 458369b..8815e84 100644 --- a/lib/fs.js +++ b/lib/utils/fs.js @@ -3,7 +3,7 @@ const https = require('node:https'); const extract = require('extract-zip'); /** - * Create a temporary directory if it does not already exist. + * Create a temporary directory if it does not alreadyW exist. */ function makeDir (dirpath) { if (!fs.existsSync(dirpath)) { diff --git a/lib/utils.js b/lib/utils/index.js similarity index 83% rename from lib/utils.js rename to lib/utils/index.js index e3da6f5..adfdd65 100644 --- a/lib/utils.js +++ b/lib/utils/index.js @@ -48,6 +48,27 @@ function getConfig (args) { return config; } +/** + * Returns the connection settings based on the provided config. + * + * @param {object} config - The configuration object containing the host, user, password, and database details. + * @return {object} - The connection settings object with the following properties: + * - connectionLimit: The maximum number of connections allowed. + * - host: The host name or IP address of the database server. + * - user: The username for authenticating with the database server. + * - password: The password for authenticating with the database server. + * - database: The name of the database to connect to. + */ +function getConnectionSettings (config) { + return { + connectionLimit: 5, + host: this.config.host, + user: this.config.user, + password: this.config.password, + database: this.config.database + }; +} + /** * Generates the download URL for a specific version of WordPress. * @@ -199,6 +220,25 @@ async function isWPCLIAvailable () { } } +/** + * Retrieve information about the WPMM and system environment. + * + * @return {void} + */ +function getInfo (config, actions) { + const version = require('../../package.json').version; + console.log('πŸ“¦ WPMM version: ' + version.toString()); + console.log('Node version: ' + process.version); + console.log('OS: ' + process.platform + ' ' + process.arch); + console.log('Current working directory: ' + process.cwd()); + console.log('------------------------------------------'); + console.log('πŸ”§ Configuration: ' + JSON.stringify(config, null, 2)); + console.log('*******************'); + // get the keys of the actions object + const actionsKeys = Object.keys(actions); + console.log('πŸš€ Command line available actions: ' + JSON.stringify(actionsKeys, null, 2)); +} + function printTimePassed (startTime) { // End the timer const endTime = Date.now(); @@ -211,8 +251,10 @@ function printTimePassed (startTime) { module.exports = { geVarFromPHPFile, getConfig, + getConnectionSettings, getWordPressDownloadUrl, getDownloadUrl, + getInfo, installNpmPackages, installComposer, replaceDbConstant, From 0aeb047bc0426bbf53381e83206f50795bb3cd37 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 18:45:08 +0100 Subject: [PATCH 06/70] Refactor code and enhance database functionality Refactored the code by moving functions to newly created utils/index.js, improving the organization of utility functions. Added `getInfo` function to the utils import. Renamed WordPressInstaller to Installer, WordPressConfigDump to Dump, and added Database class for handling new database operations. Furthermore, the handling of the 'dump' and 'init' actions have been updated along with the addition of actions for 'upload-database' and 'dump-database'. --- lib/index.js | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/index.js b/lib/index.js index 0f0f487..6db5a8b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,8 +1,9 @@ #!/usr/bin/env node -const { getConfig, printTimePassed } = require('./utils'); -const WordPressInstaller = require('./install'); -const WordPressConfigDump = require('./dump.js'); -const Initialize = require('./initialize.js'); // Import the Initialize class +const { getConfig, printTimePassed, getInfo } = require('./utils/index.js'); +const Installer = require('./installer'); +const Dump = require('./dump.js'); +const Database = require('./database.js'); +const Initialize = require('./initialize.js'); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); @@ -17,29 +18,43 @@ const config = getConfig(argv); const startTime = Date.now(); const actions = { + info: () => { + getInfo(config, actions); + }, dump: () => { - const dump = new WordPressConfigDump(this); + const dump = new Dump(this); dump.init(); - printTimePassed(startTime); - process.exit(0); }, init: () => { const initializer = new Initialize(config); initializer.generateConfig(); - printTimePassed(startTime); - process.exit(0); + }, + 'upload-database': () => { + const db = new Database(config); + db.uploadDatabase(config.database.filename).then(() => { + console.log('πŸš€ Database uploaded successfully.'); + }); + }, + 'dump-database': () => { + const date = new Date().getUTCDate(); + const newDbName = `${config.wordpress.config_DB_Name}-${date}.sql.gz`; + const db = new Database(config); + db.dumpDatabase(newDbName).then(() => { + console.log('πŸš€ Database dumped successfully to', newDbName, '.'); + }); }, default: () => { // Install WordPress - const installer = new WordPressInstaller(config); + const installer = new Installer(config); installer.run().then(() => { console.log('πŸš€ WordPress installed successfully.'); - printTimePassed(startTime); - process.exit(0); }); } }; const action = Object.keys(argv).find(key => argv[key] === true); -(actions[action] || actions.default)(); +(actions[action])(); + +printTimePassed(startTime); +process.exit(0); From 41fd51dd134d638a279fde57db9180af0da24534 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 18:46:23 +0100 Subject: [PATCH 07/70] Update package version and add new dependencies Increased the version number of package.json and package-lock.json. Also, added 'mysql2' and 'mysqldump' to the package dependencies list for improved database operations support. --- package-lock.json | 165 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 4 +- 2 files changed, 163 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 243ff4a..d7bab30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,17 @@ { "name": "wpmm", - "version": "0.0.2", + "version": "0.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wpmm", - "version": "0.0.2", + "version": "0.0.3", "license": "ISC", "dependencies": { "extract-zip": "^2.0.1", + "mysql2": "^3.6.5", + "mysqldump": "^3.2.0", "yargs": "^17.7.2" }, "bin": { @@ -2128,6 +2130,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -2927,6 +2937,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -3192,6 +3210,17 @@ "node": ">=10.17.0" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -3460,6 +3489,11 @@ "node": ">=8" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -4356,17 +4390,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -4467,6 +4510,93 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mysql2": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.5.tgz", + "integrity": "sha512-pS/KqIb0xlXmtmqEuTvBXTmLoQ5LmAz5NW/r8UyQ1ldvnprNEj3P9GbmuQQ2J0A4LO+ynotGi6TbscPa8OUb+w==", + "dependencies": { + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "engines": { + "node": ">=16.14" + } + }, + "node_modules/mysqldump": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mysqldump/-/mysqldump-3.2.0.tgz", + "integrity": "sha512-hS4jUk23BT+qZEwp668eGwyFhmsDFHkIDYANLFeNXlvPK1AhOMLV+SGoKpAk8290sUhtUAIG16R3YrNhHmZ+zQ==", + "dependencies": { + "deepmerge": "^3.2.0", + "mysql2": "^2.1.0", + "sql-formatter": "^2.3.2", + "sqlstring": "^2.3.1" + } + }, + "node_modules/mysqldump/node_modules/deepmerge": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", + "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mysqldump/node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/mysqldump/node_modules/mysql2": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", + "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", + "dependencies": { + "denque": "^2.0.1", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "lru-cache": "^6.0.0", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5104,6 +5234,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -5113,6 +5248,11 @@ "semver": "bin/semver.js" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -5223,6 +5363,22 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sql-formatter": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-2.3.4.tgz", + "integrity": "sha512-CajWtvzYoBJbD5PQeVe3E7AOHAIYvRQEPOKgF9kfKNeY8jtjBiiA6pDzkMuAID8jJMluoPvyKveLigSaA5tKQQ==", + "dependencies": { + "lodash": "^4.17.20" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -5715,8 +5871,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { "version": "17.7.2", diff --git a/package.json b/package.json index 92a0338..844e90e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wpmm", - "version": "0.0.3", + "version": "0.0.4", "description": "A Node.js script for easy installation of WordPress, themes, and plugins.", "author": "Erik Golinelli ", "repository": { @@ -26,6 +26,8 @@ "license": "ISC", "dependencies": { "extract-zip": "^2.0.1", + "mysql2": "^3.6.5", + "mysqldump": "^3.2.0", "yargs": "^17.7.2" }, "devDependencies": { From 0426834ae8a335c7f43f79975137d56e0b7d78fc Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 19:20:17 +0100 Subject: [PATCH 08/70] Add typings for WPMMconfig object properties Introduced detailed JSDoc typedefs for the WPMMconfig object and its subprocesses. This includes definitions for WordPress configuration, package data, and website details to improve code clarity and maintainability. --- lib/utils/index.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/utils/index.js b/lib/utils/index.js index adfdd65..00570e0 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -2,6 +2,43 @@ const path = require('path'); const fs = require('fs'); const { exec } = require('node:child_process'); +/** + * @typedef WPMMconfigWP + * @property {string} WPMMconfigWP.DB_NAME - The name of the database + * @property {string} WPMMconfig.DB_USER - The username for the database + * @property {string} WPMMconfig.DB_PASSWORD - The password for the database + * @property {string} WPMMconfig.DB_HOST - The host name or IP address of the database server + * @property {string} WPMMconfig.DB_CHARSET - The character set for the database + * @property {string} WPMMconfig.DB_COLLATE - The collation for the database (usually utf8_general_ci) + * @property {string} WPMMconfig.table_prefix - The table prefix for the database + * @property {boolean} WPMMconfig.WP_DEBUG - Whether to enable debugging + * @property {string} WPMMconfig.WP_SITEURL - The URL of the website + * @property {string} WPMMconfig.WP_HOME - The home URL of the website + * @property {string} WPMMconfig.WP_CONTENT_DIR - The path to the content directory + * @property {string} WPMMconfig.WP_CONTENT_URL - The URL of the content directory + * @property {boolean} WPMMconfig.DISALLOW_FILE_EDIT - Whether to disallow file editing + */ + +/** + * @typedef WPMMconfigPkg + * @property {string} WPMMconfigPkg.name - the name of the package + * @property {string} WPMMconfigPkg.version - the version of the package + * @property {string} WPMMconfigPkg.source - the source of the package + */ + +/** + * @typedef WPMMconfig + * @property {string} WPMMconfig.name - The name of the website + * @property {Object} WPMMconfig.wordpress - The WordPress pkg related data for the website + * @property {string} WPMMconfig.wordpress.version - The current version of WordPress + * @property {string} WPMMconfig.wordpress.language - The language of WordPress + * @property {WPMMconfigWP} WPMMconfig.wordpress.config - The wp-config.php file data + * @property {WPMMconfigPkg[]} WPMMconfig.themes - The themes used + * @property {WPMMconfigPkg[]} WPMMconfig.plugins - The plugins used + * @property {WPMMconfigPkg[]} WPMMconfig.database - The database data + * @property {string[]} WPMMconfig.postInstall - The post-install actions run by wp-cli + */ + /** * Reads a PHP file, extracts, and returns the WordPress version number. * From 9b2d9878a7b4a7e0b5ba986a4831927e5ef8657f Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 19:20:29 +0100 Subject: [PATCH 09/70] Refactor getConfig function for better typing Added param typing and updated JSDoc comments in the getConfig function to improve readability and maintainability. It now provides a clear understanding of the data types used and the structure of the WPMMconfig object. --- lib/utils/index.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/utils/index.js b/lib/utils/index.js index 00570e0..a94ac31 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -61,17 +61,28 @@ function geVarFromPHPFile (fileContent, variableName = '$wp_version') { * Reads wp-package.json from the root folder and extracts the value of the --template option or uses the default. * The default config is used if no template is provided. Checks if the template file exists and reads it as JSON if it does. * - * @return {Object} The configuration object. + * @param {Object} args - The arguments object. + * @param {string} args.template - the path to the template file + * @return {WPMMconfig} The configuration object. */ function getConfig (args) { -// Read wp-package.json from the root folder + /** + * Read wp-package.json from the root folder + */ const defaultConfigFile = path.join(process.cwd(), 'wp-package.json'); - const defaultConfig = fs.existsSync(defaultConfigFile) ? JSON.parse(fs.readFileSync(defaultConfigFile, 'utf8')) : {}; + /** + * The default config is used if no template is provided + * @type {WPMMconfig} defaultConfig - The default configuration object + */ + const defaultConfig = JSON.parse(fs.readFileSync(defaultConfigFile, 'utf8')); // Extract the value of the --template option or use the default const templatePath = args.template || 'wp-package.json'; - // The default config is used if no template is provided + /** + * The user-defined configuration object + * @type {WPMMconfig} config - The configuration object + */ let config = defaultConfig; // Check if the template file exists and read it as JSON if it does From 29655bf0554be1c507b5dad7390eae383687909f Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 19:33:33 +0100 Subject: [PATCH 10/70] Improve code documentation and implement proper naming The commit introduced explicit types for parameters to improve code readibility and maintainability. Furthermore, JSDoc comments were added providing clear descriptions of the function, their parameters, and expected return types. Renamed file initialize.js to Initialize.js for consistency in the code base. --- lib/Database.js | 24 ++++++++++-- lib/{initialize.js => Initialize.js} | 10 +++++ lib/Installer.js | 2 +- lib/Package.js | 18 +++++---- lib/utils/index.js | 56 ++++++++++++++-------------- 5 files changed, 69 insertions(+), 41 deletions(-) rename lib/{initialize.js => Initialize.js} (87%) diff --git a/lib/Database.js b/lib/Database.js index 4418743..e28a5f5 100644 --- a/lib/Database.js +++ b/lib/Database.js @@ -4,11 +4,23 @@ const mysqldump = require('mysqldump'); const fs = require('fs').promises; class Database { + /** + * A description of the entire function. + * + * @param {WPMMconfig} config - The configuration object. + */ constructor (config) { // Load configuration from wp-package.json this.config = config; } + /** + * Uploads a database by executing SQL queries from a specified file. + * + * @async + * @param {string} sqlFilePath - The path to the SQL file. + * @return {Promise} - A Promise that resolves to a MySQL Connection object or throws an Error. + */ async uploadDatabase (sqlFilePath) { try { console.log('Uploading database...'); @@ -35,9 +47,13 @@ class Database { } console.log('Database uploaded successfully'); - return connection.end(); + connection.end().then(() => { + console.log('πŸ‘ Database connection closed'); + }).catch(error => { + console.error('🫠 Error closing database connection:', error.message); + }); } catch (error) { - console.error('Error uploading database:', error.message); + console.error('πŸ”΄ Error uploading database:', error.message); } } @@ -63,9 +79,9 @@ class Database { compressFile: true }); - console.log('Database dumped successfully'); + console.log('πŸš€ Database dumped successfully'); } catch (error) { - console.error('Error dumping database:', error.message); + console.error(' πŸ”΄ Error dumping database:', error.message); } } } diff --git a/lib/initialize.js b/lib/Initialize.js similarity index 87% rename from lib/initialize.js rename to lib/Initialize.js index 3965e4a..a039c36 100644 --- a/lib/initialize.js +++ b/lib/Initialize.js @@ -8,11 +8,21 @@ class Initialize { this.outputPath = path.join(this.wpFolder, 'wp-package.json'); } + /** + * Generates the configuration file for Wordpress. + * + * @return {void} + */ generateConfig () { const name = this.options.name || 'wordpress'; const language = this.options.language || 'en_US'; const version = this.options.version || '6.4.1'; + /** + * The default configuration object + * + * @type {WPMMconfig} defaultConfig - The default configuration object + */ const defaultConfig = { DB_NAME: 'my_db_name', DB_USER: 'my_username', diff --git a/lib/Installer.js b/lib/Installer.js index 877cb61..50a654a 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -10,7 +10,7 @@ class Installer { /** * Initializes a new instance of the constructor. * - * @param {Object} config - The configuration object for the constructor. + * @param {WPMMconfig} config - The configuration object for the constructor. */ constructor (config) { this.config = config; diff --git a/lib/Package.js b/lib/Package.js index 4cfd6ed..a5cd97f 100644 --- a/lib/Package.js +++ b/lib/Package.js @@ -13,18 +13,20 @@ const { renameFolder } = require('./utils/fs'); +/** + * @typedef {Object} WPMMpaths - the object containing the paths + * @property {string} rootFolder - the root folder of the application + * @property {string} tempDir - the temporary directory + * @property {string} baseFolder - the script base directory + * @property {string} destFolder - The destination folder for package installation. + */ + class Package { /** * Constructs a new instance of the class. * - * @param {Object} config - the configuration object - * @param {string} config.name - the WordPress website name (used as root folder name) - * @param {string} config.version - the WordPress package version - * @param {Object} paths - the object containing the paths - * @param {string} paths.rootFolder - the root folder of the application - * @param {string} paths.tempDir - the temporary directory - * @param {string} paths.baseFolder - the script base directory - * @param {string} paths.destFolder - The destination folder for package installation. + * @param {WPMMconfig} config - the configuration object + * @param {WPMMpaths} paths - the object containing the paths */ constructor (config, paths) { this.config = config; diff --git a/lib/utils/index.js b/lib/utils/index.js index a94ac31..f2a4f6f 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -3,40 +3,40 @@ const fs = require('fs'); const { exec } = require('node:child_process'); /** - * @typedef WPMMconfigWP - * @property {string} WPMMconfigWP.DB_NAME - The name of the database - * @property {string} WPMMconfig.DB_USER - The username for the database - * @property {string} WPMMconfig.DB_PASSWORD - The password for the database - * @property {string} WPMMconfig.DB_HOST - The host name or IP address of the database server - * @property {string} WPMMconfig.DB_CHARSET - The character set for the database - * @property {string} WPMMconfig.DB_COLLATE - The collation for the database (usually utf8_general_ci) - * @property {string} WPMMconfig.table_prefix - The table prefix for the database - * @property {boolean} WPMMconfig.WP_DEBUG - Whether to enable debugging - * @property {string} WPMMconfig.WP_SITEURL - The URL of the website - * @property {string} WPMMconfig.WP_HOME - The home URL of the website - * @property {string} WPMMconfig.WP_CONTENT_DIR - The path to the content directory - * @property {string} WPMMconfig.WP_CONTENT_URL - The URL of the content directory - * @property {boolean} WPMMconfig.DISALLOW_FILE_EDIT - Whether to disallow file editing + * @typedef WPMMconfigWP - The configuration object for WordPress + * @property {string} DB_NAME - The name of the database + * @property {string} DB_USER - The username for the database + * @property {string} DB_PASSWORD - The password for the database + * @property {string} DB_HOST - The host name or IP address of the database server + * @property {string} DB_CHARSET - The character set for the database + * @property {string} DB_COLLATE - The collation for the database (usually utf8_general_ci) + * @property {string} table_prefix - The table prefix for the database + * @property {boolean} WP_DEBUG - Whether to enable debugging + * @property {string} WP_SITEURL - The URL of the website + * @property {string} WP_HOME - The home URL of the website + * @property {string} WP_CONTENT_DIR - The path to the content directory + * @property {string} WP_CONTENT_URL - The URL of the content directory + * @property {boolean} DISALLOW_FILE_EDIT - Whether to disallow file editing */ /** - * @typedef WPMMconfigPkg - * @property {string} WPMMconfigPkg.name - the name of the package - * @property {string} WPMMconfigPkg.version - the version of the package - * @property {string} WPMMconfigPkg.source - the source of the package + * @typedef WPMMconfigPkg - The package object + * @property {string} name - the name of the package + * @property {string} version - the version of the package + * @property {string} source - the source of the package */ /** - * @typedef WPMMconfig - * @property {string} WPMMconfig.name - The name of the website - * @property {Object} WPMMconfig.wordpress - The WordPress pkg related data for the website - * @property {string} WPMMconfig.wordpress.version - The current version of WordPress - * @property {string} WPMMconfig.wordpress.language - The language of WordPress - * @property {WPMMconfigWP} WPMMconfig.wordpress.config - The wp-config.php file data - * @property {WPMMconfigPkg[]} WPMMconfig.themes - The themes used - * @property {WPMMconfigPkg[]} WPMMconfig.plugins - The plugins used - * @property {WPMMconfigPkg[]} WPMMconfig.database - The database data - * @property {string[]} WPMMconfig.postInstall - The post-install actions run by wp-cli + * @typedef WPMMconfig - The configuration object + * @property {string} name - The name of the website + * @property {Object} wordpress - The WordPress pkg related data for the website + * @property {string} wordpress.version - The current version of WordPress + * @property {string} wordpress.language - The language of WordPress + * @property {WPMMconfigWP} wordpress.config - The wp-config.php file data + * @property {WPMMconfigPkg[]} themes - The themes used + * @property {WPMMconfigPkg[]} plugins - The plugins used + * @property {WPMMconfigPkg[]} database - The database data + * @property {string[]} postInstall - The post-install actions run by wp-cli */ /** From ccca81669f82cd69e5e85d47c7e259329ab64201 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 19:51:13 +0100 Subject: [PATCH 11/70] Add comprehensive JSDoc comments and refactor variable names The update significantly improves code clarity and readability by adding comprehensive JSDoc comments to classes, functions, and variables across multiple files. In addition, it includes refactoring of parameters and variables for consistent naming conventions and precise type clarity with the introduction of explicit typing. The file initialize.js was also renamed to Initialize.js to maintain codebase consistency and adhere to naming standards. --- lib/Database.js | 6 ++++++ lib/Dump.js | 39 ++++++++++++++++++++++++++++++++++++++- lib/Initialize.js | 14 ++++++++++++-- lib/Installer.js | 4 +++- lib/Package.js | 15 ++++++++++----- lib/PluginPackage.js | 6 ++++++ lib/ThemePackage.js | 6 ++++++ lib/Updater.js | 27 +++++++++++++++++++++++++++ lib/WpPackage.js | 8 +++++++- lib/index.js | 6 +++--- lib/utils/index.js | 13 +++++++++++++ 11 files changed, 131 insertions(+), 13 deletions(-) diff --git a/lib/Database.js b/lib/Database.js index e28a5f5..2c76ce3 100644 --- a/lib/Database.js +++ b/lib/Database.js @@ -3,6 +3,12 @@ const { getConnectionSettings } = require('./utils'); const mysqldump = require('mysqldump'); const fs = require('fs').promises; +/** + * Constructor for the Database class. + * + * @class Database + * @param {WPMMconfig} config - The configuration object. + */ class Database { /** * A description of the entire function. diff --git a/lib/Dump.js b/lib/Dump.js index 21dcd0e..503fb81 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -2,13 +2,32 @@ const fs = require('fs'); const path = require('path'); const { geVarFromPHPFile } = require('./utils'); +/** + * Represents a Dump class for WordPress configuration. + * + * @class Dump + */ class Dump { - constructor () {W + /** + * Constructor for the class. + * + * Initializes the class with the necessary folders for WordPress. + */ + constructor () { this.wpFolder = process.cwd(); this.themeFolder = path.join(this.wpFolder, 'wp-content', 'themes'); this.pluginsFolder = path.join(this.wpFolder, 'wp-content', 'plugins'); } + /** + * Initializes the function by logging the `wpFolder` and `themeFolder` properties, + * scanning the theme and plugins directories, retrieving the website name from the + * `wpFolder` path, getting the WordPress version from `wp-includes/version.php`, + * determining the language using `Intl.DateTimeFormat().resolvedOptions().locale`, + * and saving the result to a JSON file. + * + * @return {void} + */ init () { console.log(this.wpFolder); console.log(this.themeFolder); @@ -41,6 +60,12 @@ class Dump { console.log(`πŸ†— Wordpress configuration Dump completed. Configuration saved to ${outputPath}`); } + /** + * Scans a directory and returns an array of objects containing the name and version of each item found. + * + * @param {string} directory - The path of the directory to scan. + * @return {Array} - An array of objects with the name and version of each item found. + */ scanDirectory (directory) { const items = fs.readdirSync(directory); const result = []; @@ -76,12 +101,24 @@ class Dump { return result; } + /** + * Extracts the version number from a style file. + * + * @param {string} filePath - The path to the style file. + * @return {string|null} The version number extracted from the style file, or null if no match was found. + */ extractVersionFromStyleFile (filePath) { const content = fs.readFileSync(filePath, 'utf8'); const match = /Version:\s*([\d.]+)/i.exec(content); return match ? match[1] : null; } + /** + * Extracts the version number from a PHP file. + * + * @param {string} filePath - The path to the PHP file. + * @return {string|null} The version number extracted from the file, or null if no version is found. + */ extractVersionFromPHPFile (filePath) { const content = fs.readFileSync(filePath, 'utf8'); const match = /Version:\s*([\d.]+)/i.exec(content); diff --git a/lib/Initialize.js b/lib/Initialize.js index a039c36..56fa67a 100644 --- a/lib/Initialize.js +++ b/lib/Initialize.js @@ -1,9 +1,19 @@ const fs = require('fs'); const path = require('path'); +/** + * Initialize class for generating WordPress configuration file. + * + * @class Initialize + */ class Initialize { - constructor (options) { - this.options = options || {}; + /** + * Constructor for a new instance of the class. + * + * @param {WPMMconfig} config - Optional configuration options. + */ + constructor (config) { + this.options = config || {}; this.wpFolder = process.cwd(); this.outputPath = path.join(this.wpFolder, 'wp-package.json'); } diff --git a/lib/Installer.js b/lib/Installer.js index 50a654a..487e36f 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -4,7 +4,9 @@ const { cleanup, makeDir } = require('./utils/fs.js'); const { WordPressPackage, PluginPackage, ThemePackage } = require('./package'); /** - * The wpmm class represents the WordPress installer and provides methods for installing WordPress and its dependencies. + * The Installer class represents the WordPress installer and provides methods for installing WordPress and its dependencies. + * + * @class Installer */ class Installer { /** diff --git a/lib/Package.js b/lib/Package.js index a5cd97f..1ffa776 100644 --- a/lib/Package.js +++ b/lib/Package.js @@ -14,13 +14,18 @@ const { } = require('./utils/fs'); /** - * @typedef {Object} WPMMpaths - the object containing the paths - * @property {string} rootFolder - the root folder of the application - * @property {string} tempDir - the temporary directory - * @property {string} baseFolder - the script base directory + * @typedef {Object} WPMMpaths - The object containing the paths + * @property {string} rootFolder - The root folder of the application + * @property {string} tempDir - The temporary directory + * @property {string} baseFolder - The path to the WordPress folder. Defaults to the current working directory. * @property {string} destFolder - The destination folder for package installation. */ +/** + * Represents a package and provides methods to download and install it. + * + * @class Package + */ class Package { /** * Constructs a new instance of the class. @@ -121,7 +126,7 @@ class Package { /** * Installs a package with the given name, version, and type. * - * @param config - The configuration object. + * @param {WPMMconfig} config - The configuration object. * @param {string} packageType - The type of the package ('theme' or 'plugin'). * @return {Promise} - A promise that resolves once the package is downloaded and installed. */ diff --git a/lib/PluginPackage.js b/lib/PluginPackage.js index b358bdd..6f480ed 100644 --- a/lib/PluginPackage.js +++ b/lib/PluginPackage.js @@ -1,5 +1,11 @@ const Package = require('./package'); +/** + * Represents a theme package. + * + * @class PluginPackage + * @extends Package + */ export class PluginPackage extends Package { /** * Asynchronously installs a plugin. diff --git a/lib/ThemePackage.js b/lib/ThemePackage.js index b0aca70..8ff8869 100644 --- a/lib/ThemePackage.js +++ b/lib/ThemePackage.js @@ -1,5 +1,11 @@ const Package = require('./package'); +/** + * Represents a theme package. + * + * @class ThemePackage + * @extends Package + */ export class ThemePackage extends Package { /** * Installs a package with the specified name and version as a theme. diff --git a/lib/Updater.js b/lib/Updater.js index bacc73c..f29d047 100644 --- a/lib/Updater.js +++ b/lib/Updater.js @@ -1,11 +1,28 @@ const { exec } = require('child_process'); +/** + * Represents the Updater class. + * + * @class Updater + */ class Updater { + /** + * Constructs a new instance of the class. + * + * @param {WPMMconfig} config - the configuration object + */ constructor (config) { // Load configuration from wp-package.json this.config = config; } + /** + * Update plugins. + * + * @async + * @function updatePlugins - Updates plugins using the `wp plugin install` command. + * @throws {Error} If there is an error updating the plugins. + */ async updatePlugins () { try { const plugins = this.config.plugins; @@ -31,6 +48,11 @@ class Updater { } } + /** + * Updates the themes in the configuration by installing and activating them. + * + * @return {void} + */ async updateThemes () { try { const themes = this.config.themes; @@ -56,6 +78,11 @@ class Updater { } } + /** + * Updates the WordPress installation. + * + * @return {Promise} A promise that resolves when the update is complete. + */ async updateWordPress () { try { const command = 'wp core update'; diff --git a/lib/WpPackage.js b/lib/WpPackage.js index c17723e..0cdcfa1 100644 --- a/lib/WpPackage.js +++ b/lib/WpPackage.js @@ -1,9 +1,15 @@ const { getWordPressDownloadUrl, replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/index.js'); const path = require('path'); -const fs = require('lib/utils/fs'); +const fs = require('fs'); const { renameFolder } = require('./utils/fs'); const Package = require('./package'); +/** + * Represents a WordPress package that can be installed and configured. + * + * @class WpPackage + * @extends Package + */ export class WpPackage extends Package { /** * Installs WordPress with the specified version and language. diff --git a/lib/index.js b/lib/index.js index 6db5a8b..290a766 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,13 +8,13 @@ const Initialize = require('./initialize.js'); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); -// Get the arguments from the command line +/** @var {yargs} argv - The command line arguments. */ const argv = yargs(hideBin(process.argv)).argv; -// Get the config +/** @var {WPMMconfig} config - The configuration object for the script. */ const config = getConfig(argv); -// Start the timer +/** @var {number} startTime - the time at which the script started. */ const startTime = Date.now(); const actions = { diff --git a/lib/utils/index.js b/lib/utils/index.js index f2a4f6f..2b97183 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -188,6 +188,13 @@ async function installNpmPackages (packageDirectory) { }); } +/** + * Installs composer dependencies and generates autoloader based on composer.json file. + * + * @param {string} repoPath - The path to the repository where composer.json is located. + * + * @returns {Promise} - A promise resolving when the installation process is completed. + */ async function installComposer (repoPath) { console.log('🎻 Found composer.json'); await exec('composer install --no-dev', { cwd: repoPath }); @@ -287,6 +294,12 @@ function getInfo (config, actions) { console.log('πŸš€ Command line available actions: ' + JSON.stringify(actionsKeys, null, 2)); } +/** + * Logs the time passed in milliseconds since the given start time. + * + * @param {number} startTime - The start time in milliseconds. + * @return {undefined} + */ function printTimePassed (startTime) { // End the timer const endTime = Date.now(); From 33fba9ca2ca806cd6842f5577549dc287e5c38de Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Fri, 8 Dec 2023 20:30:49 +0100 Subject: [PATCH 12/70] Refactor codebase for clarity and add JSDoc comments This commit introduces comprehensive JSDoc comments to classes, functions, and variables for better understanding of the code and its functionality. It ensures parameters and variables follow a consistent naming convention and enhances type clarity with specific typing. It also includes a minor renaming of a file from initialize.js to Initialize.js for consistency. --- lib/index.js | 69 +++++++++++++++++++++++++++++++++++++++++++--- lib/utils/index.js | 37 ------------------------- package.json | 4 +-- readme.md | 11 ++++++++ 4 files changed, 78 insertions(+), 43 deletions(-) diff --git a/lib/index.js b/lib/index.js index 290a766..d845180 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,6 +8,43 @@ const Initialize = require('./initialize.js'); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); +/** + * @typedef WPMMconfigWP - The configuration object for WordPress + * @property {string} DB_NAME - The name of the database + * @property {string} DB_USER - The username for the database + * @property {string} DB_PASSWORD - The password for the database + * @property {string} DB_HOST - The host name or IP address of the database server + * @property {string} DB_CHARSET - The character set for the database + * @property {string} DB_COLLATE - The collation for the database (usually utf8_general_ci) + * @property {string} table_prefix - The table prefix for the database + * @property {boolean} WP_DEBUG - Whether to enable debugging + * @property {string} WP_SITEURL - The URL of the website + * @property {string} WP_HOME - The home URL of the website + * @property {string} WP_CONTENT_DIR - The path to the content directory + * @property {string} WP_CONTENT_URL - The URL of the content directory + * @property {boolean} DISALLOW_FILE_EDIT - Whether to disallow file editing + */ + +/** + * @typedef WPMMconfigPkg - The package object + * @property {string} name - the name of the package + * @property {string} version - the version of the package + * @property {string} source - the source of the package + */ + +/** + * @typedef WPMMconfig - The configuration object + * @property {string} name - The name of the website + * @property {Object} wordpress - The WordPress pkg related data for the website + * @property {string} wordpress.version - The current version of WordPress + * @property {string} wordpress.language - The language of WordPress + * @property {WPMMconfigWP} wordpress.config - The wp-config.php file data + * @property {WPMMconfigPkg[]} themes - The themes used + * @property {WPMMconfigPkg[]} plugins - The plugins used + * @property {WPMMconfigPkg[]} database - The database data + * @property {string[]} postInstall - The post-install actions run by wp-cli + */ + /** @var {yargs} argv - The command line arguments. */ const argv = yargs(hideBin(process.argv)).argv; @@ -17,34 +54,58 @@ const config = getConfig(argv); /** @var {number} startTime - the time at which the script started. */ const startTime = Date.now(); +/** + * The actions object. + * + * @param {WPMMconfig} config - The configuration object. + * @param {Object} actions - The actions object. + */ const actions = { + /** + * Retrieve information using the provided configuration and actions. + */ info: () => { getInfo(config, actions); }, + /** + * Dumps the current WordPress installation data. + * This function creates a new Dump instance and initializes it. + */ dump: () => { const dump = new Dump(this); dump.init(); }, + /** + * Initialize the WordPress installation. + */ init: () => { const initializer = new Initialize(config); initializer.generateConfig(); }, - 'upload-database': () => { + /** + * Upload a database by executing SQL queries from a specified file. + */ + 'upload-database': (file) => { const db = new Database(config); - db.uploadDatabase(config.database.filename).then(() => { + db.uploadDatabase(file || config.database.filename).then(() => { console.log('πŸš€ Database uploaded successfully.'); }); }, + /** + * Dump the current WordPress database. + */ 'dump-database': () => { const date = new Date().getUTCDate(); - const newDbName = `${config.wordpress.config_DB_Name}-${date}.sql.gz`; + const newDbName = `${config.wordpress.config.DB_NAME}-${date}.sql.gz`; const db = new Database(config); db.dumpDatabase(newDbName).then(() => { console.log('πŸš€ Database dumped successfully to', newDbName, '.'); }); }, + /** + * install WordPress + */ default: () => { - // Install WordPress const installer = new Installer(config); installer.run().then(() => { diff --git a/lib/utils/index.js b/lib/utils/index.js index 2b97183..8d40fb3 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -2,43 +2,6 @@ const path = require('path'); const fs = require('fs'); const { exec } = require('node:child_process'); -/** - * @typedef WPMMconfigWP - The configuration object for WordPress - * @property {string} DB_NAME - The name of the database - * @property {string} DB_USER - The username for the database - * @property {string} DB_PASSWORD - The password for the database - * @property {string} DB_HOST - The host name or IP address of the database server - * @property {string} DB_CHARSET - The character set for the database - * @property {string} DB_COLLATE - The collation for the database (usually utf8_general_ci) - * @property {string} table_prefix - The table prefix for the database - * @property {boolean} WP_DEBUG - Whether to enable debugging - * @property {string} WP_SITEURL - The URL of the website - * @property {string} WP_HOME - The home URL of the website - * @property {string} WP_CONTENT_DIR - The path to the content directory - * @property {string} WP_CONTENT_URL - The URL of the content directory - * @property {boolean} DISALLOW_FILE_EDIT - Whether to disallow file editing - */ - -/** - * @typedef WPMMconfigPkg - The package object - * @property {string} name - the name of the package - * @property {string} version - the version of the package - * @property {string} source - the source of the package - */ - -/** - * @typedef WPMMconfig - The configuration object - * @property {string} name - The name of the website - * @property {Object} wordpress - The WordPress pkg related data for the website - * @property {string} wordpress.version - The current version of WordPress - * @property {string} wordpress.language - The language of WordPress - * @property {WPMMconfigWP} wordpress.config - The wp-config.php file data - * @property {WPMMconfigPkg[]} themes - The themes used - * @property {WPMMconfigPkg[]} plugins - The plugins used - * @property {WPMMconfigPkg[]} database - The database data - * @property {string[]} postInstall - The post-install actions run by wp-cli - */ - /** * Reads a PHP file, extracts, and returns the WordPress version number. * diff --git a/package.json b/package.json index 844e90e..d9919d2 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "url": "https://github.com/erikyo/wpmm.git" }, "scripts": { - "test": "jest", - "docs": "npx jsdoc lib/index.js -d docs -c jsdoc.json" + "lint": "eslint ./lib ./test", + "test": "jest" }, "bin": { "wpmm": "lib/index.js" diff --git a/readme.md b/readme.md index 8cf89e4..0fb1017 100644 --- a/readme.md +++ b/readme.md @@ -26,10 +26,21 @@ WordPress Installer is a Node.js script designed to streamline the installation Installs the WordPress version, themes, and plugins defined in wp-package.json Whenever the configuration file is not found, the command will install the last WordPress version. +### `npx wpmm --version` +### `npx wpmm --v` +output the current wpmm version + +### `npx wpmm --info` +Returns the information for the Wordpress installation in the current folder ### `npx wpmm --init` Initialize the project and create a sample wp-package.json file. +### `npx wpmm --upload-database database/my.sql` +Upload a database named my.sql into the wordpress database + +### `npx wpmm --dump-database` +Download the current wp database and save it into /backups/${databasename}.sql.gz ### `npx wpmm --dump` Scan and extract version information from PHP files within themes and plugins. From e68283ebad397d0e3c506217812856a457591a14 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:28:56 +0100 Subject: [PATCH 13/70] Add user locale function and improve code readability Added a function to fetch user locale, removed unused functions, improved some variable labels and made small enhancements in scripts for better readability. Also replaced manual string concatenations with template literals. Took 3 hours 18 minutes --- lib/utils/index.js | 101 +++++++++++---------------------------------- 1 file changed, 23 insertions(+), 78 deletions(-) diff --git a/lib/utils/index.js b/lib/utils/index.js index 8d40fb3..e515516 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -20,24 +20,29 @@ function geVarFromPHPFile (fileContent, variableName = '$wp_version') { return match ? match[1] : null; } +/** + * Returns the locale of the user. + * + * @returns {string} The locale of the user. + */ +function getUserLocale() { + return Intl.DateTimeFormat().resolvedOptions().locale; +} + /** * Reads wp-package.json from the root folder and extracts the value of the --template option or uses the default. * The default config is used if no template is provided. Checks if the template file exists and reads it as JSON if it does. * - * @param {Object} args - The arguments object. + * @param {any} args - The arguments object. * @param {string} args.template - the path to the template file * @return {WPMMconfig} The configuration object. */ function getConfig (args) { /** - * Read wp-package.json from the root folder - */ - const defaultConfigFile = path.join(process.cwd(), 'wp-package.json'); - /** - * The default config is used if no template is provided + * The default config from the root plugin folder. This is used if no template is provided * @type {WPMMconfig} defaultConfig - The default configuration object */ - const defaultConfig = JSON.parse(fs.readFileSync(defaultConfigFile, 'utf8')); + const defaultConfig = JSON.parse(fs.readFileSync(path.join(__dirname, '..','..', 'wp-package.json'), 'utf8')); // Extract the value of the --template option or use the default const templatePath = args.template || 'wp-package.json'; @@ -63,7 +68,7 @@ function getConfig (args) { * Returns the connection settings based on the provided config. * * @param {object} config - The configuration object containing the host, user, password, and database details. - * @return {object} - The connection settings object with the following properties: + * @return {WPMMconfig} - The connection settings object with the following properties: * - connectionLimit: The maximum number of connections allowed. * - host: The host name or IP address of the database server. * - user: The username for authenticating with the database server. @@ -73,10 +78,10 @@ function getConfig (args) { function getConnectionSettings (config) { return { connectionLimit: 5, - host: this.config.host, - user: this.config.user, - password: this.config.password, - database: this.config.database + host: config.host, + user: config.user, + password: config.password, + database: config.database }; } @@ -164,63 +169,6 @@ async function installComposer (repoPath) { await exec('composer dumpautoload -o', { cwd: repoPath }); } -/** - * Replaces a constant in the wp-config.php file with a user-defined value. - * - * @param {string} configContent - The content of the wp-config.php file. - * @param {string} constantName - The name of the constant to replace. - * @param {string} userDefinedValue - The user-defined value to set for the constant. - * @return {string} - The updated content with the replaced constant. - */ -function replaceDbConstant (configContent, constantName, userDefinedValue) { - const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*'[^']*'\\s*\\);`); - return configContent.replace(regex, `define( '${constantName}', '${userDefinedValue}' );`); -} - -function replaceDbConstantBool (configContent, constantName, userDefinedValue) { - // Updated regex to handle boolean constants (TRUE or FALSE) - const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*[^']*\\s*\\);`); - return configContent.replace(regex, `define( '${constantName}', ${userDefinedValue} );`); -} - -/** - * Generates a random salt code for WordPress configuration. - * - * @return {string} - The generated salt code. - */ -function generateSalt () { - const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?/'; - const saltLength = 64; - return Array.from({ length: saltLength }, () => charset[Math.floor(Math.random() * charset.length)]).join(''); -} - -/** - * Replaces empty salts in the WordPress configuration with generated salt codes. - * - * @param {string} configContent - The content of the wp-config.php file. - * @return {string} - The updated content with replaced salts. - */ -function replaceEmptySalts (configContent) { - const saltConstants = [ - 'AUTH_KEY', - 'SECURE_AUTH_KEY', - 'LOGGED_IN_KEY', - 'NONCE_KEY', - 'AUTH_SALT', - 'SECURE_AUTH_SALT', - 'LOGGED_IN_SALT', - 'NONCE_SALT' - ]; - - saltConstants.forEach((constant) => { - const emptySaltRegex = new RegExp(`define\\(\\s*'${constant}'\\s*,\\s*'put your unique phrase here'\\s*\\);`); - const generatedSalt = generateSalt(); - configContent = configContent.replace(emptySaltRegex, `define( '${constant}', '${generatedSalt}' );`); - }); - - return configContent; -} - /** * Checks if WP-CLI is available. * @@ -246,9 +194,9 @@ async function isWPCLIAvailable () { function getInfo (config, actions) { const version = require('../../package.json').version; console.log('πŸ“¦ WPMM version: ' + version.toString()); - console.log('Node version: ' + process.version); - console.log('OS: ' + process.platform + ' ' + process.arch); - console.log('Current working directory: ' + process.cwd()); + console.log(`Node version: ${process.version}`); + console.log(`OS: ${process.platform} ${process.arch}`); + console.log(`Current working directory: ${process.cwd()}`); console.log('------------------------------------------'); console.log('πŸ”§ Configuration: ' + JSON.stringify(config, null, 2)); console.log('*******************'); @@ -268,12 +216,13 @@ function printTimePassed (startTime) { const endTime = Date.now(); // Calculate the time passed - const timePassed = endTime - startTime; - console.log(`πŸ•’ Time passed: ${timePassed} milliseconds`); + const timePassed = (endTime - startTime) / 1000; + console.log(`πŸ•’ Time elapsed: ${timePassed} seconds`); } module.exports = { geVarFromPHPFile, + getUserLocale, getConfig, getConnectionSettings, getWordPressDownloadUrl, @@ -281,10 +230,6 @@ module.exports = { getInfo, installNpmPackages, installComposer, - replaceDbConstant, - replaceDbConstantBool, - generateSalt, - replaceEmptySalts, isWPCLIAvailable, printTimePassed }; From fa8c1ec4bbe282aaf994f47c9d2c7fb057f5d3ae Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:29:09 +0100 Subject: [PATCH 14/70] Create WordPress configuration utility functions Added new utility functions in the wpConfig.js file. These functions are used to fetch and modify the WordPress configuration details such as getting the latest version of WordPress, getting the WordPress installation paths, fetching the current WordPress version and locale information, replacing database constants in wp-config.php, generating salt codes, and replacing empty salts in the configuration. Took 14 seconds --- lib/utils/wpConfig.js | 126 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 lib/utils/wpConfig.js diff --git a/lib/utils/wpConfig.js b/lib/utils/wpConfig.js new file mode 100644 index 0000000..9e09746 --- /dev/null +++ b/lib/utils/wpConfig.js @@ -0,0 +1,126 @@ +const path = require("path"); +const fs = require("fs"); +const {geVarFromPHPFile} = require("./index"); +const axios = require("axios"); + +/** + * Gets the default paths for the WordPress installation. + * @param {Object} config - The configuration object for the WordPress installation. + * @param {string} [rootFolder=process.cwd()] - The root folder path for the WordPress installation. Defaults to the current working directory. + * @param {string} [baseFolder='wordpress'] - The base folder path for the WordPress installation. Defaults to 'wordpress'. + * @return {WPMMpaths} - An object containing the default paths for the WordPress installation. + */ +function getWordPressPaths (config, rootFolder = process.cwd()) { + return { + tempDir: path.join(rootFolder, 'temp'), + rootFolder: rootFolder, + baseFolder: path.join(rootFolder, config.name ?? 'wordpress'), + pluginsFolder: path.join(this.baseFolder, 'wp-content', 'plugins'), + themeFolder: path.join(this.baseFolder, 'wp-content', 'themes') + }; +} + + + +/** + * Fetch the WordPress version from WordPress.org + * + * @async + * @return {string} the version of WordPress + */ +async function getLastWpVersion () { + const response = await axios.get('https://api.wordpress.org/core/version-check/1.7/') + .then(() => { + if (response.data?.offers && response.data.offers.length > 0) { + return response.data.offers[0]; + } else { + throw new Error('❗Cannot get the last version available'); + } + }); +} + +/** + * Retrieves the WordPress version and locale information from a given WordPress folder. + * + * @param {string} wpFolder - The path to the WordPress folder. + * @returns {object} - An object containing the version and locale information. + */ +function getCurrentWpInfo(wpFolder) { + + // get the WordPress version and the locale from wp-includes/version.php + const versionFile = path.join(wpFolder, 'wp-includes', 'version.php'); + const versionFileContent = fs.readFileSync(versionFile, 'utf8'); + const version = geVarFromPHPFile(versionFileContent, 'wp_version'); + const locale = geVarFromPHPFile(versionFileContent, 'wp_local_package'); + return { + version, + locale + }; +} + +/** + * Replaces a constant in the wp-config.php file with a user-defined value. + * + * @param {string} configContent - The content of the wp-config.php file. + * @param {string} constantName - The name of the constant to replace. + * @param {string} userDefinedValue - The user-defined value to set for the constant. + * @return {string} - The updated content with the replaced constant. + */ +function replaceDbConstant (configContent, constantName, userDefinedValue) { + const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*'[^']*'\\s*\\);`); + return configContent.replace(regex, `define( '${constantName}', '${userDefinedValue}' );`); +} + +function replaceDbConstantBool (configContent, constantName, userDefinedValue) { + // Updated regex to handle boolean constants (TRUE or FALSE) + const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*[^']*\\s*\\);`); + return configContent.replace(regex, `define( '${constantName}', ${userDefinedValue} );`); +} + +/** + * Generates a random salt code for WordPress configuration. + * + * @return {string} - The generated salt code. + */ +function generateSalt () { + const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?/'; + const saltLength = 64; + return Array.from({ length: saltLength }, () => charset[Math.floor(Math.random() * charset.length)]).join(''); +} + +/** + * Replaces empty salts in the WordPress configuration with generated salt codes. + * + * @param {string} configContent - The content of the wp-config.php file. + * @return {string} - The updated content with replaced salts. + */ +function replaceEmptySalts (configContent) { + const saltConstants = [ + 'AUTH_KEY', + 'SECURE_AUTH_KEY', + 'LOGGED_IN_KEY', + 'NONCE_KEY', + 'AUTH_SALT', + 'SECURE_AUTH_SALT', + 'LOGGED_IN_SALT', + 'NONCE_SALT' + ]; + + saltConstants.forEach((constant) => { + const emptySaltRegex = new RegExp(`define\\(\\s*'${constant}'\\s*,\\s*'put your unique phrase here'\\s*\\);`); + const generatedSalt = generateSalt(); + configContent = configContent.replace(emptySaltRegex, `define( '${constant}', '${generatedSalt}' );`); + }); + + return configContent; +} + +module.exports = { + getLastWpVersion, + getWordPressPaths, + getCurrentWpInfo, + replaceDbConstant, + replaceDbConstantBool, + generateSalt, + replaceEmptySalts +}; From d3b28f6bf664f5754f604256fb3e251c94c455c4 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:29:19 +0100 Subject: [PATCH 15/70] Refactor Dump class for WordPress configuration Upgraded the Dump class constructor to utilize new configuration utility functions. Ensured the class utilizes these functions to fetch the current WordPress version and locale information, and also to set the root, theme, and plugins folders. This results in cleaner, more efficient, and maintainable code. Took 9 seconds --- lib/Dump.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/Dump.js b/lib/Dump.js index 503fb81..454ba38 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -1,6 +1,8 @@ const fs = require('fs'); const path = require('path'); -const { geVarFromPHPFile } = require('./utils'); +const { geVarFromPHPFile, getUserLocale} = require('./utils'); +const {config} = require("yargs"); +const {getWordPressPaths, getCurrentWpInfo} = require("./utils/wpConfig"); /** * Represents a Dump class for WordPress configuration. @@ -13,10 +15,10 @@ class Dump { * * Initializes the class with the necessary folders for WordPress. */ - constructor () { - this.wpFolder = process.cwd(); - this.themeFolder = path.join(this.wpFolder, 'wp-content', 'themes'); - this.pluginsFolder = path.join(this.wpFolder, 'wp-content', 'plugins'); + constructor (paths) { + this.wpFolder = paths.rootFolder; + this.themeFolder = paths.themeFolder; + this.pluginsFolder = paths.pluginsFolder; } /** @@ -37,12 +39,10 @@ class Dump { // the website name const name = path.basename(this.wpFolder); - // get the WordPress version from wp-includes/version.php - const versionFile = path.join(this.wpFolder, 'wp-includes', 'version.php'); - const versionFileContent = fs.readFileSync(versionFile, 'utf8'); - const version = geVarFromPHPFile(versionFileContent, 'wp_version'); + const wpInfo = getCurrentWpInfo(this.wpFolder); - const language = Intl.DateTimeFormat().resolvedOptions().locale; + const language = wpInfo.locale || getUserLocale(); + const version = wpInfo.version || this.getLastVersion(); const result = { name, From d75d1e956730a1ad39ee670949a1036b08b4d131 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:29:41 +0100 Subject: [PATCH 16/70] Correct typo in Initialize.js comments Corrected a typo in the comment on Initialize.js where WordPress was spelt as 'Wordpress'. Providing correct information in comments is essential for maintaining readability and understanding of the code. Took 23 seconds --- lib/Initialize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Initialize.js b/lib/Initialize.js index 56fa67a..082e33d 100644 --- a/lib/Initialize.js +++ b/lib/Initialize.js @@ -19,7 +19,7 @@ class Initialize { } /** - * Generates the configuration file for Wordpress. + * Generates the configuration file for WordPress. * * @return {void} */ From 96386a77e4906fb3bf2a0fd80a8fc799fb22dd34 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:29:54 +0100 Subject: [PATCH 17/70] Refactor WordPress installer in Installer.js The Installer.js class has been streamlined by introducing utility functions and generalizing Package class for WordPress, plugin, and theme installation. This refactoring provides a more modular approach which can now handle installing WordPress and its plugins and themes more efficiently. It also improves readability and maintainability of the code. Took 13 seconds --- lib/Installer.js | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/lib/Installer.js b/lib/Installer.js index 487e36f..02ef63d 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -1,8 +1,10 @@ -const path = require('path'); const { isWPCLIAvailable } = require('./utils/index.js'); const { cleanup, makeDir } = require('./utils/fs.js'); -const { WordPressPackage, PluginPackage, ThemePackage } = require('./package'); - +const Package = require("./Package"); +const { WordPressPackage } = require('./WpPackage'); +const { child_process } = require('node:child_process'); +const {getWordPressPaths, getLastWpVersion, getCurrentWpInfo} = require("./utils/wpConfig"); +const axios = require("axios"); /** * The Installer class represents the WordPress installer and provides methods for installing WordPress and its dependencies. * @@ -16,11 +18,10 @@ class Installer { */ constructor (config) { this.config = config; - this.rootFolder = process.cwd(); - this.tempDir = path.join(this.rootFolder, 'temp'); - this.baseFolder = path.join(this.rootFolder, config.name ?? 'wordpress'); - this.pluginsFolder = path.join(this.baseFolder, 'wp-content', 'plugins'); - this.themeFolder = path.join(this.baseFolder, 'wp-content', 'themes'); + + this.paths = getWordPressPaths(config, process.cwd()); + this.tempDir = this.paths.tempDir; + this.baseFolder = this.paths.baseFolder; } /** @@ -35,28 +36,26 @@ class Installer { makeDir(this.tempDir); const promises = []; + const wpJson = []; - // the default paths for the packages - const defaultPaths = { - rootFolder: this.rootFolder, - tempDir: this.tempDir, - baseFolder: this.baseFolder, - destFolder: this.baseFolder - }; - + // Install WordPress if (wordpress) { - const wpPackage = new WordPressPackage(this.config, defaultPaths); - await wpPackage.install(); + const wp = new WordPressPackage(this.config, 'wordpress', this.paths); + await wp.install(); } if (plugins) { - const pluginPackages = plugins.map((plugin) => new PluginPackage(plugin, { ...defaultPaths, destFolder: this.pluginsFolder })); - promises.push(...pluginPackages.map((pluginPackage) => pluginPackage.install())); + const pluginPackages = plugins.map((plugin) => new Package(plugin, 'plugin', this.paths)); + promises.push(...pluginPackages.map((pluginPackage) => pluginPackage.install().then(() => { + wpJson.push(pluginPackage.pkgInfo); + }))); } if (themes) { - const themePackages = themes.map((theme) => new ThemePackage(theme, { ...defaultPaths, destFolder: this.themeFolder })); - promises.push(...themePackages.map((themePackage) => themePackage.install())); + const themePackages = themes.map((theme) => new Package(theme, 'theme', this.paths)); + promises.push(...themePackages.map((themePackage) => themePackage.install.then(() => { + wpJson.push(themePackage.pkgInfo); + }))); } // Install plugins and themes concurrently @@ -80,7 +79,7 @@ class Installer { for (const command of commands) { try { console.log(`Executing: ${command}`); - const { stdout, stderr } = await exec(command); + const { stdout, stderr } = await child_process.exec(command); if (stdout) { console.log(`Command output:\n${stdout}`); } From c34e7e2462751cd00f5f159d11236b8d20c17cdc Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:30:06 +0100 Subject: [PATCH 18/70] Refactor Package class and remove Plugin/Theme specific classes Removed PluginPackage.js and ThemePackage.js, and refactored Package.js to generalize the handling of plugin and theme installation. This update results in a more cohesive approach, increasing code maintainability and enhancing the efficiency of installing WordPress, plugins, and themes. Took 12 seconds --- lib/Package.js | 81 +++++++++++++++++++++++--------------------- lib/PluginPackage.js | 22 ------------ lib/ThemePackage.js | 22 ------------ 3 files changed, 43 insertions(+), 82 deletions(-) delete mode 100644 lib/PluginPackage.js delete mode 100644 lib/ThemePackage.js diff --git a/lib/Package.js b/lib/Package.js index 1ffa776..21329bd 100644 --- a/lib/Package.js +++ b/lib/Package.js @@ -13,14 +13,6 @@ const { renameFolder } = require('./utils/fs'); -/** - * @typedef {Object} WPMMpaths - The object containing the paths - * @property {string} rootFolder - The root folder of the application - * @property {string} tempDir - The temporary directory - * @property {string} baseFolder - The path to the WordPress folder. Defaults to the current working directory. - * @property {string} destFolder - The destination folder for package installation. - */ - /** * Represents a package and provides methods to download and install it. * @@ -30,15 +22,31 @@ class Package { /** * Constructs a new instance of the class. * - * @param {WPMMconfig} config - the configuration object + * @param {WPMMconfigPkg} pkgConfig - the configuration object + * @param {string} packageType - the type of package * @param {WPMMpaths} paths - the object containing the paths */ - constructor (config, paths) { - this.config = config; + constructor (pkgConfig, packageType, paths) { + this.pkgInfo = pkgConfig; + this.packageType = packageType; this.rootFolder = paths.rootFolder; this.tempDir = paths.tempDir; - this.baseFolder = paths.baseFolder; - this.destFolder = paths.destFolder; + this.destFolder = this.getDestFolder(paths, packageType); + } + + getInfo () { + return { + version: this.pkgInfo.version + }; + } + + getDestFolder (paths, packageType) { + if (packageType === 'plugin') { + return paths.pluginsFolder; + } else if (packageType === 'theme') { + return paths.themeFolder; + } + return paths.baseFolder; } /** @@ -64,18 +72,16 @@ class Package { * Asynchronously clones a repository from a given URL to a specified directory. * * @param {string} packageUrl - The URL of the repository to clone. - * @param {string} packageName - The name of the package to be cloned. - * @param {string} targetDirectory - The directory where the package should be cloned to. * @return {Promise} A promise that resolves to the path of the cloned package on success, or rejects with an error on failure. */ - async cloneRepo (packageUrl, packageName, targetDirectory) { - return await new Promise((resolve, reject) => { - exec(`git clone ${packageUrl} ${targetDirectory}/${packageName}`, (error, stdout, stderr) => { + async cloneRepo (packageUrl) { + return await new Promise((resolve, reject) => { + exec(`git clone ${packageUrl} ${this.destFolder}/${this.pkgInfo.name}`, (error, stdout, stderr) => { if (error) { - console.log('Failed to clone repository:', error); + console.log('Failed to clone repository:', error, stdout, stderr); reject(error); } else { - resolve(packageName); + resolve(this.pkgInfo.name); } }); }); @@ -85,56 +91,55 @@ class Package { * Downloads a package from a given URL and saves it to the target directory. * * @param {string} packageUrl - The URL of the package to download. - * @param {string} packageName - The name of the package. * @return {Promise} A promise that resolves when the package is successfully downloaded and saved, or rejects with an error. */ - async downloadPackage (packageUrl, packageName) { + async downloadPackage (packageUrl) { try { // The path of the package let extractedPath = ''; // The destination folder for the package - const destinationFolder = path.join(this.destFolder, packageName); + const pkgFolder = path.join(this.destFolder, this.pkgInfo.name); - if (fs.existsSync(destinationFolder)) { - console.log(`ℹ️ destination folder ${destinationFolder} already exists. Skipping download.`); + if (fs.existsSync(pkgFolder)) { + console.log(`ℹ️ destination folder ${pkgFolder} already exists. Skipping download.`); return; } if (packageUrl.split('.').pop() === 'git') { - await this.cloneRepo(packageUrl, packageName, this.destFolder); + await this.cloneRepo(packageUrl); } else { // Download the package - extractedPath = await this.execDownload(packageName + '.zip', packageUrl); + extractedPath = await this.execDownload(this.pkgInfo.name + '.zip', packageUrl); // Move the extracted folder to the target directory - renameFolder(path.join(this.tempDir, extractedPath), destinationFolder); + renameFolder(path.join(this.tempDir, extractedPath), pkgFolder); } - console.log(`πŸ†— ${packageName} installed successfully in ${packageUrl}`); + console.log(`πŸ†— ${this.pkgInfo.name} installed successfully in ${packageUrl}`); // if the destination path provided move the files into that directory if (packageUrl) { // install npm packages if they exist - await installNpmPackages(destinationFolder); + await installNpmPackages(pkgFolder); // install composer if exist - await installComposer(destinationFolder); + await installComposer(pkgFolder); } } catch (error) { - console.error(`πŸ”΄ Error downloading package ${packageName}:`, error); + console.error(`πŸ”΄ Error downloading package ${this.pkgInfo.name}:`, error); } } /** * Installs a package with the given name, version, and type. * - * @param {WPMMconfig} config - The configuration object. - * @param {string} packageType - The type of the package ('theme' or 'plugin'). * @return {Promise} - A promise that resolves once the package is downloaded and installed. */ - async installPackage (config, packageType) { - const { name, version, source } = config; - const packageUrl = source || getDownloadUrl(name, version, packageType); + async install () { + const { name, version, source } = this.pkgInfo; + const packageUrl = source || getDownloadUrl(name, version, this.packageType); + + await this.downloadPackage(packageUrl); - await this.downloadPackage(packageUrl, name); + console.log(`πŸ†— ${this.packageType} ${this.pkgInfo.name} installed successfully.`); } } diff --git a/lib/PluginPackage.js b/lib/PluginPackage.js deleted file mode 100644 index 6f480ed..0000000 --- a/lib/PluginPackage.js +++ /dev/null @@ -1,22 +0,0 @@ -const Package = require('./package'); - -/** - * Represents a theme package. - * - * @class PluginPackage - * @extends Package - */ -export class PluginPackage extends Package { - /** - * Asynchronously installs a plugin. - * - * @return {Promise} A Promise that resolves when the plugin is installed successfully. - */ - async install () { - return await this.installPackage(this.config, 'plugin').then(() => { - console.log(`πŸ†— Plugin ${this.config.name} installed successfully.`); - }); - } -} - -module.exports = PluginPackage; diff --git a/lib/ThemePackage.js b/lib/ThemePackage.js deleted file mode 100644 index 8ff8869..0000000 --- a/lib/ThemePackage.js +++ /dev/null @@ -1,22 +0,0 @@ -const Package = require('./package'); - -/** - * Represents a theme package. - * - * @class ThemePackage - * @extends Package - */ -export class ThemePackage extends Package { - /** - * Installs a package with the specified name and version as a theme. - * - * @return {Promise} A promise that resolves once the package is installed. - */ - async install () { - return await this.installPackage(this.config, 'theme').then(() => { - console.log(`πŸ†— Theme ${this.config.name} installed successfully.`); - }); - } -} - -module.exports = ThemePackage; From 15e6a952388e35e3f60d49b182ba50698912b977 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:30:17 +0100 Subject: [PATCH 19/70] Refactor WpPackage setup and configuration Updated WpPackage class logic in lib/WpPackage.js to improve setup and configuration. This refactor includes better management of WordPress configuration and download links through improved handling of this.pkgInfo object. The changes focus on unifying paths, streamlining the process, and simplifying the code. Took 11 seconds --- lib/WpPackage.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/WpPackage.js b/lib/WpPackage.js index 0cdcfa1..0c1ca4a 100644 --- a/lib/WpPackage.js +++ b/lib/WpPackage.js @@ -1,8 +1,10 @@ -const { getWordPressDownloadUrl, replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/index.js'); const path = require('path'); const fs = require('fs'); const { renameFolder } = require('./utils/fs'); + const Package = require('./package'); +const { replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/wpConfig.js'); +const { getWordPressDownloadUrl } = require('./utils/index.js'); /** * Represents a WordPress package that can be installed and configured. @@ -10,7 +12,7 @@ const Package = require('./package'); * @class WpPackage * @extends Package */ -export class WpPackage extends Package { +class WpPackage extends Package { /** * Installs WordPress with the specified version and language. * @@ -21,7 +23,7 @@ export class WpPackage extends Package { const downloadUrl = getWordPressDownloadUrl(version, language); try { - const destinationPath = path.join(this.rootFolder, this.config.name); + const destinationPath = path.join(this.rootFolder, this.pkgInfo.name); if (fs.existsSync(destinationPath)) { console.log('πŸ”„οΈ WordPress folder already exists. Skipping download.'); @@ -46,13 +48,13 @@ export class WpPackage extends Package { * @return {void} This function does not return anything. */ async setupWordPressConfig () { - const configPath = path.join(this.baseFolder, 'wp-config.php'); + const configPath = path.join(this.destFolder, 'wp-config.php'); try { if (fs.existsSync(configPath)) { console.log('πŸ†— WordPress configuration already set up. updating...'); } else { - const sampleConfigPath = path.join(this.baseFolder, 'wp-config-sample.php'); + const sampleConfigPath = path.join(this.destFolder, 'wp-config-sample.php'); // Copy wp-config-sample.php to wp-config.php fs.copyFileSync(sampleConfigPath, configPath); } @@ -61,13 +63,13 @@ export class WpPackage extends Package { let configContent = fs.readFileSync(configPath, 'utf8'); // Update database name, username, password, and other settings based on user-defined config - configContent = replaceDbConstant(configContent, 'DB_NAME', this.config.wordpress.config.DB_NAME); - configContent = replaceDbConstant(configContent, 'DB_USER', this.config.wordpress.config.DB_USER); - configContent = replaceDbConstant(configContent, 'DB_PASSWORD', this.config.wordpress.config.DB_PASSWORD); - configContent = replaceDbConstant(configContent, 'DB_HOST', this.config.wordpress.config.DB_HOST); - configContent = replaceDbConstant(configContent, 'DB_CHARSET', this.config.wordpress.config.DB_CHARSET); + configContent = replaceDbConstant(configContent, 'DB_NAME', this.pkgInfo.wordpress.config.DB_NAME); + configContent = replaceDbConstant(configContent, 'DB_USER', this.pkgInfo.wordpress.config.DB_USER); + configContent = replaceDbConstant(configContent, 'DB_PASSWORD', this.pkgInfo.wordpress.config.DB_PASSWORD); + configContent = replaceDbConstant(configContent, 'DB_HOST', this.pkgInfo.wordpress.config.DB_HOST); + configContent = replaceDbConstant(configContent, 'DB_CHARSET', this.pkgInfo.wordpress.config.DB_CHARSET); - configContent = replaceDbConstantBool(configContent, 'WP_DEBUG', this.config.wordpress.config.WP_DEBUG); + configContent = replaceDbConstantBool(configContent, 'WP_DEBUG', this.pkgInfo.wordpress.config.WP_DEBUG); configContent = replaceEmptySalts(configContent); @@ -86,7 +88,7 @@ export class WpPackage extends Package { * @returns {Promise} A Promise that resolves when the installation and configuration are complete. */ async install () { - const { version, language } = this.config.wordpress; + const { version, language } = this.pkgInfo.wordpress; await this.installWordPress(version, language); await this.setupWordPressConfig(); } From bc80770a20a591b7e4fc6554634e4cc10e9866d5 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:30:25 +0100 Subject: [PATCH 20/70] Refactor WordPress paths utility and action functions Enhancements have been made to the WordPress paths utility in lib/index.js for better path management. More concise action function descriptions and improved handling of default 'install' action command were also incorporated. This refactoring aims to streamline processes, simplify codes and provide a more intuitive user experience. Took 8 seconds --- lib/index.js | 73 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/lib/index.js b/lib/index.js index d845180..a6b8578 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,6 +7,7 @@ const Initialize = require('./initialize.js'); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); +const {getWordPressPaths} = require("./utils/wpConfig"); /** * @typedef WPMMconfigWP - The configuration object for WordPress @@ -23,17 +24,15 @@ const { hideBin } = require('yargs/helpers'); * @property {string} WP_CONTENT_DIR - The path to the content directory * @property {string} WP_CONTENT_URL - The URL of the content directory * @property {boolean} DISALLOW_FILE_EDIT - Whether to disallow file editing - */ - -/** + * + * Package configuration * @typedef WPMMconfigPkg - The package object * @property {string} name - the name of the package * @property {string} version - the version of the package * @property {string} source - the source of the package - */ - -/** - * @typedef WPMMconfig - The configuration object + * + * Website configuration + * @typedef WPMMconfig - The website configuration * @property {string} name - The name of the website * @property {Object} wordpress - The WordPress pkg related data for the website * @property {string} wordpress.version - The current version of WordPress @@ -45,6 +44,16 @@ const { hideBin } = require('yargs/helpers'); * @property {string[]} postInstall - The post-install actions run by wp-cli */ +/** + * @typedef {Object} WPMMpaths - The object containing the paths + * @property {string} rootFolder - The root folder of the application + * @property {string} tempDir - The temporary directory + * @property {string?} baseFolder - The path to the WordPress folder. Defaults to the current working directory. + * @property {string} destFolder - The destination folder for package installation. + * @property {string?} pluginsFolder - The destination folder for plugins + * @property {string?} themeFolder - The destination folder for themes + */ + /** @var {yargs} argv - The command line arguments. */ const argv = yargs(hideBin(process.argv)).argv; @@ -59,41 +68,42 @@ const startTime = Date.now(); * * @param {WPMMconfig} config - The configuration object. * @param {Object} actions - The actions object. + * @property {function} info - Retrieve information using the provided configuration and actions. + * @property {function} dump - Dumps the current WordPress installation data. + * @property {function} init - Initialize the WordPress installation. + * @property {function} 'upload-database' - Upload a database by executing SQL queries from a specified file. + * @property {function} 'dump-database' - Dump the current WordPress database. + * @property {function} 'dump-all' - Dump the current WordPress database, plugins and themes setup. */ const actions = { - /** - * Retrieve information using the provided configuration and actions. - */ + + /** Retrieve information using the provided configuration and actions. */ info: () => { getInfo(config, actions); }, - /** - * Dumps the current WordPress installation data. - * This function creates a new Dump instance and initializes it. - */ + + /** Dumps the current WordPress installation data. This function creates a new Dump instance and initializes it. */ dump: () => { - const dump = new Dump(this); + const paths = getWordPressPaths(config); + const dump = new Dump(paths); dump.init(); }, - /** - * Initialize the WordPress installation. - */ + + /** Initialize the WordPress installation. */ init: () => { const initializer = new Initialize(config); initializer.generateConfig(); }, - /** - * Upload a database by executing SQL queries from a specified file. - */ + + /** Upload a database by executing SQL queries from a specified file. */ 'upload-database': (file) => { const db = new Database(config); db.uploadDatabase(file || config.database.filename).then(() => { console.log('πŸš€ Database uploaded successfully.'); }); }, - /** - * Dump the current WordPress database. - */ + + /** Dump the current WordPress database. */ 'dump-database': () => { const date = new Date().getUTCDate(); const newDbName = `${config.wordpress.config.DB_NAME}-${date}.sql.gz`; @@ -102,10 +112,15 @@ const actions = { console.log('πŸš€ Database dumped successfully to', newDbName, '.'); }); }, - /** - * install WordPress - */ - default: () => { + + /** Dump the current WordPress database, plugins and themes setup. */ + 'dump-all': () => { + actions["dump-database"]() && console.log('πŸš€ WP Database dumped successfully.'); + actions["dump"]() && console.log('πŸš€ All data dumped successfully.'); + }, + + /** Install WordPress */ + install: () => { const installer = new Installer(config); installer.run().then(() => { @@ -115,7 +130,7 @@ const actions = { }; const action = Object.keys(argv).find(key => argv[key] === true); -(actions[action])(); +(!action ? actions.install : actions[action])(); printTimePassed(startTime); process.exit(0); From 0282cdc05b6e6218caa589d33e0c07a047ac8a74 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:30:57 +0100 Subject: [PATCH 21/70] Update package version and dependencies in package-lock.json Several package dependencies also have been updated, new ones have been added including 'axios' and 'node-fetch', and 'eslint-plugin-import' has been removed. Additionally, numerous dependencies shifted to a peer dependency. These changes enhance the compatibility and operability of the package. Took 32 seconds --- .eslintrc.js | 17 --- .eslintrc.json | 19 ++++ package-lock.json | 282 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 3 +- 4 files changed, 287 insertions(+), 34 deletions(-) delete mode 100644 .eslintrc.js create mode 100644 .eslintrc.json diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index ea80273..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - env: { - browser: true, - commonjs: true, - es2021: true - }, - extends: 'standard', - overrides: [ - ], - parserOptions: { - ecmaVersion: 'latest' - }, - rules: { - semi: ['error', 'always'], - 'max-len': ['error', { code: 200 }] - } -}; diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..8406200 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,19 @@ +{ + "env": { + "browser": true, + "node": true, + "es2021": true, + "jest": true + }, + "extends": "eslint:recommended", + "overrides": [ + ], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "rules": { + "semi": ["error", "always"], + "max-len": ["error", { "code": 200 }] + } +} diff --git a/package-lock.json b/package-lock.json index d7bab30..999c248 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,19 @@ { "name": "wpmm", - "version": "0.0.3", + "version": "0.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wpmm", - "version": "0.0.3", + "version": "0.0.4", "license": "ISC", "dependencies": { + "axios": "^1.6.2", "extract-zip": "^2.0.1", "mysql2": "^3.6.5", "mysqldump": "^3.2.0", + "node-fetch": "^3.3.2", "yargs": "^17.7.2" }, "bin": { @@ -20,7 +22,6 @@ "devDependencies": { "eslint": "^8.54.0", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.0", "eslint-plugin-n": "^16.3.1", "eslint-plugin-promise": "^6.1.1", "jest": "^29.7.0" @@ -731,9 +732,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -754,9 +755,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", - "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", + "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1366,7 +1367,8 @@ "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/node": { "version": "20.10.0", @@ -1523,6 +1525,7 @@ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "is-array-buffer": "^3.0.1" @@ -1536,6 +1539,7 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -1555,6 +1559,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -1574,6 +1579,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -1592,6 +1598,7 @@ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -1610,6 +1617,7 @@ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", "dev": true, + "peer": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.2", @@ -1626,11 +1634,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -1638,6 +1652,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -1869,6 +1893,7 @@ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, + "peer": true, "dependencies": { "function-bind": "^1.1.2", "get-intrinsic": "^1.2.1", @@ -2007,6 +2032,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2054,6 +2090,14 @@ "node": ">= 8" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2104,6 +2148,7 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dev": true, + "peer": true, "dependencies": { "get-intrinsic": "^1.2.1", "gopd": "^1.0.1", @@ -2118,6 +2163,7 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "peer": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -2130,6 +2176,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -2213,6 +2267,7 @@ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, + "peer": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "arraybuffer.prototype.slice": "^1.0.2", @@ -2266,6 +2321,7 @@ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, + "peer": true, "dependencies": { "get-intrinsic": "^1.2.2", "has-tostringtag": "^1.0.0", @@ -2280,6 +2336,7 @@ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, + "peer": true, "dependencies": { "hasown": "^2.0.0" } @@ -2289,6 +2346,7 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, + "peer": true, "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -2322,15 +2380,15 @@ } }, "node_modules/eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", - "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", + "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.54.0", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.55.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -2422,6 +2480,7 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, + "peer": true, "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -2433,6 +2492,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -2442,6 +2502,7 @@ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, + "peer": true, "dependencies": { "debug": "^3.2.7" }, @@ -2459,6 +2520,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -2488,6 +2550,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", "dev": true, + "peer": true, "dependencies": { "array-includes": "^3.1.7", "array.prototype.findlastindex": "^1.2.3", @@ -2519,6 +2582,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -2528,6 +2592,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -2812,6 +2877,28 @@ "pend": "~1.2.0" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -2872,15 +2959,59 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, + "peer": true, "dependencies": { "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2915,6 +3046,7 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -2933,6 +3065,7 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2967,6 +3100,7 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, + "peer": true, "dependencies": { "function-bind": "^1.1.2", "has-proto": "^1.0.1", @@ -3005,6 +3139,7 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.1" @@ -3080,6 +3215,7 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, + "peer": true, "dependencies": { "define-properties": "^1.1.3" }, @@ -3095,6 +3231,7 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, + "peer": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -3119,6 +3256,7 @@ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3137,6 +3275,7 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, + "peer": true, "dependencies": { "get-intrinsic": "^1.2.2" }, @@ -3149,6 +3288,7 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -3161,6 +3301,7 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -3173,6 +3314,7 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, + "peer": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -3295,6 +3437,7 @@ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, + "peer": true, "dependencies": { "get-intrinsic": "^1.2.2", "hasown": "^2.0.0", @@ -3309,6 +3452,7 @@ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.0", @@ -3329,6 +3473,7 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "peer": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -3341,6 +3486,7 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -3372,6 +3518,7 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -3396,6 +3543,7 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "peer": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -3449,6 +3597,7 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, + "peer": true, "engines": { "node": ">= 0.4" }, @@ -3470,6 +3619,7 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "peer": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -3499,6 +3649,7 @@ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -3515,6 +3666,7 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -3539,6 +3691,7 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "peer": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -3554,6 +3707,7 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, + "peer": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -3569,6 +3723,7 @@ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, + "peer": true, "dependencies": { "which-typed-array": "^1.1.11" }, @@ -3584,6 +3739,7 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -3595,7 +3751,8 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/isexe": { "version": "2.0.0", @@ -4322,6 +4479,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, + "peer": true, "dependencies": { "minimist": "^1.2.0" }, @@ -4475,6 +4633,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -4501,6 +4678,7 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4603,6 +4781,41 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4641,6 +4854,7 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4650,6 +4864,7 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "peer": true, "engines": { "node": ">= 0.4" } @@ -4659,6 +4874,7 @@ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.4", @@ -4677,6 +4893,7 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -4694,6 +4911,7 @@ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -4706,6 +4924,7 @@ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -5004,6 +5223,11 @@ "node": ">= 6" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -5069,6 +5293,7 @@ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -5207,6 +5432,7 @@ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1", @@ -5225,6 +5451,7 @@ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", @@ -5258,6 +5485,7 @@ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", "dev": true, + "peer": true, "dependencies": { "define-data-property": "^1.1.1", "get-intrinsic": "^1.2.1", @@ -5273,6 +5501,7 @@ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", "dev": true, + "peer": true, "dependencies": { "define-data-property": "^1.0.1", "functions-have-names": "^1.2.3", @@ -5308,6 +5537,7 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -5431,6 +5661,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -5448,6 +5679,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -5462,6 +5694,7 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -5487,6 +5720,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, + "peer": true, "engines": { "node": ">=4" } @@ -5588,6 +5822,7 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "dev": true, + "peer": true, "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -5633,6 +5868,7 @@ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1", @@ -5647,6 +5883,7 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "for-each": "^0.3.3", @@ -5665,6 +5902,7 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", "dev": true, + "peer": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", @@ -5684,6 +5922,7 @@ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "for-each": "^0.3.3", @@ -5698,6 +5937,7 @@ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, + "peer": true, "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", @@ -5776,6 +6016,14 @@ "makeerror": "1.0.12" } }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "engines": { + "node": ">= 8" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5796,6 +6044,7 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, + "peer": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -5812,6 +6061,7 @@ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, + "peer": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.4", diff --git a/package.json b/package.json index d9919d2..b298fb6 100644 --- a/package.json +++ b/package.json @@ -25,15 +25,16 @@ ], "license": "ISC", "dependencies": { + "axios": "^1.6.2", "extract-zip": "^2.0.1", "mysql2": "^3.6.5", "mysqldump": "^3.2.0", + "node-fetch": "^3.3.2", "yargs": "^17.7.2" }, "devDependencies": { "eslint": "^8.54.0", "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.0", "eslint-plugin-n": "^16.3.1", "eslint-plugin-promise": "^6.1.1", "jest": "^29.7.0" From 25421ce31687ab96de9efc3ab735e6d1194403e2 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:32:25 +0100 Subject: [PATCH 22/70] : Simplify wp-package.json and reorganize tests The main wp-package.json configuration file has been simplified to remove unnecessary values and clean up host settings. This included nullifying the WordPress version and language, while updating the database user and password details. Also, a new file tests/assets/wp-package.json has been created to separate the testing configuration. Moreover, the tests/utils.test.js file has been updated to reflect these changes. Took 1 minute --- tests/assets/wp-package.json | 44 ++++++++++++++++++++++++++++++++++++ tests/utils.test.js | 3 +-- wp-package.json | 18 ++++++--------- 3 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 tests/assets/wp-package.json diff --git a/tests/assets/wp-package.json b/tests/assets/wp-package.json new file mode 100644 index 0000000..150ea6b --- /dev/null +++ b/tests/assets/wp-package.json @@ -0,0 +1,44 @@ +{ + "name": "wordpress", + "wordpress": { + "version": "6.4.2", + "language": "en_US", + "config": { + "DB_NAME": "my_db_name", + "DB_USER": "my_db_username", + "DB_PASSWORD": "my_db_password", + "DB_HOST": "localhost", + "DB_CHARSET": "utf8", + "DB_COLLATE": "", + "table_prefix": "wp_", + "WP_DEBUG": true + } + }, + "database": { + "type": "mysql", + "filename": "databases/database.sql", + "backup-folder": "backups" + }, + "themes": [ + { + "name": "modul-r", + "version": "1.4.4", + "source": "https://github.com/wp-blocks/modul-r.git" + } + ], + "plugins": [ + { + "name": "contact-form-7", + "version": "5.8.4" + }, + { + "name": "cf7-antispam", + "version": "0.4.5", + "source": "https://github.com/wp-blocks/cf7-antispam/archive/refs/heads/main.zip" + } + ], + "postInstall": [ + "wp cache flush", + "wp plugin install $(wp plugin list --field=name) --force" + ] +} diff --git a/tests/utils.test.js b/tests/utils.test.js index 5b8323a..f3b5168 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -1,5 +1,4 @@ -const { getConfig, getWordPressDownloadUrl, generateSalt, replaceDbConstant, replaceDbConstantBool, getDownloadUrl } = require('../lib/utils.js'); -const defaultConfig = 'wp-package.json'; +const { getConfig, getWordPressDownloadUrl, generateSalt, replaceDbConstant, replaceDbConstantBool, getDownloadUrl } = require('../lib/utils/index.js'); describe('getConfig', () => { it('should read wp-package.json from the root folder and return the default configuration if the file does not exist', () => { diff --git a/wp-package.json b/wp-package.json index 51f617c..e42cd26 100644 --- a/wp-package.json +++ b/wp-package.json @@ -1,24 +1,20 @@ { "name": "wordpress", "wordpress": { - "version": "6.4.1", - "language": "en_US", + "version": null, + "language": null, "config": { "DB_NAME": "my_db_name", - "DB_USER": "my_username", - "DB_PASSWORD": "my_password", - "DB_HOST": "127.0.0.1", + "DB_USER": "my_db_username", + "DB_PASSWORD": "my_db_password", + "DB_HOST": "localhost", "DB_CHARSET": "utf8", "DB_COLLATE": "", "table_prefix": "wp_", - "WP_DEBUG": true, - "WP_SITEURL": "http://example.com", - "WP_HOME": "http://example.com", - "WP_CONTENT_DIR": "/path/to/custom/content", - "WP_CONTENT_URL": "http://example.com/custom/content", - "DISALLOW_FILE_EDIT": true + "WP_DEBUG": true } }, + "database": {}, "themes": [], "plugins": [], "postInstall": [] From ebfb6550227febbefaccd07062e8610a0be28ad5 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:34:43 +0100 Subject: [PATCH 23/70] Refactor and simplify utility test file imports The utility test file imports in tests/utils.test.js were restructured for improved legibility and clarity. Specific utilities were moved to a separate import from ../lib/utils/wpConfig, enhancing separation of concerns. This coincides with the simplification of the main wp-package.json configuration file and the creation of a new test configuration file in tests/assets/wp-package.json. Took 2 minutes --- tests/utils.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/utils.test.js b/tests/utils.test.js index f3b5168..2fbe5c0 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -1,4 +1,5 @@ -const { getConfig, getWordPressDownloadUrl, generateSalt, replaceDbConstant, replaceDbConstantBool, getDownloadUrl } = require('../lib/utils/index.js'); +const { getConfig, getWordPressDownloadUrl, getDownloadUrl } = require('../lib/utils/index.js'); +const {generateSalt, replaceDbConstant, replaceDbConstantBool} = require("../lib/utils/wpConfig"); describe('getConfig', () => { it('should read wp-package.json from the root folder and return the default configuration if the file does not exist', () => { From 6e542599f22688b9fdcd6bd7b75865aba0322f80 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:37:23 +0100 Subject: [PATCH 24/70] Provides an example for the new database related functions Took 3 minutes --- readme.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 0fb1017..6d34493 100644 --- a/readme.md +++ b/readme.md @@ -73,6 +73,11 @@ Edit the `wp-package.json` file to define the WordPress version, language, theme "DISALLOW_FILE_EDIT": true } }, + "database": { + "type": "mysql", + "filename": "databases/database.sql", + "backup-folder": "backups" + }, "themes": [ { "name": "modul-r", @@ -83,7 +88,7 @@ Edit the `wp-package.json` file to define the WordPress version, language, theme "plugins": [ { "name": "contact-form-7", - "version": "5.8.3" + "version": "5.8.4" }, { "name": "cf7-antispam", From b19665a9b85d6cedae9f24f1ea28c4f3ded2a98c Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:46:48 +0100 Subject: [PATCH 25/70] Add post-install command runner function A new utility function has been added in lib/utils/index.js for executing commands asynchronously after installation. This addition allows post-install commands to be defined and run, useful for tasks that should occur only once the installation process is completed. Additionally, code organization is improved by moving the runPostInstallCommands implementation to the utils library. Took 9 minutes --- lib/Installer.js | 43 +++++++++++-------------------------------- lib/index.js | 2 +- lib/utils/index.js | 27 ++++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/lib/Installer.js b/lib/Installer.js index 02ef63d..fc55f0e 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -2,9 +2,9 @@ const { isWPCLIAvailable } = require('./utils/index.js'); const { cleanup, makeDir } = require('./utils/fs.js'); const Package = require("./Package"); const { WordPressPackage } = require('./WpPackage'); -const { child_process } = require('node:child_process'); -const {getWordPressPaths, getLastWpVersion, getCurrentWpInfo} = require("./utils/wpConfig"); -const axios = require("axios"); +const {getWordPressPaths} = require("./utils/wpConfig"); +const {runPostInstallCommands} = require("./utils"); + /** * The Installer class represents the WordPress installer and provides methods for installing WordPress and its dependencies. * @@ -36,7 +36,7 @@ class Installer { makeDir(this.tempDir); const promises = []; - const wpJson = []; + const wpJson = this.config; // Install WordPress if (wordpress) { @@ -47,14 +47,14 @@ class Installer { if (plugins) { const pluginPackages = plugins.map((plugin) => new Package(plugin, 'plugin', this.paths)); promises.push(...pluginPackages.map((pluginPackage) => pluginPackage.install().then(() => { - wpJson.push(pluginPackage.pkgInfo); + wpJson.plugins[pluginPackage.pkgInfo.name] = pluginPackage.pkgInfo; }))); } if (themes) { const themePackages = themes.map((theme) => new Package(theme, 'theme', this.paths)); - promises.push(...themePackages.map((themePackage) => themePackage.install.then(() => { - wpJson.push(themePackage.pkgInfo); + promises.push(...themePackages.map((themePackage) => themePackage.install().then(() => { + wpJson.themes[themePackage.pkgInfo.name] = themePackage.pkgInfo; }))); } @@ -64,31 +64,10 @@ class Installer { // Run post-install commands if (isWPCLIAvailable() && postInstall && postInstall.length > 0) { console.log('πŸ€– Executing post-install commands...'); - await this.runPostInstallCommands(postInstall); - } - } - - /** - * Runs post-install commands asynchronously. - * - * @param {Array} commands - An array of WP-CLI commands to execute. - * @return {Promise} - A promise that resolves when the post-install commands complete. - */ - async runPostInstallCommands (commands) { - // Execute each post-install command - for (const command of commands) { - try { - console.log(`Executing: ${command}`); - const { stdout, stderr } = await child_process.exec(command); - if (stdout) { - console.log(`Command output:\n${stdout}`); - } - if (stderr) { - console.error(`Command error:\n${stderr}`); - } - } catch (error) { - console.error(`Error executing command: ${command}`, error); - } + await runPostInstallCommands(postInstall); + } else { + console.log('πŸ”΄ Unable to execute post-install commands. Please install WP-CLI and try again.'); + console.log('https://make.wordpress.org/cli/handbook/guides/installing/'); } } diff --git a/lib/index.js b/lib/index.js index a6b8578..0525e4c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,7 +7,7 @@ const Initialize = require('./initialize.js'); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); -const {getWordPressPaths} = require("./utils/wpConfig"); +const { getWordPressPaths } = require("./utils/wpConfig"); /** * @typedef WPMMconfigWP - The configuration object for WordPress diff --git a/lib/utils/index.js b/lib/utils/index.js index e515516..3ec3fc9 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -1,6 +1,6 @@ const path = require('path'); const fs = require('fs'); -const { exec } = require('node:child_process'); +const { exec, child_process} = require('node:child_process'); /** * Reads a PHP file, extracts, and returns the WordPress version number. @@ -186,6 +186,30 @@ async function isWPCLIAvailable () { } } +/** + * Runs post-install commands asynchronously. + * + * @param {Array} commands - An array of WP-CLI commands to execute. + * @return {Promise} - A promise that resolves when the post-install commands complete. + */ +async function runPostInstallCommands (commands) { + // Execute each post-install command + for (const command of commands) { + try { + console.log(`Executing: ${command}`); + const { stdout, stderr } = await child_process.exec(command); + if (stdout) { + console.log(`Command output:\n${stdout}`); + } + if (stderr) { + console.error(`Command error:\n${stderr}`); + } + } catch (error) { + console.error(`Error executing command: ${command}`, error); + } + } +} + /** * Retrieve information about the WPMM and system environment. * @@ -231,5 +255,6 @@ module.exports = { installNpmPackages, installComposer, isWPCLIAvailable, + runPostInstallCommands, printTimePassed }; From 263a661b43f8c03b59c7d66855ea39a03e2e93e3 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 14:50:32 +0100 Subject: [PATCH 26/70] chore Took 4 minutes --- lib/Dump.js | 5 ++--- lib/Updater.js | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/Dump.js b/lib/Dump.js index 454ba38..7039a35 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -1,8 +1,7 @@ const fs = require('fs'); const path = require('path'); -const { geVarFromPHPFile, getUserLocale} = require('./utils'); -const {config} = require("yargs"); -const {getWordPressPaths, getCurrentWpInfo} = require("./utils/wpConfig"); +const { getUserLocale } = require('./utils'); +const { getCurrentWpInfo } = require("./utils/wpConfig"); /** * Represents a Dump class for WordPress configuration. diff --git a/lib/Updater.js b/lib/Updater.js index f29d047..006af7c 100644 --- a/lib/Updater.js +++ b/lib/Updater.js @@ -37,7 +37,7 @@ class Updater { exec(command, (error, stdout, stderr) => { if (error) { - console.error(`Error updating plugin ${plugin}:`, error.message); + console.error(`Error updating plugin ${plugin}:`, error.message, stderr, stdout); } else { console.log(`Plugin ${plugin} updated successfully`); } @@ -67,7 +67,7 @@ class Updater { exec(command, (error, stdout, stderr) => { if (error) { - console.error(`Error updating theme ${theme}:`, error.message); + console.error(`Error updating theme ${theme}:`, error.message, stderr, stdout); } else { console.log(`Theme ${theme} updated successfully`); } @@ -89,7 +89,7 @@ class Updater { exec(command, (error, stdout, stderr) => { if (error) { - console.error('Error updating WordPress:', error.message); + console.error('Error updating WordPress:', error.message, stderr, stdout); } else { console.log('WordPress updated successfully'); } From 6ee55c142d06379027e363d627178b15a0dccdae Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 15:53:08 +0100 Subject: [PATCH 27/70] : Implement remote config loading The logic for handling the --template option has been improved. Now, in addition to using a local file, we can also get a configuration file from a remote source with axios, offering more flexibility. Error handling is provided in case a remote source fails to load properly. Took 53 minutes --- lib/utils/index.js | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/utils/index.js b/lib/utils/index.js index 3ec3fc9..7c28ba5 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -1,6 +1,7 @@ const path = require('path'); const fs = require('fs'); const { exec, child_process} = require('node:child_process'); +const {get} = require("axios"); /** * Reads a PHP file, extracts, and returns the WordPress version number. @@ -44,21 +45,34 @@ function getConfig (args) { */ const defaultConfig = JSON.parse(fs.readFileSync(path.join(__dirname, '..','..', 'wp-package.json'), 'utf8')); - // Extract the value of the --template option or use the default - const templatePath = args.template || 'wp-package.json'; + let config = {}; - /** - * The user-defined configuration object - * @type {WPMMconfig} config - The configuration object - */ - let config = defaultConfig; + // Extract the value of the --template option or use the default + if (! args?.template) { + const templatePath = 'wp-package.json'; - // Check if the template file exists and read it as JSON if it does - if (!fs.existsSync(templatePath)) { - console.error(`πŸ”΄ The template file ${templatePath} does not exist.`); + // Check if the template file exists and read it as JSON if it does + if (!fs.existsSync(templatePath)) { + console.error(`πŸ”΄ The template file ${templatePath} does not exist.`); + } else { + /** + * Read the user-defined configuration object from the template file and add it to the config + * @type {WPMMconfig} config - The configuration object + */ + config = {...defaultConfig, ...JSON.parse(fs.readFileSync(templatePath, 'utf8'))}; + } } else { - // Read the template file and add it to the config - config = { ...defaultConfig, ...JSON.parse(fs.readFileSync(templatePath, 'utf8')) }; + /** + * the user has provided a template file via the --template option. read the config from the remote source + */ + get(args.template) + .then(response => { + const gitConfig = response.data; + config = {...defaultConfig, ...gitConfig}; + }) + .catch(error => { + console.log("πŸ”΄ Error: " + error.message); + }); } return config; From d1484b4af9f6ea1b255104b9f5d7c22db865a8c3 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 15:53:20 +0100 Subject: [PATCH 28/70] Update baseFolder for WordPress installation The baseFolder for WordPress installation has been updated to use a constant instead of a hardcoded value. This change makes it more flexible and easier to maintain because you can change the default WordPress install folder by simply updating the constant in one place. Took 12 seconds --- lib/utils/wpConfig.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/wpConfig.js b/lib/utils/wpConfig.js index 9e09746..6b03eeb 100644 --- a/lib/utils/wpConfig.js +++ b/lib/utils/wpConfig.js @@ -2,19 +2,19 @@ const path = require("path"); const fs = require("fs"); const {geVarFromPHPFile} = require("./index"); const axios = require("axios"); +const {defaultWpInstallFolder} = require("../constants"); /** * Gets the default paths for the WordPress installation. * @param {Object} config - The configuration object for the WordPress installation. * @param {string} [rootFolder=process.cwd()] - The root folder path for the WordPress installation. Defaults to the current working directory. - * @param {string} [baseFolder='wordpress'] - The base folder path for the WordPress installation. Defaults to 'wordpress'. * @return {WPMMpaths} - An object containing the default paths for the WordPress installation. */ function getWordPressPaths (config, rootFolder = process.cwd()) { return { tempDir: path.join(rootFolder, 'temp'), rootFolder: rootFolder, - baseFolder: path.join(rootFolder, config.name ?? 'wordpress'), + baseFolder: path.join(rootFolder, config.name ?? defaultWpInstallFolder), pluginsFolder: path.join(this.baseFolder, 'wp-content', 'plugins'), themeFolder: path.join(this.baseFolder, 'wp-content', 'themes') }; From 86c4c5554962cad9f9c11cfe93d17c8baa0d7f5f Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 15:53:30 +0100 Subject: [PATCH 29/70] Add lib/constants.js file for WordPress install Created a new lib/constants.js file containing the default settings for WordPress installation. This refactoring improves the code maintainability by centralizing the configuration values for the WordPress install folder, language, and default config settings in one place. Took 10 seconds --- lib/constants.js | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 lib/constants.js diff --git a/lib/constants.js b/lib/constants.js new file mode 100644 index 0000000..fdee5a3 --- /dev/null +++ b/lib/constants.js @@ -0,0 +1,39 @@ +/** + * the default folder name for the WordPress install + * @type {string} defaultWpInstallFolder - The default folder name for the WordPress install + */ +const defaultWpInstallFolder = 'wordpress'; + +/** + * the default language for the WordPress install + * + * @type {string} defaultWpInstallLanguage - The default language for the WordPress install + */ +const defaultWpInstallLanguage = 'en_US'; + +/** + * The default configuration object + * + * @type {WPMMconfig} defaultConfig - The default configuration object + */ +const defaultWpConfig = { + DB_NAME: 'my_db_name', + DB_USER: 'my_username', + DB_PASSWORD: 'my_password', + DB_HOST: '127.0.0.1', + DB_CHARSET: 'utf8', + DB_COLLATE: '', + table_prefix: 'wp_', + WP_DEBUG: true, + WP_SITEURL: 'http://example.com', + WP_HOME: 'http://example.com', + WP_CONTENT_DIR: '/path/to/custom/content', + WP_CONTENT_URL: 'http://example.com/custom/content', + DISALLOW_FILE_EDIT: true +}; + +module.exports = { + defaultWpInstallFolder, + defaultWpInstallLanguage, + defaultWpConfig +}; From 10363221de25b4aa9860f39dc5e6a4a3954977ae Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 15:54:03 +0100 Subject: [PATCH 30/70] Refactor WordPress config generation in Initialize.js Updated the constructor and the 'generateConfig' method in Initialize.js to rely on default configuration values sourced from lib/constants.js. This makes the code cleaner and centralizes default settings for WordPress installation. Took 33 seconds --- lib/Initialize.js | 55 +++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/lib/Initialize.js b/lib/Initialize.js index 082e33d..bb4c748 100644 --- a/lib/Initialize.js +++ b/lib/Initialize.js @@ -1,5 +1,8 @@ const fs = require('fs'); const path = require('path'); +const {defaultWpInstallLanguage, defaultWpConfig, defaultWpInstallFolder} = require("./constants"); +const {getLastWpVersion} = require("./utils/wpConfig"); +const {getUserLocale} = require("./utils"); /** * Initialize class for generating WordPress configuration file. @@ -9,66 +12,48 @@ const path = require('path'); class Initialize { /** * Constructor for a new instance of the class. - * - * @param {WPMMconfig} config - Optional configuration options. */ - constructor (config) { - this.options = config || {}; + constructor () { this.wpFolder = process.cwd(); this.outputPath = path.join(this.wpFolder, 'wp-package.json'); } + hasConfig = () => fs.existsSync(this.outputPath); + /** * Generates the configuration file for WordPress. * * @return {void} */ generateConfig () { - const name = this.options.name || 'wordpress'; - const language = this.options.language || 'en_US'; - const version = this.options.version || '6.4.1'; - /** - * The default configuration object - * - * @type {WPMMconfig} defaultConfig - The default configuration object - */ - const defaultConfig = { - DB_NAME: 'my_db_name', - DB_USER: 'my_username', - DB_PASSWORD: 'my_password', - DB_HOST: '127.0.0.1', - DB_CHARSET: 'utf8', - DB_COLLATE: '', - table_prefix: 'wp_', - WP_DEBUG: true, - WP_SITEURL: 'http://example.com', - WP_HOME: 'http://example.com', - WP_CONTENT_DIR: '/path/to/custom/content', - WP_CONTENT_URL: 'http://example.com/custom/content', - DISALLOW_FILE_EDIT: true - }; + // check if the output path exists + if (this.hasConfig) { + console.error(`πŸ‘ The wp-package.json file ${this.outputPath} already exists.`); + return; + } - const customConfig = this.options.config || {}; + const name = this.wpFolder.split(path.sep).pop() || defaultWpInstallFolder; + const language = getUserLocale() || defaultWpInstallLanguage; + const version = getLastWpVersion(); + const backupFolder = path.join(this.wpFolder, 'backups'); const result = { name, wordpress: { version, language, - config: { ...defaultConfig, ...customConfig } + config: defaultWpConfig + }, + database: { + type: "mysql", + backupFolder: backupFolder }, themes: [], plugins: [], postInstall: [] }; - // check if the output path exists - if (fs.existsSync(this.outputPath)) { - console.error(`πŸ”΄ The configuration file ${this.outputPath} already exists.`); - return; - } - // write the config to the output path fs.writeFileSync(this.outputPath, JSON.stringify(result, null, 2)); console.log(`πŸ†— Wordpress configuration file created. Configuration saved to ${this.outputPath}`); From 99afe097cbdbf800a89dc96336bb90d802551e60 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 15:54:27 +0100 Subject: [PATCH 31/70] Implement WordPress package update functionality The codebase now includes an "update" feature which updates WordPress packages based on command line arguments. This feature includes conditions for updating themes, plugins, WordPress itself, or all together. Refactoring was also done to improve code neatness and configuration handling. Took 24 seconds --- lib/index.js | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/index.js b/lib/index.js index 0525e4c..3912499 100644 --- a/lib/index.js +++ b/lib/index.js @@ -54,14 +54,17 @@ const { getWordPressPaths } = require("./utils/wpConfig"); * @property {string?} themeFolder - The destination folder for themes */ +/** @var {number} startTime - the time at which the script started. */ +const startTime = Date.now(); + /** @var {yargs} argv - The command line arguments. */ const argv = yargs(hideBin(process.argv)).argv; /** @var {WPMMconfig} config - The configuration object for the script. */ const config = getConfig(argv); -/** @var {number} startTime - the time at which the script started. */ -const startTime = Date.now(); +/** @var {WPMMpaths} paths - The paths object for the script. */ +const paths = getWordPressPaths(config); /** * The actions object. @@ -74,6 +77,8 @@ const startTime = Date.now(); * @property {function} 'upload-database' - Upload a database by executing SQL queries from a specified file. * @property {function} 'dump-database' - Dump the current WordPress database. * @property {function} 'dump-all' - Dump the current WordPress database, plugins and themes setup. + * @property {function} install - Install WordPress using the provided configuration. + * @property {function} update - Update WordPress themes and/or plugins using the provided configuration. */ const actions = { @@ -84,14 +89,13 @@ const actions = { /** Dumps the current WordPress installation data. This function creates a new Dump instance and initializes it. */ dump: () => { - const paths = getWordPressPaths(config); const dump = new Dump(paths); dump.init(); }, /** Initialize the WordPress installation. */ init: () => { - const initializer = new Initialize(config); + const initializer = new Initialize(); initializer.generateConfig(); }, @@ -116,7 +120,7 @@ const actions = { /** Dump the current WordPress database, plugins and themes setup. */ 'dump-all': () => { actions["dump-database"]() && console.log('πŸš€ WP Database dumped successfully.'); - actions["dump"]() && console.log('πŸš€ All data dumped successfully.'); + actions.dump() && console.log('πŸš€ All data dumped successfully.'); }, /** Install WordPress */ @@ -126,11 +130,33 @@ const actions = { installer.run().then(() => { console.log('πŸš€ WordPress installed successfully.'); }); + }, + + /** Update WordPress packages */ + update: () => { + const updater = new Updater(config); + if (argv.plugins === true) { + updater.updateThemes(); + updater.updatePlugins(); + updater.updateWordPress(); + } else if (argv.plugins === true) { + updater.updatePlugins(); + } else if (argv.themes === true) { + updater.updateThemes(); + } else if (argv.wordpress === true) { + updater.updateWordPress(); + } else { + console.log('you must specify either all, plugins, themes, or wordpress'); + console.log('ie. wpmm update all'); + } } }; const action = Object.keys(argv).find(key => argv[key] === true); (!action ? actions.install : actions[action])(); +/** + * That's it! We're done! let's print how long it took to run the script and exit with a success code. + */ printTimePassed(startTime); process.exit(0); From 9b2bf8130db0aa9149e86917bff1d6c6bbd25c39 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 15:54:38 +0100 Subject: [PATCH 32/70] Refactor Dump.js and improve WordPress version handling This update refactors the "Dump.js" file to better handle WordPress versions. Specifically, it imports a new function called "getLastWpVersion" from the "wpConfig" module and uses it to get the latest WordPress version if no version is set. It also simplifies the initialisation process by removing redundant console log outputs. Took 11 seconds --- lib/Dump.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/Dump.js b/lib/Dump.js index 7039a35..5d9e8ef 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); const { getUserLocale } = require('./utils'); -const { getCurrentWpInfo } = require("./utils/wpConfig"); +const { getCurrentWpInfo, getLastWpVersion} = require("./utils/wpConfig"); /** * Represents a Dump class for WordPress configuration. @@ -29,9 +29,7 @@ class Dump { * * @return {void} */ - init () { - console.log(this.wpFolder); - console.log(this.themeFolder); + init() { const themes = this.scanDirectory(this.themeFolder); const plugins = this.scanDirectory(this.pluginsFolder); @@ -41,7 +39,7 @@ class Dump { const wpInfo = getCurrentWpInfo(this.wpFolder); const language = wpInfo.locale || getUserLocale(); - const version = wpInfo.version || this.getLastVersion(); + const version = wpInfo.version || getLastWpVersion(); const result = { name, From 44cd7f688f89892ccbd6991a43e471a1ce3ad4a8 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 16:01:45 +0100 Subject: [PATCH 33/70] Add husky for git hooks and update scripts Husky has been added to handle pre-commit hook that runs lint and tests automatically. The scripts in the package.json file have been updated to reflect these changes by adding a "prepare" script for Husky installation. Furthermore, paths in the "lint" script have been corrected. --- .husky/pre-commit | 5 +++++ package-lock.json | 16 ++++++++++++++++ package.json | 6 ++++-- 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..f671027 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run lint +npm test diff --git a/package-lock.json b/package-lock.json index 999c248..698a6c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "eslint-config-standard": "^17.1.0", "eslint-plugin-n": "^16.3.1", "eslint-plugin-promise": "^6.1.1", + "husky": "^8.0.3", "jest": "^29.7.0" } }, @@ -3352,6 +3353,21 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", diff --git a/package.json b/package.json index b298fb6..230df87 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,9 @@ "url": "https://github.com/erikyo/wpmm.git" }, "scripts": { - "lint": "eslint ./lib ./test", - "test": "jest" + "lint": "eslint ./lib ./tests", + "test": "jest", + "prepare": "husky install" }, "bin": { "wpmm": "lib/index.js" @@ -37,6 +38,7 @@ "eslint-config-standard": "^17.1.0", "eslint-plugin-n": "^16.3.1", "eslint-plugin-promise": "^6.1.1", + "husky": "^8.0.3", "jest": "^29.7.0" } } From b08953c88b40e385c3ed15309740fcfb7dc0cf64 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 16:01:59 +0100 Subject: [PATCH 34/70] Implement Updater class and adjust update functionality An Updater class is now required and utilized within the update method in lib/index.js. The method's logic has been expanded and reorganized to deal with different update scenarios using a new 'updateObject'. This structure improves code readability, provides more explicit control, and enhances maintainability. --- lib/index.js | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/index.js b/lib/index.js index 3912499..4364c14 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,6 +8,7 @@ const Initialize = require('./initialize.js'); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); const { getWordPressPaths } = require("./utils/wpConfig"); +const Updater = require("./Updater"); /** * @typedef WPMMconfigWP - The configuration object for WordPress @@ -135,17 +136,37 @@ const actions = { /** Update WordPress packages */ update: () => { const updater = new Updater(config); - if (argv.plugins === true) { + /** + * An object containing the update options. + * + * @type {{updateWordPress: boolean, updateAll: boolean, updateThemes: boolean, updatePlugins: boolean}} + */ + const updateObject = { + updateAll: argv.plugins === true, + updatePlugins: argv.plugins === true, + updateThemes: argv.themes === true, + updateWordPress: argv.wordpress === true, + }; + + if (updateObject.updateAll) { updater.updateThemes(); updater.updatePlugins(); updater.updateWordPress(); - } else if (argv.plugins === true) { + } + + if (updateObject.updatePlugins) { updater.updatePlugins(); - } else if (argv.themes === true) { + } + + if (updateObject.updateThemes) { updater.updateThemes(); - } else if (argv.wordpress === true) { + } + + if (updateObject.updateWordPress) { updater.updateWordPress(); - } else { + } + + if (!Object.values(updateObject).some(Boolean)) { console.log('you must specify either all, plugins, themes, or wordpress'); console.log('ie. wpmm update all'); } From b854a04c06b8bd7344e878113efa1c626acab12a Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 16:03:58 +0100 Subject: [PATCH 35/70] Refactor update method with Updater class and 'updateObject' Introduced an Updater class to the update method in lib/index.js. The update functionality has been restructured with the use of a new 'updateObject' for handling diverse update cases. This enhances the code's readability, improves control over individual update conditions, and improves maintenance. --- lib/index.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/index.js b/lib/index.js index 4364c14..3c59305 100644 --- a/lib/index.js +++ b/lib/index.js @@ -152,18 +152,19 @@ const actions = { updater.updateThemes(); updater.updatePlugins(); updater.updateWordPress(); - } + } else { - if (updateObject.updatePlugins) { - updater.updatePlugins(); - } + if (updateObject.updatePlugins) { + updater.updatePlugins(); + } - if (updateObject.updateThemes) { - updater.updateThemes(); - } + if (updateObject.updateThemes) { + updater.updateThemes(); + } - if (updateObject.updateWordPress) { - updater.updateWordPress(); + if (updateObject.updateWordPress) { + updater.updateWordPress(); + } } if (!Object.values(updateObject).some(Boolean)) { From 7b46e00e2b130f62a8b1a3a437581d245c12d054 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 16:15:29 +0100 Subject: [PATCH 36/70] Refactor updater logic by moving it to Updater class Moved updater logic from lib/index.js to the Updater class in lib/Updater.js, ensuring clearer and maintainable code. This refactor introduces an 'updateObject' that handles diverse update cases, improving control over individual update conditions and enhancing code readability. --- lib/Updater.js | 42 +++++++++++++++++++++++++++++++++++++++++- lib/index.js | 40 +++++----------------------------------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/lib/Updater.js b/lib/Updater.js index 006af7c..2dba364 100644 --- a/lib/Updater.js +++ b/lib/Updater.js @@ -51,7 +51,8 @@ class Updater { /** * Updates the themes in the configuration by installing and activating them. * - * @return {void} + * @async + * @return updateThemes - Updates themes using the `wp theme install` command. */ async updateThemes () { try { @@ -98,6 +99,45 @@ class Updater { console.error('Error updating WordPress:', error.message); } } + + async run (argv) { + + /** + * An object containing the update options. + * + * @type {{updateWordPress: boolean, updateAll: boolean, updateThemes: boolean, updatePlugins: boolean}} + */ + const updateObject = { + updateAll: argv.plugins === true, + updatePlugins: argv.plugins === true, + updateThemes: argv.themes === true, + updateWordPress: argv.wordpress === true, + }; + + if (updateObject.updateAll) { + await this.updateWordPress(); + await this.updatePlugins(); + await this.updateThemes(); + } else { + + if (updateObject.updatePlugins) { + await this.updatePlugins(); + } + + if (updateObject.updateThemes) { + await this.updateThemes(); + } + + if (updateObject.updateWordPress) { + await this.updateWordPress(); + } + } + + if (!Object.values(updateObject).some(Boolean)) { + console.log('you must specify either all, plugins, themes, or wordpress'); + console.log('ie. wpmm update all'); + } + } } module.exports = Updater; diff --git a/lib/index.js b/lib/index.js index 3c59305..02f114e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -136,41 +136,11 @@ const actions = { /** Update WordPress packages */ update: () => { const updater = new Updater(config); - /** - * An object containing the update options. - * - * @type {{updateWordPress: boolean, updateAll: boolean, updateThemes: boolean, updatePlugins: boolean}} - */ - const updateObject = { - updateAll: argv.plugins === true, - updatePlugins: argv.plugins === true, - updateThemes: argv.themes === true, - updateWordPress: argv.wordpress === true, - }; - - if (updateObject.updateAll) { - updater.updateThemes(); - updater.updatePlugins(); - updater.updateWordPress(); - } else { - - if (updateObject.updatePlugins) { - updater.updatePlugins(); - } - - if (updateObject.updateThemes) { - updater.updateThemes(); - } - - if (updateObject.updateWordPress) { - updater.updateWordPress(); - } - } - - if (!Object.values(updateObject).some(Boolean)) { - console.log('you must specify either all, plugins, themes, or wordpress'); - console.log('ie. wpmm update all'); - } + updater.run(argv).then(() => { + console.log('πŸš€ WordPress updated successfully.'); + }).catch(() => { + console.log('πŸ”΄ WordPress update failed.'); + }); } }; From adee258a4749f171bfae67704bc1b91771cc376c Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 19:17:43 +0100 Subject: [PATCH 37/70] Update and organize Initialize class methods Rearranged methods in Initialize class, redefined and repurposed the `generateConfig` method to return an object output and introduced `writeConfig` method to conduct the writing operation. The modifications were made to improve functional clarity and explicitness. Adaptations within `lib/index.js` were executed to reflect these changes. --- lib/Initialize.js | 36 ++++++++++++++++++------------------ lib/index.js | 41 +++++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/lib/Initialize.js b/lib/Initialize.js index bb4c748..d24cd25 100644 --- a/lib/Initialize.js +++ b/lib/Initialize.js @@ -1,8 +1,7 @@ const fs = require('fs'); const path = require('path'); -const {defaultWpInstallLanguage, defaultWpConfig, defaultWpInstallFolder} = require("./constants"); -const {getLastWpVersion} = require("./utils/wpConfig"); -const {getUserLocale} = require("./utils"); +const {defaultWpConfig, defaultWpInstallFolder, pkgFileName, defaultWpDatabaseType} = require("./constants.js"); +const {getLastWp, getUserLocale} = require("./utils/wpConfig.js"); /** * Initialize class for generating WordPress configuration file. @@ -15,49 +14,50 @@ class Initialize { */ constructor () { this.wpFolder = process.cwd(); - this.outputPath = path.join(this.wpFolder, 'wp-package.json'); + this.outputPath = path.join(this.wpFolder, pkgFileName); } - hasConfig = () => fs.existsSync(this.outputPath); + hasConfig = () => !! fs.existsSync( path.join( this.outputPath, pkgFileName ) ); /** * Generates the configuration file for WordPress. * - * @return {void} + * @return {WPMMconfig} The configuration object. */ - generateConfig () { + generateConfig() { // check if the output path exists - if (this.hasConfig) { - console.error(`πŸ‘ The wp-package.json file ${this.outputPath} already exists.`); + if (this.hasConfig()) { + console.log(`πŸ‘ The configuration file ${this.outputPath} already exists.`); return; } const name = this.wpFolder.split(path.sep).pop() || defaultWpInstallFolder; - const language = getUserLocale() || defaultWpInstallLanguage; - const version = getLastWpVersion(); - const backupFolder = path.join(this.wpFolder, 'backups'); + const lastWp = getLastWp(); + const userLocale = getUserLocale(); - const result = { + return { name, wordpress: { - version, - language, + version: lastWp.version, + language: userLocale, config: defaultWpConfig }, database: { - type: "mysql", - backupFolder: backupFolder + type: defaultWpDatabaseType, + backupFolder: path.join(this.wpFolder, 'backups') }, themes: [], plugins: [], postInstall: [] }; + } + writeConfig = (result) => { // write the config to the output path fs.writeFileSync(this.outputPath, JSON.stringify(result, null, 2)); console.log(`πŸ†— Wordpress configuration file created. Configuration saved to ${this.outputPath}`); - } + }; } module.exports = Initialize; diff --git a/lib/index.js b/lib/index.js index 02f114e..8864418 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -const { getConfig, printTimePassed, getInfo } = require('./utils/index.js'); +const { getConfig, printTimePassed, getInfo } = require('./utils/data.js'); const Installer = require('./installer'); const Dump = require('./dump.js'); const Database = require('./database.js'); @@ -97,7 +97,8 @@ const actions = { /** Initialize the WordPress installation. */ init: () => { const initializer = new Initialize(); - initializer.generateConfig(); + const result = initializer.generateConfig(); + initializer.writeConfig(result); }, /** Upload a database by executing SQL queries from a specified file. */ @@ -108,25 +109,9 @@ const actions = { }); }, - /** Dump the current WordPress database. */ - 'dump-database': () => { - const date = new Date().getUTCDate(); - const newDbName = `${config.wordpress.config.DB_NAME}-${date}.sql.gz`; - const db = new Database(config); - db.dumpDatabase(newDbName).then(() => { - console.log('πŸš€ Database dumped successfully to', newDbName, '.'); - }); - }, - - /** Dump the current WordPress database, plugins and themes setup. */ - 'dump-all': () => { - actions["dump-database"]() && console.log('πŸš€ WP Database dumped successfully.'); - actions.dump() && console.log('πŸš€ All data dumped successfully.'); - }, - /** Install WordPress */ install: () => { - const installer = new Installer(config); + const installer = new Installer(config, paths); installer.run().then(() => { console.log('πŸš€ WordPress installed successfully.'); @@ -141,7 +126,23 @@ const actions = { }).catch(() => { console.log('πŸ”΄ WordPress update failed.'); }); - } + }, + + /** Dump the current WordPress database. */ + 'dump-database': () => { + const date = new Date().getUTCDate(); + const newDbName = `${config.wordpress.config.DB_NAME}-${date}.sql.gz`; + const db = new Database(config); + db.dumpDatabase(newDbName).then(() => { + console.log('πŸš€ Database dumped successfully to', newDbName, '.'); + }); + }, + + /** Dump the current WordPress database, plugins and themes setup. */ + 'dump-all': () => { + actions["dump-database"]() && console.log('πŸš€ WP Database dumped successfully.'); + actions.dump() && console.log('πŸš€ All data dumped successfully.'); + }, }; const action = Object.keys(argv).find(key => argv[key] === true); From b02291d4203f29b35dcdb2a7290996074f24df66 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 19:18:04 +0100 Subject: [PATCH 38/70] Refactor dependency paths and constructor in Installer class Changes include updating the paths of imported functions in lib/Installer.js and altering the constructor of the class. The bootstrap fields now depend on an externally provided 'paths' object rather --- lib/Installer.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/Installer.js b/lib/Installer.js index fc55f0e..2616f70 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -1,9 +1,8 @@ -const { isWPCLIAvailable } = require('./utils/index.js'); +const { isWPCLIAvailable } = require('./utils/data.js'); const { cleanup, makeDir } = require('./utils/fs.js'); -const Package = require("./Package"); -const { WordPressPackage } = require('./WpPackage'); -const {getWordPressPaths} = require("./utils/wpConfig"); -const {runPostInstallCommands} = require("./utils"); +const Package = require("./Package.js"); +const { WordPressPackage } = require('./WpPackage.js'); +const {runPostInstallCommands} = require("./utils/actions.js"); /** * The Installer class represents the WordPress installer and provides methods for installing WordPress and its dependencies. @@ -16,10 +15,10 @@ class Installer { * * @param {WPMMconfig} config - The configuration object for the constructor. */ - constructor (config) { + constructor (config, paths) { this.config = config; - this.paths = getWordPressPaths(config, process.cwd()); + this.paths = paths; this.tempDir = this.paths.tempDir; this.baseFolder = this.paths.baseFolder; } From 3d64e4e844d0a2b7832140e12115c8477c2ce0e2 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 19:18:56 +0100 Subject: [PATCH 39/70] Refactor lib/utils module and update Installer class The utility functions in lib/utils have been refactored and relocated into different files. The installer class dependency paths in lib/Installer.js and its constructor have also been updated. Consequently, the bootstrap fields in Installer now depend on an externally provided 'paths' object. --- lib/utils/actions.js | 99 ++++++++++++++++++++++++ lib/utils/{index.js => data.js} | 133 ++++---------------------------- lib/utils/wpConfig.js | 43 +++++++---- 3 files changed, 140 insertions(+), 135 deletions(-) create mode 100644 lib/utils/actions.js rename lib/utils/{index.js => data.js} (56%) diff --git a/lib/utils/actions.js b/lib/utils/actions.js new file mode 100644 index 0000000..129c636 --- /dev/null +++ b/lib/utils/actions.js @@ -0,0 +1,99 @@ +import path from "path"; +import fs from "fs"; +import {child_process, exec} from "node:child_process"; + + +/** + * Installs npm packages in the specified package directory. + * + * @param {string} packageDirectory - The directory path where the package is located. + * @return {Promise} - A promise that resolves when the packages are installed and built. + */ +async function installNpmPackages (packageDirectory) { + const packageJsonPath = path.join(packageDirectory, 'package.json'); + if (!fs.existsSync(packageJsonPath)) { + // console.warn(`Local directory (${packageDirectory}) does not contain a package.json file.`) + return; + } + const packageData = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + const packageName = packageData.name; + console.log(`πŸš€ Installing and building ${packageName} from local directory...`); + + const packageLockPath = path.join(packageDirectory, 'package-lock.json'); + let command = 'npm i && npm run build'; + if (fs.existsSync(packageLockPath)) { + command = 'npm ci && npm run build'; + } + + await new Promise((resolve, reject) => { + exec(command, { cwd: packageDirectory }, (error) => { + if (error) { + reject(error); + } else { + console.log(`πŸ“¦ ${packageName} dependencies installed and built.`); + resolve(); + } + }); + }); +} + +/** + * Installs composer dependencies and generates autoloader based on composer.json file. + * + * @param {string} repoPath - The path to the repository where composer.json is located. + * + * @returns {Promise} - A promise resolving when the installation process is completed. + */ +async function installComposer (repoPath) { + console.log('🎻 Found composer.json'); + await exec('composer install --no-dev', { cwd: repoPath }); + await exec('composer dumpautoload -o', { cwd: repoPath }); +} + +/** + * Checks if WP-CLI is available. + * + * @return {boolean} - A promise that resolves to true if WP-CLI is available, false otherwise. + */ +async function isWPCLIAvailable () { + try { + // Attempt to execute a simple WP-CLI command + await exec('wp --version'); + return true; // If successful, WP-CLI is available + } catch (error) { + console.log('πŸ”΄ WP-CLI is not available on this system. Please install WP-CLI and try again.'); + console.log('Read more about at https://make.wordpress.org/cli/handbook/guides/installing/'); + return false; // If an error occurs, WP-CLI is not available + } +} + +/** + * Runs post-install commands asynchronously. + * + * @param {Array} commands - An array of WP-CLI commands to execute. + * @return {Promise} - A promise that resolves when the post-install commands complete. + */ +async function runPostInstallCommands (commands) { + // Execute each post-install command + for (const command of commands) { + try { + console.log(`Executing: ${command}`); + const { stdout, stderr } = await child_process.exec(command); + if (stdout) { + console.log(`Command output:\n${stdout}`); + } + if (stderr) { + console.error(`Command error:\n${stderr}`); + } + } catch (error) { + console.error(`Error executing command: ${command}`, error); + } + } +} + +module.exports = { + installNpmPackages, + installComposer, + isWPCLIAvailable, + runPostInstallCommands +}; diff --git a/lib/utils/index.js b/lib/utils/data.js similarity index 56% rename from lib/utils/index.js rename to lib/utils/data.js index 7c28ba5..99c6218 100644 --- a/lib/utils/index.js +++ b/lib/utils/data.js @@ -1,7 +1,8 @@ -const path = require('path'); + const fs = require('fs'); -const { exec, child_process} = require('node:child_process'); const {get} = require("axios"); +const {pkgFileName} = require("../constants"); +const Initialize = require("../Initialize"); /** * Reads a PHP file, extracts, and returns the WordPress version number. @@ -21,54 +22,43 @@ function geVarFromPHPFile (fileContent, variableName = '$wp_version') { return match ? match[1] : null; } -/** - * Returns the locale of the user. - * - * @returns {string} The locale of the user. - */ -function getUserLocale() { - return Intl.DateTimeFormat().resolvedOptions().locale; -} - /** * Reads wp-package.json from the root folder and extracts the value of the --template option or uses the default. * The default config is used if no template is provided. Checks if the template file exists and reads it as JSON if it does. * * @param {any} args - The arguments object. - * @param {string} args.template - the path to the template file + * @param {string} args.template - The path to the template file. * @return {WPMMconfig} The configuration object. */ function getConfig (args) { /** * The default config from the root plugin folder. This is used if no template is provided - * @type {WPMMconfig} defaultConfig - The default configuration object + * + * @type {WPMMconfig} config - The configuration object */ - const defaultConfig = JSON.parse(fs.readFileSync(path.join(__dirname, '..','..', 'wp-package.json'), 'utf8')); - let config = {}; // Extract the value of the --template option or use the default if (! args?.template) { - const templatePath = 'wp-package.json'; + const templatePath = pkgFileName; // Check if the template file exists and read it as JSON if it does if (!fs.existsSync(templatePath)) { - console.error(`πŸ”΄ The template file ${templatePath} does not exist.`); + console.log(`πŸ”΄ The template file ${templatePath} does not exist.`); + const init = new Initialize(); + config = init.generateConfig(); } else { - /** - * Read the user-defined configuration object from the template file and add it to the config - * @type {WPMMconfig} config - The configuration object - */ - config = {...defaultConfig, ...JSON.parse(fs.readFileSync(templatePath, 'utf8'))}; + config = JSON.parse(fs.readFileSync(templatePath, 'utf8')); + console.log(`πŸ”΄ The template file ${templatePath} exists in the root folder. Using it.`, config); } + } else { /** * the user has provided a template file via the --template option. read the config from the remote source */ get(args.template) .then(response => { - const gitConfig = response.data; - config = {...defaultConfig, ...gitConfig}; + config = response.data; }) .catch(error => { console.log("πŸ”΄ Error: " + error.message); @@ -136,94 +126,6 @@ function getDownloadUrl (packageName, packageVersion, type) { return `https://downloads.wordpress.org/${type}/${packageName}.zip`; } -/** - * Installs npm packages in the specified package directory. - * - * @param {string} packageDirectory - The directory path where the package is located. - * @return {Promise} - A promise that resolves when the packages are installed and built. - */ -async function installNpmPackages (packageDirectory) { - const packageJsonPath = path.join(packageDirectory, 'package.json'); - if (!fs.existsSync(packageJsonPath)) { - // console.warn(`Local directory (${packageDirectory}) does not contain a package.json file.`) - return; - } - const packageData = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); - const packageName = packageData.name; - console.log(`πŸš€ Installing and building ${packageName} from local directory...`); - - const packageLockPath = path.join(packageDirectory, 'package-lock.json'); - let command = 'npm i && npm run build'; - if (fs.existsSync(packageLockPath)) { - command = 'npm ci && npm run build'; - } - - await new Promise((resolve, reject) => { - exec(command, { cwd: packageDirectory }, (error) => { - if (error) { - reject(error); - } else { - console.log(`πŸ“¦ ${packageName} dependencies installed and built.`); - resolve(); - } - }); - }); -} - -/** - * Installs composer dependencies and generates autoloader based on composer.json file. - * - * @param {string} repoPath - The path to the repository where composer.json is located. - * - * @returns {Promise} - A promise resolving when the installation process is completed. - */ -async function installComposer (repoPath) { - console.log('🎻 Found composer.json'); - await exec('composer install --no-dev', { cwd: repoPath }); - await exec('composer dumpautoload -o', { cwd: repoPath }); -} - -/** - * Checks if WP-CLI is available. - * - * @return {boolean} - A promise that resolves to true if WP-CLI is available, false otherwise. - */ -async function isWPCLIAvailable () { - try { - // Attempt to execute a simple WP-CLI command - await exec('wp --version'); - return true; // If successful, WP-CLI is available - } catch (error) { - console.log('πŸ”΄ WP-CLI is not available on this system. Please install WP-CLI and try again.'); - console.log('Read more about at https://make.wordpress.org/cli/handbook/guides/installing/'); - return false; // If an error occurs, WP-CLI is not available - } -} - -/** - * Runs post-install commands asynchronously. - * - * @param {Array} commands - An array of WP-CLI commands to execute. - * @return {Promise} - A promise that resolves when the post-install commands complete. - */ -async function runPostInstallCommands (commands) { - // Execute each post-install command - for (const command of commands) { - try { - console.log(`Executing: ${command}`); - const { stdout, stderr } = await child_process.exec(command); - if (stdout) { - console.log(`Command output:\n${stdout}`); - } - if (stderr) { - console.error(`Command error:\n${stderr}`); - } - } catch (error) { - console.error(`Error executing command: ${command}`, error); - } - } -} - /** * Retrieve information about the WPMM and system environment. * @@ -260,15 +162,10 @@ function printTimePassed (startTime) { module.exports = { geVarFromPHPFile, - getUserLocale, getConfig, + getInfo, getConnectionSettings, getWordPressDownloadUrl, getDownloadUrl, - getInfo, - installNpmPackages, - installComposer, - isWPCLIAvailable, - runPostInstallCommands, printTimePassed }; diff --git a/lib/utils/wpConfig.js b/lib/utils/wpConfig.js index 6b03eeb..0b8f276 100644 --- a/lib/utils/wpConfig.js +++ b/lib/utils/wpConfig.js @@ -1,8 +1,9 @@ const path = require("path"); const fs = require("fs"); -const {geVarFromPHPFile} = require("./index"); +const {geVarFromPHPFile} = require("./data.js"); const axios = require("axios"); -const {defaultWpInstallFolder} = require("../constants"); +const {defaultWpInstallFolder} = require("../constants.js"); +const {defaultWpInstallLanguage} = require("../constants"); /** * Gets the default paths for the WordPress installation. @@ -11,32 +12,39 @@ const {defaultWpInstallFolder} = require("../constants"); * @return {WPMMpaths} - An object containing the default paths for the WordPress installation. */ function getWordPressPaths (config, rootFolder = process.cwd()) { + const baseFolder = path.join(rootFolder, config.name ?? defaultWpInstallFolder); return { tempDir: path.join(rootFolder, 'temp'), rootFolder: rootFolder, - baseFolder: path.join(rootFolder, config.name ?? defaultWpInstallFolder), - pluginsFolder: path.join(this.baseFolder, 'wp-content', 'plugins'), - themeFolder: path.join(this.baseFolder, 'wp-content', 'themes') + baseFolder: baseFolder, + pluginsFolder: path.join(baseFolder, 'wp-content', 'plugins'), + themeFolder: path.join(baseFolder, 'wp-content', 'themes') }; } - - /** * Fetch the WordPress version from WordPress.org * * @async * @return {string} the version of WordPress */ -async function getLastWpVersion () { - const response = await axios.get('https://api.wordpress.org/core/version-check/1.7/') - .then(() => { - if (response.data?.offers && response.data.offers.length > 0) { - return response.data.offers[0]; - } else { - throw new Error('❗Cannot get the last version available'); - } - }); +async function getLastWp () { + const response = await axios.get('https://api.wordpress.org/core/version-check/1.7/'); + + if (response.data?.offers && response.data.offers.length > 0) { + return response.data.offers[0]; + } else { + throw new Error('❗Cannot get the last version available'); + } +} + +/** + * Returns the locale of the user. + * + * @returns {string} The locale of the user. + */ +function getUserLocale() { + return Intl.DateTimeFormat().resolvedOptions().locale || defaultWpInstallLanguage; } /** @@ -116,7 +124,8 @@ function replaceEmptySalts (configContent) { } module.exports = { - getLastWpVersion, + getLastWp, + getUserLocale, getWordPressPaths, getCurrentWpInfo, replaceDbConstant, From 255e610e911201d345e27ab9d3665dadec0a153b Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 19:19:33 +0100 Subject: [PATCH 40/70] Refactor utility functions and update Installer constructor Utility functions in the lib/utils module now reside in separate categorised files for better maintenance, which include 'actions.js', 'data.js' and 'fs.js'. Meanwhile, an additional object 'paths' is updated to the Installer class constructor in lib/Installer.js to specify dependency paths and enhance reusability. --- lib/Database.js | 2 +- lib/Installer.js | 1 + lib/Package.js | 9 +++++---- lib/Updater.js | 2 +- lib/WpPackage.js | 6 +++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/Database.js b/lib/Database.js index 2c76ce3..9d3cf6b 100644 --- a/lib/Database.js +++ b/lib/Database.js @@ -1,5 +1,5 @@ const mysql = require('mysql2/promise'); -const { getConnectionSettings } = require('./utils'); +const { getConnectionSettings } = require('./data'); const mysqldump = require('mysqldump'); const fs = require('fs').promises; diff --git a/lib/Installer.js b/lib/Installer.js index 2616f70..187671c 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -14,6 +14,7 @@ class Installer { * Initializes a new instance of the constructor. * * @param {WPMMconfig} config - The configuration object for the constructor. + * @param {WPMMpaths} paths - The object containing the paths for the constructor. */ constructor (config, paths) { this.config = config; diff --git a/lib/Package.js b/lib/Package.js index 21329bd..ff53d95 100644 --- a/lib/Package.js +++ b/lib/Package.js @@ -2,16 +2,17 @@ const fs = require('fs'); const path = require('path'); const { exec } = require('child_process'); const { - getDownloadUrl, installNpmPackages, installComposer -} = require('./utils'); - +} = require('./utils/actions.js'); +const { + getDownloadUrl +} = require('./utils/data.js'); const { downloadFile, extractZip, renameFolder -} = require('./utils/fs'); +} = require('./utils/fs.js'); /** * Represents a package and provides methods to download and install it. diff --git a/lib/Updater.js b/lib/Updater.js index 2dba364..d63c1b6 100644 --- a/lib/Updater.js +++ b/lib/Updater.js @@ -1,4 +1,4 @@ -const { exec } = require('child_process'); +const { exec } = require('node:child_process'); /** * Represents the Updater class. diff --git a/lib/WpPackage.js b/lib/WpPackage.js index 0c1ca4a..3b9b670 100644 --- a/lib/WpPackage.js +++ b/lib/WpPackage.js @@ -1,10 +1,10 @@ const path = require('path'); const fs = require('fs'); -const { renameFolder } = require('./utils/fs'); +const { renameFolder } = require('./utils/fs.js'); -const Package = require('./package'); +const Package = require('./package.js'); const { replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/wpConfig.js'); -const { getWordPressDownloadUrl } = require('./utils/index.js'); +const { getWordPressDownloadUrl } = require('./utils/data.js'); /** * Represents a WordPress package that can be installed and configured. From caa1deea792d933f848f45b40d955eac9e285c36 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 19:19:49 +0100 Subject: [PATCH 41/70] Remove `wp-package.json` and refactor utility imports `wp-package.json` has been entirely removed and implements a more modular approach for managing utility functions. File `utils.test.js` has been adjusted to import functions from `data.js` and `wpConfig.js`. Constants have also been updated to reflect this structural change. --- lib/constants.js | 18 +++++++++++------- tests/utils.test.js | 5 ++--- wp-package.json | 21 --------------------- 3 files changed, 13 insertions(+), 31 deletions(-) delete mode 100644 wp-package.json diff --git a/lib/constants.js b/lib/constants.js index fdee5a3..b831d1c 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,9 +1,16 @@ +/** + * the name of the WordPress package.json file + */ +const pkgFileName = 'wp-package.json'; + /** * the default folder name for the WordPress install * @type {string} defaultWpInstallFolder - The default folder name for the WordPress install */ const defaultWpInstallFolder = 'wordpress'; +const defaultWpDatabaseType = 'mysql'; + /** * the default language for the WordPress install * @@ -20,20 +27,17 @@ const defaultWpConfig = { DB_NAME: 'my_db_name', DB_USER: 'my_username', DB_PASSWORD: 'my_password', - DB_HOST: '127.0.0.1', + DB_HOST: 'localhost', DB_CHARSET: 'utf8', DB_COLLATE: '', table_prefix: 'wp_', - WP_DEBUG: true, - WP_SITEURL: 'http://example.com', - WP_HOME: 'http://example.com', - WP_CONTENT_DIR: '/path/to/custom/content', - WP_CONTENT_URL: 'http://example.com/custom/content', - DISALLOW_FILE_EDIT: true + WP_DEBUG: false }; module.exports = { + pkgFileName, defaultWpInstallFolder, + defaultWpDatabaseType, defaultWpInstallLanguage, defaultWpConfig }; diff --git a/tests/utils.test.js b/tests/utils.test.js index 2fbe5c0..5fca2cc 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -1,5 +1,5 @@ -const { getConfig, getWordPressDownloadUrl, getDownloadUrl } = require('../lib/utils/index.js'); -const {generateSalt, replaceDbConstant, replaceDbConstantBool} = require("../lib/utils/wpConfig"); +const { getConfig, getWordPressDownloadUrl, getDownloadUrl } = require('../lib/utils/data.js'); +const { generateSalt, replaceDbConstant, replaceDbConstantBool } = require("../lib/utils/wpConfig.js"); describe('getConfig', () => { it('should read wp-package.json from the root folder and return the default configuration if the file does not exist', () => { @@ -8,7 +8,6 @@ describe('getConfig', () => { const config = getConfig(argv); // Assert the result expect(config).toBeInstanceOf(Object); - expect(config.name).toBe('wordpress'); expect(config.plugins).toHaveLength(0); }); }); diff --git a/wp-package.json b/wp-package.json deleted file mode 100644 index e42cd26..0000000 --- a/wp-package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "wordpress", - "wordpress": { - "version": null, - "language": null, - "config": { - "DB_NAME": "my_db_name", - "DB_USER": "my_db_username", - "DB_PASSWORD": "my_db_password", - "DB_HOST": "localhost", - "DB_CHARSET": "utf8", - "DB_COLLATE": "", - "table_prefix": "wp_", - "WP_DEBUG": true - } - }, - "database": {}, - "themes": [], - "plugins": [], - "postInstall": [] -} From f82e78964f1c8c457475f8468fa638844936e63c Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 19:38:51 +0100 Subject: [PATCH 42/70] Refactor code to consolidate utility imports Removed redundant import statements and streamlined the way utility functions are imported in various files. This includes changes in 'Dump.js', 'actions.js', 'Installer.js', and 'Database.js'. The refactoring aims to improve code modularity and maintainability. --- lib/Database.js | 2 +- lib/Dump.js | 5 ++--- lib/Installer.js | 2 +- lib/utils/actions.js | 9 ++++----- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/Database.js b/lib/Database.js index 9d3cf6b..a13cbcc 100644 --- a/lib/Database.js +++ b/lib/Database.js @@ -1,6 +1,6 @@ const mysql = require('mysql2/promise'); -const { getConnectionSettings } = require('./data'); const mysqldump = require('mysqldump'); +const {getConnectionSettings} = require("./utils/data"); const fs = require('fs').promises; /** diff --git a/lib/Dump.js b/lib/Dump.js index 5d9e8ef..1b8d7db 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -1,7 +1,6 @@ const fs = require('fs'); const path = require('path'); -const { getUserLocale } = require('./utils'); -const { getCurrentWpInfo, getLastWpVersion} = require("./utils/wpConfig"); +const { getCurrentWpInfo, getUserLocale, getLastWp} = require("./utils/wpConfig"); /** * Represents a Dump class for WordPress configuration. @@ -39,7 +38,7 @@ class Dump { const wpInfo = getCurrentWpInfo(this.wpFolder); const language = wpInfo.locale || getUserLocale(); - const version = wpInfo.version || getLastWpVersion(); + const version = wpInfo.version || getLastWp()?.version || 'latest'; const result = { name, diff --git a/lib/Installer.js b/lib/Installer.js index 187671c..846a81d 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -1,8 +1,8 @@ -const { isWPCLIAvailable } = require('./utils/data.js'); const { cleanup, makeDir } = require('./utils/fs.js'); const Package = require("./Package.js"); const { WordPressPackage } = require('./WpPackage.js'); const {runPostInstallCommands} = require("./utils/actions.js"); +const {isWPCLIAvailable} = require("./utils/actions"); /** * The Installer class represents the WordPress installer and provides methods for installing WordPress and its dependencies. diff --git a/lib/utils/actions.js b/lib/utils/actions.js index 129c636..52ed304 100644 --- a/lib/utils/actions.js +++ b/lib/utils/actions.js @@ -1,7 +1,6 @@ -import path from "path"; -import fs from "fs"; -import {child_process, exec} from "node:child_process"; - +const path = require("node:path"); +const fs = require("fs"); +const {exec} = require("child_process"); /** * Installs npm packages in the specified package directory. @@ -78,7 +77,7 @@ async function runPostInstallCommands (commands) { for (const command of commands) { try { console.log(`Executing: ${command}`); - const { stdout, stderr } = await child_process.exec(command); + const { stdout, stderr } = await exec(command); if (stdout) { console.log(`Command output:\n${stdout}`); } From a6ae21ad723ab9b63e888e6123b9749a60b70f77 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 20:29:48 +0100 Subject: [PATCH 43/70] Update base folder definition and refactor file imports Changed the definition of base folder in 'Package.js' and 'WpPackage.js'. File 'wpConfig.js' was renamed to 'wordpress.js', and all references to this file were updated. Removed redundant import statements and refactored the way functions are imported from utility files. This is done to simplify the code and improve its maintainability. Removed the function 'geVarFromPHPFile' from 'data.js' and moved it to 'wordpress.js'. --- lib/Dump.js | 4 +-- lib/Initialize.js | 2 +- lib/Installer.js | 3 +- lib/Package.js | 1 - lib/WpPackage.js | 4 +-- lib/index.js | 4 +-- lib/utils/data.js | 24 ++------------ lib/utils/{wpConfig.js => wordpress.js} | 42 ++++++++++++++++++++----- tests/utils.test.js | 2 +- 9 files changed, 46 insertions(+), 40 deletions(-) rename lib/utils/{wpConfig.js => wordpress.js} (74%) diff --git a/lib/Dump.js b/lib/Dump.js index 1b8d7db..599f35e 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -1,6 +1,6 @@ const fs = require('fs'); const path = require('path'); -const { getCurrentWpInfo, getUserLocale, getLastWp} = require("./utils/wpConfig"); +const { getCurrentWpInfo, getUserLocale, getLastWp } = require("./utils/wordpress.js"); /** * Represents a Dump class for WordPress configuration. @@ -14,7 +14,7 @@ class Dump { * Initializes the class with the necessary folders for WordPress. */ constructor (paths) { - this.wpFolder = paths.rootFolder; + this.wpFolder = paths.baseFolder; this.themeFolder = paths.themeFolder; this.pluginsFolder = paths.pluginsFolder; } diff --git a/lib/Initialize.js b/lib/Initialize.js index d24cd25..91d363d 100644 --- a/lib/Initialize.js +++ b/lib/Initialize.js @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); const {defaultWpConfig, defaultWpInstallFolder, pkgFileName, defaultWpDatabaseType} = require("./constants.js"); -const {getLastWp, getUserLocale} = require("./utils/wpConfig.js"); +const {getLastWp, getUserLocale} = require("./utils/wordpress.js"); /** * Initialize class for generating WordPress configuration file. diff --git a/lib/Installer.js b/lib/Installer.js index 846a81d..6010b87 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -1,8 +1,7 @@ const { cleanup, makeDir } = require('./utils/fs.js'); const Package = require("./Package.js"); const { WordPressPackage } = require('./WpPackage.js'); -const {runPostInstallCommands} = require("./utils/actions.js"); -const {isWPCLIAvailable} = require("./utils/actions"); +const {runPostInstallCommands,isWPCLIAvailable} = require("./utils/actions.js"); /** * The Installer class represents the WordPress installer and provides methods for installing WordPress and its dependencies. diff --git a/lib/Package.js b/lib/Package.js index ff53d95..5bb16e6 100644 --- a/lib/Package.js +++ b/lib/Package.js @@ -30,7 +30,6 @@ class Package { constructor (pkgConfig, packageType, paths) { this.pkgInfo = pkgConfig; this.packageType = packageType; - this.rootFolder = paths.rootFolder; this.tempDir = paths.tempDir; this.destFolder = this.getDestFolder(paths, packageType); } diff --git a/lib/WpPackage.js b/lib/WpPackage.js index 3b9b670..e2ea4b7 100644 --- a/lib/WpPackage.js +++ b/lib/WpPackage.js @@ -3,7 +3,7 @@ const fs = require('fs'); const { renameFolder } = require('./utils/fs.js'); const Package = require('./package.js'); -const { replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/wpConfig.js'); +const { replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/wordpress.js'); const { getWordPressDownloadUrl } = require('./utils/data.js'); /** @@ -23,7 +23,7 @@ class WpPackage extends Package { const downloadUrl = getWordPressDownloadUrl(version, language); try { - const destinationPath = path.join(this.rootFolder, this.pkgInfo.name); + const destinationPath = path.join(this.baseFolder, this.pkgInfo.name); if (fs.existsSync(destinationPath)) { console.log('πŸ”„οΈ WordPress folder already exists. Skipping download.'); diff --git a/lib/index.js b/lib/index.js index 8864418..a7504cc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,8 +7,8 @@ const Initialize = require('./initialize.js'); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); -const { getWordPressPaths } = require("./utils/wpConfig"); -const Updater = require("./Updater"); +const { getWordPressPaths } = require("./utils/wordpress.js"); +const Updater = require("./Updater.js"); /** * @typedef WPMMconfigWP - The configuration object for WordPress diff --git a/lib/utils/data.js b/lib/utils/data.js index 99c6218..31814b0 100644 --- a/lib/utils/data.js +++ b/lib/utils/data.js @@ -1,26 +1,7 @@ - const fs = require('fs'); const {get} = require("axios"); -const {pkgFileName} = require("../constants"); -const Initialize = require("../Initialize"); - -/** - * Reads a PHP file, extracts, and returns the WordPress version number. - * - * @param {string} fileContent - Path to the PHP file to read - * @param variableName - The name of the variable to search adn replace - * @return {string|null} WordPress version number or null if not found or in case of an error - */ -function geVarFromPHPFile (fileContent, variableName = '$wp_version') { - // Define a regular expression to match the variableName and its value with both the single and double quotes - const versionRegex = new RegExp(`${variableName}\\s*=\\s*['"]([^'"]+)['"]`, 'g'); - - // Use the regular expression to extract the version number - const match = fileContent.match(versionRegex); - - // Return the version number or null if not found - return match ? match[1] : null; -} +const {pkgFileName} = require("../constants.js"); +const Initialize = require("../Initialize.js"); /** * Reads wp-package.json from the root folder and extracts the value of the --template option or uses the default. @@ -161,7 +142,6 @@ function printTimePassed (startTime) { } module.exports = { - geVarFromPHPFile, getConfig, getInfo, getConnectionSettings, diff --git a/lib/utils/wpConfig.js b/lib/utils/wordpress.js similarity index 74% rename from lib/utils/wpConfig.js rename to lib/utils/wordpress.js index 0b8f276..af75981 100644 --- a/lib/utils/wpConfig.js +++ b/lib/utils/wordpress.js @@ -1,9 +1,8 @@ const path = require("path"); const fs = require("fs"); -const {geVarFromPHPFile} = require("./data.js"); const axios = require("axios"); -const {defaultWpInstallFolder} = require("../constants.js"); -const {defaultWpInstallLanguage} = require("../constants"); +const {defaultWpInstallFolder, defaultWpInstallLanguage, pkgFileName} = require("../constants.js"); + /** * Gets the default paths for the WordPress installation. @@ -12,10 +11,20 @@ const {defaultWpInstallLanguage} = require("../constants"); * @return {WPMMpaths} - An object containing the default paths for the WordPress installation. */ function getWordPressPaths (config, rootFolder = process.cwd()) { - const baseFolder = path.join(rootFolder, config.name ?? defaultWpInstallFolder); + /** + * the WordPress installation folder + * + * @type {string} + */ + let baseFolder = rootFolder; + + // if wp-config.php exists in the root folder or the wp-package.json file exists in the root folder + if (! fs.existsSync(path.join(rootFolder, 'wp-config.php')) && ! fs.existsSync(path.join(rootFolder, pkgFileName))) { + baseFolder = path.join(rootFolder, config.name ?? defaultWpInstallFolder); + } + return { tempDir: path.join(rootFolder, 'temp'), - rootFolder: rootFolder, baseFolder: baseFolder, pluginsFolder: path.join(baseFolder, 'wp-content', 'plugins'), themeFolder: path.join(baseFolder, 'wp-content', 'themes') @@ -47,6 +56,25 @@ function getUserLocale() { return Intl.DateTimeFormat().resolvedOptions().locale || defaultWpInstallLanguage; } +/** + * Reads a PHP file, extracts, and returns the WordPress version number. + * + * @param {string} fileContent - Path to the PHP file to read + * @param variableName - The name of the variable to search adn replace + * + * @return {string|null} WordPress version number or null if not found or in case of an error + */ +function getVarFromPHPFile (fileContent, variableName = '$wp_version') { + // Define a regular expression to match the variableName and its value with both the single and double quotes + const versionRegex = new RegExp(`${variableName}\\s*=\\s*['"]([^'"]+)['"]`, 'g'); + + // Use the regular expression to extract the version number + const match = fileContent.match(versionRegex); + + // Return the version number or null if not found + return match ? match[1] : null; +} + /** * Retrieves the WordPress version and locale information from a given WordPress folder. * @@ -58,8 +86,8 @@ function getCurrentWpInfo(wpFolder) { // get the WordPress version and the locale from wp-includes/version.php const versionFile = path.join(wpFolder, 'wp-includes', 'version.php'); const versionFileContent = fs.readFileSync(versionFile, 'utf8'); - const version = geVarFromPHPFile(versionFileContent, 'wp_version'); - const locale = geVarFromPHPFile(versionFileContent, 'wp_local_package'); + const version = getVarFromPHPFile(versionFileContent, 'wp_version'); + const locale = getVarFromPHPFile(versionFileContent, 'wp_local_package'); return { version, locale diff --git a/tests/utils.test.js b/tests/utils.test.js index 5fca2cc..cbde824 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -1,5 +1,5 @@ const { getConfig, getWordPressDownloadUrl, getDownloadUrl } = require('../lib/utils/data.js'); -const { generateSalt, replaceDbConstant, replaceDbConstantBool } = require("../lib/utils/wpConfig.js"); +const { generateSalt, replaceDbConstant, replaceDbConstantBool } = require("../lib/utils/wordpress.js"); describe('getConfig', () => { it('should read wp-package.json from the root folder and return the default configuration if the file does not exist', () => { From 2a795b40452566f9bad073d33ad6bba02a1c2b37 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 20:42:07 +0100 Subject: [PATCH 44/70] Refactor function name and add new tests to utils.test.js Renamed the function 'getVarFromPHPFile' to 'getDataFromFile'. This change better reflects the function's purpose and improves code clarity. Added new tests for this function in 'tests/utils.test.js' to ensure it correctly extracts data from PHP files. Additionally, adjusted import statements to reflect these changes in 'lib/utils/wordpress.js'. --- lib/utils/wordpress.js | 11 ++++++----- tests/utils.test.js | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/utils/wordpress.js b/lib/utils/wordpress.js index af75981..cf46dca 100644 --- a/lib/utils/wordpress.js +++ b/lib/utils/wordpress.js @@ -60,16 +60,16 @@ function getUserLocale() { * Reads a PHP file, extracts, and returns the WordPress version number. * * @param {string} fileContent - Path to the PHP file to read - * @param variableName - The name of the variable to search adn replace + * @param {string} variableName - The name of the variable to search adn replace * * @return {string|null} WordPress version number or null if not found or in case of an error */ -function getVarFromPHPFile (fileContent, variableName = '$wp_version') { +function getDataFromFile (fileContent, variableName = '$wp_version') { // Define a regular expression to match the variableName and its value with both the single and double quotes const versionRegex = new RegExp(`${variableName}\\s*=\\s*['"]([^'"]+)['"]`, 'g'); // Use the regular expression to extract the version number - const match = fileContent.match(versionRegex); + let match = versionRegex.exec(fileContent); // Return the version number or null if not found return match ? match[1] : null; @@ -86,8 +86,8 @@ function getCurrentWpInfo(wpFolder) { // get the WordPress version and the locale from wp-includes/version.php const versionFile = path.join(wpFolder, 'wp-includes', 'version.php'); const versionFileContent = fs.readFileSync(versionFile, 'utf8'); - const version = getVarFromPHPFile(versionFileContent, 'wp_version'); - const locale = getVarFromPHPFile(versionFileContent, 'wp_local_package'); + const version = getDataFromFile(versionFileContent, 'wp_version'); + const locale = getDataFromFile(versionFileContent, 'wp_local_package'); return { version, locale @@ -159,5 +159,6 @@ module.exports = { replaceDbConstant, replaceDbConstantBool, generateSalt, + getDataFromFile, replaceEmptySalts }; diff --git a/tests/utils.test.js b/tests/utils.test.js index cbde824..2c2f9e4 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -1,5 +1,7 @@ const { getConfig, getWordPressDownloadUrl, getDownloadUrl } = require('../lib/utils/data.js'); const { generateSalt, replaceDbConstant, replaceDbConstantBool } = require("../lib/utils/wordpress.js"); +const {getDataFromFile} = require("../lib/utils/wordpress"); + describe('getConfig', () => { it('should read wp-package.json from the root folder and return the default configuration if the file does not exist', () => { @@ -8,6 +10,7 @@ describe('getConfig', () => { const config = getConfig(argv); // Assert the result expect(config).toBeInstanceOf(Object); + //expect(config.name).toBe('wordpress'); expect(config.plugins).toHaveLength(0); }); }); @@ -84,3 +87,25 @@ describe('getDownloadUrl', () => { expect(getDownloadUrl('test2', '2.0', 'plugins')).toEqual('https://downloads.wordpress.org/plugins/test2.2.0.zip'); }); }); + +describe('getDataFromFile', () => { + it('returns the correct version from the PHP file', () => { + + // Mock file content + const fileContent = `$wp_version = "5.8.1";\n$wp_local_package = "en_US";`; + + // Call your function + const version = getDataFromFile(fileContent, 'wp_version'); + + // Check that the function returns the correct output + expect(version).toBe("5.8.1"); + }); + + it('returns null when the version is not found in the PHP file', () => { + const fileContent = `$something_else = "5.8.1";`; + + const version = getDataFromFile(fileContent, 'wp_version'); + + expect(version).toBe(null); + }); +}); From c78c061ab0e082a77456f70673f4b3e55393db3d Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sat, 9 Dec 2023 21:53:14 +0100 Subject: [PATCH 45/70] Add WordPress configuration parsing functionality and related tests Introduced a function to parse WordPress configuration file (wp-config.php) in `lib/utils/wordpress.js`. This function extracts defined constants and variables, providing crucial information for further operations. Additionally, created new test files to ensure the accuracy of this parsing function. Furthermore, some command names and function calls were updated for consistency. --- lib/Dump.js | 5 ++ lib/index.js | 10 ++-- lib/utils/wordpress.js | 84 ++++++++++++++++++++++++++++++++- tests/assets/version.php | 49 +++++++++++++++++++ tests/assets/wp-config.php | 96 ++++++++++++++++++++++++++++++++++++++ tests/wpConfig.test.js | 33 +++++++++++++ 6 files changed, 271 insertions(+), 6 deletions(-) create mode 100644 tests/assets/version.php create mode 100644 tests/assets/wp-config.php create mode 100644 tests/wpConfig.test.js diff --git a/lib/Dump.js b/lib/Dump.js index 599f35e..345c529 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -1,6 +1,7 @@ const fs = require('fs'); const path = require('path'); const { getCurrentWpInfo, getUserLocale, getLastWp } = require("./utils/wordpress.js"); +const {parseWpConfig} = require("./utils/wordpress"); /** * Represents a Dump class for WordPress configuration. @@ -35,8 +36,12 @@ class Dump { // the website name const name = path.basename(this.wpFolder); + console.log(`πŸ”οΈ Scanning ${this.wpFolder}`); const wpInfo = getCurrentWpInfo(this.wpFolder); + const wpConfigData = parseWpConfig(this.wpFolder); + console.log(`πŸŽ‰ found `, wpConfigData); + const language = wpInfo.locale || getUserLocale(); const version = wpInfo.version || getLastWp()?.version || 'latest'; diff --git a/lib/index.js b/lib/index.js index a7504cc..d831c8e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,14 +1,14 @@ #!/usr/bin/env node const { getConfig, printTimePassed, getInfo } = require('./utils/data.js'); -const Installer = require('./installer'); +const Installer = require('./installer.js'); const Dump = require('./dump.js'); const Database = require('./database.js'); const Initialize = require('./initialize.js'); +const Updater = require("./Updater.js"); +const { getWordPressPaths } = require("./utils/wordpress.js"); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); -const { getWordPressPaths } = require("./utils/wordpress.js"); -const Updater = require("./Updater.js"); /** * @typedef WPMMconfigWP - The configuration object for WordPress @@ -129,7 +129,7 @@ const actions = { }, /** Dump the current WordPress database. */ - 'dump-database': () => { + 'dump-db': () => { const date = new Date().getUTCDate(); const newDbName = `${config.wordpress.config.DB_NAME}-${date}.sql.gz`; const db = new Database(config); @@ -140,7 +140,7 @@ const actions = { /** Dump the current WordPress database, plugins and themes setup. */ 'dump-all': () => { - actions["dump-database"]() && console.log('πŸš€ WP Database dumped successfully.'); + actions["dump-db"]() && console.log('πŸš€ WP Database dumped successfully.'); actions.dump() && console.log('πŸš€ All data dumped successfully.'); }, }; diff --git a/lib/utils/wordpress.js b/lib/utils/wordpress.js index cf46dca..96f40b2 100644 --- a/lib/utils/wordpress.js +++ b/lib/utils/wordpress.js @@ -64,7 +64,7 @@ function getUserLocale() { * * @return {string|null} WordPress version number or null if not found or in case of an error */ -function getDataFromFile (fileContent, variableName = '$wp_version') { +function getDataFromFile (fileContent, variableName = 'wp_version') { // Define a regular expression to match the variableName and its value with both the single and double quotes const versionRegex = new RegExp(`${variableName}\\s*=\\s*['"]([^'"]+)['"]`, 'g'); @@ -94,6 +94,87 @@ function getCurrentWpInfo(wpFolder) { }; } +function uncommentedLines(content) { + const lines = content.split('\n'); + let inBlockComment = false; + let uncommented = ''; + + for (let line of lines) { + let newLine = line; + + if (inBlockComment) { + const endCommentIndex = newLine.indexOf('*/'); + if (endCommentIndex !== -1) { + inBlockComment = false; + newLine = newLine.substr(endCommentIndex + 2); + } else { + newLine = ''; + } + } + + if (!inBlockComment) { + const startCommentIndex = newLine.indexOf('/*'); + const endCommentIndex = newLine.indexOf('*/'); + + if (startCommentIndex !== -1 && endCommentIndex !== -1) { + newLine = newLine.slice(0, startCommentIndex) + newLine.slice(endCommentIndex + 2); + } else if (startCommentIndex !== -1) { + newLine = newLine.slice(0, startCommentIndex); + inBlockComment = true; + } + + const lineCommentIndex = newLine.indexOf('//'); + if (lineCommentIndex !== -1) { + newLine = newLine.slice(0, lineCommentIndex); + } + } + + uncommented += newLine + '\n'; + } + + return uncommented; +} + +/** + * Parses the wp-config.php file in a WordPress installation and extracts defined constants and variables. + * + * @param {string} wpFolder - The path to the WordPress installation folder. + * @return {object|null} - An object containing the extracted constants and variables, or null if there was an error parsing the file. + */ +function parseWpConfig(wpFolder) { + try { + const filePath = path.join(wpFolder, 'wp-config.php'); + const wpConfigContent = uncommentedLines(fs.readFileSync(filePath, 'utf8')); + + // Regular expressions to match define statements + const defineRegex = /define\(\s*'([^']*)'\s*,\s*'([^']*)'\s*\);/gi; + + // Regular expressions to match variable assignments + const variableRegex = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=\s*["']?(.*?[^"'])["']?(?:;|\?>|\s+\?>|$)/g; + + // Extract constants + const constantMatches = [...wpConfigContent.matchAll(defineRegex)]; + const constants = {}; + constantMatches.forEach(match => { + const key = match[1]; + constants[key] = match[2]; + }); + + // Extract variables + const variableMatches = [...wpConfigContent.matchAll(variableRegex)]; + const variables = {}; + variableMatches.forEach(match => { + const key = match[1]; + variables[key] = match[2]; + }); + + return { constants, variables }; + } catch (error) { + console.error('Error parsing wp-config.php:', error.message); + return null; + } +} + /** * Replaces a constant in the wp-config.php file with a user-defined value. * @@ -155,6 +236,7 @@ module.exports = { getLastWp, getUserLocale, getWordPressPaths, + parseWpConfig, getCurrentWpInfo, replaceDbConstant, replaceDbConstantBool, diff --git a/tests/assets/version.php b/tests/assets/version.php new file mode 100644 index 0000000..9af89ab --- /dev/null +++ b/tests/assets/version.php @@ -0,0 +1,49 @@ + { + + test("if wp-config.php valid, should return object containing extracted constants and variables", () => { + const mockWpConfigContent = ` + define( 'DB_NAME', 'nome_del_database_qui' ); + $table_prefix = 'wp_'; + $table_prefix2 = "wp2_"; + // This should be ignored + // define('IGNORE', 'ignore'); + // $ignore = 'a'; + `; + + path.join.mockReturnValue('./assets/wp-config.php'); + fs.readFileSync.mockImplementation(() => mockWpConfigContent); + + const result = parseWpConfig("dummy-path"); + + console.log(result); + + expect(result).toEqual({ + constants: { "DB_NAME": "nome_del_database_qui" }, + variables: { "table_prefix": "wp_", "table_prefix2": "wp2_" } + }); + }); +}); From 33fa39f09edcdf8a3afd2e7f4d50ebe3045cef99 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:13:44 +0100 Subject: [PATCH 46/70] Add initConfig function to data.js The code diff reveals added functionality to the 'lib/utils/data.js' file. A new function `initConfig` initializes the configuration for a provided base folder. If the base folder doesn't exist, it gets created, and a default config is generated and written into this template file. --- lib/utils/data.js | 27 +++++++++++++++++++++++++++ tests/utils.test.js | 11 ++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/utils/data.js b/lib/utils/data.js index 31814b0..049dab1 100644 --- a/lib/utils/data.js +++ b/lib/utils/data.js @@ -2,6 +2,33 @@ const fs = require('fs'); const {get} = require("axios"); const {pkgFileName} = require("../constants.js"); const Initialize = require("../Initialize.js"); +const {DefaultWpInstallFolder} = require("../constants"); +const path = require("path"); + + +/** + * Initializes the configuration for the given base folder. + * + * @param {string} baseFolder - The base folder where the configuration will be initialized. + * @returns {object} - The initialized configuration object. + */ +async function initConfig(baseFolder) { + + const init = new Initialize(baseFolder); + + // generate the default config + const config = await init.generateConfig(); + + // create the 'wordpress' folder if it does not exist + if (!fs.existsSync(baseFolder)) { + fs.mkdirSync(baseFolder); + } + + // write the default config to the template file + init.writeConfig(config); + + return config; +} /** * Reads wp-package.json from the root folder and extracts the value of the --template option or uses the default. diff --git a/tests/utils.test.js b/tests/utils.test.js index 2c2f9e4..44b32b9 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -4,14 +4,15 @@ const {getDataFromFile} = require("../lib/utils/wordpress"); describe('getConfig', () => { - it('should read wp-package.json from the root folder and return the default configuration if the file does not exist', () => { - const argv = {}; + it('should read wp-package.json from the root folder and return the default configuration if the file does not exist', async () => { + const argv = undefined; // Call the function - const config = getConfig(argv); + const config = await getConfig(argv); // Assert the result expect(config).toBeInstanceOf(Object); - //expect(config.name).toBe('wordpress'); - expect(config.plugins).toHaveLength(0); + expect(config.name).toBe('wordpress'); + expect(config.wordpress).toMatchObject({"config": {}}); + expect(config.plugins).not.toBeFalsy(); }); }); From 06758fe54bb0f4e78ecf98e15400dd4fe4553614 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:13:58 +0100 Subject: [PATCH 47/70] Refactor getConfig function and connection settings in data.js The getConfig function in 'data.js' file has been refactored to handle scenarios when a template is not provided and add an "initConfig" function for initialising the configuration with the base folder. The parameters in the getConnectionSettings function have also been modified to align with the new configuration settings format. --- lib/utils/data.js | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/utils/data.js b/lib/utils/data.js index 049dab1..ad65863 100644 --- a/lib/utils/data.js +++ b/lib/utils/data.js @@ -1,6 +1,6 @@ const fs = require('fs'); const {get} = require("axios"); -const {pkgFileName} = require("../constants.js"); +const {PkgFileName} = require("../constants.js"); const Initialize = require("../Initialize.js"); const {DefaultWpInstallFolder} = require("../constants"); const path = require("path"); @@ -38,7 +38,7 @@ async function initConfig(baseFolder) { * @param {string} args.template - The path to the template file. * @return {WPMMconfig} The configuration object. */ -function getConfig (args) { +async function getConfig (args) { /** * The default config from the root plugin folder. This is used if no template is provided * @@ -48,16 +48,20 @@ function getConfig (args) { // Extract the value of the --template option or use the default if (! args?.template) { - const templatePath = pkgFileName; - // Check if the template file exists and read it as JSON if it does - if (!fs.existsSync(templatePath)) { - console.log(`πŸ”΄ The template file ${templatePath} does not exist.`); - const init = new Initialize(); - config = init.generateConfig(); + if (!fs.existsSync(PkgFileName)) { + + // TODO: prompt the user to create the template file or use the default or decide at least the folder name + console.log(`⚠️ The template file ${PkgFileName} does not exist in the current folder.`); + + // If the template file does not exist, create it with the default config in the 'wordpress' folder + const baseFolder = path.join(process.cwd(), DefaultWpInstallFolder); + config = await initConfig(baseFolder); + } else { - config = JSON.parse(fs.readFileSync(templatePath, 'utf8')); - console.log(`πŸ”΄ The template file ${templatePath} exists in the root folder. Using it.`, config); + // If the template file exists, read it as JSON + config = JSON.parse(fs.readFileSync(PkgFileName, 'utf8')); + console.log(`ℹ️ The template file ${PkgFileName} exists in the root folder. Using it.`); } } else { @@ -89,11 +93,10 @@ function getConfig (args) { */ function getConnectionSettings (config) { return { - connectionLimit: 5, - host: config.host, - user: config.user, - password: config.password, - database: config.database + host: config.DB_HOST, + user: config.DB_USER, + password: config.DB_PASSWORD, + database: config.DB_NAME, }; } From f71f1100e550cb8bce254b7878b4be8af07aa10a Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:14:44 +0100 Subject: [PATCH 48/70] Add axiosFetch function to wordpress.js A new function, axiosFetch, has been added to wordpress.js for fetching data from a specified URL using Axios. This function includes headers for 'User-Agent', 'Content-Type', 'Accept', 'Accept-Encoding', 'Connection', and 'Pragma'. If the fetch is successful, it resolves with the fetched data; in the case of an error, it logs the error to the console. --- lib/utils/wordpress.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/utils/wordpress.js b/lib/utils/wordpress.js index 96f40b2..ff37aa9 100644 --- a/lib/utils/wordpress.js +++ b/lib/utils/wordpress.js @@ -31,6 +31,30 @@ function getWordPressPaths (config, rootFolder = process.cwd()) { }; } +/** + * Fetches data from the specified URL using Axios. + * + * @param {string} url - The URL to fetch data from. + * @return {Promise} Resolves with the fetched data if successful, otherwise logs the error to the console. + */ +async function axiosFetch(url) { + try { + const response = await axios.get(url, { + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive', + 'Pragma': 'no-cache', + } + }); + return response.data; + } catch (error) { + console.log(error); + } +} + /** * Fetch the WordPress version from WordPress.org * From 0841b18e4380138bb149d325e13024da7b3461d1 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:15:12 +0100 Subject: [PATCH 49/70] Replace axios.get with axiosFetch in getLastWp function The getLastWp function in wordpress.js has been updated to use the axiosFetch function instead of axios.get. In addition to the replacement, error handling has been improved: now in case of an error or if no offers are received from the WordPress API, an appropriate message is logged to the console instead of throwing an exception. --- lib/utils/wordpress.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/utils/wordpress.js b/lib/utils/wordpress.js index ff37aa9..453f96f 100644 --- a/lib/utils/wordpress.js +++ b/lib/utils/wordpress.js @@ -58,16 +58,19 @@ async function axiosFetch(url) { /** * Fetch the WordPress version from WordPress.org * - * @async - * @return {string} the version of WordPress + * @returns {object} The Last available WordPress version from WordPress.org + * @throws {Error} If the request to WordPress.org fails */ -async function getLastWp () { - const response = await axios.get('https://api.wordpress.org/core/version-check/1.7/'); - - if (response.data?.offers && response.data.offers.length > 0) { - return response.data.offers[0]; - } else { - throw new Error('❗Cannot get the last version available'); +async function getLastWp() { + try { + const response = await axiosFetch('https://api.wordpress.org/core/version-check/1.7/'); + if (response && response?.offers && response.offers.length > 0) { + return response?.offers[0]; + } else { + console.log('❗Cannot get the last version available from WordPress.org'); + } + } catch (error) { + console.log(error); } } From 5af2e3ce9ec4c86dc801b4a7fdd5dc9626163425 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:15:28 +0100 Subject: [PATCH 50/70] Add getWpConfigContent function in wordpress.js A new utility function called getWpConfigContent has been added to the wordpress.js file. This function reads the 'wp-config.php' content from any given WordPress folder. It includes proper error handling for cases where the file is not found or is empty, logging appropriate warning messages. --- lib/utils/wordpress.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/utils/wordpress.js b/lib/utils/wordpress.js index 453f96f..ee1c544 100644 --- a/lib/utils/wordpress.js +++ b/lib/utils/wordpress.js @@ -162,6 +162,25 @@ function uncommentedLines(content) { return uncommented; } +function getWpConfigContent(wpFolder) { + + const filePath = path.join(wpFolder, 'wp-config.php'); + + if (fs.existsSync(filePath) === false) { + console.log(`❗ wp-config.php not found in ${wpFolder}`); + return null; + } + + const wpConfigContent = fs.readFileSync(filePath, 'utf8'); + + if (!wpConfigContent) { + console.log(`❗ wp-config.php is empty in ${filePath}`); + return null; + } + + return wpConfigContent; +} + /** * Parses the wp-config.php file in a WordPress installation and extracts defined constants and variables. * From 575bc945714c62a866ed7ce00c68552885e808e4 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:15:43 +0100 Subject: [PATCH 51/70] Fix constant names and improve function parsers in wordpress.js Updated the naming of constants to be in Pascal case as per code style conventions. Changes also include improvements to how files and variables are parsed in functions, and code readability has also been enhanced by removing unnecessary comments and reformatting the code structures. --- lib/utils/wordpress.js | 76 ++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/lib/utils/wordpress.js b/lib/utils/wordpress.js index ee1c544..dd6248c 100644 --- a/lib/utils/wordpress.js +++ b/lib/utils/wordpress.js @@ -1,7 +1,7 @@ const path = require("path"); const fs = require("fs"); const axios = require("axios"); -const {defaultWpInstallFolder, defaultWpInstallLanguage, pkgFileName} = require("../constants.js"); +const {DefaultWpInstallFolder, DefaultWpInstallLanguage, PkgFileName} = require("../constants.js"); /** @@ -10,7 +10,7 @@ const {defaultWpInstallFolder, defaultWpInstallLanguage, pkgFileName} = require( * @param {string} [rootFolder=process.cwd()] - The root folder path for the WordPress installation. Defaults to the current working directory. * @return {WPMMpaths} - An object containing the default paths for the WordPress installation. */ -function getWordPressPaths (config, rootFolder = process.cwd()) { +function getWordPressPaths(config, rootFolder = process.cwd()) { /** * the WordPress installation folder * @@ -19,12 +19,12 @@ function getWordPressPaths (config, rootFolder = process.cwd()) { let baseFolder = rootFolder; // if wp-config.php exists in the root folder or the wp-package.json file exists in the root folder - if (! fs.existsSync(path.join(rootFolder, 'wp-config.php')) && ! fs.existsSync(path.join(rootFolder, pkgFileName))) { - baseFolder = path.join(rootFolder, config.name ?? defaultWpInstallFolder); + if (!fs.existsSync(path.join(rootFolder, 'wp-config.php')) && !fs.existsSync(path.join(rootFolder, PkgFileName))) { + baseFolder = path.join(rootFolder, config.name ?? DefaultWpInstallFolder); } return { - tempDir: path.join(rootFolder, 'temp'), + tempDir: path.join(baseFolder, 'temp'), baseFolder: baseFolder, pluginsFolder: path.join(baseFolder, 'wp-content', 'plugins'), themeFolder: path.join(baseFolder, 'wp-content', 'themes') @@ -80,7 +80,7 @@ async function getLastWp() { * @returns {string} The locale of the user. */ function getUserLocale() { - return Intl.DateTimeFormat().resolvedOptions().locale || defaultWpInstallLanguage; + return Intl.DateTimeFormat().resolvedOptions().locale || DefaultWpInstallLanguage; } /** @@ -91,7 +91,7 @@ function getUserLocale() { * * @return {string|null} WordPress version number or null if not found or in case of an error */ -function getDataFromFile (fileContent, variableName = 'wp_version') { +function getDataFromFile(fileContent, variableName = 'wp_version') { // Define a regular expression to match the variableName and its value with both the single and double quotes const versionRegex = new RegExp(`${variableName}\\s*=\\s*['"]([^'"]+)['"]`, 'g'); @@ -184,41 +184,36 @@ function getWpConfigContent(wpFolder) { /** * Parses the wp-config.php file in a WordPress installation and extracts defined constants and variables. * - * @param {string} wpFolder - The path to the WordPress installation folder. + * @param {string} wpConfigContent - The content of the wp-config.php file. * @return {object|null} - An object containing the extracted constants and variables, or null if there was an error parsing the file. */ -function parseWpConfig(wpFolder) { - try { - const filePath = path.join(wpFolder, 'wp-config.php'); - const wpConfigContent = uncommentedLines(fs.readFileSync(filePath, 'utf8')); +function parseWpConfig(wpConfigContent) { - // Regular expressions to match define statements - const defineRegex = /define\(\s*'([^']*)'\s*,\s*'([^']*)'\s*\);/gi; + const cleanWpConfigContent = uncommentedLines(wpConfigContent); - // Regular expressions to match variable assignments - const variableRegex = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=\s*["']?(.*?[^"'])["']?(?:;|\?>|\s+\?>|$)/g; + // Regular expressions to match define statements + const defineRegex = /define\(\s*'([^']*)'\s*,\s*'([^']*)'\s*\);/gi; - // Extract constants - const constantMatches = [...wpConfigContent.matchAll(defineRegex)]; - const constants = {}; - constantMatches.forEach(match => { - const key = match[1]; - constants[key] = match[2]; - }); + // Regular expressions to match variable assignments + const variableRegex = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=\s*["']?(.*?[^"'])["']?(?:;|\?>|\s+\?>|$)/g; - // Extract variables - const variableMatches = [...wpConfigContent.matchAll(variableRegex)]; - const variables = {}; - variableMatches.forEach(match => { - const key = match[1]; - variables[key] = match[2]; - }); + // Extract constants + const constantMatches = [...cleanWpConfigContent.matchAll(defineRegex)]; + const constants = {}; + constantMatches.forEach(match => { + const key = match[1]; + constants[key] = match[2]; + }); - return { constants, variables }; - } catch (error) { - console.error('Error parsing wp-config.php:', error.message); - return null; - } + // Extract variables + const variableMatches = [...cleanWpConfigContent.matchAll(variableRegex)]; + const variables = {}; + variableMatches.forEach(match => { + const key = match[1]; + variables[key] = match[2]; + }); + + return {constants, variables}; } /** @@ -229,12 +224,12 @@ function parseWpConfig(wpFolder) { * @param {string} userDefinedValue - The user-defined value to set for the constant. * @return {string} - The updated content with the replaced constant. */ -function replaceDbConstant (configContent, constantName, userDefinedValue) { +function replaceDbConstant(configContent, constantName, userDefinedValue) { const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*'[^']*'\\s*\\);`); return configContent.replace(regex, `define( '${constantName}', '${userDefinedValue}' );`); } -function replaceDbConstantBool (configContent, constantName, userDefinedValue) { +function replaceDbConstantBool(configContent, constantName, userDefinedValue) { // Updated regex to handle boolean constants (TRUE or FALSE) const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*[^']*\\s*\\);`); return configContent.replace(regex, `define( '${constantName}', ${userDefinedValue} );`); @@ -245,10 +240,10 @@ function replaceDbConstantBool (configContent, constantName, userDefinedValue) { * * @return {string} - The generated salt code. */ -function generateSalt () { +function generateSalt() { const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?/'; const saltLength = 64; - return Array.from({ length: saltLength }, () => charset[Math.floor(Math.random() * charset.length)]).join(''); + return Array.from({length: saltLength}, () => charset[Math.floor(Math.random() * charset.length)]).join(''); } /** @@ -257,7 +252,7 @@ function generateSalt () { * @param {string} configContent - The content of the wp-config.php file. * @return {string} - The updated content with replaced salts. */ -function replaceEmptySalts (configContent) { +function replaceEmptySalts(configContent) { const saltConstants = [ 'AUTH_KEY', 'SECURE_AUTH_KEY', @@ -282,6 +277,7 @@ module.exports = { getLastWp, getUserLocale, getWordPressPaths, + getWpConfigContent, parseWpConfig, getCurrentWpInfo, replaceDbConstant, From 9d79a14ebc708323ceef33b3132fe5ce3d00078b Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:16:55 +0100 Subject: [PATCH 52/70] Refactor database dump method and improve error handling The existing `dumpDatabase` method has been overhauled to improve user experience. The change includes generating filenames based on database name and date, creating the backup directory if it doesn't exist, and better error handling. The readability of the code has also been improved with some restructuring. --- lib/Database.js | 56 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/lib/Database.js b/lib/Database.js index a13cbcc..939e39a 100644 --- a/lib/Database.js +++ b/lib/Database.js @@ -1,7 +1,8 @@ const mysql = require('mysql2/promise'); const mysqldump = require('mysqldump'); const {getConnectionSettings} = require("./utils/data"); -const fs = require('fs').promises; +const path = require("path"); +const fs = require('fs'); /** * Constructor for the Database class. @@ -20,6 +21,15 @@ class Database { this.config = config; } + dbDumpFilename = () => { + const date = new Date().toDateString().replace(" ", "-"); + if (! this.config.wordpress?.config?.DB_NAME) { + console.log('πŸš€ No database name provided. Skipping database dump.', this.config.wordpress); + return; + } + return `${this.config.wordpress.config.DB_NAME}-${date}.sql.gz`; + }; + /** * Uploads a database by executing SQL queries from a specified file. * @@ -43,7 +53,7 @@ class Database { const connection = await mysql.createConnection(databaseConnectionConfig); - const sqlFileContent = await fs.readFile(sqlFilePath, 'utf8'); + const sqlFileContent = fs.readFileSync(sqlFilePath, 'utf8'); const queries = sqlFileContent.split(';'); for (const query of queries) { @@ -66,29 +76,39 @@ class Database { /** * Asynchronously dumps the database to the specified output file. * - * @param {string} outputFilePath - The path of the file to dump the database to. + * @param basePath - The base path of the WordPress installation. + * * @return {Promise} - A promise that resolves if the database is dumped successfully, or rejects with an error message if an error occurs. */ - async dumpDatabase (outputFilePath) { - try { - console.log('Dumping database...'); + async dumpDatabase (basePath) { + const dumpPath = path.join( basePath, 'backups', 'db'); + const dumpFile = path.join( dumpPath, this.dbDumpFilename()); - if (!this.config) { - return new Error('Database configuration not found'); - } + // Check if the directory exists and create it if it doesn't + if (!fs.existsSync(dumpPath)) { + fs.mkdirSync( dumpPath, { recursive: true }); + console.log(`ℹ️ Directory created: ${dumpPath}`); + } - const databaseConnectionConfig = getConnectionSettings(this.config); + console.log(`✳️ Dumping database to ${dumpFile}...`); - await mysqldump({ - databaseConnectionConfig, - dumpToFile: outputFilePath, - compressFile: true - }); + if (!this.config.wordpress.config) { + return new Error('πŸ”΄ Database configuration not found'); + } - console.log('πŸš€ Database dumped successfully'); - } catch (error) { - console.error(' πŸ”΄ Error dumping database:', error.message); + const databaseConnectionConfig = getConnectionSettings(this.config.wordpress.config); + + if (databaseConnectionConfig.host === 'localhost') { + console.log('⚠️ Warning: You are using localhost as your database host. This may not work correctly.'); } + + mysqldump({ + connection: databaseConnectionConfig, + dumpToFile: dumpFile, + compressFile: true + }).catch(error => { + console.error('πŸ”΄ Error dumping database:', error.message); + }); } } From 1b4993fb12c9cc4504bd3aff583d88512e35b425 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:17:07 +0100 Subject: [PATCH 53/70] Update Dump class in lib/Dump.js The `parseWpConfig` method in the Dump class has been updated to use the `getWpConfigContent` method. The configuration details are now included in the result, combining both constant and variable values. --- lib/Dump.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/Dump.js b/lib/Dump.js index 345c529..74fcd44 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); const { getCurrentWpInfo, getUserLocale, getLastWp } = require("./utils/wordpress.js"); -const {parseWpConfig} = require("./utils/wordpress"); +const {parseWpConfig, getWpConfigContent} = require("./utils/wordpress"); /** * Represents a Dump class for WordPress configuration. @@ -39,8 +39,9 @@ class Dump { console.log(`πŸ”οΈ Scanning ${this.wpFolder}`); const wpInfo = getCurrentWpInfo(this.wpFolder); - const wpConfigData = parseWpConfig(this.wpFolder); - console.log(`πŸŽ‰ found `, wpConfigData); + const wpConfigData = parseWpConfig( + getWpConfigContent(this.wpFolder) + ); const language = wpInfo.locale || getUserLocale(); const version = wpInfo.version || getLastWp()?.version || 'latest'; @@ -49,7 +50,11 @@ class Dump { name, wordpress: { version, - language + language, + config: { + ...wpConfigData.constants, + ...wpConfigData.variables + } }, themes, plugins From 130c25c81278743463d2ed11ebb16f9209238428 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:17:28 +0100 Subject: [PATCH 54/70] Update casing of constant declarations in lib/constants.js All names of the declared constants were updated to use upper camel casing. This ensures consistency with other declarations in the code and adheres to the chosen coding style conventions. --- lib/constants.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/constants.js b/lib/constants.js index b831d1c..aeef330 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,29 +1,29 @@ /** * the name of the WordPress package.json file */ -const pkgFileName = 'wp-package.json'; +const PkgFileName = 'wp-package.json'; /** * the default folder name for the WordPress install * @type {string} defaultWpInstallFolder - The default folder name for the WordPress install */ -const defaultWpInstallFolder = 'wordpress'; +const DefaultWpInstallFolder = 'wordpress'; -const defaultWpDatabaseType = 'mysql'; +const DefaultWpDatabaseType = 'mysql'; /** * the default language for the WordPress install * * @type {string} defaultWpInstallLanguage - The default language for the WordPress install */ -const defaultWpInstallLanguage = 'en_US'; +const DefaultWpInstallLanguage = 'en_US'; /** * The default configuration object * * @type {WPMMconfig} defaultConfig - The default configuration object */ -const defaultWpConfig = { +const DefaultWpConfig = { DB_NAME: 'my_db_name', DB_USER: 'my_username', DB_PASSWORD: 'my_password', @@ -35,9 +35,9 @@ const defaultWpConfig = { }; module.exports = { - pkgFileName, - defaultWpInstallFolder, - defaultWpDatabaseType, - defaultWpInstallLanguage, - defaultWpConfig + PkgFileName, + DefaultWpInstallFolder, + DefaultWpDatabaseType, + DefaultWpInstallLanguage, + DefaultWpConfig }; From 120d1ad4f06ac96ab1e0b76515a70a5436a1c566 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:18:03 +0100 Subject: [PATCH 55/70] Adapt lib/index.js to handle asynchronous configuration loading The library entry point lib/index.js has been refactored to handle asynchronous loading of configuration data properly. The configuration object is now fetched using a promise, and operations dependent on that configuration are processed within the 'then' clause. All other changes are related: variable declarations were moved inside the 'then' clause and usage of the 'actions' object was wrapped in a condition to prevent premature invocation. --- lib/index.js | 185 ++++++++++++++++++++++++++------------------------- 1 file changed, 95 insertions(+), 90 deletions(-) diff --git a/lib/index.js b/lib/index.js index d831c8e..98e5673 100644 --- a/lib/index.js +++ b/lib/index.js @@ -62,94 +62,99 @@ const startTime = Date.now(); const argv = yargs(hideBin(process.argv)).argv; /** @var {WPMMconfig} config - The configuration object for the script. */ -const config = getConfig(argv); +getConfig(argv).then(config => { + + /** @var {WPMMpaths} paths - The paths object for the script. */ + const paths = getWordPressPaths(config); + + console.log('πŸš€ WordPress installation started.', paths , config); + + /** + * The actions object. + * + * @param {WPMMconfig} config - The configuration object. + * @param {Object} actions - The actions object. + * @property {function} info - Retrieve information using the provided configuration and actions. + * @property {function} dump - Dumps the current WordPress installation data. + * @property {function} init - Initialize the WordPress installation. + * @property {function} 'upload-database' - Upload a database by executing SQL queries from a specified file. + * @property {function} 'dump-database' - Dump the current WordPress database. + * @property {function} 'dump-all' - Dump the current WordPress database, plugins and themes setup. + * @property {function} install - Install WordPress using the provided configuration. + * @property {function} update - Update WordPress themes and/or plugins using the provided configuration. + */ + const actions = { + + /** Retrieve information using the provided configuration and actions. */ + info: () => { + getInfo(config, actions); + }, + + /** Dumps the current WordPress installation data. This function creates a new Dump instance and initializes it. */ + dump: () => { + const dump = new Dump(paths); + dump.init(); + }, + + /** Initialize the WordPress installation. */ + init: () => { + const initializer = new Initialize(); + const result = initializer.generateConfig(); + initializer.writeConfig(result); + }, + + /** Upload a database by executing SQL queries from a specified file. */ + 'upload-database': () => { + const db = new Database(config); + db.uploadDatabase( config.wordpress.config.DB_NAME ).then(() => { + console.log('πŸš€ Database uploaded successfully.'); + }); + }, + + /** Install WordPress */ + install: () => { + const installer = new Installer(config, paths); + + installer.run().then(() => { + console.log('πŸš€ WordPress installed successfully.'); + }); + }, + + /** Update WordPress packages */ + update: () => { + const updater = new Updater(config); + updater.run(argv).then(() => { + console.log('πŸš€ WordPress updated successfully.'); + }).catch(() => { + console.log('πŸ”΄ WordPress update failed.'); + }); + }, + + /** Dump the current WordPress database. */ + 'dump-db': () => { + + const db = new Database(config); + db.dumpDatabase(paths.baseFolder).then(() => { + console.log( `πŸš€ Database dumped successfully.` ); + }); + }, + + /** Dump the current WordPress database, plugins and themes setup. */ + 'dump-all': () => { + actions["dump-db"]() && console.log('πŸš€ WP Database dumped successfully.'); + actions.dump() && console.log('πŸš€ All data dumped successfully.'); + }, + }; + + const action = Object.keys(argv).find(key => argv[key] === true); + if (typeof actions[action] !== 'function') + actions.install(); + else actions[action](); + + /** + * That's it! We're done! let's print how long it took to run the script and exit with a success code. + */ + printTimePassed(startTime); + process.exit(0); +}); -/** @var {WPMMpaths} paths - The paths object for the script. */ -const paths = getWordPressPaths(config); - -/** - * The actions object. - * - * @param {WPMMconfig} config - The configuration object. - * @param {Object} actions - The actions object. - * @property {function} info - Retrieve information using the provided configuration and actions. - * @property {function} dump - Dumps the current WordPress installation data. - * @property {function} init - Initialize the WordPress installation. - * @property {function} 'upload-database' - Upload a database by executing SQL queries from a specified file. - * @property {function} 'dump-database' - Dump the current WordPress database. - * @property {function} 'dump-all' - Dump the current WordPress database, plugins and themes setup. - * @property {function} install - Install WordPress using the provided configuration. - * @property {function} update - Update WordPress themes and/or plugins using the provided configuration. - */ -const actions = { - - /** Retrieve information using the provided configuration and actions. */ - info: () => { - getInfo(config, actions); - }, - - /** Dumps the current WordPress installation data. This function creates a new Dump instance and initializes it. */ - dump: () => { - const dump = new Dump(paths); - dump.init(); - }, - - /** Initialize the WordPress installation. */ - init: () => { - const initializer = new Initialize(); - const result = initializer.generateConfig(); - initializer.writeConfig(result); - }, - - /** Upload a database by executing SQL queries from a specified file. */ - 'upload-database': (file) => { - const db = new Database(config); - db.uploadDatabase(file || config.database.filename).then(() => { - console.log('πŸš€ Database uploaded successfully.'); - }); - }, - - /** Install WordPress */ - install: () => { - const installer = new Installer(config, paths); - - installer.run().then(() => { - console.log('πŸš€ WordPress installed successfully.'); - }); - }, - - /** Update WordPress packages */ - update: () => { - const updater = new Updater(config); - updater.run(argv).then(() => { - console.log('πŸš€ WordPress updated successfully.'); - }).catch(() => { - console.log('πŸ”΄ WordPress update failed.'); - }); - }, - - /** Dump the current WordPress database. */ - 'dump-db': () => { - const date = new Date().getUTCDate(); - const newDbName = `${config.wordpress.config.DB_NAME}-${date}.sql.gz`; - const db = new Database(config); - db.dumpDatabase(newDbName).then(() => { - console.log('πŸš€ Database dumped successfully to', newDbName, '.'); - }); - }, - - /** Dump the current WordPress database, plugins and themes setup. */ - 'dump-all': () => { - actions["dump-db"]() && console.log('πŸš€ WP Database dumped successfully.'); - actions.dump() && console.log('πŸš€ All data dumped successfully.'); - }, -}; - -const action = Object.keys(argv).find(key => argv[key] === true); -(!action ? actions.install : actions[action])(); - -/** - * That's it! We're done! let's print how long it took to run the script and exit with a success code. - */ -printTimePassed(startTime); -process.exit(0); From c3bfc0916827d707680391e21272ac3d445c1860 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:18:26 +0100 Subject: [PATCH 56/70] Refactor code to use async functions and rename constants The code in lib/Initialize.js was refactored to use async functions, providing clearer asynchronous control flow. This implies transforming some synchronous operations into asynchronous ones. At the same time, the names of the constants coming from "constants.js" have been capitalized to follow naming conventions more closely. Furthermore, minor changes have been made to improve code readability and structure, including the addition of default parameters and clarification of folder paths. --- lib/Initialize.js | 20 ++++++++++---------- lib/Installer.js | 4 ++-- lib/WpPackage.js | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/Initialize.js b/lib/Initialize.js index 91d363d..6e92a56 100644 --- a/lib/Initialize.js +++ b/lib/Initialize.js @@ -1,6 +1,6 @@ const fs = require('fs'); const path = require('path'); -const {defaultWpConfig, defaultWpInstallFolder, pkgFileName, defaultWpDatabaseType} = require("./constants.js"); +const {DefaultWpConfig, DefaultWpInstallFolder, PkgFileName, DefaultWpDatabaseType} = require("./constants.js"); const {getLastWp, getUserLocale} = require("./utils/wordpress.js"); /** @@ -12,19 +12,19 @@ class Initialize { /** * Constructor for a new instance of the class. */ - constructor () { - this.wpFolder = process.cwd(); - this.outputPath = path.join(this.wpFolder, pkgFileName); + constructor (wpFolder = null, outputPath = null) { + this.wpFolder = wpFolder || process.cwd(); + this.outputPath = outputPath || path.join(this.wpFolder, PkgFileName); } - hasConfig = () => !! fs.existsSync( path.join( this.outputPath, pkgFileName ) ); + hasConfig = () => !! fs.existsSync( path.join( this.outputPath, PkgFileName ) ); /** * Generates the configuration file for WordPress. * * @return {WPMMconfig} The configuration object. */ - generateConfig() { + async generateConfig() { // check if the output path exists if (this.hasConfig()) { @@ -32,8 +32,8 @@ class Initialize { return; } - const name = this.wpFolder.split(path.sep).pop() || defaultWpInstallFolder; - const lastWp = getLastWp(); + const name = this.wpFolder.split(path.sep).pop() || DefaultWpInstallFolder; + const lastWp = await getLastWp(); const userLocale = getUserLocale(); return { @@ -41,10 +41,10 @@ class Initialize { wordpress: { version: lastWp.version, language: userLocale, - config: defaultWpConfig + config: DefaultWpConfig }, database: { - type: defaultWpDatabaseType, + type: DefaultWpDatabaseType, backupFolder: path.join(this.wpFolder, 'backups') }, themes: [], diff --git a/lib/Installer.js b/lib/Installer.js index 6010b87..3e504b8 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -1,6 +1,6 @@ const { cleanup, makeDir } = require('./utils/fs.js'); const Package = require("./Package.js"); -const { WordPressPackage } = require('./WpPackage.js'); +const WpPackage = require('./WpPackage.js'); const {runPostInstallCommands,isWPCLIAvailable} = require("./utils/actions.js"); /** @@ -39,7 +39,7 @@ class Installer { // Install WordPress if (wordpress) { - const wp = new WordPressPackage(this.config, 'wordpress', this.paths); + const wp = new WpPackage(this.config, 'wordpress', this.paths); await wp.install(); } diff --git a/lib/WpPackage.js b/lib/WpPackage.js index e2ea4b7..d1dbf95 100644 --- a/lib/WpPackage.js +++ b/lib/WpPackage.js @@ -23,7 +23,7 @@ class WpPackage extends Package { const downloadUrl = getWordPressDownloadUrl(version, language); try { - const destinationPath = path.join(this.baseFolder, this.pkgInfo.name); + const destinationPath = path.join(this.destFolder, this.pkgInfo.name); if (fs.existsSync(destinationPath)) { console.log('πŸ”„οΈ WordPress folder already exists. Skipping download.'); From 719d2c021ff8154e2127cb9f0bdb620619e6a7d7 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 14:18:41 +0100 Subject: [PATCH 57/70] Update parseWpConfig tests and move test assets to fixtures Modified parseWpConfig tests to read actual file content instead of mocking it in the wpConfig.test.js file. Furthermore, test assets have been moved from the "assets" directory to a new "fixtures" directory. This change enhances test configuration and better reflects real-world scenarios by using actual files instead of mocked instances. --- tests/{assets => fixtures}/version.php | 0 tests/{assets => fixtures}/wp-config.php | 0 tests/{assets => fixtures}/wp-package.json | 3 +-- tests/wpConfig.test.js | 26 +++++++++++++--------- 4 files changed, 17 insertions(+), 12 deletions(-) rename tests/{assets => fixtures}/version.php (100%) rename tests/{assets => fixtures}/wp-config.php (100%) rename tests/{assets => fixtures}/wp-package.json (92%) diff --git a/tests/assets/version.php b/tests/fixtures/version.php similarity index 100% rename from tests/assets/version.php rename to tests/fixtures/version.php diff --git a/tests/assets/wp-config.php b/tests/fixtures/wp-config.php similarity index 100% rename from tests/assets/wp-config.php rename to tests/fixtures/wp-config.php diff --git a/tests/assets/wp-package.json b/tests/fixtures/wp-package.json similarity index 92% rename from tests/assets/wp-package.json rename to tests/fixtures/wp-package.json index 150ea6b..1b006b3 100644 --- a/tests/assets/wp-package.json +++ b/tests/fixtures/wp-package.json @@ -16,8 +16,7 @@ }, "database": { "type": "mysql", - "filename": "databases/database.sql", - "backup-folder": "backups" + "filename": "databases/database.sql" }, "themes": [ { diff --git a/tests/wpConfig.test.js b/tests/wpConfig.test.js index 537b3c3..3685f21 100644 --- a/tests/wpConfig.test.js +++ b/tests/wpConfig.test.js @@ -2,13 +2,11 @@ const fs = require("fs"); const path = require("path"); const {parseWpConfig} = require("../lib/utils/wordpress"); -jest.mock("fs"); -jest.mock("path"); +describe('parseWpConfig with real file', () => { -describe("parseWpConfig tests", () => { + it('should parse wp-config.php file content and return constants and variables', () => { - test("if wp-config.php valid, should return object containing extracted constants and variables", () => { const mockWpConfigContent = ` define( 'DB_NAME', 'nome_del_database_qui' ); $table_prefix = 'wp_'; @@ -18,16 +16,24 @@ describe("parseWpConfig tests", () => { // $ignore = 'a'; `; - path.join.mockReturnValue('./assets/wp-config.php'); - fs.readFileSync.mockImplementation(() => mockWpConfigContent); - - const result = parseWpConfig("dummy-path"); - - console.log(result); + const result = parseWpConfig(mockWpConfigContent); expect(result).toEqual({ constants: { "DB_NAME": "nome_del_database_qui" }, variables: { "table_prefix": "wp_", "table_prefix2": "wp2_" } }); }); + + it('should parse wp-config.php file content and return constants and variables', () => { + const filePath = path.join( __dirname,'fixtures', 'wp-config.php'); + + // Read the actual file content + const wpConfigContent = fs.readFileSync(filePath, 'utf8'); + + const result = parseWpConfig(wpConfigContent); + + // Ensure the result is as expected based on the actual file content + expect(result).toBeTruthy(); + expect(result).toMatchObject({ constants: {}, variables: {table_prefix: "wp_"} }); + }); }); From c63a8d131429cdb124464286c717962463ea267d Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 16:04:04 +0100 Subject: [PATCH 58/70] Add directory existence validation to scanDirectory method Added a check within the `scanDirectory` method in `lib/Dump.js` to validate whether a directory exists before attempting to read its content. This will prevent errors from occurring when a directory is not found, instead returning an empty result set. --- lib/Dump.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Dump.js b/lib/Dump.js index 74fcd44..2aacc34 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -73,6 +73,12 @@ class Dump { * @return {Array} - An array of objects with the name and version of each item found. */ scanDirectory (directory) { + // Check if the directory exists + if (! fs.existsSync(directory)) { + console.log(`⚠️ The directory ${directory} does not exist.`); + return []; + } + const items = fs.readdirSync(directory); const result = []; From 3b633b70f00e2d9a036720497448f552d0704436 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 16:04:33 +0100 Subject: [PATCH 59/70] Refactor WordPress configuration definitions and simplify action invocations Reorganized WordPress and package configuration definitions, moving them from `lib/index.js` to `lib/constants.js`. Streamlined how actions such as installation and update are invoked in `lib/index.js`, improving code readability and maintainability. As a result, the `actions` object in `lib/index.js` is now cleaner and operations are displayed in a more logical order. --- lib/constants.js | 45 ++++++++++++++ lib/index.js | 152 +++++------------------------------------------ 2 files changed, 60 insertions(+), 137 deletions(-) diff --git a/lib/constants.js b/lib/constants.js index aeef330..a1a70c5 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,3 +1,48 @@ +/** + * @typedef WPMMconfigWP - The configuration object for WordPress + * @property {string} DB_NAME - The name of the database + * @property {string} DB_USER - The username for the database + * @property {string} DB_PASSWORD - The password for the database + * @property {string} DB_HOST - The host name or IP address of the database server + * @property {string} DB_CHARSET - The character set for the database + * @property {string} DB_COLLATE - The collation for the database (usually utf8_general_ci) + * @property {string} table_prefix - The table prefix for the database + * @property {boolean} WP_DEBUG - Whether to enable debugging + * @property {string} WP_SITEURL - The URL of the website + * @property {string} WP_HOME - The home URL of the website + * @property {string} WP_CONTENT_DIR - The path to the content directory + * @property {string} WP_CONTENT_URL - The URL of the content directory + * @property {boolean} DISALLOW_FILE_EDIT - Whether to disallow file editing + * + * Package configuration + * @typedef WPMMconfigPkg - The package object + * @property {string} name - the name of the package + * @property {string} version - the version of the package + * @property {string} source - the source of the package + * + * Website configuration + * @typedef WPMMconfig - The website configuration + * @property {string} name - The name of the website + * @property {Object} wordpress - The WordPress pkg related data for the website + * @property {string} wordpress.version - The current version of WordPress + * @property {string} wordpress.language - The language of WordPress + * @property {WPMMconfigWP} wordpress.config - The wp-config.php file data + * @property {WPMMconfigPkg[]} themes - The themes used + * @property {WPMMconfigPkg[]} plugins - The plugins used + * @property {WPMMconfigPkg[]} database - The database data + * @property {string[]} postInstall - The post-install actions run by wp-cli + */ + +/** + * @typedef {Object} WPMMpaths - The object containing the paths + * @property {string} rootFolder - The root folder of the application + * @property {string} tempDir - The temporary directory + * @property {string?} baseFolder - The path to the WordPress folder. Defaults to the current working directory. + * @property {string} destFolder - The destination folder for package installation. + * @property {string?} pluginsFolder - The destination folder for plugins + * @property {string?} themeFolder - The destination folder for themes + */ + /** * the name of the WordPress package.json file */ diff --git a/lib/index.js b/lib/index.js index 98e5673..78c42a0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,59 +1,10 @@ #!/usr/bin/env node -const { getConfig, printTimePassed, getInfo } = require('./utils/data.js'); -const Installer = require('./installer.js'); -const Dump = require('./dump.js'); -const Database = require('./database.js'); -const Initialize = require('./initialize.js'); -const Updater = require("./Updater.js"); +const { getConfig, printTimePassed } = require('./utils/data.js'); const { getWordPressPaths } = require("./utils/wordpress.js"); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); - -/** - * @typedef WPMMconfigWP - The configuration object for WordPress - * @property {string} DB_NAME - The name of the database - * @property {string} DB_USER - The username for the database - * @property {string} DB_PASSWORD - The password for the database - * @property {string} DB_HOST - The host name or IP address of the database server - * @property {string} DB_CHARSET - The character set for the database - * @property {string} DB_COLLATE - The collation for the database (usually utf8_general_ci) - * @property {string} table_prefix - The table prefix for the database - * @property {boolean} WP_DEBUG - Whether to enable debugging - * @property {string} WP_SITEURL - The URL of the website - * @property {string} WP_HOME - The home URL of the website - * @property {string} WP_CONTENT_DIR - The path to the content directory - * @property {string} WP_CONTENT_URL - The URL of the content directory - * @property {boolean} DISALLOW_FILE_EDIT - Whether to disallow file editing - * - * Package configuration - * @typedef WPMMconfigPkg - The package object - * @property {string} name - the name of the package - * @property {string} version - the version of the package - * @property {string} source - the source of the package - * - * Website configuration - * @typedef WPMMconfig - The website configuration - * @property {string} name - The name of the website - * @property {Object} wordpress - The WordPress pkg related data for the website - * @property {string} wordpress.version - The current version of WordPress - * @property {string} wordpress.language - The language of WordPress - * @property {WPMMconfigWP} wordpress.config - The wp-config.php file data - * @property {WPMMconfigPkg[]} themes - The themes used - * @property {WPMMconfigPkg[]} plugins - The plugins used - * @property {WPMMconfigPkg[]} database - The database data - * @property {string[]} postInstall - The post-install actions run by wp-cli - */ - -/** - * @typedef {Object} WPMMpaths - The object containing the paths - * @property {string} rootFolder - The root folder of the application - * @property {string} tempDir - The temporary directory - * @property {string?} baseFolder - The path to the WordPress folder. Defaults to the current working directory. - * @property {string} destFolder - The destination folder for package installation. - * @property {string?} pluginsFolder - The destination folder for plugins - * @property {string?} themeFolder - The destination folder for themes - */ +const actions = require("./actions"); /** @var {number} startTime - the time at which the script started. */ const startTime = Date.now(); @@ -67,94 +18,21 @@ getConfig(argv).then(config => { /** @var {WPMMpaths} paths - The paths object for the script. */ const paths = getWordPressPaths(config); - console.log('πŸš€ WordPress installation started.', paths , config); - - /** - * The actions object. - * - * @param {WPMMconfig} config - The configuration object. - * @param {Object} actions - The actions object. - * @property {function} info - Retrieve information using the provided configuration and actions. - * @property {function} dump - Dumps the current WordPress installation data. - * @property {function} init - Initialize the WordPress installation. - * @property {function} 'upload-database' - Upload a database by executing SQL queries from a specified file. - * @property {function} 'dump-database' - Dump the current WordPress database. - * @property {function} 'dump-all' - Dump the current WordPress database, plugins and themes setup. - * @property {function} install - Install WordPress using the provided configuration. - * @property {function} update - Update WordPress themes and/or plugins using the provided configuration. - */ - const actions = { - - /** Retrieve information using the provided configuration and actions. */ - info: () => { - getInfo(config, actions); - }, - - /** Dumps the current WordPress installation data. This function creates a new Dump instance and initializes it. */ - dump: () => { - const dump = new Dump(paths); - dump.init(); - }, - - /** Initialize the WordPress installation. */ - init: () => { - const initializer = new Initialize(); - const result = initializer.generateConfig(); - initializer.writeConfig(result); - }, - - /** Upload a database by executing SQL queries from a specified file. */ - 'upload-database': () => { - const db = new Database(config); - db.uploadDatabase( config.wordpress.config.DB_NAME ).then(() => { - console.log('πŸš€ Database uploaded successfully.'); - }); - }, - - /** Install WordPress */ - install: () => { - const installer = new Installer(config, paths); - - installer.run().then(() => { - console.log('πŸš€ WordPress installed successfully.'); - }); - }, - - /** Update WordPress packages */ - update: () => { - const updater = new Updater(config); - updater.run(argv).then(() => { - console.log('πŸš€ WordPress updated successfully.'); - }).catch(() => { - console.log('πŸ”΄ WordPress update failed.'); - }); - }, - - /** Dump the current WordPress database. */ - 'dump-db': () => { - - const db = new Database(config); - db.dumpDatabase(paths.baseFolder).then(() => { - console.log( `πŸš€ Database dumped successfully.` ); - }); - }, + const actionLauncher = actions(config, paths); - /** Dump the current WordPress database, plugins and themes setup. */ - 'dump-all': () => { - actions["dump-db"]() && console.log('πŸš€ WP Database dumped successfully.'); - actions.dump() && console.log('πŸš€ All data dumped successfully.'); - }, - }; + for (const key of Object.keys(argv)) { + // Skip the loop iteration when the key is '_' or '$0' + if (key === '_' || key === '$0') { + continue; + } - const action = Object.keys(argv).find(key => argv[key] === true); - if (typeof actions[action] !== 'function') - actions.install(); - else actions[action](); + actionLauncher.invokeAction(key, argv); + } - /** - * That's it! We're done! let's print how long it took to run the script and exit with a success code. - */ - printTimePassed(startTime); - process.exit(0); + /** + * That's it! We're done! let's print how long it took to run the script and exit with a success code. + */ + printTimePassed(startTime); + process.exit(0); }); From 57cbbb7cd2b6d9b42df613316e74a55e91134a80 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 16:04:55 +0100 Subject: [PATCH 60/70] Add actions.js file with WordPress management functionalities Introduced `actions.js` that centralizes all WordPress management actions. This new file encapsulates functions for retrieving information, initializing WordPress installation, installing and updating WordPress, dumping data and database, among others. This centralization is intended to facilitate maintenance and improve extendibility. --- lib/Updater.js | 2 +- lib/actions.js | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 lib/actions.js diff --git a/lib/Updater.js b/lib/Updater.js index d63c1b6..b1f0260 100644 --- a/lib/Updater.js +++ b/lib/Updater.js @@ -135,7 +135,7 @@ class Updater { if (!Object.values(updateObject).some(Boolean)) { console.log('you must specify either all, plugins, themes, or wordpress'); - console.log('ie. wpmm update all'); + console.log('ie. wpmm --update all'); } } } diff --git a/lib/actions.js b/lib/actions.js new file mode 100644 index 0000000..295869c --- /dev/null +++ b/lib/actions.js @@ -0,0 +1,97 @@ +const {getInfo} = require("./utils/data"); +const Initialize = require("./Initialize"); +const Installer = require("./Installer"); +const Updater = require("./Updater"); +const Dump = require("./Dump"); +const Database = require("./Database"); + + +/** + * The actions object. + * + * @param {WPMMconfig} config - The configuration object. + * @param {WPMMpaths} paths - The paths object. + */ +function actions({config, paths}) { + /** + * @function wpmmActions.info - Retrieve information using the provided configuration and actions. + * @function wpmmActions.dump - Dumps the current WordPress installation data. + * @function wpmmActions.init - Initialize the WordPress installation. + * @function wpmmActions.upload-db - Upload a database by executing SQL queries from a specified file. + * @function wpmmActions.dump-db - Dump the current WordPress database. + * @function wpmmActions.dump-all - Dump the current WordPress database, plugins and themes setup. + * @function wpmmActions.install - Install WordPress using the provided configuration. + * @function wpmmActions.update - Update WordPress themes and/or plugins using the provided configuration. + */ + const wpmmActions = { + /** Retrieve information using the provided configuration and actions. */ + info: () => getInfo(config, actions), + + /** Initialize the WordPress installation. */ + init: () => { + const initializer = new Initialize(); + const result = initializer.generateConfig(); + initializer.writeConfig(result); + }, + + /** Install WordPress */ + install: () => { + const installer = new Installer(config, paths); + + installer.run().then(() => { + console.log('πŸš€ WordPress installed successfully.'); + }); + }, + + /** Update WordPress packages */ + update: ({argv}) => { + const updater = new Updater(config); + updater.run(argv).then(() => { + console.log('πŸš€ WordPress updated successfully.'); + }).catch(() => { + console.log('πŸ”΄ WordPress update failed.'); + }); + }, + + /** Dumps the current WordPress installation data. This function creates a new Dump instance and initializes it. */ + dump: () => { + const dump = new Dump(paths); + dump.init(); + }, + + /** Dump the current WordPress database. */ + 'dump-db': () => { + + const db = new Database(config); + db.dumpDatabase(paths.baseFolder).then(() => { + console.log(`πŸš€ Database dumped successfully.`); + }); + }, + + /** Dump the current WordPress database, plugins and themes setup. */ + 'dump-all': () => { + actions["dump-db"]() && console.log('πŸš€ WP Database dumped successfully.'); + actions.dump() && console.log('πŸš€ All data dumped successfully.'); + }, + + /** Upload a database by executing SQL queries from a specified file. */ + 'upload-db': () => { + const db = new Database(config); + db.uploadDatabase(config.wordpress.config.DB_NAME).then(() => { + console.log('πŸš€ Database uploaded successfully.'); + }); + }, + }; + + return { + invokeAction: (key, argv) => { + if (typeof wpmmActions[key] === 'function') { + wpmmActions[key]({config, paths, argv}); + } else { + console.log(`Invalid action: ${key}`); + } + }, + }; +} + +module.exports = actions; From 98f741da0014116bcf6139c9ef6475dff30a530b Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 16:21:07 +0100 Subject: [PATCH 61/70] Refactor actions in actions.js and index.js Adjusted argument intake in `actions.js` and `index.js` files for better encapsulation. In `actions.js` we replaced `actions` with `wpmmActions` to reflect consolidated function. Similarly, in `index.js`, the actionLauncher now takes an object containing both config and paths. These changes focused on improving code maintainability and readability. --- lib/actions.js | 2 +- lib/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/actions.js b/lib/actions.js index 295869c..f24c09e 100644 --- a/lib/actions.js +++ b/lib/actions.js @@ -25,7 +25,7 @@ function actions({config, paths}) { */ const wpmmActions = { /** Retrieve information using the provided configuration and actions. */ - info: () => getInfo(config, actions), + info: () => getInfo(config, wpmmActions), /** Initialize the WordPress installation. */ init: () => { diff --git a/lib/index.js b/lib/index.js index 78c42a0..3b2a8a6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -18,7 +18,7 @@ getConfig(argv).then(config => { /** @var {WPMMpaths} paths - The paths object for the script. */ const paths = getWordPressPaths(config); - const actionLauncher = actions(config, paths); + const actionLauncher = actions({config, paths}); for (const key of Object.keys(argv)) { // Skip the loop iteration when the key is '_' or '$0' From c982e82dfd5a8a7a0a67e2b0b3378666dfbb7aa2 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 23:33:30 +0100 Subject: [PATCH 62/70] Refactor code for clarity and error handling in data.js Replaced the direct use of axios's get method in `data.js` with `axiosFetch` from `wordpress.js` to improve consistency and readability of code. This also aids in error handling and response data extraction, making the code easier to understand and maintain. --- lib/utils/{actions.js => commands.js} | 16 +++---- lib/utils/data.js | 68 ++++++++++++++------------- lib/utils/fs.js | 65 +++++++++++++------------ lib/utils/wordpress.js | 68 ++++++++++++++++++++------- 4 files changed, 130 insertions(+), 87 deletions(-) rename lib/utils/{actions.js => commands.js} (87%) diff --git a/lib/utils/actions.js b/lib/utils/commands.js similarity index 87% rename from lib/utils/actions.js rename to lib/utils/commands.js index 52ed304..fac74e9 100644 --- a/lib/utils/actions.js +++ b/lib/utils/commands.js @@ -30,7 +30,7 @@ async function installNpmPackages (packageDirectory) { reject(error); } else { console.log(`πŸ“¦ ${packageName} dependencies installed and built.`); - resolve(); + resolve(true); } }); }); @@ -41,12 +41,12 @@ async function installNpmPackages (packageDirectory) { * * @param {string} repoPath - The path to the repository where composer.json is located. * - * @returns {Promise} - A promise resolving when the installation process is completed. + * @returns {void} - A promise resolving when the installation process is completed. */ -async function installComposer (repoPath) { +function installComposer (repoPath) { console.log('🎻 Found composer.json'); - await exec('composer install --no-dev', { cwd: repoPath }); - await exec('composer dumpautoload -o', { cwd: repoPath }); + exec('composer install --no-dev', { cwd: repoPath }); + exec('composer dumpautoload -o', { cwd: repoPath }); } /** @@ -54,10 +54,10 @@ async function installComposer (repoPath) { * * @return {boolean} - A promise that resolves to true if WP-CLI is available, false otherwise. */ -async function isWPCLIAvailable () { +function isWPCLIAvailable () { try { // Attempt to execute a simple WP-CLI command - await exec('wp --version'); + exec('wp --version'); return true; // If successful, WP-CLI is available } catch (error) { console.log('πŸ”΄ WP-CLI is not available on this system. Please install WP-CLI and try again.'); @@ -69,7 +69,7 @@ async function isWPCLIAvailable () { /** * Runs post-install commands asynchronously. * - * @param {Array} commands - An array of WP-CLI commands to execute. + * @param {string[]} commands - An array of WP-CLI commands to execute. * @return {Promise} - A promise that resolves when the post-install commands complete. */ async function runPostInstallCommands (commands) { diff --git a/lib/utils/data.js b/lib/utils/data.js index ad65863..e38f58a 100644 --- a/lib/utils/data.js +++ b/lib/utils/data.js @@ -1,22 +1,25 @@ const fs = require('fs'); -const {get} = require("axios"); const {PkgFileName} = require("../constants.js"); const Initialize = require("../Initialize.js"); const {DefaultWpInstallFolder} = require("../constants"); const path = require("path"); - +const {axiosFetch} = require("./wordpress.js"); /** * Initializes the configuration for the given base folder. * * @param {string} baseFolder - The base folder where the configuration will be initialized. - * @returns {object} - The initialized configuration object. + * @returns {Promise} - The initialized configuration object. */ async function initConfig(baseFolder) { const init = new Initialize(baseFolder); // generate the default config + /** + * the default config + * @type {import('../constants.js').WPMMconfig} config + */ const config = await init.generateConfig(); // create the 'wordpress' folder if it does not exist @@ -34,47 +37,45 @@ async function initConfig(baseFolder) { * Reads wp-package.json from the root folder and extracts the value of the --template option or uses the default. * The default config is used if no template is provided. Checks if the template file exists and reads it as JSON if it does. * - * @param {any} args - The arguments object. - * @param {string} args.template - The path to the template file. - * @return {WPMMconfig} The configuration object. + * @param {any} args - The path to the template file. + * @return {Promise} The configuration object. */ async function getConfig (args) { /** * The default config from the root plugin folder. This is used if no template is provided * - * @type {WPMMconfig} config - The configuration object + * @type {import('../constants.js').WPMMconfig} config - The configuration object */ - let config = {}; + let config; - // Extract the value of the --template option or use the default - if (! args?.template) { - // Check if the template file exists and read it as JSON if it does - if (!fs.existsSync(PkgFileName)) { - - // TODO: prompt the user to create the template file or use the default or decide at least the folder name - console.log(`⚠️ The template file ${PkgFileName} does not exist in the current folder.`); + // Check if the template file exists and read it as JSON if it does + if (!fs.existsSync(PkgFileName)) { - // If the template file does not exist, create it with the default config in the 'wordpress' folder - const baseFolder = path.join(process.cwd(), DefaultWpInstallFolder); - config = await initConfig(baseFolder); + // TODO: prompt the user to create the template file or use the default or decide at least the folder name + console.log(`⚠️ The template file ${PkgFileName} does not exist in the current folder.`); - } else { - // If the template file exists, read it as JSON - config = JSON.parse(fs.readFileSync(PkgFileName, 'utf8')); - console.log(`ℹ️ The template file ${PkgFileName} exists in the root folder. Using it.`); - } + // If the template file does not exist, create it with the default config in the 'wordpress' folder + const baseFolder = path.join(process.cwd(), DefaultWpInstallFolder); + config = await initConfig(baseFolder); } else { + // If the template file exists, read it as JSON + config = JSON.parse(fs.readFileSync(PkgFileName, 'utf8')); + console.log(`ℹ️ The template file ${PkgFileName} exists in the root folder. Using it.`); + } + + // Extract the value of the --template option or use the default + /** + * @property {string} args.template - The path to the template file. + */ + if (args?.template) { /** * the user has provided a template file via the --template option. read the config from the remote source */ - get(args.template) - .then(response => { - config = response.data; - }) - .catch(error => { - console.log("πŸ”΄ Error: " + error.message); - }); + const templateContig = await axiosFetch(args.template); + + // merge the template config with the default config + config = { ...config, ...templateContig }; } return config; @@ -83,8 +84,8 @@ async function getConfig (args) { /** * Returns the connection settings based on the provided config. * - * @param {object} config - The configuration object containing the host, user, password, and database details. - * @return {WPMMconfig} - The connection settings object with the following properties: + * @param {import('../constants.js').WPMMconfigWP} config - The configuration object containing the host, user, password, and database details. + * @return {mysqldump.ConnectionOptions} - The connection settings object with the following properties: * - connectionLimit: The maximum number of connections allowed. * - host: The host name or IP address of the database server. * - user: The username for authenticating with the database server. @@ -139,8 +140,9 @@ function getDownloadUrl (packageName, packageVersion, type) { /** * Retrieve information about the WPMM and system environment. - * * @return {void} + * @param {any} config + * @param {{}} actions */ function getInfo (config, actions) { const version = require('../../package.json').version; diff --git a/lib/utils/fs.js b/lib/utils/fs.js index 8815e84..ad68f88 100644 --- a/lib/utils/fs.js +++ b/lib/utils/fs.js @@ -4,6 +4,8 @@ const extract = require('extract-zip'); /** * Create a temporary directory if it does not alreadyW exist. + * + * @param {fs.PathLike} dirpath - The path of the temporary directory. */ function makeDir (dirpath) { if (!fs.existsSync(dirpath)) { @@ -15,13 +17,14 @@ function makeDir (dirpath) { * Asynchronously cleans up a temporary directory. * * @param {string} dir - The path to the temporary directory. + * * @return {void} A promise that resolves when the cleanup is complete. */ -async function cleanup (dir) { +function cleanup (dir) { try { fs.rmSync(dir, { recursive: true }); console.log(`🧹 ${dir} removed successfully.`); - } catch (err) { + } catch (/** @type {any} */ err) { // File deletion failed console.error(err.message); } @@ -66,7 +69,7 @@ async function downloadFile (url, targetFile) { } const fileWriter = fs.createWriteStream(targetFile).on('finish', () => { - resolve({}); + resolve(void 0); }); response.pipe(fileWriter); @@ -74,7 +77,7 @@ async function downloadFile (url, targetFile) { reject(error); }); }); - } catch (error) { + } catch ( /** @type {any} error */ error) { throw new Error(error); } } @@ -84,39 +87,43 @@ async function downloadFile (url, targetFile) { * * @param {string} zipFilePath - The path of the zip file to extract. * @param {string} targetDirectory - The directory to extract the zip file to. - * @return {Promise} Returns true if the extraction is successful, false otherwise. + * @throws {Error} Throws an error if the extraction fails. + * + * @return {Promise} Returns the common root path of the extracted files. */ async function extractZip (zipFilePath, targetDirectory) { - let commonRootPath; // Variable to store the common root path + /** @type {string} commonRootPath - The common root path of the extracted files */ + let commonRootPath= ""; - try { - await extract(zipFilePath, { - dir: targetDirectory, - onEntry: (entry) => { - const entryPathParts = entry.fileName.split('/'); + await extract(zipFilePath, { + dir: targetDirectory, + onEntry: (entry) => { + const entryPathParts = entry.fileName.split('/'); - if (!commonRootPath) { - // Initialize the common root path with the first entry - commonRootPath = entryPathParts[0]; - } else { - // Update the common root path based on the current entry - for (let i = 0; i < entryPathParts.length; i++) { - if (commonRootPath.split('/')[i] !== entryPathParts[i]) { - commonRootPath = commonRootPath.split('/').slice(0, i).join('/'); - break; - } + if (!commonRootPath) { + // Initialize the common root path with the first entry + commonRootPath = entryPathParts[0]; + } else { + // Update the common root path based on the current entry + for (let i = 0; i < entryPathParts.length; i++) { + if (commonRootPath.split('/')[i] !== entryPathParts[i]) { + commonRootPath = commonRootPath.split('/').slice(0, i).join('/'); + break; } } } - }); + return commonRootPath; + } + }).then((result) => { + // Return the root folder name + console.log(`πŸ“‚ Extracted to ${result}`); + /** @type {string} result - The common root path of the extracted files */ + return result; + }).catch((err) => { + throw new Error(`πŸ“› Error extracting ${zipFilePath} zip: ${err}`); + }); - // Return the root folder name - console.log(`πŸ“‚ Extracted to ${commonRootPath}`); - return commonRootPath; - } catch (err) { - console.error(`πŸ“› Error extracting ${zipFilePath} zip: ${err}`); - return err; - } + return commonRootPath; } module.exports = { diff --git a/lib/utils/wordpress.js b/lib/utils/wordpress.js index dd6248c..5b489f3 100644 --- a/lib/utils/wordpress.js +++ b/lib/utils/wordpress.js @@ -1,14 +1,14 @@ const path = require("path"); const fs = require("fs"); -const axios = require("axios"); +const axios = require("axios").default; const {DefaultWpInstallFolder, DefaultWpInstallLanguage, PkgFileName} = require("../constants.js"); /** * Gets the default paths for the WordPress installation. - * @param {Object} config - The configuration object for the WordPress installation. + * @param {import("../constants.js").WPMMconfig} config - The configuration object for the WordPress installation. * @param {string} [rootFolder=process.cwd()] - The root folder path for the WordPress installation. Defaults to the current working directory. - * @return {WPMMpaths} - An object containing the default paths for the WordPress installation. + * @return {import("../constants.js").WPMMpaths} - An object containing the default paths for the WordPress installation. */ function getWordPressPaths(config, rootFolder = process.cwd()) { /** @@ -26,6 +26,8 @@ function getWordPressPaths(config, rootFolder = process.cwd()) { return { tempDir: path.join(baseFolder, 'temp'), baseFolder: baseFolder, + rootFolder: rootFolder, + destFolder: rootFolder, pluginsFolder: path.join(baseFolder, 'wp-content', 'plugins'), themeFolder: path.join(baseFolder, 'wp-content', 'themes') }; @@ -35,7 +37,7 @@ function getWordPressPaths(config, rootFolder = process.cwd()) { * Fetches data from the specified URL using Axios. * * @param {string} url - The URL to fetch data from. - * @return {Promise} Resolves with the fetched data if successful, otherwise logs the error to the console. + * @return {Promise} Resolves with the fetched data if successful, otherwise logs the error to the console. */ async function axiosFetch(url) { try { @@ -57,17 +59,23 @@ async function axiosFetch(url) { /** * Fetch the WordPress version from WordPress.org - * - * @returns {object} The Last available WordPress version from WordPress.org - * @throws {Error} If the request to WordPress.org fails */ async function getLastWp() { try { - const response = await axiosFetch('https://api.wordpress.org/core/version-check/1.7/'); - if (response && response?.offers && response.offers.length > 0) { - return response?.offers[0]; + /** + * @typedef {{version: string}} Offer the WordPress version + * @typedef {{offers: Offer[]}} Offers the WordPress api response + * @returns {Promise} The Last available WordPress version from WordPress.org + * @throws {Error} If the request to WordPress.org fails + * + * @type {Offers|undefined} The Last available WordPress version from WordPress.org + */ + const wpApiVersions = await axiosFetch('https://api.wordpress.org/core/version-check/1.7/'); + if (wpApiVersions && wpApiVersions?.offers && wpApiVersions.offers.length > 0) { + return wpApiVersions?.offers[0]; } else { console.log('❗Cannot get the last version available from WordPress.org'); + return { version: 'latest' }; } } catch (error) { console.log(error); @@ -106,21 +114,27 @@ function getDataFromFile(fileContent, variableName = 'wp_version') { * Retrieves the WordPress version and locale information from a given WordPress folder. * * @param {string} wpFolder - The path to the WordPress folder. - * @returns {object} - An object containing the version and locale information. + * @returns {{version: string, locale: string}} - An object containing the version and locale information. */ function getCurrentWpInfo(wpFolder) { // get the WordPress version and the locale from wp-includes/version.php const versionFile = path.join(wpFolder, 'wp-includes', 'version.php'); const versionFileContent = fs.readFileSync(versionFile, 'utf8'); - const version = getDataFromFile(versionFileContent, 'wp_version'); - const locale = getDataFromFile(versionFileContent, 'wp_local_package'); + const version = getDataFromFile(versionFileContent, 'wp_version') || 'latest'; + const locale = getDataFromFile(versionFileContent, 'wp_local_package') || getUserLocale(); return { version, locale }; } +/** + * Removes all commented lines from the given content. + * + * @param {string} content - The content that contains the lines to be uncommented. + * @return {string} The content without any commented lines. + */ function uncommentedLines(content) { const lines = content.split('\n'); let inBlockComment = false; @@ -162,6 +176,12 @@ function uncommentedLines(content) { return uncommented; } +/** + * Retrieves the content of the wp-config.php file located in the specified WordPress folder. + * + * @param {string} wpFolder - The path to the WordPress folder. + * @return {string|null} The content of the wp-config.php file, or null if the file does not exist or is empty. + */ function getWpConfigContent(wpFolder) { const filePath = path.join(wpFolder, 'wp-config.php'); @@ -199,18 +219,23 @@ function parseWpConfig(wpConfigContent) { // Extract constants const constantMatches = [...cleanWpConfigContent.matchAll(defineRegex)]; + /** + * @type {Record} constants - An object containing the extracted constants. + */ const constants = {}; constantMatches.forEach(match => { - const key = match[1]; - constants[key] = match[2]; + constants[match[1]] = match[2]; }); // Extract variables const variableMatches = [...cleanWpConfigContent.matchAll(variableRegex)]; + + /** + * @type {Record} variables - An object containing the extracted constants. + */ const variables = {}; variableMatches.forEach(match => { - const key = match[1]; - variables[key] = match[2]; + variables[match[1]] = match[2]; }); return {constants, variables}; @@ -229,6 +254,14 @@ function replaceDbConstant(configContent, constantName, userDefinedValue) { return configContent.replace(regex, `define( '${constantName}', '${userDefinedValue}' );`); } +/** + * Replaces a database constant boolean value in the given configuration content. + * + * @param {string} configContent - The content of the configuration. + * @param {string} constantName - The name of the constant to be replaced. + * @param {boolean} userDefinedValue - The user-defined value to replace the constant with. + * @return {string} The updated configuration content with the replaced constant. + */ function replaceDbConstantBool(configContent, constantName, userDefinedValue) { // Updated regex to handle boolean constants (TRUE or FALSE) const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*[^']*\\s*\\);`); @@ -274,6 +307,7 @@ function replaceEmptySalts(configContent) { } module.exports = { + axiosFetch, getLastWp, getUserLocale, getWordPressPaths, From aabc72decf0cba48794b0d56fbe58ca69e9c84e2 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 23:33:57 +0100 Subject: [PATCH 63/70] Refactor code and update documentation Refactored action functions by renaming certain modules and improving error handling. Updated comments and parameter types across multiple modules for better code readability and understanding. Integrated new method descriptions and enhanced the clarity of existing ones. --- lib/Database.js | 34 +++++++++++++++++---------- lib/Dump.js | 3 +++ lib/Initialize.js | 27 ++++++++++++++++----- lib/Installer.js | 8 +++---- lib/Package.js | 25 ++++++++++++++++---- lib/Updater.js | 24 ++++++++++++++----- lib/WpPackage.js | 26 +++++++++++++++++--- lib/actions.js | 60 ++++++++++++++++++++++++++++------------------- lib/constants.js | 39 ++++++++++++++++-------------- lib/index.js | 29 +++++++++++++++-------- 10 files changed, 187 insertions(+), 88 deletions(-) diff --git a/lib/Database.js b/lib/Database.js index 939e39a..d55884f 100644 --- a/lib/Database.js +++ b/lib/Database.js @@ -1,5 +1,5 @@ const mysql = require('mysql2/promise'); -const mysqldump = require('mysqldump'); +const mysqldump = require('mysqldump').default; const {getConnectionSettings} = require("./utils/data"); const path = require("path"); const fs = require('fs'); @@ -8,25 +8,25 @@ const fs = require('fs'); * Constructor for the Database class. * * @class Database - * @param {WPMMconfig} config - The configuration object. */ class Database { /** * A description of the entire function. * - * @param {WPMMconfig} config - The configuration object. + * @param {import('./constants').WPMMconfig} config - The configuration object. */ constructor (config) { // Load configuration from wp-package.json this.config = config; } + /** + * Generates a unique filename for the database dump. + * + * @returns {string} - The generated filename. + */ dbDumpFilename = () => { const date = new Date().toDateString().replace(" ", "-"); - if (! this.config.wordpress?.config?.DB_NAME) { - console.log('πŸš€ No database name provided. Skipping database dump.', this.config.wordpress); - return; - } return `${this.config.wordpress.config.DB_NAME}-${date}.sql.gz`; }; @@ -35,7 +35,7 @@ class Database { * * @async * @param {string} sqlFilePath - The path to the SQL file. - * @return {Promise} - A Promise that resolves to a MySQL Connection object or throws an Error. + * @return {Promise} - A Promise that resolves to a MySQL Connection object or throws an Error. */ async uploadDatabase (sqlFilePath) { try { @@ -49,8 +49,11 @@ class Database { return new Error('Database configuration not found'); } - const databaseConnectionConfig = getConnectionSettings(this.config); + const databaseConnectionConfig = getConnectionSettings(this.config.wordpress.config); + /** + * @type {import('mysql2/promise').Connection} connection - The MySQL connection object. + */ const connection = await mysql.createConnection(databaseConnectionConfig); const sqlFileContent = fs.readFileSync(sqlFilePath, 'utf8'); @@ -68,7 +71,7 @@ class Database { }).catch(error => { console.error('🫠 Error closing database connection:', error.message); }); - } catch (error) { + } catch (/** @type {any} */ error) { console.error('πŸ”΄ Error uploading database:', error.message); } } @@ -76,9 +79,9 @@ class Database { /** * Asynchronously dumps the database to the specified output file. * - * @param basePath - The base path of the WordPress installation. - * - * @return {Promise} - A promise that resolves if the database is dumped successfully, or rejects with an error message if an error occurs. + * @param {string} basePath - The base path of the WordPress installation. + * @async + * @return {Promise} - A promise that resolves if the database is dumped successfully, or rejects with an error message if an error occurs. */ async dumpDatabase (basePath) { const dumpPath = path.join( basePath, 'backups', 'db'); @@ -96,6 +99,11 @@ class Database { return new Error('πŸ”΄ Database configuration not found'); } + /** + * the connection settings for the database + * + * @type {mysqldump.ConnectionOptions} databaseConnectionConfig - The connection settings for the database. + */ const databaseConnectionConfig = getConnectionSettings(this.config.wordpress.config); if (databaseConnectionConfig.host === 'localhost') { diff --git a/lib/Dump.js b/lib/Dump.js index 2aacc34..4e8387c 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -37,6 +37,9 @@ class Dump { const name = path.basename(this.wpFolder); console.log(`πŸ”οΈ Scanning ${this.wpFolder}`); + /** + * @type {{locale: string|null, version: string|null}} + */ const wpInfo = getCurrentWpInfo(this.wpFolder); const wpConfigData = parseWpConfig( diff --git a/lib/Initialize.js b/lib/Initialize.js index 6e92a56..de0209d 100644 --- a/lib/Initialize.js +++ b/lib/Initialize.js @@ -11,35 +11,50 @@ const {getLastWp, getUserLocale} = require("./utils/wordpress.js"); class Initialize { /** * Constructor for a new instance of the class. + * + * @param {string|undefined} wpFolder - The path to the WordPress folder. + * @param {string|undefined} outputPath - The path to the output file. */ - constructor (wpFolder = null, outputPath = null) { + constructor (wpFolder = undefined, outputPath = undefined) { this.wpFolder = wpFolder || process.cwd(); this.outputPath = outputPath || path.join(this.wpFolder, PkgFileName); } + /** + * Whenever the config file exists in the output path + * + * @return {boolean} + */ hasConfig = () => !! fs.existsSync( path.join( this.outputPath, PkgFileName ) ); + readConfig = () => { + if (this.hasConfig()) { + /** @type {import('./constants.js').WPMMconfig} */ + return JSON.parse(fs.readFileSync(this.outputPath, 'utf8')); + } + }; + /** * Generates the configuration file for WordPress. * - * @return {WPMMconfig} The configuration object. + * @return {Promise} The configuration object. */ async generateConfig() { // check if the output path exists if (this.hasConfig()) { console.log(`πŸ‘ The configuration file ${this.outputPath} already exists.`); - return; + return this.readConfig(); } const name = this.wpFolder.split(path.sep).pop() || DefaultWpInstallFolder; - const lastWp = await getLastWp(); + const lastWp = await getLastWp() ?? { version: 'latest' }; const userLocale = getUserLocale(); return { name, wordpress: { - version: lastWp.version, + version: lastWp?.version || 'latest', language: userLocale, config: DefaultWpConfig }, @@ -53,7 +68,7 @@ class Initialize { }; } - writeConfig = (result) => { + writeConfig = (/** @type {import('./constants.js').WPMMconfig} */ result) => { // write the config to the output path fs.writeFileSync(this.outputPath, JSON.stringify(result, null, 2)); console.log(`πŸ†— Wordpress configuration file created. Configuration saved to ${this.outputPath}`); diff --git a/lib/Installer.js b/lib/Installer.js index 3e504b8..748219e 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -1,7 +1,7 @@ const { cleanup, makeDir } = require('./utils/fs.js'); const Package = require("./Package.js"); const WpPackage = require('./WpPackage.js'); -const {runPostInstallCommands,isWPCLIAvailable} = require("./utils/actions.js"); +const {runPostInstallCommands,isWPCLIAvailable} = require("./utils/commands.js"); /** * The Installer class represents the WordPress installer and provides methods for installing WordPress and its dependencies. @@ -12,8 +12,8 @@ class Installer { /** * Initializes a new instance of the constructor. * - * @param {WPMMconfig} config - The configuration object for the constructor. - * @param {WPMMpaths} paths - The object containing the paths for the constructor. + * @param {import('./constants.js').WPMMconfig} config - The configuration object for the constructor. + * @param {import('./constants.js').WPMMpaths} paths - The object containing the paths for the constructor. */ constructor (config, paths) { this.config = config; @@ -26,7 +26,7 @@ class Installer { /** * Installs packages based on the configuration provided. * - * @return {Promise} A promise that resolves when all the packages are installed. + * @return {Promise} A promise that resolves when all the packages are installed. */ async installPackages () { const { wordpress, plugins, themes, postInstall } = this.config; diff --git a/lib/Package.js b/lib/Package.js index 5bb16e6..fa2ead9 100644 --- a/lib/Package.js +++ b/lib/Package.js @@ -4,7 +4,7 @@ const { exec } = require('child_process'); const { installNpmPackages, installComposer -} = require('./utils/actions.js'); +} = require('./utils/commands.js'); const { getDownloadUrl } = require('./utils/data.js'); @@ -23,9 +23,9 @@ class Package { /** * Constructs a new instance of the class. * - * @param {WPMMconfigPkg} pkgConfig - the configuration object + * @param {import('./constants.js').WPMMconfigPkg} pkgConfig - the configuration object * @param {string} packageType - the type of package - * @param {WPMMpaths} paths - the object containing the paths + * @param {{baseFolder: *}} paths - the object containing the paths */ constructor (pkgConfig, packageType, paths) { this.pkgInfo = pkgConfig; @@ -34,12 +34,27 @@ class Package { this.destFolder = this.getDestFolder(paths, packageType); } + /** + * This is a method that retrieves the version of the current package. + * + * No parameters are needed for this method. + * + * @return {Object} Returns an object with the version of the current package. + */ getInfo () { return { version: this.pkgInfo.version }; } + /** + * Gets the destination folder based on the package type. + * + * @param {import('./constants.js').WPMMpaths} paths - An array of paths. + * @param {string} packageType - The type of package ('plugin' or 'theme'). + * + * @return {string|undefined} The destination folder path. + */ getDestFolder (paths, packageType) { if (packageType === 'plugin') { return paths.pluginsFolder; @@ -91,7 +106,7 @@ class Package { * Downloads a package from a given URL and saves it to the target directory. * * @param {string} packageUrl - The URL of the package to download. - * @return {Promise} A promise that resolves when the package is successfully downloaded and saved, or rejects with an error. + * @return {Promise} A promise that resolves when the package is successfully downloaded and saved, or rejects with an error. */ async downloadPackage (packageUrl) { try { @@ -121,7 +136,7 @@ class Package { // install npm packages if they exist await installNpmPackages(pkgFolder); // install composer if exist - await installComposer(pkgFolder); + installComposer(pkgFolder); } } catch (error) { console.error(`πŸ”΄ Error downloading package ${this.pkgInfo.name}:`, error); diff --git a/lib/Updater.js b/lib/Updater.js index b1f0260..0e4faaf 100644 --- a/lib/Updater.js +++ b/lib/Updater.js @@ -9,7 +9,7 @@ class Updater { /** * Constructs a new instance of the class. * - * @param {WPMMconfig} config - the configuration object + * @param {import('./constants').WPMMconfig} config - the configuration object */ constructor (config) { // Load configuration from wp-package.json @@ -43,7 +43,7 @@ class Updater { } }); } - } catch (error) { + } catch (/** @type {any} */ error) { console.error('Error updating plugins:', error.message); } } @@ -74,7 +74,7 @@ class Updater { } }); } - } catch (error) { + } catch (/** @type {any} */ error) { console.error('Error updating themes:', error.message); } } @@ -82,7 +82,7 @@ class Updater { /** * Updates the WordPress installation. * - * @return {Promise} A promise that resolves when the update is complete. + * @return {Promise} A promise that resolves when the update is complete. */ async updateWordPress () { try { @@ -95,17 +95,29 @@ class Updater { console.log('WordPress updated successfully'); } }); - } catch (error) { + } catch (/** @type {any} */ error) { console.error('Error updating WordPress:', error.message); } } + /** + * A description of the entire function. + * + * @param {{wordpress: boolean, all: boolean, themes: boolean, plugins: boolean}} argv - An object containing the update options. + * It should have the following properties: + * - updateWordPress: boolean + * - updateAll: boolean + * - updateThemes: boolean + * - updatePlugins: boolean + * @return {Promise} A promise that resolves when the function completes. + * It does not return any value. + */ async run (argv) { /** * An object containing the update options. * - * @type {{updateWordPress: boolean, updateAll: boolean, updateThemes: boolean, updatePlugins: boolean}} + * @typedef {{updateWordPress: boolean, updateAll: boolean, updateThemes: boolean, updatePlugins: boolean}} updateObject - An object containing the update options. */ const updateObject = { updateAll: argv.plugins === true, diff --git a/lib/WpPackage.js b/lib/WpPackage.js index d1dbf95..52bc49d 100644 --- a/lib/WpPackage.js +++ b/lib/WpPackage.js @@ -2,7 +2,7 @@ const path = require('path'); const fs = require('fs'); const { renameFolder } = require('./utils/fs.js'); -const Package = require('./package.js'); +const Package = require('./Package.js'); const { replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/wordpress.js'); const { getWordPressDownloadUrl } = require('./utils/data.js'); @@ -13,6 +13,26 @@ const { getWordPressDownloadUrl } = require('./utils/data.js'); * @extends Package */ class WpPackage extends Package { + + /** + * the constructor of the class + * @param {import('./constants.js').WordpressPkg} pkgConfig - the configuration object + * @param {import('./constants.js').WPMMpaths} paths - the object containing the paths + */ + constructor (pkgConfig, paths) { + /** + * @type {import('./constants.js').WordpressPkg} pkgConfig - the configuration object + */ + super( + pkgConfig, + 'wordpress', + { + ...paths, + baseFolder: paths.baseFolder + } + ); + } + /** * Installs WordPress with the specified version and language. * @@ -45,7 +65,7 @@ class WpPackage extends Package { * replacing the placeholder values with the actual configuration values, * and saving the updated config file. * - * @return {void} This function does not return anything. + * @return {Promise} This function does not return anything. */ async setupWordPressConfig () { const configPath = path.join(this.destFolder, 'wp-config.php'); @@ -85,7 +105,7 @@ class WpPackage extends Package { /** * Installs WordPress and sets up the WordPress configuration. * - * @returns {Promise} A Promise that resolves when the installation and configuration are complete. + * @returns {Promise} A Promise that resolves when the installation and configuration are complete. */ async install () { const { version, language } = this.pkgInfo.wordpress; diff --git a/lib/actions.js b/lib/actions.js index f24c09e..4c681a6 100644 --- a/lib/actions.js +++ b/lib/actions.js @@ -5,32 +5,34 @@ const Updater = require("./Updater"); const Dump = require("./Dump"); const Database = require("./Database"); - /** * The actions object. * - * @param {WPMMconfig} config - The configuration object. - * @param {WPMMpaths} paths - The paths object. + * @param {Object} props - The configuration object. + * @param {import("./constants").WPMMconfig} props.config - The configuration object. + * @param {import("./constants").WPMMpaths} props.paths - The paths object. + * + * @property {function} invokeAction - Invokes an action based on the given key and arguments. + * + * @return {{ invokeAction : function }} */ function actions({config, paths}) { /** - * @function wpmmActions.info - Retrieve information using the provided configuration and actions. - * @function wpmmActions.dump - Dumps the current WordPress installation data. - * @function wpmmActions.init - Initialize the WordPress installation. - * @function wpmmActions.upload-db - Upload a database by executing SQL queries from a specified file. - * @function wpmmActions.dump-db - Dump the current WordPress database. - * @function wpmmActions.dump-all - Dump the current WordPress database, plugins and themes setup. - * @function wpmmActions.install - Install WordPress using the provided configuration. - * @function wpmmActions.update - Update WordPress themes and/or plugins using the provided configuration. + * The actions object. + * @typedef {Function} WpmmAction - The action function. + * @type {{[key: string]: WpmmAction}} wpmmActions - The actions object. */ const wpmmActions = { /** Retrieve information using the provided configuration and actions. */ info: () => getInfo(config, wpmmActions), - /** Initialize the WordPress installation. */ - init: () => { + /** + * Initialize the WordPress installation. + * @returns {Promise} + * */ + init: async () => { const initializer = new Initialize(); - const result = initializer.generateConfig(); + const result = await initializer.generateConfig(); initializer.writeConfig(result); }, @@ -43,7 +45,11 @@ function actions({config, paths}) { }); }, - /** Update WordPress packages */ + /** + * Update WordPress packages + * + * @param {any} argv - The arguments object. + */ update: ({argv}) => { const updater = new Updater(config); updater.run(argv).then(() => { @@ -53,25 +59,24 @@ function actions({config, paths}) { }); }, - /** Dumps the current WordPress installation data. This function creates a new Dump instance and initializes it. */ + /** Dumps the current WordPress installation data. This function creates a new Dump instance and initializes it.*/ dump: () => { const dump = new Dump(paths); dump.init(); }, /** Dump the current WordPress database. */ - 'dump-db': () => { + 'dump-db': async () => { const db = new Database(config); - db.dumpDatabase(paths.baseFolder).then(() => { - console.log(`πŸš€ Database dumped successfully.`); - }); + await db.dumpDatabase(paths.baseFolder); + console.log(`πŸš€ Database dumped successfully.`); }, /** Dump the current WordPress database, plugins and themes setup. */ 'dump-all': () => { - actions["dump-db"]() && console.log('πŸš€ WP Database dumped successfully.'); - actions.dump() && console.log('πŸš€ All data dumped successfully.'); + wpmmActions["dump-db"]() || console.log('πŸš€ WP Database dumped successfully.'); + wpmmActions.dump() || console.log('πŸš€ All data dumped successfully.'); }, /** Upload a database by executing SQL queries from a specified file. */ @@ -84,13 +89,20 @@ function actions({config, paths}) { }; return { + /** + * Invokes an action based on the given key and arguments. + * + * @function + * @param {string} key - The key representing the action to be invoked. + * @param {import("yargs").Argv} argv - An array of arguments to be passed to the invoked action. + */ invokeAction: (key, argv) => { if (typeof wpmmActions[key] === 'function') { wpmmActions[key]({config, paths, argv}); } else { - console.log(`Invalid action: ${key}`); + console.warn(`Invalid action: ${key}`); } - }, + } }; } diff --git a/lib/constants.js b/lib/constants.js index a1a70c5..aceaceb 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -8,39 +8,44 @@ * @property {string} DB_COLLATE - The collation for the database (usually utf8_general_ci) * @property {string} table_prefix - The table prefix for the database * @property {boolean} WP_DEBUG - Whether to enable debugging - * @property {string} WP_SITEURL - The URL of the website - * @property {string} WP_HOME - The home URL of the website - * @property {string} WP_CONTENT_DIR - The path to the content directory - * @property {string} WP_CONTENT_URL - The URL of the content directory - * @property {boolean} DISALLOW_FILE_EDIT - Whether to disallow file editing + * @property {string=} WP_SITEURL - The URL of the website + * @property {string=} WP_HOME - The home URL of the website + * @property {string=} WP_CONTENT_DIR - The path to the content directory + * @property {string=} WP_CONTENT_URL - The URL of the content directory + * @property {boolean=} DISALLOW_FILE_EDIT - Whether to disallow file editing * * Package configuration * @typedef WPMMconfigPkg - The package object * @property {string} name - the name of the package - * @property {string} version - the version of the package - * @property {string} source - the source of the package + * @property {string=} version - the version of the package + * @property {string=} source - the source of the package + * + * the WordPress configuration dataset + * @typedef {Object} WordpressPkg - The WordPress pkg related data for the website + * @property {string} WordpressPkg.version - The current version of WordPress + * @property {string} WordpressPkg.language - The language of WordPress + * @property {WPMMconfigWP} WordpressPkg.config - The wp-config.php file data * * Website configuration * @typedef WPMMconfig - The website configuration * @property {string} name - The name of the website - * @property {Object} wordpress - The WordPress pkg related data for the website - * @property {string} wordpress.version - The current version of WordPress - * @property {string} wordpress.language - The language of WordPress - * @property {WPMMconfigWP} wordpress.config - The wp-config.php file data + * @property {WordpressPkg} wordpress - The WordPress data for the website * @property {WPMMconfigPkg[]} themes - The themes used * @property {WPMMconfigPkg[]} plugins - The plugins used - * @property {WPMMconfigPkg[]} database - The database data - * @property {string[]} postInstall - The post-install actions run by wp-cli + * @property {Object=} database - The database data + * @property {string=} database.type - The type of database + * @property {string=} database.backupFolder - The path to the backup folder + * @property {string[]=} postInstall - The post-install actions run by wp-cli */ /** * @typedef {Object} WPMMpaths - The object containing the paths * @property {string} rootFolder - The root folder of the application * @property {string} tempDir - The temporary directory - * @property {string?} baseFolder - The path to the WordPress folder. Defaults to the current working directory. + * @property {string=} baseFolder - The path to the WordPress folder. Defaults to the current working directory. * @property {string} destFolder - The destination folder for package installation. - * @property {string?} pluginsFolder - The destination folder for plugins - * @property {string?} themeFolder - The destination folder for themes + * @property {string=} pluginsFolder - The destination folder for plugins + * @property {string=} themeFolder - The destination folder for themes */ /** @@ -66,7 +71,7 @@ const DefaultWpInstallLanguage = 'en_US'; /** * The default configuration object * - * @type {WPMMconfig} defaultConfig - The default configuration object + * @type {WPMMconfigWP} defaultConfig - The default configuration object */ const DefaultWpConfig = { DB_NAME: 'my_db_name', diff --git a/lib/index.js b/lib/index.js index 3b2a8a6..826e9df 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,21 +4,28 @@ const { getWordPressPaths } = require("./utils/wordpress.js"); const yargs = require('yargs'); const { hideBin } = require('yargs/helpers'); + const actions = require("./actions"); /** @var {number} startTime - the time at which the script started. */ const startTime = Date.now(); -/** @var {yargs} argv - The command line arguments. */ +/** @var {import("yargs").argv} argv - The command line arguments. */ const argv = yargs(hideBin(process.argv)).argv; -/** @var {WPMMconfig} config - The configuration object for the script. */ -getConfig(argv).then(config => { - - /** @var {WPMMpaths} paths - The paths object for the script. */ - const paths = getWordPressPaths(config); +getConfig(argv) + .then( + /** @param {import('./constants.js').WPMMconfig} config - The configuration object for the script. */ + (config) => { + const paths = getWordPressPaths(config); - const actionLauncher = actions({config, paths}); + /** + * The launcher object. + * @typedef {{[key: string]: function}} WpmmLauncher - The launcher object. + * + * @type {WpmmLauncher} + */ + const launcher = actions({config, paths}); for (const key of Object.keys(argv)) { // Skip the loop iteration when the key is '_' or '$0' @@ -26,13 +33,15 @@ getConfig(argv).then(config => { continue; } - actionLauncher.invokeAction(key, argv); + launcher.invokeAction(key, argv); } - + }) + .catch((/** @type {Error} */ err) => console.error(err)) + .finally(() => { /** * That's it! We're done! let's print how long it took to run the script and exit with a success code. */ printTimePassed(startTime); process.exit(0); -}); + }); From 6a29c76f076d952f2016a6ac55895cc1d2b21fca Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Sun, 10 Dec 2023 23:34:20 +0100 Subject: [PATCH 64/70] Update project dependencies and introduce TypeScript Added TypeScript along with necessary TypeScript definitions to the project. Removed 'node-fetch' and other unused dependencies. The operation includes the generation of a new tsconfig.json file. --- package-lock.json | 131 +++++++++++++++------------------------------- package.json | 11 ++-- readme.md | 7 ++- tsconfig.json | 18 +++++++ 4 files changed, 73 insertions(+), 94 deletions(-) create mode 100644 tsconfig.json diff --git a/package-lock.json b/package-lock.json index 698a6c3..9b307e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,19 +13,23 @@ "extract-zip": "^2.0.1", "mysql2": "^3.6.5", "mysqldump": "^3.2.0", - "node-fetch": "^3.3.2", "yargs": "^17.7.2" }, "bin": { "wpmm": "lib/index.js" }, "devDependencies": { + "@types/axios": "^0.14.0", + "@types/jest": "^29.5.11", + "@types/node": "^20.10.4", + "@types/yargs": "^17.0.32", "eslint": "^8.54.0", "eslint-config-standard": "^17.1.0", "eslint-plugin-n": "^16.3.1", "eslint-plugin-promise": "^6.1.1", "husky": "^8.0.3", - "jest": "^29.7.0" + "jest": "^29.7.0", + "typescript": "^5.3.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1290,6 +1294,16 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@types/axios": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", + "integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==", + "deprecated": "This is a stub types definition for axios (https://github.com/mzabriskie/axios). axios provides its own type definitions, so you don't need @types/axios installed!", + "dev": true, + "dependencies": { + "axios": "*" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1364,6 +1378,16 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -1372,9 +1396,9 @@ "peer": true }, "node_modules/@types/node": { - "version": "20.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz", - "integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==", + "version": "20.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", + "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", "devOptional": true, "dependencies": { "undici-types": "~5.26.4" @@ -2091,14 +2115,6 @@ "node": ">= 8" } }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2878,28 +2894,6 @@ "pend": "~1.2.0" } }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3002,17 +2996,6 @@ "node": ">= 6" } }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4797,41 +4780,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5948,6 +5896,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -6032,14 +5993,6 @@ "makeerror": "1.0.12" } }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "engines": { - "node": ">= 8" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 230df87..8d4c586 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "scripts": { "lint": "eslint ./lib ./tests", "test": "jest", - "prepare": "husky install" + "prepare": "husky install", + "locatesting": "npx . --info --template https://gist.githubusercontent.com/erikyo/795746c96f422168121e47c2a876fb31/raw/1ec18a8bc10b6f80221b69944d9dad19c25613c1/wp-package.json" }, "bin": { "wpmm": "lib/index.js" @@ -30,15 +31,19 @@ "extract-zip": "^2.0.1", "mysql2": "^3.6.5", "mysqldump": "^3.2.0", - "node-fetch": "^3.3.2", "yargs": "^17.7.2" }, "devDependencies": { + "@types/axios": "^0.14.0", + "@types/jest": "^29.5.11", + "@types/yargs": "^17.0.32", + "@types/node": "^20.10.4", "eslint": "^8.54.0", "eslint-config-standard": "^17.1.0", "eslint-plugin-n": "^16.3.1", "eslint-plugin-promise": "^6.1.1", "husky": "^8.0.3", - "jest": "^29.7.0" + "jest": "^29.7.0", + "typescript": "^5.3.3" } } diff --git a/readme.md b/readme.md index 6d34493..4e7ecaf 100644 --- a/readme.md +++ b/readme.md @@ -36,10 +36,13 @@ Returns the information for the Wordpress installation in the current folder ### `npx wpmm --init` Initialize the project and create a sample wp-package.json file. -### `npx wpmm --upload-database database/my.sql` +### `npx wpmm --template https://gist.githubusercontent.com/erikyo/795746c96f422168121e47c2a876fb31/raw/1ec18a8bc10b6f80221b69944d9dad19c25613c1/wp-package.json` +Initialize the project using a template wp-package.json file. + +### `npx wpmm --upload-db database/my.sql` Upload a database named my.sql into the wordpress database -### `npx wpmm --dump-database` +### `npx wpmm --dump-db` Download the current wp database and save it into /backups/${databasename}.sql.gz ### `npx wpmm --dump` diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ca5b60c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2015", + "moduleResolution": "node", + "strict": true, + "pretty": false, + "allowJs": true, + "checkJs": true, + "resolveJsonModule": true, + "rootDir": "lib", + "forceConsistentCasingInFileNames": true, + "typeRoots": ["node_modules/@types"] + }, + "include": [ + "./lib/**/*" + ] +} From 9973f23732329700248d531ebda964112915adad Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Mon, 11 Dec 2023 09:45:08 +0100 Subject: [PATCH 65/70] Refactor and optimize WordPress installation configuration The commit encompasses several important changes targeted towards WordPress package installation and configuration. It first optimizes the WpPackage.js file for more streamlined and efficient handling of WordPress installations. It also includes smarter parsing and generation of WordPress and other package config files. Furthermore, it enhances the database.js file to use better defined configurations. Major changes involve movement of parser-related functions to a new utils/parsers.js file, and inclusion of user prompts related to WordPress installation in a new utils/prompts.js file. --- lib/Database.js | 12 ++- lib/Dump.js | 49 ++++++---- lib/Initialize.js | 15 ++- lib/Installer.js | 6 +- lib/Package.js | 5 +- lib/WpPackage.js | 43 ++++---- lib/actions.js | 12 ++- lib/constants.js | 16 +-- lib/index.js | 17 ++-- lib/utils/commands.js | 2 +- lib/utils/data.js | 53 +++++----- lib/utils/wordpress.js | 216 ++++------------------------------------- 12 files changed, 147 insertions(+), 299 deletions(-) diff --git a/lib/Database.js b/lib/Database.js index d55884f..31c7953 100644 --- a/lib/Database.js +++ b/lib/Database.js @@ -27,7 +27,7 @@ class Database { */ dbDumpFilename = () => { const date = new Date().toDateString().replace(" ", "-"); - return `${this.config.wordpress.config.DB_NAME}-${date}.sql.gz`; + return `${this.config.wordpress.WP_config.DB_NAME}-${date}.sql.gz`; }; /** @@ -49,7 +49,11 @@ class Database { return new Error('Database configuration not found'); } - const databaseConnectionConfig = getConnectionSettings(this.config.wordpress.config); + /** + * the connection settings for the database + * @type {mysql.ConnectionOptions} databaseConnectionConfig - The connection settings for the database. + */ + const databaseConnectionConfig = getConnectionSettings(this.config.wordpress.WP_config); /** * @type {import('mysql2/promise').Connection} connection - The MySQL connection object. @@ -95,7 +99,7 @@ class Database { console.log(`✳️ Dumping database to ${dumpFile}...`); - if (!this.config.wordpress.config) { + if (!this.config.wordpress.WP_config) { return new Error('πŸ”΄ Database configuration not found'); } @@ -104,7 +108,7 @@ class Database { * * @type {mysqldump.ConnectionOptions} databaseConnectionConfig - The connection settings for the database. */ - const databaseConnectionConfig = getConnectionSettings(this.config.wordpress.config); + const databaseConnectionConfig = getConnectionSettings(this.config.wordpress.WP_config); if (databaseConnectionConfig.host === 'localhost') { console.log('⚠️ Warning: You are using localhost as your database host. This may not work correctly.'); diff --git a/lib/Dump.js b/lib/Dump.js index 4e8387c..259f318 100644 --- a/lib/Dump.js +++ b/lib/Dump.js @@ -1,7 +1,8 @@ const fs = require('fs'); const path = require('path'); -const { getCurrentWpInfo, getUserLocale, getLastWp } = require("./utils/wordpress.js"); -const {parseWpConfig, getWpConfigContent} = require("./utils/wordpress"); +const {getCurrentWpInfo} = require("./utils/wordpress.js"); +const {getWpConfigContent} = require("./utils/wordpress.js"); +const {parseWpConfig} = require("./utils/parsers.js"); /** * Represents a Dump class for WordPress configuration. @@ -13,50 +14,51 @@ class Dump { * Constructor for the class. * * Initializes the class with the necessary folders for WordPress. + * @param {import("./constants").WPMMpaths} paths - The object containing the paths for the WordPress installation. */ constructor (paths) { - this.wpFolder = paths.baseFolder; + this.baseFolder = paths.baseFolder; this.themeFolder = paths.themeFolder; this.pluginsFolder = paths.pluginsFolder; } /** - * Initializes the function by logging the `wpFolder` and `themeFolder` properties, + * Initializes the function by logging the `baseFolder` and `themeFolder` properties, * scanning the theme and plugins directories, retrieving the website name from the - * `wpFolder` path, getting the WordPress version from `wp-includes/version.php`, + * `baseFolder` path, getting the WordPress version from `wp-includes/version.php`, * determining the language using `Intl.DateTimeFormat().resolvedOptions().locale`, * and saving the result to a JSON file. * - * @return {void} + * @return {Promise} - The configuration object. */ - init() { + async init() { const themes = this.scanDirectory(this.themeFolder); const plugins = this.scanDirectory(this.pluginsFolder); // the website name - const name = path.basename(this.wpFolder); + const name = path.basename(this.baseFolder); - console.log(`πŸ”οΈ Scanning ${this.wpFolder}`); + console.log(`πŸ”οΈ Scanning ${this.baseFolder}`); /** * @type {{locale: string|null, version: string|null}} */ - const wpInfo = getCurrentWpInfo(this.wpFolder); + const wpInfo = getCurrentWpInfo(this.baseFolder); const wpConfigData = parseWpConfig( - getWpConfigContent(this.wpFolder) + getWpConfigContent(this.baseFolder) ); - const language = wpInfo.locale || getUserLocale(); - const version = wpInfo.version || getLastWp()?.version || 'latest'; + const version = wpInfo.version || 'latest'; + const language = wpInfo.locale || 'en_US'; const result = { - name, wordpress: { + name, version, language, - config: { - ...wpConfigData.constants, - ...wpConfigData.variables + WP_config: { + ...wpConfigData?.constants, + ...wpConfigData?.variables } }, themes, @@ -67,13 +69,15 @@ class Dump { fs.writeFileSync(outputPath, JSON.stringify(result, null, 2)); console.log(`πŸ†— Wordpress configuration Dump completed. Configuration saved to ${outputPath}`); + + return result; } /** * Scans a directory and returns an array of objects containing the name and version of each item found. * * @param {string} directory - The path of the directory to scan. - * @return {Array} - An array of objects with the name and version of each item found. + * @return {import('./constants').WPMMconfigPkg[]|null} - An array of objects with the name and version of each item found. */ scanDirectory (directory) { // Check if the directory exists @@ -83,7 +87,12 @@ class Dump { } const items = fs.readdirSync(directory); - const result = []; + /** + * The array of parsed items + * + * @type {import('./constants').WPMMconfigPkg[]} result + */ + let result= []; for (const item of items) { const fullPath = path.join(directory, item); @@ -113,7 +122,7 @@ class Dump { } } - return result; + return result.length ? result : null; } /** diff --git a/lib/Initialize.js b/lib/Initialize.js index de0209d..93e9d28 100644 --- a/lib/Initialize.js +++ b/lib/Initialize.js @@ -37,9 +37,10 @@ class Initialize { /** * Generates the configuration file for WordPress. * + * @param {{version ?: string, language?: string}=} options - The options for the initialization. * @return {Promise} The configuration object. */ - async generateConfig() { + async generateConfig(options) { // check if the output path exists if (this.hasConfig()) { @@ -48,15 +49,13 @@ class Initialize { } const name = this.wpFolder.split(path.sep).pop() || DefaultWpInstallFolder; - const lastWp = await getLastWp() ?? { version: 'latest' }; - const userLocale = getUserLocale(); return { - name, wordpress: { - version: lastWp?.version || 'latest', - language: userLocale, - config: DefaultWpConfig + name, + version: options?.version || (await getLastWp())?.version || 'latest', + language: options?.language || getUserLocale(), + 'WP_config': DefaultWpConfig }, database: { type: DefaultWpDatabaseType, @@ -71,7 +70,7 @@ class Initialize { writeConfig = (/** @type {import('./constants.js').WPMMconfig} */ result) => { // write the config to the output path fs.writeFileSync(this.outputPath, JSON.stringify(result, null, 2)); - console.log(`πŸ†— Wordpress configuration file created. Configuration saved to ${this.outputPath}`); + console.log(`πŸ†— WordPress configuration file created. Configuration saved to ${this.outputPath}`); }; } diff --git a/lib/Installer.js b/lib/Installer.js index 748219e..65782ef 100644 --- a/lib/Installer.js +++ b/lib/Installer.js @@ -39,21 +39,21 @@ class Installer { // Install WordPress if (wordpress) { - const wp = new WpPackage(this.config, 'wordpress', this.paths); + const wp = new WpPackage(this.config.wordpress, 'wordpress', this.paths); await wp.install(); } if (plugins) { const pluginPackages = plugins.map((plugin) => new Package(plugin, 'plugin', this.paths)); promises.push(...pluginPackages.map((pluginPackage) => pluginPackage.install().then(() => { - wpJson.plugins[pluginPackage.pkgInfo.name] = pluginPackage.pkgInfo; + wpJson.plugins.push(pluginPackage.pkgInfo); }))); } if (themes) { const themePackages = themes.map((theme) => new Package(theme, 'theme', this.paths)); promises.push(...themePackages.map((themePackage) => themePackage.install().then(() => { - wpJson.themes[themePackage.pkgInfo.name] = themePackage.pkgInfo; + wpJson.themes.push(themePackage.pkgInfo); }))); } diff --git a/lib/Package.js b/lib/Package.js index fa2ead9..8f7aa22 100644 --- a/lib/Package.js +++ b/lib/Package.js @@ -18,6 +18,7 @@ const { * Represents a package and provides methods to download and install it. * * @class Package + * @template Package */ class Package { /** @@ -25,7 +26,7 @@ class Package { * * @param {import('./constants.js').WPMMconfigPkg} pkgConfig - the configuration object * @param {string} packageType - the type of package - * @param {{baseFolder: *}} paths - the object containing the paths + * @param {import('./constants.js').WPMMpaths} paths - the object containing the paths */ constructor (pkgConfig, packageType, paths) { this.pkgInfo = pkgConfig; @@ -53,7 +54,7 @@ class Package { * @param {import('./constants.js').WPMMpaths} paths - An array of paths. * @param {string} packageType - The type of package ('plugin' or 'theme'). * - * @return {string|undefined} The destination folder path. + * @return {string} The destination folder path. */ getDestFolder (paths, packageType) { if (packageType === 'plugin') { diff --git a/lib/WpPackage.js b/lib/WpPackage.js index 52bc49d..086be08 100644 --- a/lib/WpPackage.js +++ b/lib/WpPackage.js @@ -3,43 +3,35 @@ const fs = require('fs'); const { renameFolder } = require('./utils/fs.js'); const Package = require('./Package.js'); -const { replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/wordpress.js'); +const { replaceDbConstant, replaceDbConstantBool, replaceEmptySalts } = require('./utils/parsers.js'); const { getWordPressDownloadUrl } = require('./utils/data.js'); /** * Represents a WordPress package that can be installed and configured. * * @class WpPackage - * @extends Package + * @extends Package pkgConfig - the configuration object */ class WpPackage extends Package { /** - * the constructor of the class + * Constructs a new instance of the class. * @param {import('./constants.js').WordpressPkg} pkgConfig - the configuration object + * @param {string} packageType - the type of package * @param {import('./constants.js').WPMMpaths} paths - the object containing the paths */ - constructor (pkgConfig, paths) { - /** - * @type {import('./constants.js').WordpressPkg} pkgConfig - the configuration object - */ - super( - pkgConfig, - 'wordpress', - { - ...paths, - baseFolder: paths.baseFolder - } - ); + constructor(pkgConfig, packageType, paths) { + super(pkgConfig, 'wordpress', paths); + this.pkgInfo = pkgConfig; } /** * Installs WordPress with the specified version and language. * - * @param {string} version - The version of WordPress to install. - * @param {string} language - The language of WordPress to install. + * @param {string=} version - The version of WordPress to install. + * @param {string=} language - The language of WordPress to install. */ - async installWordPress (version, language) { + async installWordPress (version = 'latest', language = 'en_US') { const downloadUrl = getWordPressDownloadUrl(version, language); try { @@ -83,13 +75,14 @@ class WpPackage extends Package { let configContent = fs.readFileSync(configPath, 'utf8'); // Update database name, username, password, and other settings based on user-defined config - configContent = replaceDbConstant(configContent, 'DB_NAME', this.pkgInfo.wordpress.config.DB_NAME); - configContent = replaceDbConstant(configContent, 'DB_USER', this.pkgInfo.wordpress.config.DB_USER); - configContent = replaceDbConstant(configContent, 'DB_PASSWORD', this.pkgInfo.wordpress.config.DB_PASSWORD); - configContent = replaceDbConstant(configContent, 'DB_HOST', this.pkgInfo.wordpress.config.DB_HOST); - configContent = replaceDbConstant(configContent, 'DB_CHARSET', this.pkgInfo.wordpress.config.DB_CHARSET); + configContent = replaceDbConstant(configContent, 'DB_NAME', this.pkgInfo.WP_config.DB_NAME); + configContent = replaceDbConstant(configContent, 'DB_USER', this.pkgInfo.WP_config.DB_USER); + configContent = replaceDbConstant(configContent, 'DB_PASSWORD', this.pkgInfo.WP_config.DB_PASSWORD); + configContent = replaceDbConstant(configContent, 'DB_HOST', this.pkgInfo.WP_config.DB_HOST); + configContent = replaceDbConstant(configContent, 'DB_CHARSET', this.pkgInfo.WP_config.DB_CHARSET); + + configContent = replaceDbConstantBool(configContent, 'WP_DEBUG', this.pkgInfo.WP_config.WP_DEBUG); - configContent = replaceDbConstantBool(configContent, 'WP_DEBUG', this.pkgInfo.wordpress.config.WP_DEBUG); configContent = replaceEmptySalts(configContent); @@ -108,7 +101,7 @@ class WpPackage extends Package { * @returns {Promise} A Promise that resolves when the installation and configuration are complete. */ async install () { - const { version, language } = this.pkgInfo.wordpress; + const { version, language } = this.pkgInfo; await this.installWordPress(version, language); await this.setupWordPressConfig(); } diff --git a/lib/actions.js b/lib/actions.js index 4c681a6..e2f4110 100644 --- a/lib/actions.js +++ b/lib/actions.js @@ -62,12 +62,13 @@ function actions({config, paths}) { /** Dumps the current WordPress installation data. This function creates a new Dump instance and initializes it.*/ dump: () => { const dump = new Dump(paths); - dump.init(); + dump.init().then(() => { + console.log('πŸš€ All data dumped successfully.'); + }); }, /** Dump the current WordPress database. */ 'dump-db': async () => { - const db = new Database(config); await db.dumpDatabase(paths.baseFolder); console.log(`πŸš€ Database dumped successfully.`); @@ -75,14 +76,15 @@ function actions({config, paths}) { /** Dump the current WordPress database, plugins and themes setup. */ 'dump-all': () => { - wpmmActions["dump-db"]() || console.log('πŸš€ WP Database dumped successfully.'); - wpmmActions.dump() || console.log('πŸš€ All data dumped successfully.'); + wpmmActions["dump-db"]() && + wpmmActions.dump() && + console.log('πŸš€ All data dumped successfully.'); }, /** Upload a database by executing SQL queries from a specified file. */ 'upload-db': () => { const db = new Database(config); - db.uploadDatabase(config.wordpress.config.DB_NAME).then(() => { + db.uploadDatabase(config.wordpress.WP_config.DB_NAME).then(() => { console.log('πŸš€ Database uploaded successfully.'); }); }, diff --git a/lib/constants.js b/lib/constants.js index aceaceb..1413119 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -22,13 +22,14 @@ * * the WordPress configuration dataset * @typedef {Object} WordpressPkg - The WordPress pkg related data for the website - * @property {string} WordpressPkg.version - The current version of WordPress - * @property {string} WordpressPkg.language - The language of WordPress - * @property {WPMMconfigWP} WordpressPkg.config - The wp-config.php file data + * @property {string} name - The name of the website + * @property {string=} version - The current version of WordPress + * @property {string=} source - the source of the New WordPress installation + * @property {string=} language - The language of WordPress + * @property {WPMMconfigWP} WP_config - The wp-config.php file data * * Website configuration * @typedef WPMMconfig - The website configuration - * @property {string} name - The name of the website * @property {WordpressPkg} wordpress - The WordPress data for the website * @property {WPMMconfigPkg[]} themes - The themes used * @property {WPMMconfigPkg[]} plugins - The plugins used @@ -40,12 +41,11 @@ /** * @typedef {Object} WPMMpaths - The object containing the paths - * @property {string} rootFolder - The root folder of the application * @property {string} tempDir - The temporary directory - * @property {string=} baseFolder - The path to the WordPress folder. Defaults to the current working directory. + * @property {string} baseFolder - The path to the WordPress folder. Defaults to the current working directory. * @property {string} destFolder - The destination folder for package installation. - * @property {string=} pluginsFolder - The destination folder for plugins - * @property {string=} themeFolder - The destination folder for themes + * @property {string} pluginsFolder - The destination folder for plugins + * @property {string} themeFolder - The destination folder for themes */ /** diff --git a/lib/index.js b/lib/index.js index 826e9df..caec4a3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -17,7 +17,12 @@ getConfig(argv) .then( /** @param {import('./constants.js').WPMMconfig} config - The configuration object for the script. */ (config) => { - const paths = getWordPressPaths(config); + /** + * The object that will hold the paths for the script. + * + * @type {import('./constants.js').WPMMpaths} + */ + const paths = getWordPressPaths(config.wordpress.name); /** * The launcher object. @@ -29,17 +34,17 @@ getConfig(argv) for (const key of Object.keys(argv)) { // Skip the loop iteration when the key is '_' or '$0' - if (key === '_' || key === '$0') { - continue; - } + if (key === '_' || key === '$0') continue; launcher.invokeAction(key, argv); } }) - .catch((/** @type {Error} */ err) => console.error(err)) + .catch( + (/** @type {Error} */ err) => console.error(err) + ) .finally(() => { /** - * That's it! We're done! let's print how long it took to run the script and exit with a success code. + * That's it πŸŽ‰! We're done! let's print how long it took to run the script and exit with a success code. */ printTimePassed(startTime); process.exit(0); diff --git a/lib/utils/commands.js b/lib/utils/commands.js index fac74e9..9fba9d2 100644 --- a/lib/utils/commands.js +++ b/lib/utils/commands.js @@ -1,4 +1,4 @@ -const path = require("node:path"); +const path = require("path"); const fs = require("fs"); const {exec} = require("child_process"); diff --git a/lib/utils/data.js b/lib/utils/data.js index e38f58a..326a2c7 100644 --- a/lib/utils/data.js +++ b/lib/utils/data.js @@ -1,18 +1,19 @@ const fs = require('fs'); const {PkgFileName} = require("../constants.js"); const Initialize = require("../Initialize.js"); -const {DefaultWpInstallFolder} = require("../constants"); -const path = require("path"); const {axiosFetch} = require("./wordpress.js"); +const {isWordpressFolder} = require("./wordpress.js"); +const {askForConfiguration, askForDump} = require("./prompts.js"); /** * Initializes the configuration for the given base folder. * * @param {string} baseFolder - The base folder where the configuration will be initialized. + * @param {{version ?: string, language?: string}} options - The options for the initialization. * @returns {Promise} - The initialized configuration object. */ -async function initConfig(baseFolder) { - +async function initConfig(baseFolder, options) { + // check if the output path exists const init = new Initialize(baseFolder); // generate the default config @@ -20,7 +21,7 @@ async function initConfig(baseFolder) { * the default config * @type {import('../constants.js').WPMMconfig} config */ - const config = await init.generateConfig(); + const config = await init.generateConfig(options); // create the 'wordpress' folder if it does not exist if (!fs.existsSync(baseFolder)) { @@ -48,20 +49,27 @@ async function getConfig (args) { */ let config; - // Check if the template file exists and read it as JSON if it does - if (!fs.existsSync(PkgFileName)) { - - // TODO: prompt the user to create the template file or use the default or decide at least the folder name - console.log(`⚠️ The template file ${PkgFileName} does not exist in the current folder.`); - - // If the template file does not exist, create it with the default config in the 'wordpress' folder - const baseFolder = path.join(process.cwd(), DefaultWpInstallFolder); - config = await initConfig(baseFolder); - + // Check if the script is running from the WordPress folder + if ( ! isWordpressFolder( process.cwd() ) ) { + console.log(`⚠️ Cannot find any Wordpress files in the current folder and the template file ${PkgFileName} is missing.`); + // Ask the user if they want to create a new WordPress installation + config = await askForConfiguration(); } else { - // If the template file exists, read it as JSON - config = JSON.parse(fs.readFileSync(PkgFileName, 'utf8')); - console.log(`ℹ️ The template file ${PkgFileName} exists in the root folder. Using it.`); + if (fs.existsSync(PkgFileName)) { + // If the template file exists, read it as JSON + config = JSON.parse(fs.readFileSync(PkgFileName, 'utf8')); + console.log(`ℹ️ The template file ${PkgFileName} exists in the root folder. Using it.`); + } else { + console.error( `⚠️ The template file ${PkgFileName} does not exist in the current folder.`); + const newConfig = await askForDump(); + // the new config was dumped from the current WordPress installation + if (newConfig) { + config = newConfig; + } else { + console.log(`πŸ”΄ Script terminated. Cannot find any Wordpress information in the current folder and the template file ${PkgFileName} is missing.`); + process.exit(1); + } + } } // Extract the value of the --template option or use the default @@ -85,7 +93,7 @@ async function getConfig (args) { * Returns the connection settings based on the provided config. * * @param {import('../constants.js').WPMMconfigWP} config - The configuration object containing the host, user, password, and database details. - * @return {mysqldump.ConnectionOptions} - The connection settings object with the following properties: + * @return {{host: string, user: string, password: string, database: string}} - The connection settings object with the following properties: * - connectionLimit: The maximum number of connections allowed. * - host: The host name or IP address of the database server. * - user: The username for authenticating with the database server. @@ -120,11 +128,11 @@ function getWordPressDownloadUrl (version, language) { * Generates a download URL for a given package. * * @param {string} packageName - The name of the package. - * @param {string} packageVersion - The version of the package (optional). - * @param {string} type - The type of the package (e.g., 'plugins', 'themes'). + * @param {string=} packageVersion - The version of the package (optional). + * @param {string=} type - The type of the package (e.g., 'plugins', 'themes'). * @return {string} The download URL for the package. */ -function getDownloadUrl (packageName, packageVersion, type) { +function getDownloadUrl (packageName, packageVersion = undefined, type = 'plugins') { // Using the absolute uri of the package if (packageName.startsWith('http://') || packageName.startsWith('https://')) { return packageName; @@ -174,6 +182,7 @@ function printTimePassed (startTime) { } module.exports = { + initConfig, getConfig, getInfo, getConnectionSettings, diff --git a/lib/utils/wordpress.js b/lib/utils/wordpress.js index 5b489f3..8633ee0 100644 --- a/lib/utils/wordpress.js +++ b/lib/utils/wordpress.js @@ -2,32 +2,35 @@ const path = require("path"); const fs = require("fs"); const axios = require("axios").default; const {DefaultWpInstallFolder, DefaultWpInstallLanguage, PkgFileName} = require("../constants.js"); +const {getDataFromFile} = require("./parsers"); +/** + * Determines if the provided folder is a WordPress folder. + * + * @param {string} currentDirectory - The folder to check for WordPress files. + * @return {boolean} Returns true if the folder is not a WordPress folder, false otherwise. + */ +function isWordpressFolder(currentDirectory) { + // if wp-config.php exists in the root folder or the wp-package.json file exists in the root folder + return fs.existsSync(path.join(currentDirectory, 'wp-config.php')) || fs.existsSync(path.join(currentDirectory, PkgFileName)); +} /** * Gets the default paths for the WordPress installation. - * @param {import("../constants.js").WPMMconfig} config - The configuration object for the WordPress installation. - * @param {string} [rootFolder=process.cwd()] - The root folder path for the WordPress installation. Defaults to the current working directory. + * @param {string} websiteName - The name of the WordPress installation. + * @param {string} [baseFolder=process.cwd()] - The root folder path for the WordPress installation. Defaults to the current working directory. * @return {import("../constants.js").WPMMpaths} - An object containing the default paths for the WordPress installation. */ -function getWordPressPaths(config, rootFolder = process.cwd()) { - /** - * the WordPress installation folder - * - * @type {string} - */ - let baseFolder = rootFolder; +function getWordPressPaths(websiteName, baseFolder = process.cwd()) { - // if wp-config.php exists in the root folder or the wp-package.json file exists in the root folder - if (!fs.existsSync(path.join(rootFolder, 'wp-config.php')) && !fs.existsSync(path.join(rootFolder, PkgFileName))) { - baseFolder = path.join(rootFolder, config.name ?? DefaultWpInstallFolder); + if ( ! isWordpressFolder(baseFolder) ) { + baseFolder = path.join(baseFolder, websiteName ?? DefaultWpInstallFolder); } return { tempDir: path.join(baseFolder, 'temp'), baseFolder: baseFolder, - rootFolder: rootFolder, - destFolder: rootFolder, + destFolder: baseFolder, pluginsFolder: path.join(baseFolder, 'wp-content', 'plugins'), themeFolder: path.join(baseFolder, 'wp-content', 'themes') }; @@ -43,10 +46,9 @@ async function axiosFetch(url) { try { const response = await axios.get(url, { headers: { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', + 'User-Agent': 'WPMM - WordPress Package Manager https://github.com/wp-blocks/wpmm', 'Content-Type': 'application/json', 'Accept': 'application/json', - 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'Pragma': 'no-cache', } @@ -65,7 +67,7 @@ async function getLastWp() { /** * @typedef {{version: string}} Offer the WordPress version * @typedef {{offers: Offer[]}} Offers the WordPress api response - * @returns {Promise} The Last available WordPress version from WordPress.org + * @returns {Promise} The Last available WordPress version from WordPress.org * @throws {Error} If the request to WordPress.org fails * * @type {Offers|undefined} The Last available WordPress version from WordPress.org @@ -91,25 +93,6 @@ function getUserLocale() { return Intl.DateTimeFormat().resolvedOptions().locale || DefaultWpInstallLanguage; } -/** - * Reads a PHP file, extracts, and returns the WordPress version number. - * - * @param {string} fileContent - Path to the PHP file to read - * @param {string} variableName - The name of the variable to search adn replace - * - * @return {string|null} WordPress version number or null if not found or in case of an error - */ -function getDataFromFile(fileContent, variableName = 'wp_version') { - // Define a regular expression to match the variableName and its value with both the single and double quotes - const versionRegex = new RegExp(`${variableName}\\s*=\\s*['"]([^'"]+)['"]`, 'g'); - - // Use the regular expression to extract the version number - let match = versionRegex.exec(fileContent); - - // Return the version number or null if not found - return match ? match[1] : null; -} - /** * Retrieves the WordPress version and locale information from a given WordPress folder. * @@ -129,53 +112,6 @@ function getCurrentWpInfo(wpFolder) { }; } -/** - * Removes all commented lines from the given content. - * - * @param {string} content - The content that contains the lines to be uncommented. - * @return {string} The content without any commented lines. - */ -function uncommentedLines(content) { - const lines = content.split('\n'); - let inBlockComment = false; - let uncommented = ''; - - for (let line of lines) { - let newLine = line; - - if (inBlockComment) { - const endCommentIndex = newLine.indexOf('*/'); - if (endCommentIndex !== -1) { - inBlockComment = false; - newLine = newLine.substr(endCommentIndex + 2); - } else { - newLine = ''; - } - } - - if (!inBlockComment) { - const startCommentIndex = newLine.indexOf('/*'); - const endCommentIndex = newLine.indexOf('*/'); - - if (startCommentIndex !== -1 && endCommentIndex !== -1) { - newLine = newLine.slice(0, startCommentIndex) + newLine.slice(endCommentIndex + 2); - } else if (startCommentIndex !== -1) { - newLine = newLine.slice(0, startCommentIndex); - inBlockComment = true; - } - - const lineCommentIndex = newLine.indexOf('//'); - if (lineCommentIndex !== -1) { - newLine = newLine.slice(0, lineCommentIndex); - } - } - - uncommented += newLine + '\n'; - } - - return uncommented; -} - /** * Retrieves the content of the wp-config.php file located in the specified WordPress folder. * @@ -201,122 +137,12 @@ function getWpConfigContent(wpFolder) { return wpConfigContent; } -/** - * Parses the wp-config.php file in a WordPress installation and extracts defined constants and variables. - * - * @param {string} wpConfigContent - The content of the wp-config.php file. - * @return {object|null} - An object containing the extracted constants and variables, or null if there was an error parsing the file. - */ -function parseWpConfig(wpConfigContent) { - - const cleanWpConfigContent = uncommentedLines(wpConfigContent); - - // Regular expressions to match define statements - const defineRegex = /define\(\s*'([^']*)'\s*,\s*'([^']*)'\s*\);/gi; - - // Regular expressions to match variable assignments - const variableRegex = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=\s*["']?(.*?[^"'])["']?(?:;|\?>|\s+\?>|$)/g; - - // Extract constants - const constantMatches = [...cleanWpConfigContent.matchAll(defineRegex)]; - /** - * @type {Record} constants - An object containing the extracted constants. - */ - const constants = {}; - constantMatches.forEach(match => { - constants[match[1]] = match[2]; - }); - - // Extract variables - const variableMatches = [...cleanWpConfigContent.matchAll(variableRegex)]; - - /** - * @type {Record} variables - An object containing the extracted constants. - */ - const variables = {}; - variableMatches.forEach(match => { - variables[match[1]] = match[2]; - }); - - return {constants, variables}; -} - -/** - * Replaces a constant in the wp-config.php file with a user-defined value. - * - * @param {string} configContent - The content of the wp-config.php file. - * @param {string} constantName - The name of the constant to replace. - * @param {string} userDefinedValue - The user-defined value to set for the constant. - * @return {string} - The updated content with the replaced constant. - */ -function replaceDbConstant(configContent, constantName, userDefinedValue) { - const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*'[^']*'\\s*\\);`); - return configContent.replace(regex, `define( '${constantName}', '${userDefinedValue}' );`); -} - -/** - * Replaces a database constant boolean value in the given configuration content. - * - * @param {string} configContent - The content of the configuration. - * @param {string} constantName - The name of the constant to be replaced. - * @param {boolean} userDefinedValue - The user-defined value to replace the constant with. - * @return {string} The updated configuration content with the replaced constant. - */ -function replaceDbConstantBool(configContent, constantName, userDefinedValue) { - // Updated regex to handle boolean constants (TRUE or FALSE) - const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*[^']*\\s*\\);`); - return configContent.replace(regex, `define( '${constantName}', ${userDefinedValue} );`); -} - -/** - * Generates a random salt code for WordPress configuration. - * - * @return {string} - The generated salt code. - */ -function generateSalt() { - const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?/'; - const saltLength = 64; - return Array.from({length: saltLength}, () => charset[Math.floor(Math.random() * charset.length)]).join(''); -} - -/** - * Replaces empty salts in the WordPress configuration with generated salt codes. - * - * @param {string} configContent - The content of the wp-config.php file. - * @return {string} - The updated content with replaced salts. - */ -function replaceEmptySalts(configContent) { - const saltConstants = [ - 'AUTH_KEY', - 'SECURE_AUTH_KEY', - 'LOGGED_IN_KEY', - 'NONCE_KEY', - 'AUTH_SALT', - 'SECURE_AUTH_SALT', - 'LOGGED_IN_SALT', - 'NONCE_SALT' - ]; - - saltConstants.forEach((constant) => { - const emptySaltRegex = new RegExp(`define\\(\\s*'${constant}'\\s*,\\s*'put your unique phrase here'\\s*\\);`); - const generatedSalt = generateSalt(); - configContent = configContent.replace(emptySaltRegex, `define( '${constant}', '${generatedSalt}' );`); - }); - - return configContent; -} - module.exports = { axiosFetch, + isWordpressFolder, getLastWp, getUserLocale, getWordPressPaths, getWpConfigContent, - parseWpConfig, - getCurrentWpInfo, - replaceDbConstant, - replaceDbConstantBool, - generateSalt, - getDataFromFile, - replaceEmptySalts + getCurrentWpInfo }; From bb8be617781e8a5e434a13519b7ce286006461f1 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Mon, 11 Dec 2023 09:49:37 +0100 Subject: [PATCH 66/70] Add utility functions for WordPress configuration parsing and modification New functions have been added to a utility file parsers.js to handle parsing and modification of WordPress config files. These functions include parsing the wp-config.php file, replacing database constants and empty salts, and generating salt codes. Also, certain configurations related to WordPress installation have been moved to this utility for better structuring and ease of use. --- lib/utils/parsers.js | 184 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 lib/utils/parsers.js diff --git a/lib/utils/parsers.js b/lib/utils/parsers.js new file mode 100644 index 0000000..17dc566 --- /dev/null +++ b/lib/utils/parsers.js @@ -0,0 +1,184 @@ +/** + * Removes all commented lines from the given content. + * + * @param {string} content - The content that contains the lines to be uncommented. + * @return {string} The content without any commented lines. + */ +function uncommentedLines(content) { + const lines = content.split('\n'); + let inBlockComment = false; + let uncommented = ''; + + for (let line of lines) { + let newLine = line; + + if (inBlockComment) { + const endCommentIndex = newLine.indexOf('*/'); + if (endCommentIndex !== -1) { + inBlockComment = false; + newLine = newLine.substring(endCommentIndex + 2); + } else { + newLine = ''; + } + } + + if (!inBlockComment) { + const startCommentIndex = newLine.indexOf('/*'); + const endCommentIndex = newLine.indexOf('*/'); + + if (startCommentIndex !== -1 && endCommentIndex !== -1) { + newLine = newLine.slice(0, startCommentIndex) + newLine.slice(endCommentIndex + 2); + } else if (startCommentIndex !== -1) { + newLine = newLine.slice(0, startCommentIndex); + inBlockComment = true; + } + + const lineCommentIndex = newLine.indexOf('//'); + if (lineCommentIndex !== -1) { + newLine = newLine.slice(0, lineCommentIndex); + } + } + + uncommented += newLine + '\n'; + } + + return uncommented; +} + +/** + * Parses the wp-config.php file in a WordPress installation and extracts defined constants and variables. + * + * @param {string|null} wpConfigContent - The content of the wp-config.php file. + * @return {{constants: Record, variables: Record}} - An object containing the extracted constants and variables, or null if there was an error parsing the file. + */ +function parseWpConfig(wpConfigContent) { + + if (wpConfigContent === null) { + return {constants: {}, variables: {}}; + } + + const cleanWpConfigContent = uncommentedLines(wpConfigContent); + + // Regular expressions to match define statements + const defineRegex = /define\(\s*'([^']*)'\s*,\s*'([^']*)'\s*\);/gi; + + // Regular expressions to match variable assignments + const variableRegex = /\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=\s*["']?(.*?[^"'])["']?(?:;|\?>|\s+\?>|$)/g; + + // Extract constants + const constantMatches = [...cleanWpConfigContent.matchAll(defineRegex)]; + /** + * @type {Record} constants - An object containing the extracted constants. + */ + const constants = {}; + constantMatches.forEach(match => { + constants[match[1]] = match[2]; + }); + + // Extract variables + const variableMatches = [...cleanWpConfigContent.matchAll(variableRegex)]; + + /** + * @type {Record} variables - An object containing the extracted constants. + */ + const variables = {}; + variableMatches.forEach(match => { + variables[match[1]] = match[2]; + }); + + return {constants, variables}; +} + +/** + * Replaces a constant in the wp-config.php file with a user-defined value. + * + * @param {string} configContent - The content of the wp-config.php file. + * @param {string} constantName - The name of the constant to replace. + * @param {string} userDefinedValue - The user-defined value to set for the constant. + * @return {string} - The updated content with the replaced constant. + */ +function replaceDbConstant(configContent, constantName, userDefinedValue) { + const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*'[^']*'\\s*\\);`); + return configContent.replace(regex, `define( '${constantName}', '${userDefinedValue}' );`); +} + +/** + * Replaces a database constant boolean value in the given configuration content. + * + * @param {string} configContent - The content of the configuration. + * @param {string} constantName - The name of the constant to be replaced. + * @param {boolean} userDefinedValue - The user-defined value to replace the constant with. + * @return {string} The updated configuration content with the replaced constant. + */ +function replaceDbConstantBool(configContent, constantName, userDefinedValue) { + // Updated regex to handle boolean constants (TRUE or FALSE) + const regex = new RegExp(`define\\(\\s*'${constantName}'\\s*,\\s*[^']*\\s*\\);`); + return configContent.replace(regex, `define( '${constantName}', ${userDefinedValue} );`); +} + +/** + * Generates a random salt code for WordPress configuration. + * + * @return {string} - The generated salt code. + */ +function generateSalt() { + const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?/'; + const saltLength = 64; + return Array.from({length: saltLength}, () => charset[Math.floor(Math.random() * charset.length)]).join(''); +} + +/** + * Replaces empty salts in the WordPress configuration with generated salt codes. + * + * @param {string} configContent - The content of the wp-config.php file. + * @return {string} - The updated content with replaced salts. + */ +function replaceEmptySalts(configContent) { + const saltConstants = [ + 'AUTH_KEY', + 'SECURE_AUTH_KEY', + 'LOGGED_IN_KEY', + 'NONCE_KEY', + 'AUTH_SALT', + 'SECURE_AUTH_SALT', + 'LOGGED_IN_SALT', + 'NONCE_SALT' + ]; + + saltConstants.forEach((constant) => { + const emptySaltRegex = new RegExp(`define\\(\\s*'${constant}'\\s*,\\s*'put your unique phrase here'\\s*\\);`); + const generatedSalt = generateSalt(); + configContent = configContent.replace(emptySaltRegex, `define( '${constant}', '${generatedSalt}' );`); + }); + + return configContent; +} + +/** + * Reads a PHP file, extracts, and returns the WordPress version number. + * + * @param {string} fileContent - Path to the PHP file to read + * @param {string} variableName - The name of the variable to search adn replace + * + * @return {string|null} WordPress version number or null if not found or in case of an error + */ +function getDataFromFile(fileContent, variableName = 'wp_version') { + // Define a regular expression to match the variableName and its value with both the single and double quotes + const versionRegex = new RegExp(`${variableName}\\s*=\\s*['"]([^'"]+)['"]`, 'g'); + + // Use the regular expression to extract the version number + let match = versionRegex.exec(fileContent); + + // Return the version number or null if not found + return match ? match[1] : null; +} + +module.exports = { + parseWpConfig, + replaceDbConstant, + replaceDbConstantBool, + getDataFromFile, + generateSalt, + replaceEmptySalts, + uncommentedLines +}; From 3f8eb2844010f9a82c71056118565f52c38d679b Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Mon, 11 Dec 2023 09:49:51 +0100 Subject: [PATCH 67/70] Add utility prompts for WordPress configuration and dump options Introducing new utility file prompts.js which contains two asynchronous functions; one that prompts the user for their new WordPress installation details for configuration initialization and another that checks if the user wants to dump installation. These prompts help to improve the user experience by allowing interactive customization for WordPress installations and dump handling, and serve to better structure the codebase. --- lib/utils/prompts.js | 99 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 lib/utils/prompts.js diff --git a/lib/utils/prompts.js b/lib/utils/prompts.js new file mode 100644 index 0000000..cc525ae --- /dev/null +++ b/lib/utils/prompts.js @@ -0,0 +1,99 @@ +const inquirer = require("inquirer"); +const {DefaultWpInstallFolder, PkgFileName} = require("../constants"); +const path = require("path"); +const {initConfig} = require("./data"); +const {getWordPressPaths} = require("./wordpress"); +const Dump = require("../Dump"); + +/** + * This function prompts the user for configuration details regarding a new WordPress installation. + * It first checks if the user wants to create a new installation. If so, it asks for the name, + * version, and language of the site. It then initializes the configuration based on the provided + * details. If the user does not want a new installation, it terminates the process with an error + * message asking for a template file. + * + * @return {Promise} Returns a promise that resolves with the result of the initialization of the + * configuration or ends the process if the template file does not exist. + */ +async function askForConfiguration() { + return await inquirer.prompt([ + { + type: 'confirm', + name: 'newInstallation', + message: '❓ Do you want to create a new WordPress installation?', + default: true + }, + { + type: 'input', + name: 'name', + message: 'Enter the name of your website:', + default: process.cwd().split("/").pop() || DefaultWpInstallFolder, + when: (answers) => answers.newInstallation // This question will be asked only if 'newInstallation' is true + }, + { + type: 'input', + name: 'version', + message: 'Enter the WordPress version:', + default: 'Latest', + when: (answers) => answers.newInstallation + }, + { + type: 'input', + name: 'language', + message: 'Enter the language for your WordPress site:', + default: 'English', + when: (answers) => answers.newInstallation + } + ]) + .then((answers) => { + if (answers.newInstallation) { + const {name, version, language} = answers; + + // If the template file does not exist, create it with the default config in the 'wordpress' folder + const baseFolder = path.join(process.cwd(), name ?? DefaultWpInstallFolder); + return initConfig(baseFolder, {version, language}); + } else { + console.error(`⚠️ The template file ${PkgFileName} does not exist in the current folder. Please provide a template file using the --template option.`); + process.exit(1); + } + }) + .then((result) => { + return result; + }); +} + +/** + * This async function asks the user if they want to dump the WordPress + * installation. If the user confirms, it gets the WordPress paths, creates + * a new Dump instance, and initiates it. + * + * It does not take any parameters. + * + * @return {Promise} The result of the dump initiation, if it is initiated. + */ +async function askForDump() { + return await inquirer.prompt([ + { + type: 'confirm', + name: 'dump', + message: '❓ Do you want to Dump this WordPress installation?', + default: true + } + ]).then(answers => { + if (answers.dump) { + const baseFolder = process.cwd(); + const paths = getWordPressPaths( path.dirname(baseFolder), baseFolder ); + const dump = new Dump(paths); + return dump.init(); + } + }).then( (/** @type {import("../constants").DefaultWpConfig|undefined} */ result) => { + if (result.wordpress.name) { + return result; + } + }); +} + +module.exports = { + askForConfiguration, + askForDump +}; From c635334f834ac4b5291ae13ee4f531651bebf462 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Mon, 11 Dec 2023 09:50:30 +0100 Subject: [PATCH 68/70] Refactor utility imports and update test configurations The commit organizes the imports from utils directory by moving data processing functions to parsers.js. Also, it updates the test configurations by replacing the deprecated method getConfig with initConfig and changes parameters to make it more representative of deploy conditions. This results in a neater, more intuitive import schema and test set-up, enhancing code readability and understanding. --- tests/fixtures/wp-package.json | 4 ++-- tests/utils.test.js | 12 +++++------- tests/wpConfig.test.js | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/fixtures/wp-package.json b/tests/fixtures/wp-package.json index 1b006b3..dee4720 100644 --- a/tests/fixtures/wp-package.json +++ b/tests/fixtures/wp-package.json @@ -1,9 +1,9 @@ { - "name": "wordpress", "wordpress": { + "name": "wordpress", "version": "6.4.2", "language": "en_US", - "config": { + "WP_config": { "DB_NAME": "my_db_name", "DB_USER": "my_db_username", "DB_PASSWORD": "my_db_password", diff --git a/tests/utils.test.js b/tests/utils.test.js index 44b32b9..1c7eae8 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -1,17 +1,15 @@ -const { getConfig, getWordPressDownloadUrl, getDownloadUrl } = require('../lib/utils/data.js'); -const { generateSalt, replaceDbConstant, replaceDbConstantBool } = require("../lib/utils/wordpress.js"); -const {getDataFromFile} = require("../lib/utils/wordpress"); +const { getWordPressDownloadUrl, getDownloadUrl } = require('../lib/utils/data.js'); +const { generateSalt, replaceDbConstant, replaceDbConstantBool, getDataFromFile } = require("../lib/utils/parsers.js"); +const {initConfig} = require("../lib/utils/data"); describe('getConfig', () => { it('should read wp-package.json from the root folder and return the default configuration if the file does not exist', async () => { - const argv = undefined; // Call the function - const config = await getConfig(argv); + const config = await initConfig( 'tests', {version: '5.7.1', language: 'en_US'}); // Assert the result expect(config).toBeInstanceOf(Object); - expect(config.name).toBe('wordpress'); - expect(config.wordpress).toMatchObject({"config": {}}); + expect(config.wordpress).toMatchObject({"WP_config": {}, version: '5.7.1', language: 'en_US'}); expect(config.plugins).not.toBeFalsy(); }); }); diff --git a/tests/wpConfig.test.js b/tests/wpConfig.test.js index 3685f21..30fbeae 100644 --- a/tests/wpConfig.test.js +++ b/tests/wpConfig.test.js @@ -1,6 +1,6 @@ const fs = require("fs"); const path = require("path"); -const {parseWpConfig} = require("../lib/utils/wordpress"); +const {parseWpConfig} = require("../lib/utils/parsers.js"); describe('parseWpConfig with real file', () => { From da2a62d59fa34c24c04588ae6d98ffc8720761fc Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Mon, 11 Dec 2023 09:50:57 +0100 Subject: [PATCH 69/70] Modify typescript target, update eslint script and add dependencies This commit modifies the typescript target to "es5" for maximum compatibility and updates the "lint" script in package.json to automatically fix problems. It also introduces new dependencies including "inquirer" and corresponding types, as well as the "prettier" code formatter, and updated typescript and "lint" configurations to reflect these changes. These updates enhance the linting process, introduce user inquiry functionality, and ensure consistent code style. --- package-lock.json | 442 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 13 +- tsconfig.json | 3 +- 3 files changed, 443 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b307e5..8253062 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "axios": "^1.6.2", "extract-zip": "^2.0.1", + "inquirer": "^8.2.6", "mysql2": "^3.6.5", "mysqldump": "^3.2.0", "yargs": "^17.7.2" @@ -20,6 +21,7 @@ }, "devDependencies": { "@types/axios": "^0.14.0", + "@types/inquirer": "8.2.10", "@types/jest": "^29.5.11", "@types/node": "^20.10.4", "@types/yargs": "^17.0.32", @@ -29,6 +31,7 @@ "eslint-plugin-promise": "^6.1.1", "husky": "^8.0.3", "jest": "^29.7.0", + "prettier": "npm:wp-prettier@^3.0.3", "typescript": "^5.3.3" } }, @@ -1354,6 +1357,16 @@ "@types/node": "*" } }, + "node_modules/@types/inquirer": { + "version": "8.2.10", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-8.2.10.tgz", + "integrity": "sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA==", + "dev": true, + "dependencies": { + "@types/through": "*", + "rxjs": "^7.2.0" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -1410,6 +1423,15 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, + "node_modules/@types/through": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", + "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -1481,7 +1503,6 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, "dependencies": { "type-fest": "^0.21.3" }, @@ -1496,7 +1517,6 @@ "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, "engines": { "node": ">=10" }, @@ -1800,6 +1820,35 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1863,6 +1912,29 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -1970,7 +2042,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1991,6 +2062,11 @@ "node": ">=10" } }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -2012,6 +2088,36 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -2025,6 +2131,14 @@ "node": ">=12" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -2160,6 +2274,17 @@ "node": ">=0.10.0" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", @@ -2831,6 +2956,30 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -2894,6 +3043,28 @@ "pend": "~1.2.0" } }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3249,7 +3420,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -3362,6 +3532,25 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -3428,8 +3617,45 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } }, "node_modules/internal-slot": { "version": "1.0.6", @@ -3591,6 +3817,14 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -4558,6 +4792,32 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -4655,7 +4915,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, "engines": { "node": ">=6" } @@ -4687,6 +4946,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, "node_modules/mysql2": { "version": "3.6.5", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.5.tgz", @@ -4913,7 +5177,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -4941,6 +5204,47 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5148,6 +5452,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "name": "wp-prettier", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/wp-prettier/-/wp-prettier-3.0.3.tgz", + "integrity": "sha512-X4UlrxDTH8oom9qXlcjnydsjAOD2BmB6yFmvS4Z2zdTzqqpRWb+fbqrH412+l+OUXmbzJlSXjlMFYPgYG12IAA==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -5252,6 +5572,19 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", @@ -5343,6 +5676,18 @@ "node": ">=10" } }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5368,6 +5713,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5391,6 +5744,14 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-array-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", @@ -5410,6 +5771,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", @@ -5514,8 +5894,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sisteransi": { "version": "1.0.5", @@ -5594,6 +5973,14 @@ "node": ">=8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5714,7 +6101,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -5754,6 +6140,22 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -5794,6 +6196,11 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5970,6 +6377,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/v8-to-istanbul": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", @@ -5993,6 +6405,14 @@ "makeerror": "1.0.12" } }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 8d4c586..d49269f 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "url": "https://github.com/erikyo/wpmm.git" }, "scripts": { - "lint": "eslint ./lib ./tests", + "lint": "eslint ./lib ./tests --fix", "test": "jest", "prepare": "husky install", "locatesting": "npx . --info --template https://gist.githubusercontent.com/erikyo/795746c96f422168121e47c2a876fb31/raw/1ec18a8bc10b6f80221b69944d9dad19c25613c1/wp-package.json" @@ -29,21 +29,30 @@ "dependencies": { "axios": "^1.6.2", "extract-zip": "^2.0.1", + "inquirer": "^8.2.6", "mysql2": "^3.6.5", "mysqldump": "^3.2.0", "yargs": "^17.7.2" }, "devDependencies": { "@types/axios": "^0.14.0", + "@types/inquirer": "^8.2.10", "@types/jest": "^29.5.11", - "@types/yargs": "^17.0.32", "@types/node": "^20.10.4", + "@types/yargs": "^17.0.32", "eslint": "^8.54.0", "eslint-config-standard": "^17.1.0", "eslint-plugin-n": "^16.3.1", "eslint-plugin-promise": "^6.1.1", "husky": "^8.0.3", "jest": "^29.7.0", + "prettier": "npm:wp-prettier@^3.0.3", "typescript": "^5.3.3" + }, + "prettier": { + "trailingComma": "es5", + "tabWidth": 4, + "semi": false, + "singleQuote": true } } diff --git a/tsconfig.json b/tsconfig.json index ca5b60c..d2da2a8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,9 @@ { "compilerOptions": { "module": "commonjs", - "target": "es2015", + "target": "es5", "moduleResolution": "node", "strict": true, - "pretty": false, "allowJs": true, "checkJs": true, "resolveJsonModule": true, From 0999c8a133900a57bc9ec5d816d6c198f86bd830 Mon Sep 17 00:00:00 2001 From: Erik Golinelli Date: Mon, 11 Dec 2023 10:09:01 +0100 Subject: [PATCH 70/70] Update configuration handling and refactor prompts This commit updates handling of configuration in data.js by adjusting the method of creating a new WordPress installation if template file does not exist. It moves the configuration initialization process to data.js from prompts.js, for consistent handling of configuration. The look and feel of prompts.js are simplified by removing the process of calling initConfig() function and unnecessary results handling. These changes lead to better code organization and simplified logic in the prompt functionality. --- lib/utils/data.js | 11 ++++++++++- lib/utils/prompts.js | 18 ++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/utils/data.js b/lib/utils/data.js index 326a2c7..1a44ece 100644 --- a/lib/utils/data.js +++ b/lib/utils/data.js @@ -4,6 +4,8 @@ const Initialize = require("../Initialize.js"); const {axiosFetch} = require("./wordpress.js"); const {isWordpressFolder} = require("./wordpress.js"); const {askForConfiguration, askForDump} = require("./prompts.js"); +const path = require("path"); +const {DefaultWpInstallFolder} = require("../constants"); /** * Initializes the configuration for the given base folder. @@ -53,7 +55,14 @@ async function getConfig (args) { if ( ! isWordpressFolder( process.cwd() ) ) { console.log(`⚠️ Cannot find any Wordpress files in the current folder and the template file ${PkgFileName} is missing.`); // Ask the user if they want to create a new WordPress installation - config = await askForConfiguration(); + const answers = await askForConfiguration(); + + // If the template file does not exist, create it with the default config in the 'wordpress' folder + const baseFolder = path.join(process.cwd(), answers?.name ?? DefaultWpInstallFolder); + + // Initialize the configuration + return await initConfig(baseFolder, {version: answers?.version, language: answers?.language}); + } else { if (fs.existsSync(PkgFileName)) { // If the template file exists, read it as JSON diff --git a/lib/utils/prompts.js b/lib/utils/prompts.js index cc525ae..7ecddef 100644 --- a/lib/utils/prompts.js +++ b/lib/utils/prompts.js @@ -1,9 +1,10 @@ const inquirer = require("inquirer"); -const {DefaultWpInstallFolder, PkgFileName} = require("../constants"); const path = require("path"); -const {initConfig} = require("./data"); -const {getWordPressPaths} = require("./wordpress"); -const Dump = require("../Dump"); + +const {DefaultWpInstallFolder, PkgFileName} = require("../constants.js"); + +const {getWordPressPaths} = require("./wordpress.js"); +const Dump = require("../Dump.js"); /** * This function prompts the user for configuration details regarding a new WordPress installation. @@ -47,19 +48,12 @@ async function askForConfiguration() { ]) .then((answers) => { if (answers.newInstallation) { - const {name, version, language} = answers; - - // If the template file does not exist, create it with the default config in the 'wordpress' folder - const baseFolder = path.join(process.cwd(), name ?? DefaultWpInstallFolder); - return initConfig(baseFolder, {version, language}); + return answers; } else { console.error(`⚠️ The template file ${PkgFileName} does not exist in the current folder. Please provide a template file using the --template option.`); process.exit(1); } }) - .then((result) => { - return result; - }); } /**