I remember at the beginning of learning vue, directly import vue. Js file to learn, then use VUE-CLI, has been using, recently suddenly remembered how vue-CLI is implemented, so I went to see the scaffolding construction

Now, the way I know how to build,

  1. Put the template in the Github repository and run commands to pull code from Github locally
  2. Using Yeoman-generator, place the template in the generator and use yo to pull the template from the generator locally

So simple, just put the code in a certain place, when you need to pull the code to a local, but in addition to pull the related file to local, used the scaffold students must know, at the time of initialization, scaffolding will put forward some problems, according to different input we choose, the final configuration is different also, Git clone and Yo <name> can be used to scaffold scaffold

I wrote two simple scaffolding, SCav-cli and generator-wxfile respectively, corresponding to the above two methods. Since I started writing generator just to write a generator, I wrote the name like that directly, and did not change it temporarily. The following content is the original version. I have updated both of them later, so when downloading and using, I still directly read the corresponding documents I wrote. Scav-cli is to pull the vUe-related templates to the local using Git Clone

Generator-wxfile takes the files from the template to the currently created project directory using the yo command

scav-cli


I’m going to use a couple of NPM packages here

Inquirer: used to ask questions and get answers from the user chalk: changes the style of the content printed on the command line. Child_process: used to execute commands on the command line. Commander: used to define your own commands

Initialize scaffolding

Here you first initialize your own scaffolding

mkdir scav-cli
cd scav-cli
npm init
Copy the code

After initialization, write the corresponding development dependencies in package.json

"Dependencies" : {" chalk ", "^ 3.0.0", "child_process" : "^ 1.0.2", "commander" : "^ 4.4.1", "the inquirer" : "^ 7.0.4"},Copy the code
npm i
Copy the code

Download the relevant NPM packages to node_modules, and then start scaffolding, creating scav.js files in the bin folder to define the relevant commands

Define related commands

The first step is to define the scaffolding file path, introduce the related dependency packages, and define the scaffolding version

#! /usr/bin/env node --harmony
'use strict'
// Define the scaffold file path, __dirname is the current file path
process.env.NODE_PATH = __dirname + '/.. /node_modules/'

const program = require('commander')

// Get version from package.json as the project version number
program.version(require('.. /package').version)
// Define the use of scaffolding, as shown in the program.help method
program.usage('<command>')
Copy the code

Define initialization commands for scaffolding

