What is scaffolding, Baidu Encyclopedia explanation is: scaffolding is to ensure the smooth construction process and set up the working platform. In front end work, I think of it as a set of tools that allow us to focus on our business code development without having to spend time doing repetitive work that is often done.

preface

Pig with qi hand grain, we are working people 🤣.

The use of three king Kong front-end development partners must have used various scaffolding tools, DO not know if there is a hand itching to try to write, la la la, anyway, I looked at the author’s article is itching. I’ve always wanted to learn NodeJS scaffolding development, but I’ve been defeated by my laziness every time, so I’ve spent some time writing a small DEMO, which is a notepad tool in the article. This tool does not involve git download (see other articles in the article). As for how to publish NPM packages, you can see my other article 🍖, which is a step-by-step guide to publishing NPM packages. This article is more about the workflow of a scaffold and the use of common tools. Without further ado, let’s get started!

Code annotation written more clearly, expect every small partner can understand. Of course, the author itself is only a primary front-end, which if there is what to say wrong, also please big guys point out, thank you 🙈, of course, if feel that write well, might as well give three even yo.

Initializing the Environment

1, requirements,

2. Project dependency

  • NodeJS: v12.18.3

3. Tool library

  • Chalk: Modify the text color of input and output and add the icon of success and failure
  • Commander: Command line assistance library. You can add command prompts and parse parameters
  • Inquirer: Command line interaction, input, options, etc
  • Minimis “: command line parameter resolution, can be replaced by commander
  • Ora: Terminal rotator, beautify progress
  • Prettier: Code formatting
  • Dayjs: time formatting tool

4. Project structure

Files that need attention

├── ├─ ├─ new exercises, ├─ new exercises, new exercises, new exercises, new Exercises, new Exercises, new Exercises, new Exercises, new Exercises - the command directory │ │ ├ ─ ─ the add. Js │ │ ├ ─ ─ del. Js │ │ ├ ─ ─ the exit. The js │ │ ├ ─ ─ the find, js │ │ ├ ─ ─ the list. The js │ │ └ ─ ─ open. Js │ ├ ─ ─ the config - configuration file directory │ │ └ ─ ─ index. The js │ ├ ─ ─ service. Js - entry documents │ └ ─ ─ utils - tool class encapsulates │ ├ ─ ─ the file. The js │ └ ─ ─ utils. Js ├ ─ ─ package. The jsonCopy the code

5, case

git clone https://github.com/taosiqi/notepad-cli.git
cdNotepad -cli yarn NPM link // Link to global notepad-cli open or NPM install -g notepad -CLI installation experienceCopy the code

An introduction to case

1. Create a project

1Mkdir notepad-cli && CD notepad-cli2Create a directory based on the directory structure above3Install the libraries mentioned aboveCopy the code

NPM init creates package.json, which should produce a file like this

{
  "name": "notepad-cli"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": []."author": ""."license": "ISC"
}
Copy the code

Adding the bin command

"bin": {
    "notepad-cli": "bin/notepad-cli.js"  // Scaffolding can be used with notepad-cli after executing NPM link
 },
Copy the code

This is what it looks like when you add the dependencies

{
  "name": "notepad-cli"."version": "1.0.0"."description": "Node Notebook"."main": "lib/service.js"."homepage": "https://github.com/taosiqi/notepad-cli"."author": {
    "name": "siqiJson"
  },
  "keywords": [
    "notepad"."cli"]."bin": {
    "notepad-cli": "bin/notepad-cli.js"
  },
  "license": "MIT"."repository": {
    "type": "git"."url": "https://github.com/taosiqi/notepad-cli"
  },
  "dependencies": {
    "chalk": "^ 4.1.0." "."commander": "^ 6.2.1." "."dayjs": "^ 1.9.8"."inquirer": "^ 7.3.3." "."minimist": "^ 1.2.5." "."ora": "^ 5.1.0"."prettier": "^ 2.1.1"}}Copy the code

2. Simple examples

Create the /bin/notepad-cli.js file

#! /usr/bin/env node
// The first line is essential because it tells the system that the script needs to be executed using the Node interpreter.
console.log('NodeJS Scaffolding')
Copy the code

Project root type NPM link, which links the specified execution file globally (equivalent to NPM -g install XXX) so that we can use the scaffolding tool globally.

NPM link error can be first uninstall scaffolding NPM -g uninstall notepad-cli

Executing notepad-cli will execute the /bin/notepad-cli.js file

F:\notepad-cli>notepad-cli nodejs scaffoldingCopy the code

Case analysis

The following is the basic code annotation, friends refueling!

1, entry file notepad-cli

#! /usr/bin/env node
// The first line is essential because it tells the system that the script needs to be executed using the Node interpreter.
// This file is mainly used to handle the entry file that accepts parameters,
const Service = require('.. /lib/service') // Import our entry file
const service = new Service() // Instantiate Service

