preface

In actual development, building the structure of a project from scratch can be a headache, so a variety of scaffolding tools were created. Yoeman, Express-Generator, and VUe-CLI are the ones I use most. They are rich in functions, but the most core function is to quickly build a complete project structure, developers only need to develop on the basis of the generated project structure, very simple and efficient.

As a non-fidgeting Death Star person, I started thinking about how they worked after I got familiar with them. After poring over the documentation and source code, I finally figured out its core principles and built a scaffolding called SCION based on that principle.

Now let’s take SCION as an example and build our own scaffolding tool from scratch.

Core principles

The YoEMAN construction project requires a Yoeman-Generator. Yoeman-generator is essentially a template of a project with a complete file structure. Users need to manually download these generators locally, and Yoeman automatically generates various projects based on these generators.

Vue-cli provides quite a wealth of options and Settings, but it’s also about pulling different templates locally from a remote repository, rather than being a “locally generated” hack.

The idea is to first create different sample projects, and then the scaffolding will reference the sample projects to generate the actual projects according to the user’s instructions. Sample projects can be built into scaffolding or deployed in remote warehouses. SCION takes the second approach for a wider range of applications.

Technology selection

  • Node.js: A fundamental part of the entire scaffolding tool, and the latest version is recommended.

  • Es6: The latest version of Node. js supports ES6, which greatly improves development efficiency and experience.

  • Commander: A tool developed by TJ to better organize and process command line input.

  • Co: TJ developed asynchronous flow control tool, with a more comfortable way to write asynchronous code.

  • Co-prompt: Or TJ’s work… In the traditional command line, you can only enter all parameters and options in a single line at a time. Using this tool, you can automatically provide prompt information and receive input step by step, similar to the process of entering parameters step by step in NPM Init.

The overall architecture

International convention, before starting to develop to understand the overall structure, look at the picture:

First understand the concept of a template. A template is a template for a project, containing the complete structure and information of the project. The template information is stored in a file called templates.json. You can add, delete, and list templates.json from the command line. By selecting different templates, SCION will automatically pull the corresponding templates from the remote repository to the local site to complete the project construction.

The final file structure of the entire scaffold is as follows:

=================
  |__ bin
    |__ scion
  |__ command
    |__ add.js
    |__ delete.js
    |__ init.js
    |__ list.js
  |__ node_modules
  |__ package.json
  |__ templates.json
Copy the code

Entrance to the file

First create the project, write the dependencies in package.json and execute NPM install:

"dependencies": {
    "chalk": "^ 1.1.3." "."co": "^ 4.6.0"."co-prompt": "^ 1.0.0"."commander": "^ 2.9.0"
  }Copy the code

Create the \bin folder in the root directory and create a scion file with no suffix in it. The bin\scion file is the entry file to the scaffold, so we write it first.

First, some initialization code:

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

const program = require('commander')

 // Define the current version
program
    .version(require('.. /package').version )

// Define the usage method
program
    .usage('<command>')Copy the code

As you can see from the previous architecture diagram, scaffolding supports user input of four different commands. Now let’s write how to handle these four commands:

program
    .command('add')
    .description('Add a new template')
  .alias('a')
  .action((a)= > {
    require('.. /command/add')()
  })

program
    .command('list')
    .description('List all the templates')
    .alias('l')
    .action((a)= > {
        require('.. /command/list')()
    })

program
    .command('init')
    .description('Generate a new project')
  .alias('i')
  .action((a)= > {
    require('.. /command/init')()
  })

program
    .command('delete')
    .description('Delete a template')
    .alias('d')
    .action((a)= > {
        require('.. /command/delete')()
    })Copy the code

The specific usage of COMMANDER is not explained here. You can go to the official website for detailed documentation. Finally, don’t forget to handle parameters and provide help information:

program.parse(process.argv)

if(! program.args.length){ program.help() }Copy the code

See the complete code here. Run the file using Node and see the following output to prove that the entry file has been written.

Usage: scion <command>


  Commands:

    add|a      Add a new template
    list|l     List all the templates
    init|i     Generate a new project
    delete|d   Delete a template

  Options:

    -h, --help output usage information
    -V, --version output the version numberCopy the code

Processing user input

Create a \command folder at the root of your project to store command processing files. Templates. Json file in the root directory and write the following to hold template information:

{"tpl": {}}Copy the code

Add the template

Go to \command and create a new add.js file:

