ba-block-cli
Version:
Auto-create blocks for WordPress ACF
331 lines (286 loc) • 12 kB
JavaScript
// program.args[0]
const { program } = require('commander');
const promptly = require('promptly');
const path = require('path');
const pjson = require('./package.json');
let themePath = process.cwd();
let localPath = path.resolve(__dirname, "./src");
const fs = require('fs');
const slugify = require('slugify');
// helper function: returns a promise that gets resolved when the specified event is fired
const waitForEvent = (emitter, event) => new Promise((resolve, reject) => emitter.once(event, resolve));
/**
* Block validator
* @param {Boolean} value
* @returns
*/
const blockValidator = function (value) {
if (value != 'y' && value != 'yes') {
throw new Error('Module not created.');
}
return true;
}
/**
* Create block command
*/
program
.command('block:create') // sub-command name
.alias('cr') // alternative sub-command is `al`
.option('-n, --name [value]', 'Block name') // args.sugar = value, optional, default is 'Low'
.description('Generate block folder with scss/js/php/block files.') // command description
// function to execute when command is uses
.action(function (args) {
block = slugify(args.name, '-').toLowerCase();
if (!fs.existsSync(`${themePath}/blocks/${block}`)) {
(async () => {
try {
const confirm = await promptly.prompt(`Create module with name: ${args.name} [y/n]: `, { blockValidator, retry: false });
// Since retry is true by default, promptly will keep asking for a name until it is valid
// Between each prompt, the error message from the validator will be printed
if( confirm == 'y' ){
createBlock(args.name);
}
} catch (err) {
console.error(`- ${err.message}`);
}
})();
}else{
console.log(`Block ${block} already exists.`);
}
});
/**
* Create block function
* @param {String} name
* @returns {Boolean}
*/
function createBlock(name) {
var block = slugify(name, '-');
if (!fs.existsSync('blocks')) {
(async () => {
fs.mkdirSync(`${themePath}/blocks`);
})();
}
block = slugify(block, '-').toLowerCase();
(async () => {
try {
// Create block folder
fs.mkdirSync(`${themePath}/blocks/${block}`, { recursive: true });
// Create block.json
var blockContent = fs.readFileSync(`${localPath}/views/block.json`,'utf8');
blockContent = blockContent.replaceAll('default', block);
blockContent = blockContent.replaceAll('Default', name);
fs.writeFile(`${themePath}/blocks/${block}/block.json`, blockContent, () => {} );
// Create blockname.js
var jsContent = fs.readFileSync(`${localPath}/views/block.js`,'utf8');
fs.writeFile(`${themePath}/blocks/${block}/${block}.js`, jsContent, () => {} );
// Create blockname.scss
var styleContent = fs.readFileSync(`${localPath}/views/block.scss`, 'utf8');
styleContent = styleContent.replace('block-section-wrapper', `${block}-section-wrapper`);
fs.writeFile(`${themePath}/blocks/${block}/${block}.scss`, styleContent, () => {} );
// Create blockname.php
var phpContent = fs.readFileSync(`${localPath}/views/block.php`,'utf8');
phpContent = phpContent.replace('Block name', name);
phpContent = phpContent.replace('block-section-wrapper', `${block}-section-wrapper`);
fs.writeFile(`${themePath}/blocks/${block}/${block}.php`, phpContent, () => {} );
// Create blockname.png
const imageReadStreem = fs.createReadStream(`${localPath}/views/block.png`);
await waitForEvent(imageReadStreem, 'ready');
const imageWriteStreem = fs.createWriteStream(`${themePath}/blocks/${block}/${block}.png`);
await waitForEvent(imageWriteStreem, 'ready');
imageReadStreem.pipe(imageWriteStreem);
await waitForEvent(imageReadStreem, 'end');
} catch (err) {
console.error(`- ${err.message}`);
}
})();
return true;
}
/**
* Copy block command
*/
program
.command('block:copy') // sub-command name
.alias('cp') // alternative sub-command is `al`
.option('-n, --name [value]', 'Block name') // args.sugar = value, optional, default is 'Low'
.description('Copy a predefined block and local JSON by name.') // command description
// function to execute when command is uses
.action(function (args) {
block = slugify(args.name, '-').toLowerCase();
if (!fs.existsSync(`${themePath}/blocks/${block}`) && fs.existsSync(`${localPath}/blocks/${block}`)) {
(async () => {
try {
const confirm = await promptly.prompt(`Copy an existing block with name: ${args.name} [y/n]: `, { blockValidator, retry: false });
// Since retry is true by default, promptly will keep asking for a name until it is valid
// Between each prompt, the error message from the validator will be printed
if (confirm == 'y') {
copyBlock(args.name);
}
} catch (err) {
console.error(`- ${err.message}`);
}
})();
} else if (!fs.existsSync(`${localPath}/blocks/${block}`)) {
console.log(`There is no predefined block with name ${args.name}.`);
} else {
console.log(`Block ${block} already exists.`);
}
});
/**
* Create block function
* @param {String} name
* @returns {Boolean}
*/
function copyBlock(name) {
var block = slugify(name, '-');
// Check if the blocks folder exists. If doesn't create it
if (!fs.existsSync('blocks')) {
(async () => {
fs.mkdirSync(`${themePath}/blocks`);
})();
}
// Check if the acf-json folder exists. If doesn't create it
if (!fs.existsSync('acf-json')) {
(async () => {
fs.mkdirSync(`${themePath}/acf-json`);
})();
}
// Create block slug
block = slugify(block, '-').toLowerCase();
(async () => {
try {
// Create block folder inside the blocks folder in theme root
fs.mkdirSync(`${themePath}/blocks/${block}`, { recursive: true });
// Create block.json
var blockContent = fs.readFileSync(`${localPath}/views/block.json`,'utf8');
blockContent = blockContent.replaceAll('default', block);
blockContent = blockContent.replaceAll('Default', name);
fs.writeFile(`${themePath}/blocks/${block}/block.json`, blockContent, () => {} );
// Copy {block}.js
var jsContent = fs.readFileSync(`${localPath}/blocks/${block}/${block}.js`,'utf8');
fs.writeFile(`${themePath}/blocks/${block}/${block}.js`, jsContent, () => {} );
// Copy {block}.scss
var styleContent = fs.readFileSync(`${localPath}/blocks/${block}/${block}.scss`,'utf8');
fs.writeFile(`${themePath}/blocks/${block}/${block}.scss`, styleContent, () => {} );
// Copy {block}.php
var phpContent = fs.readFileSync(`${localPath}/blocks/${block}/${block}.php`,'utf8');
fs.writeFile(`${themePath}/blocks/${block}/${block}.php`, phpContent, () => {} );
// Copy {block}.png
const imageReadStreem = fs.createReadStream(`${localPath}/blocks/${block}/${block}.png`);
await waitForEvent(imageReadStreem, 'ready');
const imageWriteStreem = fs.createWriteStream(`${themePath}/blocks/${block}/${block}.png`);
await waitForEvent(imageWriteStreem, 'ready');
imageReadStreem.pipe(imageWriteStreem);
await waitForEvent(imageReadStreem, 'end');
// Copy the local JSON file
var jsonContent = fs.readFileSync(`${localPath}/acf-json/${block}.json`,'utf8'),
realJson = JSON.parse(jsonContent),
key = realJson.key;
fs.writeFile(`${themePath}/acf-json/${key}.json`, jsonContent, () => {});
} catch (err) {
console.error(`- ${err.message}`);
}
})();
return true;
}
/**
* Delete block command
*/
program
.command('block:delete') // sub-command name
.alias('rm') // alternative sub-command is `al`
.option('-n, --name [value]', 'Block name') // args.sugar = value, optional, default is 'Low'
.description('Delete block folder containing scss/js/php/block files.') // command description
// function to execute when command is uses
.action(function (args) {
block = slugify(args.name, '-').toLowerCase();
if (fs.existsSync(`${themePath}/blocks/${block}`)) {
(async () => {
try {
const confirm = await promptly.prompt(`Are you sure that you want to delete block: ${args.name} [y/n]: `, { blockValidator, retry: false });
// Since retry is true by default, promptly will keep asking for a name until it is valid
// Between each prompt, the error message from the validator will be printed
if (confirm == 'y') {
block = slugify(args.name, '-').toLowerCase();
fs.rm(`${themePath}/blocks/${block}`, { recursive: true }, err => {
if (err) {
throw err
}
console.log(`The ${args.name} block has successfully removed.`);
// Check if there is a local JSON file created by this module
if (fs.existsSync(`${themePath}/acf-json/${block}.json`)) {
fs.rm(`${themePath}/acf-json/${block}.json`, { recursive: true }, err => {
if (err) {
throw err
}
console.log(`The ${block}.json local JSON file has successfully removed.`);
});
}
});
}
} catch (err) {
console.error(`- ${err.message}`);
}
})();
} else {
console.log(`Block ${block} doesn't exists.`);
}
});
/**
* Output the list of available blocks
*/
program
.command('block:list')
.alias('list')
.description('Output the list of available blocks')
.action(function () {
console.log('List of all blocks (use ba-block cp -n "BLOCKNAME" to copy): \n');
fs.readdir(`${localPath}/blocks/`, (err, files) => {
files.forEach(file => {
console.log(file);
});
});
});
/**
* Return the current version
*/
program
.version(pjson.version) // set the version for the program
.option('-v, --version', 'Output the version number');
/**
* Output information about the package
*/
program
.command('block:info')
.alias('info')
.description('Output information about the package')
.action(function () {
console.log(`
${pjson.description}
Available commands:
\n
Check the current version of this package:
ba-block -v
ba-block --version
\n
Get informations about this package:
ba-block info
\n
Create a new block:
ba-block cr -n "BLCOKNAME"
\n
List of all blocks:
ba-block list
\n
Copy an existing block:
ba-block cp -n "BLCOKNAME"
\n
Remove a block:
ba-block rm -n "BLCOKNAME"
`);
});
/*
* Parse
*/
program
.parse(process.argv);