const rawArgv = process.argv.slice(2)
console.log(rawArgv) //[ 'open' ]

const args = require('minimist')(rawArgv) // Parse command line arguments
console.log(args) //{ _: [ 'open' ] }

const command = args._[0]
// Perform initialization
service.run(command, args, rawArgv)
Copy the code

2. Run the service command

Run (command, args, rawArgv) will execute the /lib/service.js file

const program = require('commander') // Add version command prompt

const { packageInfo, open } = require('./config')  // Config file
const {readDoc}=require('./utils/file') // File manipulation


module.exports = class Service {
    constructor() {
        readDoc() // Save the file under doc to a global variable for easy operation
        setupDefaultCommands() // Set the default command
        registerCommands() // Register custom commands
    }
    run(_id, _args = {}, rawArgv = []) {
        program.parse(rawArgv, { from: 'user' })  // Execute the corresponding command}}// Set the default command
const setupDefaultCommands = () = > {
    program.version(packageInfo.version, '-v, --version'.'Output current version number')
    program.helpOption('-h, --help'.'Get Help')
    program.addHelpCommand(false)}// Register the command
const registerCommands = () = > {
    return program.command('open').description('Open Notepad').alias('o').action(() = > {
        open.apply(program.args)
    })
}

Copy the code

3. Open the Notepad command open

/lib/service.js file to register the custom command open, run notepad-cli open.

const inquirer = require('inquirer') //// Command line interaction
const { readKey } = require('.. /utils/file')// File manipulation
async function init(commands) {
    const promptList = {
        type: 'list'.message: 'Welcome to siqi Notepad, please select the function you want to operate :'.name: 'tools'.choices: () = > {
            let promptList = []
            // Read commands all commands, push options, for the user to select
            for (const commandsKey in commands) {
                promptList.push({
                    name: commands[commandsKey].description,
                    value: commandsKey
                })
            }
            return promptList
        }
    }
    return promptList
}

module.exports = {
    description: 'Open Notepad'.apply: async() = > {let commands = await readKey()
        let promptList = await init(commands)
        await inquirer.prompt(promptList).then(async (answers) => {
            await commands[answers.tools].apply() // Execute the selected command
            const command = require(`${__dirname}/open.js`)
            await command.apply() // Return to the current page after performing the above functions}}})Copy the code

4. Add Notepad add

const inquirer = require('inquirer') // Command line interaction
const { succeed } = require('.. /utils/utils') // Encapsulate the prompt type log
const { add } = require('.. /utils/file') // File manipulation
const dayjs = require('dayjs')
const promptList = [
    {
        type: 'input'.message: 'Please enter title :'.name: 'title'.validate: (val) = > {
            returnval ! = =' '}}, {type: 'input'.message: 'Please enter :'.name: 'content'.validate: (val) = > {
            returnval ! = =' '}}]module.exports = {
    description: 'Add notes'.apply: async() = > {await inquirer.prompt(promptList).then(async (answers) => {
            // Construct the file structure
            const timestamp = new Date().getTime()
            constdata = { ... answers,time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
                timestamp: timestamp
            }
            // Create a notepad file
            add(data, timestamp)
        })
        succeed('Added successfully')
        return Promise.resolve()
    }
}
Copy the code

Delete notepad del

const { del } = require('.. /utils/file')
const { succeed, info } = require('.. /utils/utils')
const inquirer = require('inquirer')
function init() {
    const promptList = {
        type: 'input'.message: 'Please enter the record serial number you want to delete :'.name: 'del'.validate: (val) = > {
            // We can make rule judgments here
            let value = parseInt(val)
            return (
                typeof value === 'number'&&!isNaN(value) &&
                value > 0 &&
                value < articleList.length + 1)}}return promptList
}
// Get the list of notes
function initLog() {
    let log = ' '
    articleList.forEach((item, index) = > {
        log += `${index + 1}:${item.title} \n`
    })
    return log
}
module.exports = {
    description: 'Delete notes'.apply: async() = > {let promptList = await init()
        let log = await initLog() // Displays a list of all articles
        info(log)
        await inquirer.prompt(promptList).then(async (answers) => {
            await del(answers['del'])
            succeed('Deleted successfully')
            return Promise.resolve()
        })
    }
}
Copy the code

6. Exit Notepad exit

const { succeed } = require('.. /utils/utils')
module.exports = {
    description: 'Exit chronicle'.apply: async (env) => {
        succeed('Exit successful')
        process.exit(1)}}Copy the code

7, Find notepad

const inquirer = require('inquirer')
const { info, underline } = require('.. /utils/utils')
// Enter the search keyword
function init() {
    const promptList = {
        type: 'input'.message: 'Please enter the key words of note article :'.name: 'find'.validate: (val) = > {
            returnval ! =' '}}return promptList
}
// Filter by keyword
function filtrate(keyWord) {
    const filtrateData = {
        type: 'list'.message: 'Diary list :'.name: 'tools'.choices: () = > {
            let promptList = []
            const data = articleList.filter((item, index) = > {
                return( item.title.indexOf(keyWord) ! = -1|| item.content.indexOf(keyWord) ! = -1
                )
            })
            data.forEach((item, index) = > {
                promptList.push({
                    name: item.title + ' ' + item.time,
                    value: index
                })
            })
            return promptList
        }
    }
    return filtrateData
}
module.exports = {
    description: 'Find notes'.apply: async() = > {let promptList = await init()
        let keyWord = ' '
        await inquirer.prompt(promptList).then((answers) = > {
            keyWord = answers.find
        })
        let filtrateData = await filtrate(keyWord)
        if(filtrateData.choices().length ! = =0) {
            await inquirer.prompt(filtrateData).then((answers) = > {
                console.log(underline(articleList[answers.tools].content))
            })
        } else {
            info('Contents not found')}}}Copy the code

8, Notepad list list

const inquirer = require('inquirer')
const { info, underline } = require('.. /utils/utils')
function init() {
    const promptList = {
        type: 'list'.message: 'Diary list :'.name: 'tools'.choices: () = > {
            let promptList = []
            articleList.forEach((item, index) = > {
                promptList.push({
                    name: item.title + ' ' + item.time,
                    value: index
                })
            })
            return promptList
        }
    }
    return promptList
}
module.exports = {
    description: 'List of notes'.apply: async() = > {let promptList = await init()
        if(promptList.choices().length ! = =0) {
            await inquirer.prompt(promptList).then((answers) = > {
                console.log(underline(articleList[answers.tools].content))
            })
        } else {
            info('Contents not found')}}}Copy the code

9. Configure file config/index

module.exports = {
    packageInfo: require('.. /.. /package.json'), // Get the package file to display the version number
    open: require('.. /commands/open')// Get the open file
}
Copy the code

10, file operation file

// Notepad operations on files are placed in this area, where a global variable articleList is created to store the contents of notepad for the first time
const fs = require('fs')
const path = require('path')
const docPath = path.join(__dirname, `.. /.. /doc`)
const commandPath = path.join(__dirname, `.. /commands`)
module.exports = {
    // Add file
    add: (data, timestamp) = > {
        fs.writeFile(
            `${docPath}/${timestamp}.txt`.JSON.stringify(data),
            (error) = > {
                if (error) {
                    console.error('Write failed')}else {
                    articleList.push(data)
                }
            }
        )
    },
    // Delete files
    del: (name) = > {
        fs.unlinkSync(`${docPath}/${articleList[name - 1].timestamp}.txt`)
        articleList.splice(name - 1.1)},// Read commands from the commands directory
    readKey: () = > {
        let commands = {}
        fs.readdirSync(commandPath).forEach((paths) = > {
            const command = require(`${commandPath}/${paths}`)
            if (Object.keys(command).length ! = =0) {
                let commandName = paths.slice(0, -3)
                commands[commandName] = command
            }
        })
        return commands
    },
    // Read the contents of the doc file
    readDoc: () = > {
        global.articleList = []
        fs.readdirSync(docPath).forEach((fileName) = > {
            fs.readFile(
                `${docPath}/${fileName}`.'utf-8'.function (err, data) {
                    if (err) {
                        console.error(err)
                    } else {
                        articleList.push(JSON.parse(data))
                    }
                }
            )
        })
        articleList.reverse()
    }
}
Copy the code

11. Text prompt util

const ora = require('ora')
const chalk = require('chalk')

module.exports = {
  // Log information
  log: (message) = > {
    console.log(message)
  },
  // Success message
  succeed: (. message) = > {
    ora().succeed(chalk.greenBright.bold(message))
  },
  // Prompt message
  info: (. message) = > {
    ora().info(chalk.blueBright.bold(message))
  },
  // Error message
  error: (. message) = > {
    ora().fail(chalk.redBright.bold(message))
  },
  // Underline important information
  underline: (message) = > {
    return chalk.underline.blueBright.bold(message)
  }
}
Copy the code

conclusion

The DEMO does not use difficult things, mainly to give you a basic understanding of scaffolding development. If you still don’t understand your friends, you can download the Github case and run. It is best to copy it and rewrite it again. A good memory is better than a bad pen, I believe you will have a deeper understanding.

After watching the friends can move their hands to point a “like” and then go oh, your support is the biggest encouragement to me!!

reference

  1. deploy-cli-service
  2. NodeJS Chinese website