'use strict'
const co = require('co')
const prompt = require('co-prompt')
const config = require('.. /templates')
const chalk = require('chalk')
const fs = require('fs')

module.exports = (a)= > {
 co(function* () {

   // Accept parameters entered by the user step by step
   let tplName = yield prompt('Template name: ')
   let gitUrl = yield prompt('Git https link: ')
   let branch = yield prompt('Branch: ')
    
   // Avoid repeated additions
   if(! config.tpl[tplName]) { config.tpl[tplName] = {} config.tpl[tplName]['url'] = gitUrl.replace(/[\u0000-\u0019]/g.' ') // Filter Unicode characters
     config.tpl[tplName]['branch'] = branch
   } else {
     console.log(chalk.red('Template has already existed! '))
     process.exit()
   }
   
   // Write the template information to templates.json
   fs.writeFile(__dirname + '/.. /templates.json'.JSON.stringify(config), 'utf-8', (err) => {
     if (err) console.log(err)
     console.log(chalk.green('New template added! \n'))
     console.log(chalk.grey('The last template list is: \n'))
     console.log(config)
     console.log('\n')
     process.exit()
    })
 })
}Copy the code

To delete a template

Similarly, create the delete.js file in the \command folder:

'use strict'
const co = require('co')
const prompt = require('co-prompt')
const config = require('.. /templates')
const chalk = require('chalk')
const fs = require('fs')

module.exports = (a)= > {
    co(function* () {
        // Accepts parameters entered by the user
        let tplName = yield prompt('Template name: ')

        // Delete the corresponding template
        if (config.tpl[tplName]) {
            config.tpl[tplName] = undefined
        } else {
            console.log(chalk.red('Template does not exist! '))
            process.exit()
        }
        
        / / into the template. The json
        fs.writeFile(__dirname + '/.. /templates.json'.JSON.stringify(config),     'utf-8', (err) => {
            if (err) console.log(err)
            console.log(chalk.green('Template deleted! '))
            console.log(chalk.grey('The last template list is: \n'))
            console.log(config)
            console.log('\n')
            process.exit()
        })
    })
}Copy the code

List template

Create list.js file:

'use strict'
const config = require('.. /templates')

module.exports = (a)= > {
     console.log(config.tpl)
     process.exit()
}Copy the code

Build the project

Now comes the most important part — building the project. Also, create a new file called init.js in the \command directory:

'use strict'
const exec = require('child_process').exec
const co = require('co')
const prompt = require('co-prompt')
const config = require('.. /templates')
const chalk = require('chalk')

module.exports = (a)= > {
 co(function* () {
    // Process user input
      let tplName = yield prompt('Template name: ')
      let projectName = yield prompt('Project name: ')
      let gitUrl
      let branch

    if(! config.tpl[tplName]) {console.log(chalk.red('\n × Template does not exit! '))
        process.exit()
    }
    gitUrl = config.tpl[tplName].url
    branch = config.tpl[tplName].branch

    // git command to remotely pull the project and customize the project name
    let cmdStr = `git clone ${gitUrl} ${projectName} && cd ${projectName} && git checkout ${branch}`

    console.log(chalk.white('\n Start generating... '))

    exec(cmdStr, (error, stdout, stderr) => {
      if (error) {
        console.log(error)
        process.exit()
      }
      console.log(chalk.green(Generation '\ n) completed! '))
      console.log(`\n cd ${projectName} && npm install \n`)
      process.exit()
    })
  })
}Copy the code

As you can see, this part of the code is also very simple, and the key sentence is

let cmdStr = `git clone ${gitUrl} ${projectName} && cd ${projectName} && git checkout ${branch}`Copy the code

What it does is clone from the remote repository to the custom directory and switch to the corresponding branch. Git command: git command: git command: git command: git command: Git command: Git command

The global

To be used globally, we need to set this in package.json:

"bin": {
    "scion": "bin/scion"
  },Copy the code

Local debugging is performed in the root directory

npm linkCopy the code

You can bind the scion command globally, so you can start the command with scion instead of typing a long Node scion command.

Now that our scaffolding tools are set up, let’s give it a try!

Use the test

  • The add | add a template command

  • Init | I generating project command

  • The delete | d | l delete template command and list are listed template command

And you’re done! Now that the scaffolding tool is complete, all you need to know is the git HTTPS address and branch of the template to continue to add to SCION. For teamwork, just share SCION templates. Json files.