React/Vue/Angular frameworks are the basics for getting started. Learning about these frameworks starts with learning to use their scaffolding. For example: Create-react-app, vue-cli, angular-cli can quickly create projects and improve efficiency.

But we can’t limit ourselves to just knowing how to use it. Knowing how it works helps us a lot in the team, especially when building infrastructure. Let’s take a look

The basic flow of scaffolding

Here’s an example of create-react-app:

Create the project using the command line

$ create-react-app my-project
Copy the code

(CRA does not have as many templates to choose from as VUe-CLI)

My-project ├─ Node_module ├─ public │ ├─ FavIcon. Ico │ ├─ index.html │.... (omitted) other ├ ─ SRC │ ├ ─ App. Js │ ├ ─ index. The js │ └ ─... └─ garbageCopy the code

With a single command line like this, or more than a few steps of interactive selection, you can quickly generate a project. Here I use TS to write a scaffolding [dyi-CLI] to explore with you

#1 Create a new project dyi-CLI-demo

$ mkdir dyi-cli-demo && cd dyi-cli-demo && npm init  && tsc --init
Copy the code

Ts # 2 configuration

$ tsc --init
Copy the code

Let’s simply configure tsconfig.json

{
  "compilerOptions": {
    "target": "es6"."module": "commonjs"."outDir": "./lib"."baseUrl": "./src"."strict": true."moduleResolution": "node"."esModuleInterop": true."skipLibCheck": true."forceConsistentCasingInFileNames": true,},"include": [
    "./src"]},Copy the code

#3 We can install our dependency package at the first time

$ npm i commander chalk dyi-tool fs-extra inquirer ora util typescript glob esbuild --save-dev
Copy the code

#4 Create the import file SRC /bin/cli.ts

#! /usr/bin/env node import { Command } from 'commander' import { chalk } from 'chalk' const program = new Command() // On ('--help', () => {console.log(' \r\n Run ${chalk. Green (' dyi <command> --help', )} to understand the details\r\n `, ) }) program.parse(process.argv)Copy the code

#5 Package. json is configured as follows

{" name ":" dyi - cli - demo ", "version" : "1.0.0", "description" : ""," main ":" index. Js ", "bin" : {" dyi ": "./lib/bin/cli.js" }, .... }Copy the code

Execute TSC, generate lib folder and compile to JS file, and NPM link to global

tsc && npm link
Copy the code

Execute dyi to verify that our CLI command has taken effect

$ dyi
Copy the code
Usage: dyi <command> [option] Options: -V, --version output the version number -h, --help display help for command Commands: create [options] <project-name> create a new project help [command] display help for command Run dyi <command> --help to  understand the detailsCopy the code

Ok, our CLI command is successful, add to improve our CLI.ts:

#! /usr/bin/env node

import chalk from 'chalk'
import { Command } from 'commander'
import create from '.. /create'
const program = new Command()

// Create file command
program
  .command('create <project-name>')
  .description('create a new project')
  .option('-f --force'.'if it exist, overwrite directory')
  .action((name: string, options: any) = > {
   	console.log('Name of project to be created', name, options)
  })

// Set the version number
program.version(require(.. /.. /package.json).version).usage('<command> [option]')

// Configure the help information
program.on('--help'.() = > {
  console.log(
    `\r\n Run ${chalk.green(
      `dyi <command> --help`)}, to understand the details \r\n `,)})// Parse the parameters
program.parse(process.argv)
Copy the code

Execute dyi create my-project

➜ dyi-cli-demo git:(master) qualified dyi create my-project my-project {}Copy the code

As you can see, we successfully executed dyi create < app-name > and successfully got the callback, so now we can do whatever we want from here.

Create SRC /create.ts file

import chalk from 'chalk'
import { existsSync, remove } from 'fs-extra'
import path from 'path'

