Written in 2016.08.06

Jrain Lau Project Address: github.com/jrainlau/sc…

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: New versionnode.jsfores6The support has been very high for usees6Can greatly improve the development efficiency and development 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: 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 user input step by step. The experience is similarnpm initStep – by – step process of input parameters.

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 of 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(() => {
    require('.. /command/add')()
  })

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

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

program
	.command('delete')
	.description('Delete a template')
	.alias('d')
	.action(() => {
		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 = () => {
 co(function*() {// Step by step to receive user input parameterslet tplName = yield prompt('Template name: ')
   let gitUrl = yield prompt('Git https link: ')
   let branch = yield prompt('Branch: ') // Avoid repeated additionsif(! config.tpl[tplName]) { config.tpl[tplName] = {} config.tpl[tplName]['url'] = gitUrl.replace(/[\u0000-\u0019]/g, ' ') // Filter unicode character config.tpl[tplName]['branch'] = branch
   } else {
     console.log(chalk.red('Template has already existed! ')) process.exit()} // write 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 = () => {
    co(function*() {// Accept parameters entered by the userlet tplName = yield prompt('Template name: '// Delete the corresponding templateif (config.tpl[tplName]) {
            config.tpl[tplName] = undefined
        } else {
            console.log(chalk.red('Template does not exist! '// Write to template.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 = () => {
 	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 = () => {
 co(function*() {// process user inputlet 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]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 link
Copy 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.

Afterword.

It takes a lot of thought to build something that doesn’t look complicated from scratch. The biggest problem is not knowing how to process user input step by step as NPM init does in the beginning, just knowing a command line with all the parameters is a really bad user experience. After studying vue-CLI and Yoeman, I did not find the corresponding code, so I had to keep googling and finally found an article, which could be implemented with the two tools of CO and co-prompt. Once again, I worshipped the omnipotent TJ god, and I also hope that some friends could tell me how vue-CLI is implemented.

The scaffolding is rudimentary and far from being as good as anything on the market, so you’ll have to fill it in later, but you learned a lot while working on the SCION.

Thank you for reading. I am Jrain, welcome to pay attention to my column, will not regularly share their own learning experience, development experience, handling dry goods outside the wall. See you next time!