How to use Node.js to write CLI scaffolding

Complete Scaffolding Code

What is front-end scaffolding?

Let’s first check the definition of “scaffolding” in Baidu Encyclopedia:

Scaffolding is a working platform erected to ensure the smooth progress of each construction process.

As the concept of front-end engineering becomes more and more popular, front-end scaffolding came into being.

Simply put, "front-end scaffolding" refers to a tool that quickly builds the basic code of a project by selecting a few options. It can effectively avoid the same code framework and basic configuration of our ctrl + C and ctrl + V.

background

When we are ready to develop a new project:

  • It is relatively difficult to build a complete project basic environment (complete technology stack, rich auxiliary functions, and taking into account different environments) in a short period of time;
  • Different project requirements and team situations mean that we cannot remain static when building the basic project environment;

And the front-end scaffolding tools:

  • Can help us quickly generate the basic code of the project;
  • The project templates of scaffolding tools have been refined and tested by developers and represent the best practices for certain types of projects to a certain extent;
  • Scaffolding tools support the use of custom templates, which we can "customize" according to different projects;

To summarize: Scaffolding is a tool that helps you reduce repetitive work and do repetitive work.

The relatively stable and outstanding front-end scaffolding "scaffolding" includes the following categories:

  • Vue/React Scaffolding

  • Use Node and yeoman to build your own scaffolding

  • Build webpack scaffolding from scratch

What is vue scaffolding?

  • Vue CLI is a complete system for rapid development based on Vue.js
  • The author has helped us configure most of the development environment. We can download the scaffolding and develop directly without having to think about building these tool environments.

This new package can be installed with either of the following commands:

npm install -g @vue/cli
# OR
yarn global add @vue/cli

Run the following command to create a new project:

vue create kk-test

You will be prompted to select a preset. You can choose the default preset that contains basic Babel + ESLint settings, or you can choose "Manually select features" to select the features you need.
default directory

The generated code directory is as follows:
[The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-sl7XXtHE-1687761801189) (…/images/kk-test-catalog.png)]

This default setting is great for quickly prototyping a new project, while the manual setting provides more options that are more needed for production-oriented projects.
manual setting

Now let us take any-cli as an example to write our own scaffolding tool.

core principle

yoemanRequired for building projects yoeman-generator. yoeman-generatorIt is essentially a template with a complete file structure. Users need to manually download these templates to their local area, and then yoemanvarious projects will be automatically generated based on these templates.

vue-cli provides quite a wealth of options and setting functions, but its essence is to pull different templates from the remote warehouse to the local, rather than some "locally generated" black technology.

From this point of view, the idea is there - first create different templates, and then the scaffolding references the templates to generate actual projects according to the user's instructions.

Templates can be built into scaffolding or deployed in remote warehouses.

  • Built into scaffolding, use node fileactions to clone templates locally.

    The advantage is that there is no need to create a new warehouse to save the template. Especially when there are multiple project templates, for example, instead init pcof init mobilegenerating two different projects separately, we only need one warehouse to store the scaffolding.

    The second advantage: Regardless of scaffolding or template changes, you only need to submit them once.

  • Deploy in a remote warehouse and git cloneclone the project locally.

    The advantage is that every time there is a code change in the template, there is no need for users to upgrade the scaffolding locally.

    If the template is built into the scaffolding, every time the template changes, because it is in the same warehouse, the user needs to upgrade the scaffolding. It is different when deployed in a remote warehouse. We only need git clone to get the latest template.

overall process

According to standard practice, let’s first look at the overall process

添加模板->输入模板名->是否有重名模板?-添加成功:给出提示

删除模板->输入模板名->是否有模板?-删除成功:给出提示

模板列表->列出所有模板

初始化模板-输入模板名-是否有模板?-输入模块名-克隆远程仓库到本地模块-切换分支:给出提示

Technical points

process.cwd()与__dirname

Commands are located in scaffolding, and the places where commands are executed are often in template projects.

For example, the scaffolding path is D:\Documents\Downloads\any-cli\src\command, and the path to execute the global scaffolding command is C:\Users\Administrator.

a.jsFor example: the scaffolding code wants to read the files in the scaffolding directory and write them into the template project (the location where the global scaffolding command is executed) b.js,

Then the path of readFile is path.resolve(__dirname,'a.js'),

The writeFile path is path.resolve(process.cwd(),'b.js').

  • process.cwd(): The working directory of the current Node.js process when it is executed:
  • __dirname: directory name of the current module

bin

Many npm modules have executables that want to be installed into the global system path.

A bin field needs to be provided in package.json, which is a mapping of command names to file paths. as follows:

"bin": {
    "any": "./bin/any.js"
},

This will map the any command to the local executable file ./bin/any. In other words, when you execute the any command on the command line, the ./bin/any executable file will be executed.

  • For global installation, npm will use symbolic links to link these files to the /usr/local/bin directory, where all system binary commands are located.

  • If it is installed locally, it will be linked to the ./node_modules/.bin directory. It will only take effect when the any command is run in the current directory.

If you only have one executable file with the same name as the package name. Then you can just use a string (file path), like:

{
    "name": "any-cli",
    "version": "1.2.5",
    "bin": "./bin/any"
}

Equivalent to:

{
    "name": "any-cli",
    "version": "1.2.5",
    "bin": {
        "any-cli": "./bin/any.js"
    }
}

