preface
Front-end engineering is often mentioned as a way to improve development efficiency, reduce costs and ensure quality. Scaffolding tools are a very important part of front-end engineering, and a good general purpose scaffolding tool for Web engineering can do much of the above mentioned.
We should not only be able to use a lot of mature scaffolding on the market, but also according to the actual project situation, to achieve some suitable for their own project scaffolding. This article will work with you to implement a basic general-purpose scaffolding tool that you can extend as you wish.
The project structure
The overall structure of the project is as follows, and we will write the code step by step to implement the entire scaffolding tool.
Xman - cli ├ ─ bin │ └ ─ xman. Js ├ ─ command │ ├ ─ the add. Js │ ├ ─ the delete. Js │ ├ ─ init. Js │ └ ─ list. Js ├ ─ lib │ ├ ─ remove. Js │ └ ─ Update.js ├─.gitignore ├─ LICENSE ├─ Package.json ├─ readme.txt ├─ templatesCopy the code
The specific implementation
Initialize the project
This can be created using NPM init or modified according to package.json listed below.
{
"name": "xman-cli"."version": "1.0.0"."description": "Web Universal scaffolding tool"."bin": {
"xman": "bin/xman.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git"."url": "https://github.com/XmanLin/xman-cli.git"
},
"keywords": [
"cli"]."author": "xmanlin"."license": "MIT"."bugs": {
"url": "https://github.com/XmanLin/xman-cli/issues"
},
"homepage": "https://github.com/XmanLin/xman-cli#readme"."dependencies": {
"chalk": "^ 4.1.2." "."clear": "^ 0.1.0 from"."clui": "^ 0.3.6"."commander": "^ 8.2.0"."figlet": "^ 1.5.2." "."handlebars": "^ 4.7.7"."inquirer": "^ 8.1.5"."update-notifier": "^ 5.1.0"}}Copy the code
Here are two points:
bin
Fields: Commands that you can customize the scaffolding tool, such as the one abovexman
And thexman
The command execution script follows.- Dependencies in your project will be used later and described when they are used.
Write the bin/xman. Js
To make the script executable, add the following code to the top of xman.js:
#! /usr/bin/env nodeCopy the code
Use commander (the complete solution to the Node.js command line interface). You can click on the link or go to the NPM website to see how the API is used. The following lists of dependencies are the same.
#! /usr/bin/env node
const { program } = require('commander');
Copy the code
At this point, we can define the current scaffold version and the command to view the version.
#! /usr/bin/env node
const { program } = require('commander');
program
.version(require('.. /package').version, '-v, --version');
program.parse(process.argv); // This is necessary
if(! program.args.length) { program.help(); }Copy the code
After executing NPM link in the current Xman-CLI directory, you can debug the scaffolding tool locally.
Then execute it in the current directory
xman -v
Copy the code
You can see the version number we defined, which proves that the scaffolding tool was initially set up successfully.
Initialize the build project with the scaffolding tool
This is the core function point of the scaffolding tool, which uses the scaffolding tool command to quickly select and pull the base project template in advance in the Git repository. We can customize the project template according to the actual needs, and develop relevant development specifications and conventions in the project.
The name field in the package.json of the project should be written in the following form:
{
"name": "{{name}}",
}
Copy the code
The reason for this will be explained later in the code.
Then create templates.json in the root directory:
{
"templates": {
"xman-manage": {
"url": "https://github.com/XmanLin/xman-manage.git",
"branch": "master"
},
"xman-web": {
"url": "https://github.com/XmanLin/xman-web.git",
"branch": "master"
}
}
}
Copy the code
Xman-manage and Xman-Web represent different projects and can be customized based on the actual situation. The URL is the address of the basic project, and the branch is the branch that is automatically pulled.
Next, create init.js in the Command folder (which holds the implementation logic for the following list of commands) :
const fs = require('fs'); // node.js file system
const exec = require('child_process').exec; // Start a new process to execute commands
const config = require('.. /templates'); // Introduce a defined list of base items
const chalk = require('chalk'); // Add color to the prompt
const clear = require('clear'); // Clear command
const figlet = require('figlet'); // Can be used to customize the header during CLI execution
const inquirer = require('inquirer'); // Provides an interactive command line
const handlebars = require('handlebars'); // A simple template language, you can search for
const clui = require('clui'); // Provide the wait state
const Spinner = clui.Spinner;
const status = new Spinner('Downloading... ');
const removeDir = require('.. /lib/remove'); // Delete files and folders
module.exports = () = > {
let gitUrl;
let branch;
clear();
// Customize cool CLI headers
console.log(chalk.yellow(figlet.textSync('XMAN-CLI', {
horizontalLayout: 'full'
})));
inquirer.prompt([
{
name: 'templateName'.type: 'list'.message: 'Please select the project template you want:'.choices: Object.keys(config.templates),
},
{
name: 'projectName'.type: 'input'.message: 'Please enter your project name:'.validate: function (value) {
if (value.length) {
return true;
} else {
return 'Please enter your project name';
}
},
}
])
.then(answers= > {
gitUrl = config.templates[answers.templateName].url;
branch = config.templates[answers.templateName].branch;
// Execute the command to clone the desired project template from git
let cmdStr = `git clone ${gitUrl} ${answers.projectName} && cd ${answers.projectName} && git checkout ${branch}`;
status.start();
exec(cmdStr, (error, stdou, stderr) = > {
status.stop();
if (error) {
console.log('An error occurred:', chalk.red(JSON.stringify(error)));
process.exit();
}
const meta = {
name: answers.projectName
};
// The name in the project template package.json should be written as "name": "{{name}}"
const content = fs.readFileSync(`${answers.projectName}/package.json`).toString();
// Use handlebars.compile to compile {{name}}
const result = handlebars.compile(content)(meta);
fs.writeFileSync(`${answers.projectName}/package.json`, result);
// Delete the.git file that comes with the template
removeDir(`${answers.projectName}/.git`);
console.log(chalk.green('\n √ Download complete! '));
console.log(chalk.cyan(`\n cd ${answers.projectName} && yarn \n`));
process.exit();
})
})
.catch(error= > {
console.log(error);
console.log('An error occurred:', chalk.red(JSON.stringify(error)));
process.exit();
});
}
Copy the code
lib/remove.js
const fs = require('fs'); let path = require('path'); function removeDir(dir) { let files = fs.readdirSync(dir); For (var I = 0; var I = 0; i < files.length; i++) { let newPath = path.join(dir, files[i]); let stat = fs.statSync(newPath); If (stat.isdirectory ()) {removeDir(newPath); if (stat.isdirectory ()) { } else {// Delete file fs.unlinksync (newPath); } } fs.rmdirSync(dir); // If the folder is empty, delete it}; module.exports = removeDir;Copy the code
Finally, continue to define the command in xman.js:
#! /usr/bin/env node
const { program } = require('commander'); . program .command('init')
.description('Generate a new project')
.alias('i')
.action(() = > {
require('.. /command/init') ()}); .Copy the code
Find another folder to execute the defined command:
xman i
Copy the code
Open the template project we downloaded and take a look:
Add the project template configuration by command
Now we can build the project by command pull, but what if we have a new project template later? You have to change templates.json manually every time. This, of course, is not reasonable, so the next step is to implement adding project templates by command.
Create a new project template in git repository, whatever it is, xman-mobile in my case, and start writing the logic and commands to add the project template. Create command/add.js:
const config = require('.. /templates.json');
const chalk = require('chalk');
const fs = require('fs');
const inquirer = require('inquirer');
const clear = require('clear');
module.exports = () = > {
clear();
inquirer.prompt([
{
name: 'templateName'.type: 'input'.message: 'Please enter template name:'.validate: function (value) {
if (value.length) {
if (config.templates[value]) {
return 'Template already exists, please re-enter';
} else {
return true; }}else {
return 'Please enter a template name'; }}}, {name: 'gitLink'.type: 'input'.message: 'Git HTTPS link:'.validate: function (value) {
if (value.length) {
return true;
} else {
return 'Please enter Git HTTPS link'; }}}, {name: 'branch'.type: 'input'.message: 'Please enter branch name:'.validate: function (value) {
if (value.length) {
return true;
} else {
return 'Please enter a branch name';
}
},
}
])
.then(res= > {
config.templates[res.templateName] = {};
config.templates[res.templateName]['url'] = res.gitLink.replace(/[\u0000-\u0019]/g.' '); // Filter Unicode characters
config.templates[res.templateName]['branch'] = res.branch;
fs.writeFile(__dirname + '/.. /templates.json'.JSON.stringify(config), 'utf-8'.(err) = > {
if (err) {
console.log(err);
} else {
console.log(chalk.green('New template added successfully! \n'));
}
process.exit();
})
})
.catch(error= > {
console.log(error);
console.log('An error occurred:', chalk.red(JSON.stringify(error)));
process.exit();
});
}
Copy the code
Continue adding commands in bin/xman.js
#! /usr/bin/env node
const { program } = require('commander'); . program .command('add')
.description('Add a new template')
.alias('a')
.action(() = > {
require('.. /command/add') ()}); .Copy the code
Run the NPM link –force command and then run the xman a command.
You can see that in templates.json, new template information has been added.
Delete the project template configuration by using commands
If there is an add, there must be a delete command. Also, create command/delete.js:
const fs = require('fs');
const config = require('.. /templates');
const chalk = require('chalk');
const inquirer = require('inquirer');
const clear = require('clear');
module.exports = () = > {
clear();
inquirer.prompt([
{
name: 'templateName'.type: 'input'.message: 'Please enter the name of the template to delete:'.validate: function (value) {
if (value.length) {
if(! config.templates[value]) {return 'Template does not exist, please re-enter';
} else {
return true; }}else {
return 'Please enter the name of the template to delete';
}
},
}
])
.then(res= > {
config.templates[res.templateName] = undefined;
fs.writeFile(__dirname + '/.. /templates.json'.JSON.stringify(config), 'utf-8'.(err) = > {
if (err) {
console.log(err);
} else {
console.log(chalk.green('Template deleted! '));
}
process.exit();
});
})
.catch(error= > {
console.log(error);
console.log('An error occurred:', chalk.red(JSON.stringify(error)));
process.exit();
});
}
Copy the code
Continue adding commands:
#! /usr/bin/env node
const { program } = require('commander'); . program .command('delete')
.description('Delete a template')
.alias('d')
.action(() = > {
require('.. /command/delete') ()}); .Copy the code
Run the NPM link –force command and then run the xman d command. Looking at templates.json, we have removed the template information we want to remove.
You can run commands to quickly view existing templates
It is generally not possible to remember all the templates that have been added, and sometimes a quick look is necessary. So we’ll implement a simple command to quickly view a list of templates:
The new command/list. Js
const config = require('.. /templates');
const chalk = require('chalk');
module.exports = () = > {
let str = ' ';
Object.keys(config.templates).forEach((item, index, array) = > {
if (index === array.length - 1) {
str += item;
} else {
str += `${item} \n`; }});console.log(chalk.cyan(str));
process.exit();
}
Copy the code
Add command:
#! /usr/bin/env node
const { program } = require('commander'); . program .command('list')
.description('show temlpate list')
.alias('l')
.action(() = > {
require('.. /command/list') ()}); .Copy the code
Run the NPM link –force command and then run the xman l command:
Run commands to check whether the CLI version is the latest
A general purpose scaffolding tool is definitely not for one person. The user may need to know if the CLI has the latest version, so it also needs to have the ability to check the CLI version.
The new bin/update. Js:
const updateNotifier = require('update-notifier'); // Updates notifications for CLI applications
const chalk = require('chalk');
const pkg = require('.. /package.json');
const notifier = updateNotifier({
pkg,
updateCheckInterval: 1000 * 60 * 60.// Default is 1000 * 60 * 60 * 24 (1 day)
})
function updateChk() {
if (notifier.update) {
console.log('A new version is available:${chalk.cyan(notifier.update.latest)}, you are advised to update 'before using);
notifier.notify();
} else {
console.log(chalk.cyan('Already the latest version')); }};module.exports = updateChk;
Copy the code
Add command:
#! /usr/bin/env node
const { program } = require('commander'); . program .command('upgrade')
.description("Check the js-plugin-cli version.")
.alias('u')
.action(() = >{ updateChk(); }); .Copy the code
Run the NPM link –force command and then run the xman u command:
At this point, we have implemented a basic but complete general-purpose scaffolding tool for Web engineering. You can modify and expand according to your actual needs.
conclusion
The essence of a universal web engineering scaffold is the following:
- Quickly create the infrastructure project structure;
- Provide specifications and conventions for project development;
- Customize different functions according to actual project requirements to improve our efficiency.