preface

Because code templates are often created for secondary development, they are sometimes uploaded to a Git repository for direct use next time. However, due to the increasing number of templates, sometimes it may not be able to quickly find the required template repository. The most important is, every time to go to manual search, too much trouble!! And since most of the major frameworks now have a corresponding CLI for creating initial projects, I decided to emulate them by creating my own scaffolding tool.

The preparatory work

Since we are using the Node.js environment for development, we rely on some third-party libraries:

  • interaction
    • Comcommander.js: A complete Node.js command-line solution inspired by Ruby’s Commander.
    • Inquirer. Js: A collection of generic interactive command-line user interfaces.
    • Clear: Clear the terminal. You can run the clear command on the terminal.
    • Chalk: Adds the correct color (custom terminal font color) to the terminal string.
    • Log-symbols: Colorful symbols (such as √ and ×) added for printed logs.
    • figlet.js: A FIG driver written in JavaScript, able to generate based onASCIIArt word.
  • Create a template
    • download-git-repoBased on:node.jsDownload and extract git repositories (GitHub, GitLab, Bitbucket).
    • handlebars.js: template engine, used to fill in template files with information obtained from interaction with, for example, thepackage.jsonContent for dynamic creation.
    • Ora: Elegant terminal spinner that can be used to handle download animations during the download process.
    • Clui (Optional) : A more complete terminal graphics library that also includes spinner and other components.

** Note: ** because this article is just a demonstration, so only the above part of the library will be used for the time being, students who need other libraries can come down to understand.

npm install commander inquirer clear chalk log-symbols figlet download-git-repo handlebars ora -S
# or
yarn add commander inquirer clear chalk log-symbols figlet download-git-repo handlebars ora
Copy the code

coding

Based on the third-party libraries above, you will now create a scaffolding tool called Cotpl-CLI, which has the option to create code templates. Specific use is as follows:

cotpl create <project>
Copy the code

When the above command is executed, the user is prompted to terminate the process if the specified project already exists, or to select a template if it does not.

Basic interaction

To achieve basic command line interaction, commander.js and Inquirer.js are sufficient.

// ./src/index.js
// version
const fs = require('fs')
const path = require('path')
const program = require('commander')
const inquirer = require('inquirer')

// Set the version number directly from package.json
program.version(
  require(path.resolve(__dirname, '.. /package.json')).version,
  '-V, --version'
)

// Create a command
program
		// How to use it
   .command('create <name>')
		// Command description
   .description('create a new project powered by cotpl-cli')
	 // Run the result
   .action(async (name) => {
   // name is the project name passed when creating the project above
   if (fs.existsSync(name)) {
      console.error(`Target directory ${process.cwd()}/${name} already exists.`)}else {
      // Introduce interaction
      const { type } = await inquirer.prompt([ // Each member is a question, from top to bottom, and the value answered by the user is assigned to the value of the member's name attribute
        {
          type: 'list'.name: 'type'.message: 'choose a templete type you want to create:'.choices: ['Vue'.'React'.'Node.js'.'Electron']}])console.log(type)
    }
   });

// Parses commands and arguments passed by the user
program.parse(process.argv);
Copy the code
{
  "scripts": {
    "start": "node ./src/index.js"}},Copy the code

Then run NPM run start create demo in the project

Interactive optimization

  • Log-symbols and Chalk provide users with more personalized responses.

  • Clear Clears the console after a user enters a command.

  • Figlet presents users with personalized ASCII art characters.

// ./src/index.js
// ...
const chalk = require('chalk')
const clear = require('clear')
const figlet = require('figlet')
const symbols = require('log-symbols')


// create
program
  .command('create <name>')
  .description('create a new project powered by cotpl-cli')
  .action(async (name) => {
    if (fs.existsSync(name)) {
      / /...
      console.error(
        // Error symbol
        symbols.error,
        // chalk is one of the uses of chalk
        chalk` {RGB (255255255) the Target directory {RGB (130223226).${process.cwd()}/${name}} already exists.}`)}else {
			/ /...}})// clear terminal
clear()

// logo
console.log(
  chalk.blueBright(figlet.textSync('Cotpl', { horizontalLayout: 'full'})))// parse the argv from terminal
program.parse(process.argv)
Copy the code

Download the template

Use download-git-repo to download the template project for the Git-managed repository.

// ./src/download.js
const download = require('download-git-repo')
// Loading icon for downloading
const ora = require('ora')
/ * * * *@param {string} path
 * @param {string} name* /
function downloadRepositorie(path, name) {
  return new Promise((resolve, reject) = > {
    const spinner = ora('downloading template... ')
    spinner.start()
    download(path, name, { clone: true }, (err) = > {
      if (err) {
        spinner.fail()
        reject(err)
      }
      spinner.succeed()
      resolve()
    })
  })
}

module.exports = downloadRepositorie
Copy the code
// ./src/repositories.js
// Template repository mapping
module.exports = {
  Vue: {},
  React: {
    'umi-admin-template':
      'https://github.com/Col0ring/umi-admin-template.git#master'
  },
  'Node.js': {},
  Electron: {
    'create-react-app + typescript + electron':
      'https://github.com/Col0ring/ts-react-electron-template.git#master'.'create-react-app + typescript + electron + antd + less + css-modules':
      'https://github.com/Col0ring/ts-react-electron-template.git#antd'}}Copy the code