/* command indicates the command to be executed. Description indicates the description of the command. Alias indicates the action of the command
program
    .command('init')
    .description('init a vue-based project')
    .alias('i')
    .action((a)= >{
        console.log('I'm the initialization method')})// program.parse(arguments) handles arguments and unused options are stored in the program.args array
program.parse(process.argv)
Copy the code

Execute the following command in the root directory

node bin/scav init
Copy the code

If the following information is displayed, the configuration is successful

"bin": {
    "scav": "bin/scav.js"
},
Copy the code

Then use the NPM link to go global and use scAV init directly on the command line, as shown

// If an option is placed in program.args, that is, not processed by program.parse, the default is to use program.help() to print out commands that the NPM package can execute
// Help can be customized with program.on('--help', function(){}
if (program.args.length) {
    program.help()
}
Copy the code

We can print out program.args before we decide

console.log('program.args: ', program.args);
Copy the code

Write specific operations

To define the initialization methods, create a command folder in the root directory to store the command operations and create init.js to write the initialization operations

// init.js
const inquirer = require('inquirer')
const chalk =require('chalk')
const {exec} = require('child_process')
chalk.level = 3 // Set chalk level to 3

module.exports = (a)= >{
    console.log(chalk.green('Start initializing file'))
    console.log(chalk.gray('Initializing... '))
    console.log(chalk.green('Initialization completed'))}Copy the code

Call the init.js file in the init.js file

program
    .command('init')
    .description('init a vue-based project')
    .alias('i')
    .action((a)= >{
        require('.. /command/init.js')()
    })
Copy the code

Try the initialization command again, as shown in the figure

module.exports = (a)= >{
    console.log(chalk.green('Start initializing file'))
    inquirer.prompt([{
        type:'input'.// The question type is fill-in-the-blank
        message:'your projectName:'.// Problem description
        name:'projectName'.// The attribute corresponding to the problem
        validate:(val) = >{ // Determine the input value
            if(val==="") {return chalk.red('Project name cannot be empty, please re-enter')}return true
        }
    }]).then(answer= >{
        console.log(chalk.gray('Initializing... '))
        console.log(chalk.green('Initialization completed'))})}Copy the code

The effect is shown in figure

module.exports = (a)= >{
    inquirer.prompt([{
        type:'input'.// The question type is fill-in-the-blank
        message:'your projectName:'.// Problem description
        name:'projectName'.The user input is stored in the property corresponding to the first parameter in the then method
        validate:(val) = >{ // Determine the input value
            if(val==="") {return chalk.red('Project name cannot be empty, please re-enter')}return true
        }
    }]).then(answer= >{ // Perform various operations through user input
        console.log(chalk.green('Start initializing file \n'))
        console.log(chalk.gray('Initializing... '))
        const gitUrl = 'https://github.com/QZEming/vue-temp.git'
        exec(`git clone ${gitUrl} ${answer.projectName}`,(error,stdout,stderr)=>{
            if (error) { // Print an error if there is an error and exit the operation
                console.log(chalk.red(error))
                process.exit()
            }
            console.log(chalk.green('Initialization completed'))
            process.exit() Exit the command line operation})})}Copy the code

The configuration is successful as shown in the figure

Modify the generated file

Json, the name is vue-temp, which is not what we expected. So we need to introduce a FS module to read and write the package.json. To get the path to the current command line, use process.cwd()

const fs = require('fs')
module.exports = (a)= >{
    inquirer.prompt([{
        type:'input'.// The question type is fill-in-the-blank
        message:'your projectName:'.// Problem description
        name:'projectName'.The user input is stored in the property corresponding to the first parameter in the then method
        validate:(val) = >{ // Determine the input value
            if(val==="") {return chalk.red('Project name cannot be empty, please re-enter')}return true
        }
    }]).then(answer= >{ // Perform various operations through user input
        console.log(chalk.green('Start initializing file \n'))
        console.log(chalk.gray('Initializing... '))
        const gitUrl = 'https://github.com/QZEming/vue-temp.git'
        exec(`git clone ${gitUrl} ${answer.projectName}`,(error,stdout,stderr)=>{ // Clone the template and go to the project root directory
            if (error) { // Print an error if there is an error and exit the operation
                console.log(chalk.red('Failed to copy file'))
                process.exit()
            }
            fs.readFile(`${process.cwd()}/${answer.projectName}/package.json`,(err,data)=>{
                if(error){
                    console.log(chalk.red('File reading failed'))
                    process.exit()
                }
                data= JSON.parse(data.toString())
                data.name = answer.projectName
                fs.writeFile(`${process.cwd()}/${answer.projectName}/package.json`.JSON.stringify(data,""."\t"),err=>{
                    if(err){
                        console.log(chalk.red('Failed to write file'))
                        process.exit()
                    }
                    console.log(chalk.green('Initialization completed'))
                    process.exit() Exit the command line operation})})})})}Copy the code

When we open package.json, we find that it is the same as the project name we entered

I have published the scaffolding to NPM, the relevant code on Github, can also be NPM I scav-cli -g try related content

generator-wxfile


The basic function

In this case, I used Yeoman to generate duplicate files, and in this case I used Plop to do this, and I used Yeoman to pull the original files locally

  1. Ask the user to build the project name, which is the folder name by default, and write it to the project.config.json file
  2. Ask the user for the APPID used in the build, which is simply written to the project.config.json file
  3. Ask the user for the file name of the default page when building the project, build the corresponding folder and file, and write the corresponding route in app.json
  4. Ask the user if plop is enabled to quickly build duplicate files, and if so add plopfile.js and template files to the project
  5. Use the plop tool to write duplicate files to the Pages folder, and write corresponding routes to app.json

File directory

The basic file directory is the same as that in the tempPage file, except that the syntax of the replacement text is different. The replacement text in the tempPage file is written using Yeoman, so it uses ejS syntax, <%= prop%> syntax. The files in the Plop-temp folder are templates used when the plop tool is used, using {{prop}} syntax instead

github

Yeoman implements the initialization process


Asks the user

As mentioned above, I want to implement the method of asking the user. The NPM package inquire can be used here, but since yeoman already has this functionality, I will use the Prompt method of yeoman-generator directly

The yeoman-generator package is first introduced, and a class is exported, where the this.prompt method is called in the only insurgent method, with the following code

const Generator = require("yeoman-generator")

module.exports = class extends Generator{
    prompting(){
        return this.prompt([{ // Ask the user for the name of the project to create
            type:"input".name:"projectName".message:"your project name is".default:this.appname // The name of the folder where the project is located}, {// Ask the user what the appID is
            type:"input".name:"appID".message:"your appID is"}, {// Ask the user what is the first page name to initialize
            type:"input".name:"pageName".message:"the initialized page name is".default:"index" // The index page is created by default}, {// Ask the user whether to use the plop tool
            type:"confirm".name:"isPlop".message:"do you use plop".default:true // By default
        }]).then(answer= >{
            this.answer = answer // Place the answer in the answer property}}})Copy the code

Writes files based on user input

Above we get the user’s input into this.answer. In writing we use these answers to change the name of the corresponding page to be written. The pageName obtained above is the name of the folder in the page that we initialized. And the names of js, JSON, WXSS and WXML files under the folder, and according to the isPlop obtained above to determine whether to add the contents of the plopfile.js folder and plopfile.js file to the project, because it involves the creation of folder, so the FS module is introduced here, the code is as follows

const Generator = require("yeoman-generator")
const fs = require("fs")

module.exports = class extends Generator{
    prompting(){
        // ...
    }
    writing(){
        const answer = this.answer
        const pageName = answer.pageName
        // Process the page template file
        let tempPageFiles = ["tempPage.js"."tempPage.json"."tempPage.wxml"."tempPage.wxss"]
            .map(path= >"tempPage/"+path)
        // List of other files
        let tempOtherFiles = ["app.js"."app.json"."app.wxss"."project.config.json"."sitemap.json"."package.json"]
        // Merge all template files
        let tempFiles = [...tempPageFiles,...tempOtherFiles]
        // Process the page output file
        let outputPageFile = [`${pageName}.js`.`${pageName}.json`.`${pageName}.wxml`.`${pageName}.wxss`]
            .map(path= >`pages/${pageName}/${path}`)
        // Merge all output files
        let outputFiles = [...outputPageFile,...tempOtherFiles]
        if(answer.isPlop){ // If you use the plop tool, write the corresponding file
            tempFiles=[...tempFiles,...["plop-temp"."plopfile.js"]]
            outputFiles=[...outputFiles,...["plop-temp"."plopfile.js"]]}// Create a folder and call the callback function to write the file after the folder is created
        fs.mkdir(`pages/${pageName}`.'1', () = > {// File write
            for(let i=0; i<tempFiles.length; i++){this.fs.copyTpl(this.templatePath(tempFiles[i]),this.destinationPath(outputFiles[i]),answer) 
            }
        }) 
    }
}
Copy the code

The file that needs to be replaced with text

Plop writes repeated files


The preparation of the plopfile. Js

The plop tool asks the user for the name of the page to be added, creates a file based on that name, and writes the route to package.json. Since plop doesn’t have the ability to read and write files, I’ve introduced the FS module. Use plop.setActionType to implement a new type. The full action code for using this type in setGenerator actions is shown below

const fs = require('fs')

module.exports = plop= >{
    plop.setActionType('changeRouter',(answers,config,plop)=>{
        fs.readFile('app.json',{},(err,data)=>{ // Read the app.json file
            let d= JSON.parse(data.toString())
            d.pages.push(`pages/${answers.pageName}/${answers.pageName}`) // Write the current addition to app.json
            d = JSON.stringify(d,""."\t")
            fs.writeFile('app.json',d,err=>{
                if(err)
                    throw err
            })
        })
    })
    plop.setGenerator('wxfile', {// Wxfile is a self-defined name that is used on the command line
        description:'create the repeat wxfile'.// Here is the function description of this plop
        prompts:[{
            type:'input'.// The type of problem
            name:'pageName'.// The question corresponds to the variable name of the answer, which can be used in actions
            message:'your pageName is'.// A problem at the command line
            default:'page' // The default answer to the question}].actions: [{type:'add'.// Operation type, here is add file
            path:'pages/{{pageName}}/{{pageName}}.json'.// The path to the added file
            templateFile:'plop-temp/tempPage.json' // Path to the template file}, {type:'add'.path:'pages/{{pageName}}/{{pageName}}.js'.templateFile:'plop-temp/tempPage.js'}, {type:'add'.path:'pages/{{pageName}}/{{pageName}}.wxss'.templateFile:'plop-temp/tempPage.wxss'}, {type:'add'.path:'pages/{{pageName}}/{{pageName}}.wxml'.templateFile:'plop-temp/tempPage.wxml'}, {// Modify the route in app.json
            type:'changeRouter'})}}]Copy the code

The file that needs to be replaced with text

Method of use

If yo is not available locally, install Yo globally

npm i yo
Copy the code

So let’s do that

yo wxfile
Copy the code

If you want to use the plop tool, if you don’t have a ploP locally, you can just

npm i
Copy the code

You can put ploP into the project as a development dependency and execute it

plop wxfile
Copy the code

You can use the corresponding function