preface
I remember when I first touched scaffolding, I thought it was a magical thing. No complex configuration, no need to look up documents, take @vue/ CLI as an example, directly line vue create my-project command, press Enter, OK. Then there is the pleasant wait time, watching the project quickly generate automatically, not a bit faster than the previous manual configuration item by item. This is simply a tool to liberate productivity. There are many configurations and structures that can be reused from project to project, and project types are limited and can be enumerated, so you can quickly start a new project with a corresponding template.
Analysis of the
At present, the most widely used and least coupled idea is the separation of scaffold tools and template projects, that is, the template project is put in a remote warehouse or somewhere, scaffold tools will pull the template to the local, and then generate the corresponding project according to the relevant configuration commands. It was explained in one sentence. It didn’t seem difficult. While this article will focus on scaffolding tools, templates are another topic that will be covered in the next article, Re From Scratch Routing Module writing. Let’s look at the implementation details of this tool.
Installation and use
When we finally use it, we should be able to use it globally on the command line like @vue/cli. This brings us to the bin field in the NPM configuration file package.js.
MyCli is the custom command name, and './bin/ mycli.js' is the boot file
{
"bin": {
"myCli": "./bin/myCli.js"
}
}
Copy the code
This field is parsed by NPM during NPM install
- If it is a global installation, except to install the package in the NPM installation directory
node_modules
In addition, a soft chain is created under the bin file in the NPM installation directorynode_modules/my-cli/bin/myCli.js
At this point inPATH
Will also add related path mapping, in usemyCli create myProject
Is equivalent to executingNode/NPM Installation directory /bin/myCli create myProject
.
- If it is not installed globally, it is in the project
node_modules/.bin
Create soft chain, chain tonode_modules/my-cli/bin/myCli.js
In thenpm run
, the related path mapping is added toPATH
In the.
This makes it possible to use the global command on the command line
The project structure
Let’s start with the project structure
The structure is not complicated
sc.js
This is the boot file described abovecli.js
Distribute the matched commands to different processing files (cmd
A bunch of add, delete, change, check handlers in the directory)format.js
Tool functiontemplates.json
Save configuration records
Receive command input
There are several function points that need to be addressed in this step
- The ability to enter commands directly on the command line
- You need to receive input from the command line
- The input process is asynchronous. You need to wait for all configuration commands to be received before processing them
- Handle errors that may occur, such as input errors, network errors, etc
On second thought, fetching commands from the command line and stepping through input may not seem so easy to accomplish, but it’s not a problem.
prompts
Standing on the shoulders of giants, before completing the first three steps, introduce a key plug-in. Prompts the prompt package is a good encapsulation of command-line processing. It is simple to configure and can be used in steps.
// The parameters can be obtained by simple configuration
(async() = > {
const preOption = [{
type: 'text'.
name: 'project'.
message: 'Project name? '.
validate: value= > value ? true : 'Please enter content'
}, {
type: 'text'.
name: 'url'.
message: 'Project warehouse address? '.
validate: value= > value ? true : 'Please enter content'
}]
const preResponse = await prompts(preOption);
}) ()
Copy the code
// Can also set command prompt message
program
.version(packageInfo.version)
program
.command('init')
.description('Initialize a project')
.alias('i')
.action((a)= > {
require('./cmd/init').init();
});
program
.command('add')
.description('New Project')
.alias('a')
.action((a)= > {
require('./cmd/add').add();
});
Copy the code
Processing relevant input
The core function here is to automatically initialize the project, in addition to the project list and configuration of the add, delete, change, check, after all, we have more than one template, need to maintain a project list. Let’s take a look at some core pieces of code.
Initialize a project
(async() = > {
const preOption = [{
type: 'text'.
name: 'project'.
message: 'Project name? '.
validate: value= > value ? true : 'Please enter content'
}]
format.table(templates)
const preResponse = await prompts(preOption);
if (!Object.keys(preResponse).length) {
console.log(chalk.yellow('Program interrupt'))
process.exit();
}
const {
project
} = preResponse
if(! templates[project] || ! project) {
console.log(chalk.red('Template name not empty or template does not exist'))
process.exit()
}
const {
url,
branch
} = templates[project]
spinner.start('Initializing project');
exec(`git clone ${url} ${project} && cd ${project} && git checkout ${branch}`, (err) => {
// Delete git files
exec('cd ' + project + ' && rm -rf .git', (err, out) => {
spinner.succeed('Template pulled successfully')
process.exit()
});
});
}) ()
Copy the code
Add a project record
(async() = > {
const option = [{
type: 'text'.
name: 'project'.
message: 'Project name? '.
validate: value= > value ? true : 'Please enter content'
}, {
type: 'text'.
name: 'url'.
message: 'Project warehouse address? '.
validate: value= > value ? true : 'Please enter content'
}, {
type: 'text'.
name: 'branch'.
message: 'Project branch? '.
initial: 'master'.
validate: value= > value ? true : 'Please enter content'
}, {
type: 'text'.
name: 'des'.
message: 'Project description? '.
validate: value= > value ? true : 'Please enter content'
}]
const response = await prompts(option);
if (!Object.keys(response).length) {
console.log(chalk.yellow('Program interrupt'))
process.exit();
}
const {
project
} = response
if(templates[project] || ! project) {
console.log(chalk.red('Template name not empty or template already exists'))
process.exit()
}
templates[project] = response
fs.writeFile(__dirname + '/.. /.. /templates.json'.JSON.stringify(templates), 'utf-8', (err) => {
console.log(chalk.green('Added successfully'))
process.exit();
});
}) ();
Copy the code
Deletes a project record
(async() = > {
const option = [{
type: 'text'.
name: 'project'.
message: 'Project name? '.
validate: value= > value ? true : 'Please enter content'
}]
const response = await prompts(option);
if (!Object.keys(response).length) {
console.log(chalk.yellow('Program interrupt'))
process.exit();
}
const {
project
} = response
if(! templates[project] || ! project) {
console.log(chalk.red('Template name not empty or template does not exist'))
process.exit()
}
if (project in templates) {
delete templates[project]
fs.writeFile(__dirname + '/.. /.. /templates.json'.JSON.stringify(templates), 'utf-8', (err) => {
console.log(chalk.green('Deleted successfully'))
format.table(templates)
process.exit();
});
} else {
console.log(chalk.red('This template is not available'))
process.exit();
}
}) ()
Copy the code
For example, the update function is similar to the above, and some details can be viewed in the complete code. Of course, there’s a lot of functionality left unimplemented, but this is just a quick version to help you understand how scaffolding tools like @vue/ CLI work.
Publish the project
Publish on NPM in 2020. Publish on NPM. See Re’s Component Library Building and Publishing Process from Scratch for details.
The complete code
Warehouse address https://github.com/GoldWorker/slucky-cli
The end of the
In fact, such a tool is very simple, the implementation of the loan prompts package process bypassing the relatively tedious processing of the command line. Of course, if you want the functionality of the real @vue/ CLI, you just need to continue to add related commands and procedures.
Or something that interests you
Re from scratch series
- Re From Scratch Component Library Building and Publishing Process
- Re from scratch UI library authoring life specification
- Re from scratch UI library writing buttons for Life
- Re writing forms for Life from scratch UI Library
- Re writing life’s Table Components from scratch UI library
- Re writing life from scratch UI Library – Step Management Component Steps
- Re from scratch UI library writing life-tree components
- Re From Scratch backend learning configuration Ubuntu+Ngnix+Nodejs+Mysql environment
- Configuring LAMP environments for Re From Scratch Back-end Learning
Interesting tools
- 200 lines of code to create a useful custom gadget for your holiday avatar.
Web Security Series
- “Web Attack and Defense Warrior Directory -XSS&CSRF”