Skip to content

Commit

Permalink
Merge pull request #14 from erikyo/feature/init-command
Browse files Browse the repository at this point in the history
Feature/init and dump command
  • Loading branch information
erikyo authored Dec 2, 2023
2 parents c0ad71e + 6dc7923 commit 8a0680b
Show file tree
Hide file tree
Showing 11 changed files with 381 additions and 156 deletions.
92 changes: 92 additions & 0 deletions lib/dump.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const fs = require('fs');
const path = require('path');
const { geVarFromPHPFile } = require('./utils');

class dump {
constructor () {
this.wpFolder = process.cwd();
this.themeFolder = path.join(this.wpFolder, 'wp-content', 'themes');
this.pluginsFolder = path.join(this.wpFolder, 'wp-content', 'plugins');
}

init () {
console.log(this.wpFolder);
console.log(this.themeFolder);
const themes = this.scanDirectory(this.themeFolder);
const plugins = this.scanDirectory(this.pluginsFolder);

// 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 language = Intl.DateTimeFormat().resolvedOptions().locale;

const result = {
name,
wordpress: {
version,
language
},
themes,
plugins
};

const outputPath = path.join(process.cwd(), 'wp-package.json');

fs.writeFileSync(outputPath, JSON.stringify(result, null, 2));
console.log(`🆗 Wordpress configuration Dump completed. Configuration saved to ${outputPath}`);
}

scanDirectory (directory) {
const items = fs.readdirSync(directory);
const result = [];

for (const item of items) {
const fullPath = path.join(directory, item);
console.log(`🔍️ Scanning ${fullPath}`);
const isDirectory = fs.statSync(fullPath).isDirectory();

if (isDirectory) {
const stylePath = path.join(fullPath, 'style.css');
const themeFile = path.join(fullPath, `${item}.php`);
const pluginFile = `${item}.php`; // ie: 'hello-dolly.php'

if (fs.existsSync(stylePath)) {
const version = this.extractVersionFromStyleFile(stylePath);
if (version) {
console.log(`ℹ️ Found ${item} version ${version}`);
result.push({ name: item, version });
}
}

if (fs.existsSync(themeFile) || fs.existsSync(pluginFile)) {
const version = this.extractVersionFromPHPFile(themeFile) || this.extractVersionFromPHPFile(pluginFile);
if (version) {
console.log(`ℹ️ Found ${item} version ${version}`);
result.push({ name: item, version });
}
}
}
}

return result;
}

extractVersionFromStyleFile (filePath) {
const content = fs.readFileSync(filePath, 'utf8');
const match = /Version:\s*([\d.]+)/i.exec(content);
return match ? match[1] : null;
}

extractVersionFromPHPFile (filePath) {
const content = fs.readFileSync(filePath, 'utf8');
const match = /Version:\s*([\d.]+)/i.exec(content);
return match ? match[1] : null;
}
}

module.exports = dump;
128 changes: 128 additions & 0 deletions lib/fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
const fs = require('fs');
const https = require('node:https');
const extract = require('extract-zip');

/**
* Create a temporary directory if it does not already exist.
*/
function makeDir (dirpath) {
if (!fs.existsSync(dirpath)) {
fs.mkdirSync(dirpath, { recursive: true });
}
}

/**
* 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) {
try {
fs.rmSync(dir, { recursive: true });
console.log(`🧹 ${dir} removed successfully.`);
} catch (err) {
// File deletion failed
console.error(err.message);
}
}

/**
* Renames a folder from the old path to the new path.
*
* @param {string} oldPath - The path of the folder to be renamed.
* @param {string} newPath - The new path of the folder.
*/
function renameFolder (oldPath, newPath) {
fs.renameSync(oldPath, newPath);
}

/**
* Downloads a file from the specified URL and saves it to the target file.
*
* @param {string} url - The URL of the file to download.
* @param {string} targetFile - The file path where the downloaded file will be saved.
* @return {Promise<void>} A promise that resolves when the file is successfully downloaded and saved, or rejects with an error if there was an issue.
*/
async function downloadFile (url, targetFile) {
if (fs.existsSync(targetFile)) {
console.log(`ℹ️ ${targetFile} already exists. Skipping download.`);
return;
}
try {
return await new Promise((resolve, reject) => {
https.get(
url,
{ headers: { 'User-Agent': 'nodejs' } },
async (response) => {
const code = response.statusCode ?? 0;

if (code >= 400) {
return reject(new Error(response.statusMessage));
}

if (code > 300 && code < 400 && !!response.headers.location) {
return resolve(await downloadFile(response.headers.location, targetFile));
}

const fileWriter = fs.createWriteStream(targetFile).on('finish', () => {
resolve({});
});

response.pipe(fileWriter);
}).on('error', (error) => {
reject(error);
});
});
} catch (error) {
throw new Error(error);
}
}

