Writing in the front

You’ve probably used a lot of front-end scaffolding tools, but have you ever wondered how to write your own scaffold? I wrote a simple version of scaffolding before, if you just want to understand this, this article may be helpful to you, the following code logic combing, you can also go to experience

npm install duffy-cli -g

The flow chart

Technology stack

  • Development environment: Win7
  • Development tool: VScode
  • Required dependency packages:

Node.js: The entire scaffolding environment. The node. js version of this scaffold is V8.11.1.

Es6: New syntax for JavaScript.

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

Chalk: Colorful terminal tool.

Ora: Elegant terminal spinner that controls terminal output, mainly used for loading effects on the command line.

Download-git-repo: used to download remote repositories to GitHub, GitLab, and Bitbucket

Handlebars: A well-known template engine

Ini: INI format parser and serializer for a node

Inquirer: Used to interact with developers on the command line

Metalsmith: Static web site generator

Request: a tool for sending HTTP requests.

Rimraf: equivalent to the UNIX rm -rf command

Semver: version number processing tool

User-home: used to obtain the root directory of the user

Babel-preset -env: New features not supported by the target environment are selected for translation

Project structures,

Initialize the project

After creating the project directory, execute NPM init to complete the initialization of the project as prompted.

Configure global usage

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

"bin": {
   "dfcli": "./bin/www"
 },
Copy the code

For local debugging, run: NPM link in the project root directory. You can bind the duffy-cli command globally so that you can start the command directly with dfcli

Install dependencies

Dependency is the dependency package above,

npm install semver rimraf .....
Copy the code

Entry file Settings

Current project folder New folder bin create www.js file

#! /usr/bin/env node
require('.. /dist/main.js')
Copy the code

Command management (SRC /main.js)

Use commander to set different commands.

  • The command method sets the name of the command.
  • The description method sets the description.
  • The alias method is a shorthand for setting.
  • The action method sets the callback.
  Object.keys(configMap).forEach((action) => {
    program
    .command(action)
    .alias(configMap[action].alias)
    .description(configMap[action].description)
    .action(function () {
        if (action == 'new'CheckVersion (()=>{main(action,... process.argv.slice(3)) }) }); })Copy the code

Each time you execute the master file, check whether the version is up to date and prompt

Processing user input (\ SRC \config.js)

Create a.bgrc file under the root directory and write the following content, which is used to store the user managed platform name, warehouse address, template information, etc. Template information is still under development, the template information will be put out for special processing.

Handle user input commands (\ SRC \utils\rc.js)

New/Modified

dfcli config set <key> <value>
Copy the code

The set function has two functions, the first implementation of user input to add and modify, the second implementation of initialization rc file.

Implementation code:

export let set = async (k,v) => {
   let has = await exist(rcUrl)
    if (k && v) {
      let opts
      if (has) {
        opts = await readFile(rcUrl, 'utf-8')
        opts = decode(opts)
        opts = Object.assign(opts,{[k]: v})
      } else {
        opts = Object.assign(DEFAULT,{[k]: v}) 
      }
      await writeFile(rcUrl, encode(opts), 'utf-8')}else{// rc initializationif (has) return
      await writeFile(rcUrl, encode(DEFAULT), 'utf-8')}}Copy the code

View/Obtain

dfcli config get <key>
Copy the code

If there is no get followed by a key, getAll() is used to obtain all the contents of the BGRC file

Eg: dfcli config get

export let get = async (k) => {
  let has = await exist(rcUrl)
  let opts
  if (has) {
    opts = await readFile(rcUrl, 'utf-8')
    opts = decode(opts)
    console.log(opts[k])
  } else {
    return ' '}}export let getAll = async () => {
  let has = await exist(rcUrl)
  let opts
  if (has) {
    opts = await readFile(rcUrl, 'utf-8')
    return decode(opts)
  } else {
    return ' '}}Copy the code

delete

dfcli config remove <key> 
Copy the code

Code implementation:

export let remove = async (k) => {
  let has = await exist(rcUrl)
  let opts
  if (has) {
    opts = await readFile(rcUrl, 'utf-8')
    opts = decode(opts)
    if (opts.hasOwnProperty(k)) {
      delete opts[k]
    }
    await writeFile(rcUrl, encode(opts), 'utf-8')}else {
    return ' '}}Copy the code

Changing the Template Address

dfcli config set repertroy github:owner
Copy the code
dfcli config set username yourname
Copy the code

View the template list (\ SRC \list.js)

dfcli list
Copy the code

Initialize the project (\ SRC \install.js)

Command:

dfcli init
Copy the code

Get the name of the template hosted on Github according to the configuration of the.bgrc file:

// List of template nameslet getTplNameList = async () => {
  let loading = ora('Loading template list ....... ')
  loading.start()
  let list = await getTplList()
  loading.succeed('template list complete.')
  let name = list.map((list) => list.name)
  return name
}
Copy the code

