What happens when you run a terminal command to create a scaffold?
Scaffolding, of course, uses a lot of command line knowledge. This article will introduce you to a simple VUE scaffolding from scratch to the whole process, I hope you have a harvest.
Project directory
| ─ ─ bin | └ ─ ─ the try. Js | ─ ─ the config | └ ─ ─ config. Js | ─ ─ lib | ├ ─ ─ actions. Js | ├ ─ ─ the create. Js | └ ─ ─ otherCommand. Js | ─ ─ utils | └ ─ ─ terminal. Js | ─ ─ templates | └ ─ ─ component. The ejsCopy the code
Use the library
Command line control tool -- commander Download and clone tool -- download-git-repo command line interaction tool -- Inquirer file operation -- fs-extra address operation -- pathCopy the code
Directly to fit
1. Create a project
npm init -y // Initialize package.json
Copy the code
2. Create the execution file and configure the directory
2.1 Create the /bin folder and configure the entry file of the command: /bin/try.js
The first line of the file must read:
#! /usr/bin/env node
Copy the code
#! Called shebang, this line is added to specify that the script file is executed as node.
2.2 Configure the bin field in package.json
We named our first scaffold tool: just-cli, and the value corresponding to the key name refers to the address of the command line execution entry file.
{
"bin": {
"just-cli": "./bin/try.js"}},Copy the code
3 Link commands globally
Run ** NPM link ** on the terminal to register the configured commands globally.
npm link // Unlink: NPM unlink
Copy the code
When Windows users go to the NPM installation directory, they will find that we have these files:
- just-cli
- just-cli.cmd
- just-cli.ps1
PS: MAC users can check /usr/local/lib/node_modules.
4 Configure the version number
4.1 First install the commander package
npm install commander --save
Copy the code
4.2 Configuring the version: just-cli -v & just-cli -v
#! /usr/bin/env node
/* * file: /bin/try.js */
/ / into the commander
const program = require('commander')
// Set the version number
program.version(`Version is The ${require('.. /package.json').version}`.'-v, --version')// Support lowercase: '-v'
.description('Write a scaffold by hand')
.usage('<command> [options]')
// Parses arguments, usually after the definition command
program.parse(process.argv);
Copy the code
At this point, you have configured the version information for just-cli, which is dynamically obtained from the scaffold project package.json and supports the following two case-insensitive ways to view the version information.
just-cli -v
just-cli -V
Copy the code
Try printing your own version of the scaffolding!
5 First command
Define a command of the form: create name [options] that is used to initialize the scaffold.
5.1 Defining Commands
First we define the command, configure the command to take an argument: name, add a description of the command, give it the ability of -f, and specify the execute function: createAction
/* * File: /lib/create.js * Command: scaffolding * create
*/
const program = require('commander')
const {createAction} = require('./actions')
const create = function(){
program.command('create <name>')
.description('create a new project')
.option('-f, --force'.'title to use before name')
.action(createAction)
}
module.exports =create
Copy the code
Then import the above configuration into the command entry file.
/* * file: /bin/try.js */
/ /...
// Import the split configuration file
const createConfig = require('.. /lib/create')
// Add a split configuration: create
createConfig()
/ /...
Copy the code
5.2 Defining the Execution content of the command
/* * File: /lib/actions.js * Purpose: Define the execution of the command */
const open = require('open')
const { promisify } = require('util')
const download = promisify(require('download-git-repo'))
const { labAdress } = require('.. /config/lab-config')
const { spawnCommand } = require('.. /utils/terminal')
const createAction = async (project,options) => {
console.log(`creating a project:${project},\noptions:\nThe ${JSON.stringify(options)}`)
// Clone the template
console.log(`downloading the templates documents`)
await download(labAdress, project, { clone: true })
// Run the NPM install command
// Check the type of the command in the operating system, and Windows will return 'win32' regardless of its own operating system 32-bit / 64-bit
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'
console.log(`spawn npm install:`)
await spawnCommand(npm, ['install'] and {cwd: `. /${project}` })
// Run the NPM run dev command synchronously
// The reason for synchronous calls: the thread blocks subsequent code execution after the project runs
console.log(`spawn npm run dev`)
spawnCommand(npm, ['run'.'dev'] and {cwd: `. /${project}` })
// // Open the browser synchronously
open('http://localhost:8080');
}
module.exports = {
createAction
}
Copy the code
See what needs to be understood in particular:
- The /config/config.js file stores the download address of the template. Global variables are stored in this file.
- The promisify method provides a way to turn synchronous methods into asynchronous methods, avoiding callback hell.
- Opening a browser is not usually configured in a scaffold, but is used here to visualize the entire scaffold creation process. It is usually specified in the Webpack of the template to open the page for the corresponding port after the packaging is complete.
- SpawnCommand is the encapsulated child_process. Spwan method. The encapsulated content is as follows: In fact, both the exec and spawn methods of child_process can execute terminal commands. Child_process. spawn is suitable for scenarios where there is no limit to the amount of data returned.
/* Run the terminal command */
const { spawn } = require('child_process')
// Args parameters include command, args, and options
const spawnCommand = (. args) = > {
return new Promise((resolve, reject) = > {
constspawnProcess = spawn(... args)// The copy console prints to the main process
spawnProcess.stdout.pipe(process.stdout)
spawnProcess.stderr.pipe(process.stderr)
// The shutdown of the listener process is triggered upon completion or error
spawnProcess.on('close'.() = > {
resolve()
})
})
}
module.exports = {
spawnCommand
}
Copy the code
6 Design interactive logic
In the previous section, we implemented the use of the command line to create the scaffold template. Next, we need to optimize the process of creating the scaffold by adding the necessary interactive actions and logical judgments to the execution method of the command. The inquirer tool is introduced to solve the problem of command line interaction, interaction and logical judgment code is as follows.
/* * File: /lib/actions.js * Purpose: Define the execution of the command */
const open = require('open')
const path = require('path')
const fsextra = require('fs-extra')
const Inquirer = require('inquirer')
const { promisify } = require('util')
const download = promisify(require('download-git-repo'))
const { labAdress } = require('.. /config/lab-config')
const { spawnCommand } = require('.. /utils/terminal')
const { delDir } = require('.. /utils/common')
async function createAction(projectName, options) {
console.log(`${projectName},\noptions:\nThe ${JSON.stringify(options)}`)
const cwd = process.cwd();// Obtain the working directory where the current command is executed
const targetDir = path.join(cwd, projectName);// The target directory
console.log(targetDir)
if (fsextra.existsSync(targetDir)) {
if (options.force) {
delDir(targetDir);
console.log('Deletion of original directory succeeded')
create(projectName)
} else {
let { action } = await Inquirer.prompt([
{
name: 'action'.type: 'list'.message: 'Destination folder already exists, please choose whether to overwrite :'.choices: [{name: 'cover'.value: true },
{ name: 'cancel'.value: false}}]])if(! action) {console.log('Cancel operation')
return
} else {
console.log('\r\n is deleting the original directory.... `);
delDir(targetDir)
console.log('Deletion succeeded')
create(projectName)
}
}
} else {
create(projectName)
}
}
const create = async (projectName) => {
// See section 5.2
}
module.exports = {
createAction
}
Copy the code
DelDir is a recursive method that iterates through files and deletes folders, defined as follows:
// Introduce the fs module
const fs = require('fs-extra');
function delDir(path) {
// Read all files and folders in the folder
const list = fs.readdirSync(path)
list.forEach((item) = > {
// Splice paths
const url = path + '/' + item
// Read the file information
const stats = fs.statSync(url)
// Determine whether it is a file or a folder
if (stats.isFile()) {
// If it is a file, delete the file
fs.unlinkSync(url)
} else {
// If the current folder is a folder, then call itself recursively
arguments.callee(url)
}
})
// Delete the empty folder
fs.rmdirSync(path)
}
module.exports={
delDir
}
Copy the code
7 Other functions of scaffolding
In addition to creating templates, scaffolding should have other instructions that make it easier to work. Here’s how to use the scaffolding command to add a new component. We define a form like:
just-cli component <componentName> -d <destination>
Copy the code
-d and destination are optional. We need the following four steps to execute this command.
- Template file
- Interactive judgment: whether to exist/whether to override/address parameters, etc
- Pass in the parameters and compile the template
- Generates a.vue file for the specified location/default location
Do it now! First we define the command, configure the -f and -d capabilities, and take the dest argument to specify the location of the file
/* * file: /lib/newComponent.js * Define command: add page * page
*/
const program = require('commander')
const { addPageAction } = require('./actions')
const page = function () {
program.command('page <name>')
.description('add a new page')
.option('-f, --force'.'spwan with force')
.option('-d, --dest <dest>'.'Specify a storage address, for example :just-cli component
-d /studio/task'
)
.action(addPageAction)
}
module.exports = page
Copy the code
7.1 Template Files
Use the EJS template to implement the template parameter placeholder, a default. Vue file template as follows:
<template>
<div class="<%= data.lowerName %>">{{msg}}
<h1>{{message}}</h1>
</div>
</template>
<script>
export default {
name: '<%= data.name %>'.props: {
msg: String
},
components: {},
mixins: [].data() {
return {
message: "<%= data.name %>"}},create(){},mounted(){},computed: {}}</script>
<style>
.<%=data.lowerName %> {}
</style>
Copy the code
Note: Template strings can also do this.
7.2 Interactive Judgment
Omitted, refer to section 6
7.3 Compiling templates
The template compiler functions are encapsulated as follows:
const ejs = require('ejs')
const path = require('path');
const compileEjs = (templateName, data) = > {
const templatePosition = `.. /templates/${templateName}`
const templatePath = path.join(__dirname, templatePosition)
return new Promise((resolve, reject) = > {
ejs.renderFile(templatePath, { data }, {}, (err, res) = > {
if (err) {
console.log(err);
reject(err)
return
}
resolve(res)
})
})
}
Copy the code
The data parameter contains the parameter to be passed to the template, such as name.
7.4 Generating a. Vue file in the specified location/default location
The file write function is encapsulated as follows:
const fs = require('fs-extra');
const writeToFile = (path, templateContent) = > {
return fs.promises.writeFile(path, templateContent)
}
Copy the code
After completing these four steps in sequence, a command to add a new component is complete!
————————————————————
Learn here, friends have been able to use the common project configuration by uploading the code warehouse, running scaffolding to generate the project scaffolding template, but this is just a little scaffolding, scaffolding customization is far more than these content, please open your imagination, begin to customize your own scaffolding!