/**
* Extracts a zip file to a target directory.
*
* @param {string} zipFilePath - The path of the zip file to extract.
* @param {string} targetDirectory - The directory to extract the zip file to.
* @return {Promise<string>} Returns true if the extraction is successful, false otherwise.
*/
async function extractZip (zipFilePath, targetDirectory) {
let commonRootPath; // Variable to store the common root path

try {
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;
}
}
}
}
});

// Return the root folder name
console.log(`📂 Extracted to ${commonRootPath}`);
return commonRootPath;
} catch (err) {
console.error(`📛 Error extracting ${zipFilePath} zip: ${err}`);
return err;
}
}

module.exports = {
makeDir,
cleanup,
renameFolder,
downloadFile,
extractZip
};
51 changes: 36 additions & 15 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,45 @@
#!/usr/bin/env node
const { getConfig } = require('./utils');
const { WordPressInstaller } = require('./install');
const { getConfig, printTimePassed } = require('./utils');
const WordPressInstaller = require('./install');
const WordPressConfigDump = require('./dump.js');
const Initialize = require('./initialize.js'); // Import the Initialize class

const config = getConfig();
const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');

// Install WordPress
const installer = new WordPressInstaller(config);
// Get the arguments from the command line
const argv = yargs(hideBin(process.argv)).argv;

// Get the config
const config = getConfig(argv);

// Start the timer
const startTime = Date.now();

installer.run().then(() => {
console.log('🚀 WordPress installed successfully.');

// End the timer
const endTime = Date.now();
const actions = {
dump: () => {
const dump = new WordPressConfigDump(this);
dump.init();
printTimePassed(startTime);
process.exit(0);
},
init: () => {
const initializer = new Initialize(config);
initializer.generateConfig();
printTimePassed(startTime);
process.exit(0);
},
default: () => {
// Install WordPress
const installer = new WordPressInstaller(config);

// Calculate the time passed
const timePassed = endTime - startTime;
console.log(`🕒 Time passed: ${timePassed} milliseconds`);
installer.run().then(() => {
console.log('🚀 WordPress installed successfully.');
printTimePassed(startTime);
process.exit(0);
});
}
};

process.exit(0);
});
const action = Object.keys(argv).find(key => argv[key] === true);
(actions[action] || actions.default)();
58 changes: 58 additions & 0 deletions lib/initialize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const fs = require('fs');
const path = require('path');

class Initialize {
constructor (options) {
this.options = options || {};
this.wpFolder = process.cwd();
this.outputPath = path.join(this.wpFolder, 'wp-package.json');
}

generateConfig () {
const name = this.options.name || 'wordpress';
const language = this.options.language || 'en_US';
const version = this.options.version || '6.4.1';

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
};

const customConfig = this.options.config || {};

const result = {
name,
wordpress: {
version,
language,
config: { ...defaultConfig, ...customConfig }
},
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}`);
}
}

module.exports = Initialize;
7 changes: 4 additions & 3 deletions lib/install.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const path = require('path');
const { cleanup, makeDir, isWPCLIAvailable} = require('./utils');
const { isWPCLIAvailable } = require('./utils');
const { cleanup, makeDir } = require('./fs');
const { WordPressPackage, PluginPackage, ThemePackage } = require('./package');

/**
Expand Down Expand Up @@ -72,7 +73,7 @@ class WordPressInstaller {
* @param {Array} commands - An array of WP-CLI commands to execute.
* @return {Promise<void>} - A promise that resolves when the post-install commands complete.
*/
async runPostInstallCommands(commands) {
async runPostInstallCommands (commands) {
// Execute each post-install command
for (const command of commands) {
try {
Expand Down Expand Up @@ -101,4 +102,4 @@ class WordPressInstaller {
}
}

exports.WordPressInstaller = WordPressInstaller;
module.exports = WordPressInstaller;
9 changes: 6 additions & 3 deletions lib/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const {
downloadFile,
extractZip,
getDownloadUrl,
getWordPressDownloadUrl,
installNpmPackages,
renameFolder,
replaceDbConstant,
replaceEmptySalts, installComposer, replaceDbConstantBool
} = require('./utils');

const {
downloadFile,
extractZip,
renameFolder
} = require('./fs');

class Package {
/**
* Constructs a new instance of the class.
Expand Down
Loading

0 comments on commit 8a0680b

Please sign in to comment.