npm link

  • Local directory linked to global module

    npm link can link local directories to global modules.

    For module developers, this is the most valuable command. For example, when we develop any-clia module, we need to use it in the command line anyto test our code (if there is no release during development, the module cannot be installed globally). Don't worry, everything is very easy with npm link.

    For example, in our any-cliproject package.json, there is a command as follows:

    "bin": {
        "any": "./bin/any.js"
    },
    

    Using npm link from the command line

    $ npm link
    

    get the following result

    /usr/local/bin/any -> /usr/local/lib/node_modules/any-cli/bin/any.js
    /usr/local/lib/node_modules/any-cli -> /Users/{Username}/work/any-cli
    

    Under the window:

    C:\Users\Administrator\AppData\Roaming\npm\any -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\any-cli\bin\any.js
    C:\Users\Administrator\AppData\Roaming\npm\node_modules\any-cli -> D:\Documents\Downloads\any-cli
    
    

    Go to the /usr/local/bin and /usr/local/lib/node_modules directories to check. We found that there were more anyexecutable files and any-clidirectories inside.
    In this way, every time there is a change in the local warehouse, the global command will also be updated. We can then develop and test at the same time
    .

  • Local directory references global module

    If you have other modules temp-clithat depend on any-clithe module, you can use the following command to anylink the global to the current module.

    $ cd C:\temp-cli
    $ npm link any-cli # 把全局模式的模块链接到本地
    

    The npm link any-cli command will /usr/local/lib/node_modulessearch for the any-cli module in the directory. After finding the module, it /usr/local/lib/node_modules/any-cliwill link the directory to the current temp-clidirectory ./node_modules/any-cli.

    Now any changes to the any-cli module will be directly mapped to temp-cli.

Other fields: engine and engineStrict (in pakeage.js)

node7.6.0开始支持async,如何保证用户本地安装node7.6.0以上版本呢
  • engine
    You can install node-specific versions locally:

    "engines": { "install-node": "7.6.0" }
    

    You can also use enginesfields to specify which npmversion better initializes your program, such as:

    { "engines" : { "npm" : "~1.0.20" } }
    
  • engineStrict

If you are sure that the module can only work properly with the version specified by the engines parameter, you can package.jsonset it in the file engineStrict:trueand it will override the user's engine-strictsettings.
This feature has been deprecated in npm 3.0.0.

Third-party package: pre-commit/ora/commander/chalk

  • pre-commit : Git hook script useful for identifying simple issues before committing to code review;
  • Commander: is a lightweight nodejs module that provides powerful functions for user command line input and parameter parsing;
  • Chalk: used to output text in different colors on the command line;
  • ora: used to display the loading effect, similar to the loading effect;

code file

create any-cliproject

mkdir any-cli
cd any-cli
git init && npm init

package.json contents

{
  "name": "any-cli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "pub": "npm version patch && npm publish",
    "pre-commit": "eslint src"
  },
  "author": "kk",
  "devDependencies": {
    "eslint": "^3.16.1",
    "eslint-config-airbnb": "^12.0.0",
    "eslint-plugin-babel": "^3.0.0",
    "eslint-plugin-import": "^1.6.1",
    "eslint-plugin-jsx-a11y": "^2.0.1",
    "eslint-plugin-markdown": "*",
    "eslint-plugin-react": "^6.3.0",
    "eslint-tinker": "^0.3.2",
    "pre-commit": "^1.2.2"
  },
  "dependencies": {
    "chalk": "^1.1.3",
    "child_process": "^1.0.2",
    "commander": "^2.9.0",
    "prompt": "^1.0.0"
  },
  "engines": {
    "install-node": "7.6.0"
  },
  "pre-commit": [
    "pre-commit"
  ],
  "bin": {
    "any": "./bin/any.js"
  },
  "license": "ISC"
}

  • The configuration under the field bin is treated as a command line executable command. Point to any file under /bin.
  • The field engines is used to install the 7.6.0 version node in the current directory. You can use the async function directly without using the co module.
  • pre-commit is used to check the code before submission and run the pre-commit script, which is eslint.

Create the /bin folder in the root directory and create the any file.

mkdir bin

This /bin/any is the entry file of the whole scaffolding, so we write it first.

cd bin
cd.> any.js
#!/usr/bin/env node //解决不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件
const add = require('../src/command/add')
const list = require('../src/command/list')
const init = require('../src/command/init')
const del = require('../src/command/del')
const program = require('commander')
const { version } = require('../package')

// 定义当前版本
program
.version(version)

program.parse(process.argv)
if (!program.args.length) {
  program.help()
}

We continue to add code in /bin/any

// 定义使用方法
/*
* 添加模板
* */
program
.command('add')
.description('add template')
.alias('a')
.action(add)

/*
 * 删除模板
 * */
program
.command('del')
.description('Delete a template')
.alias('d')
.action(del)

/*
 * 模板列表
 * */
program
.command('list')
.description('List all the templates')
.alias('l')
.action(list)

/*
 * 删除初始化
 * */
program
.command('init')
.description('Generate a new project')
.alias('i')
.action(init)

command is used to configure the parameters of any command, alias configures the abbreviation, and action configures what function to run.

other:

commander 的具体使用方法在这里就不展开了,可以直接到 [官网][2] 去看详细的文档。

Run npm link to link the current project to the global one. In this way, you can directly use the any command on the command line to test the code below /bin/any

// D:\Documents\Downloads\any-cli
npm link

Use the any command and see the following output, which proves that the entry file has been written.

  Usage: any [options] [command]


  Commands:

    add|a    add template
    del|d    Delete a template
    list|l   List all the templates
    init|i   Generate a new project

  Options:

    -h, --help     output usage information
    -V, --version  output the version number

Next, we create the src/command directory,

Create the files corresponding to the four parameters just now.

The content of the file is the specific business code (corresponding to the initialization of addition, deletion and query respectively),

No introduction will be made here. Please refer to the github link .

Guess you like

Origin blog.csdn.net/kang_k/article/details/131397292