After obtaining a list of template names, let the user select a template

const promptList = [{
      type: 'list',
      message: 'Please select a template: ',
      name: 'tpl_name',
      choices: tplnameList
  }]
  let ans = await inquirer.prompt(promptList)
  console.log(ans.tpl_name)
Copy the code

1. Check whether the current template is cached locally. If yes, ask the user whether the template is overwritten

2. After selecting the template, ask the user to enter the project name and the project target folder

3. Generate the render template to the specified location

// Check whether there is a template locallylet localCheckTpl = (tmpName) => {// Remote template address const tmpRepo=path.resolve(userHome,'.tpl'Const tmpDest=path.join(tmpRepo,tmpName) const tmpDest=path.join(tmpRepo,tmpName)return{isExist: exists(tmpDest), tmpDest}} // Download the templateletDownloadTplAndGenrate = async (proName) => {// Remote template address const tmpRepo=path.resolve(userHome,'.tpl'Const tmpDest=path.join(tmpRepo,proName) // Local template store const tmpDest=path.join(tmpRepo,proName)let all = await getAll()
  letloading = ora(`download template start... `) loading.start() await download( `${all.repertroy}/${proName}`, home + '/.tpl/' + proName)
  loading.succeed(`template download complete.`)
  await generate(tmpDest)
}

// generate.js
exportDefault Async (tmpPath)=>{// Initialize Metalsmith object const Metalsmith =Metalsmith(tmpPath) // User enters the project name and target folderlet answer = await inquirer.prompt([{
    type:'input',
    name:'name',
    message:'Please enter your project name:',
    default:'dfcli-project'}, {type:'input',
    name:'destination',
    message:'Please enter the path where your project will be stored:'. Default :process.cwd()}]) // Project generation path const destination=path.join(absolutePathFormat(answer.destination),answer.name) const  loading = ora('generating... '// Add a new global variable object.assign (metalsmith.metadata(),answer) // console.log(metalsmith.metadata()) load.start () metalsmith .source('. ')
  .destination(destination)
  .clean(false)
  .build(function(err) {      
    loading.stop()
    if (err) throw err
    console.log()
    console.log(chalk.green('Build Successfully'))
    console.log()
    console.log((`${chalk.green('Please cd')} ${destination} ${chalk.green('to start your coding')}`))
    console.log()
  })
}
Copy the code

Description of project Structure

| - bin | ` - WWW / / master file entry, Start to run the main. Js | - package. Json ` - SRC | -- config. Js configuration of the rc file to add and delete. | - create js new to create a single page or template | -- the generate. | - generated js building project content Index. js Executes different commands (dfcli init, dfcli config, dfcli list, dfcli new) according to user input action commands. Specific tasks separately from here | - the js template initialization (dfcli init) | -- list. Js local template list (dfcli list) | -- main. Js by commander initialization and set different command | - New. Create the realization of a page or template ` js -- utils | -- checkVersion. Js package version check | -- constant. Js need some constants | -- gitHandle. | - js git related operationslocalPath.js Path check '-- rc.js RC file add, delete, change check specific operation methodCopy the code