// ./src/index.js
const fs = require('fs')
const path = require('path')
const program = require('commander')
const chalk = require('chalk')
const clear = require('clear')
const figlet = require('figlet')
const inquirer = require('inquirer')
const symbols = require('log-symbols')
const download = require('./download')
const repositories = require('./repositories')

// version
program.version(
  require(path.resolve(__dirname, '.. /package.json')).version,
  '-V, --version'
)

// create
program
  .command('create <name>')
  .description('create a new project powered by cotpl-cli')
  .action(async (name) => {
    if (fs.existsSync(name)) {
      console.error(
        symbols.error,
        chalk` {RGB (255255255) the Target directory {RGB (130223226).${process.cwd()}/${name}} already exists.}`)}else {
      const { type } = await inquirer.prompt([
        {
          type: 'list'.name: 'type'.message: 'choose a templete type you want to create:'.choices: ['Vue'.'React'.'Node.js'.'Electron']}])// Get the type
      const typeRepositories = repositories[type]
      const typeKeys = Object.keys(typeRepositories)
      // Check whether there is a corresponding warehouse
      if (typeRepositories && typeKeys.length > 0) {
        const { templateName } = await inquirer.prompt([
          {
            name: 'templateName'.message: `choose the template you need:`.type: 'list'.choices: typeKeys
          }
        ])
        Download git-repo: direct: indicates the complete URL to the git repository. Direct :url#my-branch is also required
        const downloadPath = `direct:${typeRepositories[templateName]}`
        // Download the template
        download(downloadPath, name)
          .then(() = > {
          / / success
            console.log(
              symbols.success,
              chalk` {RGB (87190, 56) download successfully! } `
            )
          })
          .catch((err) = > {
          / / fail
            console.error(symbols.error, chalk` {RGB (255255255).${err}} `)
            console.error(
              symbols.error,
              chalk'{RGB (255,255,255) Download template fail,please check your network connection and try again.}'
            )
            process.exit()
          })
      } else {
        console.log(
          symbols.info,
          chalk'{RGB (255,255,255) There are no templates for${type}} `)}}})// clear terminal
clear()

// logo
console.log(
  chalk.blueBright(figlet.textSync('Cotpl', { horizontalLayout: 'full'})))// parse the argv from terminal
program.parse(process.argv)
Copy the code

The above code has completed the function of selecting the template that has been downloaded from git repository to the specified folder.

Render meta information

Through the above steps we have completed the template download function, if you want to modify the project information can be directly into the project to modify, but if you want to let the user when creating a project to input some information and automatically fill in the template file, also need to use the help of the template engine.

// ./src/render.js
const fs = require('fs')
const handlebars = require('handlebars')
/ * * * *@param {object} meta metadata
 * @param {string} meta.description
 * @param {string} meta.name
 */
function render(meta) {
  const fileName = `${meta.name}/package.json`
  const content = fs.readFileSync(fileName).toString()
  // compile the package.json template
  const result = handlebars.compile(content)(meta)
  fs.writeFileSync(fileName, result)
}

module.exports = render
Copy the code

Json to write to the template field to compile:

{
  "name":"{{name}}"
  "description":"{{description}}"
}
Copy the code

The above code requires us to pass in name and description. Name uses the name of the project created by the user, and description needs to be manually entered by the user

// .src/index.js
/ /...
const render = require('./render')

// create
program
  .command('create <name>')
  .description('create a new project powered by cotpl-cli')
  .action(async (name) => {
  			/ /...
  			Fill in the project description immediately after selecting the template
        const { templateName, description } = await inquirer.prompt([
          {
            name: 'templateName'.message: `choose the template you need:`.type: 'list'.choices: typeKeys
          },
          {
            name: 'description'.message: 'description:'}])// ...
        download(downloadPath, name)
          .then(() = > {
           // Modify package.json after downloading the template
            render({ description, name })
            // ...
          })
      // ...}})// ...
Copy the code

After the above configuration, you can see the modified information in your project’s package.json.

How to use

We’ve done all the coding for the CLI, but if you want to use the CLI as a command, you need to modify the project configuration.

1. Add the bin field to pagckage.json

{
  "bin": {
    "cotpl": "./src/index.js"}}Copy the code

If it ispackage.jsonAdd the bin field and the corresponding executable will be linked to the current project’s./node_modules/.binIs available in local projectsnpx cotplRun the command, which can be used directly if it is a global module.

2. Add a script interpreter to the run file

Add the following code to the specified bin run file, which tells the system to dynamically look for Node in an environment variable to execute the file.

// ./src/index.js
#!/usr/bin/env node
Copy the code

3. Add commands to the environment

  • If you are developing locally, you can run it directly from the project root directorynpm link, so that you can use the CLI tool directly on the machine.
  • If you want to share it with other people or on different computers, follow the process for publishing third-party libraries tonpmGo up and useNPM install library name -gTo use globally.

conclusion

This article builds a zero-to-one scaffolding for downloading code templates and optimizes the interface as much as possible. If you’re interested in how to create a command-line tool or want to quickly generate a code template, try writing your own scaffolding tool by hand.

The relevant code has been uploaded to Github

reference

  • Develop simple scaffolding tools using Node.js
  • Build a JavaScript Command Line Interface (CLI) with Node.js
  • npm-package.json

🏆 nuggets technical essay | double festival special articles