const Create = async (name: string, options: any) => {
  // 1. Get the current location (the current input command line location)
  const cwd = process.cwd()

  // 2. The file to be created (at the location where the name is currently entered)
  const targetPath = path.join(cwd, name)

  // 3. From the interactive command line, select the template we want to create
  
  // 4. Check whether the project already exists
  if (existsSync(targetPath)) {
    // Force replacement: dyi create my-project -f
    if (options.force) {
      await remove(targetPath)
    } else {
      // If so, the interactive command asks whether to overwrite the project}}// 5. Copy the template we prepared
  console.log(
    `${chalk.green('\n Successfully created project')}  ${chalk.cyan(name)}`)},export default Create
Copy the code

Create project template (SRC /template)

├─ SRC │ ├─ Template │ ├─ react-tmp │ ├──── ──── ──── ──── ──── ──── ──── ── │ ├─ Vue - TMP │ ├─ react │ ├─... (omitted, vue project files) │ ├── tar-tmp │ ├──... (omitted, taro project document) ├ ─ (omit others)Copy the code

Create. Ts: create. Ts: create. Ts: create. Ts: create.

The complete create.ts file:

import chalk from 'chalk'
import { copyDir } from 'dyi-tool'
import { existsSync, remove } from 'fs-extra'
import { prompt } from 'inquirer'
import ora from 'ora'
import path from 'path'

const Create = async (name: string, options: any) => {
  // 1. Get the current location (the current input command line location)
  const cwd = process.cwd()

  // 2. The file to be created (at the location where the name is currently entered)
  const targetPath = path.join(cwd, name)
	
  // 3. From the interactive command line, select the template we want to create
  const { projectName } = await prompt({
    name: 'projectName'.type: 'list'.choices: [{name: 'react-tmp'.value: 'react-tmp' },
      { name: 'vue-tmp'.value: 'vue-tmp' },
      { name: 'taro-tmp'.value: 'taro-tmp'},].message: 'Please select a project template to create',})// 4. Check whether the project already exists
  if (existsSync(targetPath)) {
    // Force replacement: dyi create my-project -f
    if (options.force) {
      await remove(targetPath)
    } else {
      // If so, the interactive command asks whether to overwrite the project
      const { replace } = await prompt([
        {
          name: 'replace'.type: 'list'.message: 'The project already exists, confirm overwrite?${chalk.grey(
            'Original project cannot be restored after overwriting')},`.choices: [{name: 'Confirm coverage'.value: true },
            { name: 'On second thought, not yet.'.value: false},],},])if(! replace) {return
      }
      await remove(targetPath)
    }
  }
  
	// 5. Copy the template we prepared
  const spinner = ora('downloading template... ')
  spinner.start()
  
  // copyDir Copies the contents of the folder
  const res = await copyDir(`./src/template/${projectName}`.`. /${name}`)
  if (res === false) {
    console.log(chalk.red(`downloading failed ... `))
    spinner.fail('downloading failed ... ')
    return false
  }
  spinner.succeed()
  console.log(
    `${chalk.green('\n Successfully created project')}  ${chalk.cyan(name)}`.)console.log(`\n cd ${chalk.cyan(name)}`)
  console.log('\n npm install')
  console.log('\n npm run dev \n')}export default Create
Copy the code

Here says below copyDir is I am for you to write a copy folder method, specific usage can see the dyi-tool dependency package

Introduce the create method in our cli.ts

.import create from '.. /create'

// Create file command
program
  .command('create <project-name>')
  .description('create a new project')
  .option('-f --force'.'if it exist, overwrite directory')
  .action((name: string, options: any) = > {
     create(name, options)
  })

...
Copy the code

We have already written the basic configuration file, to test the effect, execute TSP & NPM link, create our project dyi create my-project

$ dyi create my-project
Copy the code

Select the template we need

➜ dyi-cli-demo git:(master) qualify dyi create my-project? Select a project template to go onto university (Use arrow keys) ❯ react-tmp vue- TMP taro-tmp // after selecting the template ================== ➜ dyi-cli-demo git:(master) qualify dyi create my-project ? Select a project template to create the react-tmp TAB Downloading template... Successfully created project my-project cd my-project npm install npm run devCopy the code

Created successfully and generated our project template project under the root directory

├─ Heavy Metal Exercises - - Heavy Metal Exercises - - Heavy Metal Exercises ├─ Mode_module │.... (package) rely on ├ ─ SRC │ ├ ─ bin │ ├ ─ ─ ─ the cli. Ts │ ├ ─ template │ ├ ─ ─ ─ the react - TMP │ ├ ─ ─ ─ the vue - TMP │ ├ ─ ─ ─ taro - TMP └ ─ package. The jsonCopy the code

At this point, some friends will ask, where is the promised esbuild? In the new era, we all pay attention to efficiency, so let’s go straight to esbuild, a fast and simple building tool.

#8 Package our project using esBuild builds

Install esbuild

$ npm i esbuild --save -dev
Copy the code

Create the build.js file in the root directory, SRC /build.js

const esbuild = require('esbuild')
const path = require('path')
const glob = require('glob')
const chalk = require('chalk')
const fs = require('fs-extra')
const libPath = path.join(process.cwd(), 'lib')

/** If the lib folder already exists, empty */
if (fs.existsSync(libPath)) {
  fs.emptyDirSync(libPath)
}

/** Matches all ts files in the SRC folder */
const matchFiles = async() = > {return await new Promise((resolve) = > {
    glob('src/**/*.ts', { root: process.cwd() }, function (err, files) {
      if (err) {
        console.error(err)
        process.exit(1)
      }
      resolve(files)
    })
  })
}
/** esbuild configuration */
const build = async function () {
  await esbuild.build({
    entryPoints: await matchFiles(),
    bundle: false.splitting: false.outdir: path.join(process.cwd(), 'lib'),
    format: 'cjs'.platform: 'node'.minify: false.color: true,})console.log(chalk.green('success build \r\n'))
}

build()
Copy the code

Modify the package.json file

"scripts": {
	"build": "node build.js"
},
Copy the code

NPM run build generates a lib folder under the root directory,

$ npm run build

$ npm link 

$ dyi create my-project
Copy the code

Download git-repo (git-repo); download git-repo (git-repo);

For a more specific template, check out my Github’s dyi-CLI-demo, and make sure to click on a star