Node.js is used for a wide range of purposes, and is well known to developers for developing services, desktop applications, and so on. There is another very useful scenario for Node.js – command-line applications. This article will introduce the CLI development process and common functions, and take meeting-CLI as an example to develop a CLI tool that can be used in production step by step. Meet – CLI is now open source and can be downloaded directly.

According to the CLI?

Before we go any further, let’s look at a few issues.

What is CLI?


The Command Line Interface, as its name implies, is a tool or application that interacts through the Command Line. SPA applications are commonly used such as VUe-CLI, Angular-CLI, Node. js express-Generator, ORM framework Sequelize-CLI, webpack, NPM and so on. They are tools for Web developers, designed to reduce low-level rework, focus on business, improve development efficiency, and standardize Develop Workflow.

The typical Angular-CLI, for example, allows Angular developers to quickly create best practice Angular applications, launch quickly, and create components, directives, pipes, services, modules, and so on. Used to say very easy to use, now each frame has a supporting CLI.

The CLI has different functions according to different service scenarios. However, all the functions are the same. In essence, the CLI runs codes on the local PC and performs some tasks through command line interaction.

What are the benefits of CLI?

We can summarize complex, regular, or simple repetitive work from the work to complete the CLI, just a few commands, quickly complete simple basic work. Here are my summary examples of existing work that can be implemented using CLI tools:

  1. Quickly generate application templates, such as vue-CLI, and generate application frameworks based on some interactive q&A with developers
  2. Create module template files, such as angular-cli, create Component,module; Sequelize – CLI Creates models that are mapped to mysql tables
  3. The service is started, for example, ng Serve
  4. Eslint, code validation, such as Vue, Angular, basically have this functionality
  5. Automated tests such as Vue and Angular have this functionality
  6. Builds, such as Vue and Angular, do this
  7. * Compile and analyze, using the WebPack plug-in for analysis
  8. * git operation
  9. * Generated code is uploaded to the CDN
  10. * It can also be a gadget function, such as HTTP request API, image compression, Sprite image generation, etc. You can do anything you want

In general, it is some quick operation to replace manual repetitive labor and improve development efficiency.

Contrast with NPM Scripts

NPM scripts can also implement a development workflow by configuring the associated NPM command on the scripts object in package.json to execute the associated JS to achieve the same goal;

But what are the advantages of CLI tools over NPM Scripts?

  1. NPM scripts are project-specific and can only be used within that project. Cli can be globally installed and used by multiple projects.
  2. Using NPM Scripts to embed workflows in business projects is too coupling; Cli allows business code workflow to be stripped of relevant code and business code to focus on business
  3. Cli tools can be developed, evolved, and precipitated iteratively.

Meeting-cli Comes into being according to the actual requirements of the project. Let’s look at some of the meeting-CLI capabilities;

MEET-CLI


This article is based on

The web development work of Meigrapefruit mainly involves the H5 part of hybrid application and the H5 part of advertising and marketing interaction, which are often independent of each other. The following problems have been found in the work:

  • Each H5 creates a few columns of directories and files, and each H5 has a common base code
  • NPM watch and build commands need to be configured for each new feature. We need a template creation feature
  • Each project has its own build code and uploads CDN code, which are different from each other, and the developers are slow to develop the project
  • With code that needs to be repeated (or copied and pasted) every time you create a new build, we need a common upload function

Based on the problems in work, I added some additional functions, meeting-CLI was born. Here are some of its functions.

1, meet-help view the function list


Each CLI tool has the function of viewing help. As shown in the figure, meet-CLI has the function of creating module, compiling, publishing (Git submission and resource uploading CDN), specifying file uploading CDN separately, and analyzing and generating files

Let’s quickly demonstrate some of the main features above

2, meet the init

Meet init generates a meet. Config. js file in the project root directory to configure the use of the meet tool

const path = require('path'); Module.exports = {modulePath: path.resolve('public'), // Module template moduleTemplatePath: path.resolve('meet/templates/module'), // project git url gitUrl:'http://gitlab.meiyou.com/advertise/ad-activity.git', // module build npm command npmBuildCommand:'npm run release:', // upload assets config upload:{// CDN Server :'alioss',// alioss - 'alioss', Qn - 'qn' // alioss server config config:{accessKeyId: "LTAIovxxxx0", accessKeySecret: "5xkxyxxd8 ", bucket: Resolve ('public/assets'),// Upload dist folder ignoreDir: false, deduplication: true, prefix: "ad-activity.meiyou.com", } }, // is publish after build? autoPublish: True, // Test the submitted text};Copy the code

3, Meet new [module]

Quickly create h5 module directory and base files, basic CSS, HTML, JS, necessary dependencies, (can also do express routing configuration, specify module compilation configuration)


4, Meet build [module]

Build module, generate code, analyze with Webpack-bundle-Analyzer, visually display resource ratio, and check whether there are problems in code volume at a glance, which is a benefit for performance optimization


5, git create +upload CDN


Other functions such as meet Analysis and meet Upload are part of the above functions. Meet Upload can specify a path to upload resources, as a separate upload tool.

At present practical function is less, still can add a few function behind

This is a cool wave of operations, but how do you do that? The core is just coming up now. How do you develop a CLI from scratch?

Develop CLI from scratch

We will develop meet-CLI from scratch to demonstrate a complete CLI development process; (Note: In order not to affect the meet-CLI of my computer, I will name the CLI demo as MEi-CLI, please forgive me!)

Basic module

1. Create the NPM module

Run the following command to create an NPM module

npm init -y
Copy the code

get


You’re all familiar with this step;

Bin import file

Add bin objects to package.json files

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

The first line of js is set as follows:

#! /usr/bin/env nodeCopy the code

The above sentence tells the operating system to run the file using Node

This can be done in js console.log(‘hello mei’)

3. Global installation

Run NPM install -g to install MEI-CLI globally. So, the simplest CLI was born, find an arbitrary location to enter mei command, execute your./bin/index.js file,console.log a sentence, ‘hello mei’. The MEI-CLI module can be published to NPM so that it can be used by the community. Here is my blog post on how to publish the NPM module.

Node. Js knowledge

Node.js has filesystem module, which allows developers to read, write, create, and delete files.

Process, CHILD_process, PATH, commonJS modularity knowledge, etc

With the basics in hand, let’s look at some common components 😊

  • Commander Common CLI development framework
  • Chalk terminal text and color JS component
  • Clui Spinners, Sparklines, Progress bars design display components
  • Shelljs node.js runs the shell command component
  • Blessed -contrib Visualized component on the command line
  • Lnquirer Command line interaction information collection component
  • figlet FIGlet is a program that creates large characters out of ordinary screen characters

In addition, there is the meet-Ali-OSS upload module developed by Yougod

These are enough to get you started on a cool CLI. If not, there are 50 more to choose from.

We want to complete the CLI main structure diagram


Js is the main entry file, and commands are specially placed in the main command function logic. According to the division of command modules, the detailed function implementation can be divided into components and placed in the lib folder, and the rest of the configuration, template, etc., can be placed in the meet folder

Main entry file

#! /usr/bin/env node const path = require('path'); const fs = require('fs'); const program = require('commander'); const gmodule = require('.. /packages/commands/module'); // const serve = require('.. /packages/commands/serve'); const question = require('.. /packages/commands/question'); const build = require('.. /packages/commands/build'); const publish = require('.. /packages/commands/publish'); const upload = require('.. /packages/commands/upload'); const analysis = require('.. /packages/lib/analysis'); const initial = require('.. /packages/commands/initial'); let config = {}; If (fs.existssync (path.resolve('meet.config.js'))){config = require(path.resolve('meet.config.js')); } the program version (' 1.0.0 ', '- v, --version') .command('init') .description('initialize your meet config') .action(initial); program .command('new [module]') .description('generator a new module') .action(function(module){ gmodule(config,module) }); program .command('build [module]') .description('git build specify module and assets upload to CDN! ') .action(function(module){ build(config,module) }); program .command('publish') .description('upload assets to CDN and git commit && push') .action(function(){ publish(config) }); program .command('upload') .description('upload your build dist files to CDN server') .action(function () { upload(config.upload); }); program .command('analysis') .description('analysis dist files size and percent') .action(function () { analysis(config.upload.config.srcDir); }); program .command('question') .description('analysis dist files size and percent') .action(function(){ question() }); program.parse(process.argv);Copy the code

The main entry file uses commander to trigger the corresponding module to run when the monitoring terminal enters a command. Commander automatically generates the Mei-help command to display the supported commands. It is a best practice for the CLI to name a command as short as possible, display a message for an unsupported command, and display a correct message and response for an error.

Here meet. Config. js is read from the main entry file and the corresponding configuration information is passed to the corresponding module. If the configuration information uploaded by CDN is passed to the uploading module, the

Use commander to find the CLI is not much technical content 😂.

Meet new [module] triggers the running JS

const path = require('path'); const fs = require('fs'); const chalk = require('chalk'); const inquirer = require('inquirer'); // let templatePath; // Target folder root path let targetRootPath; function deleteFolderRecursive (path) { if (fs.existsSync(path)) { fs.readdirSync(path).forEach(function(file, index){ var curPath = path + "/" + file; if (fs.lstatSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(curPath); } else { // delete file fs.unlinkSync(curPath); }}); fs.rmdirSync(path); }}; function copyTemplates(name){ function readAndCopyFile(parentPath,tempPath){ let files = fs.readdirSync(parentPath); files.forEach((file)=>{ let curPath = `${parentPath}/${file}`; let stat = fs.statSync(curPath); let filePath = `${targetRootPath}/${tempPath}/${file}`; if(stat.isDirectory()){ fs.mkdirSync(filePath); readAndCopyFile(`${parentPath}/${file}`,`${tempPath}/${file}`); } else{ const contents = fs.readFileSync(curPath,'utf8'); fs.writeFileSync(filePath,contents, 'utf8'); }}); } readAndCopyFile(templatePath,name); } function generateModule(meetConfig,name){ templatePath = typeof meetConfig.moduleTemplatePath ! == 'undefined'? path.resolve(meetConfig.moduleTemplatePath):path.join(__dirname,'.. ','meet/module'); targetRootPath = meetConfig.modulePath; let targetDir = path.join(targetRootPath,name); If (fs.existssync (targetDir)){ Prompt ([{name:'module-overwrite', type:'confirm', message:`Module named ${name} is already existed, are you sure to overwrite?`, validate: function(input){ if(input.lowerCase !== 'y' && input.lowerCase !== 'n' ){ return 'Please input y/n ! ' } else{ return true; } } } ]) .then(answers=>{ console.log('answers',answers); // Overwrite if(answers['module-overwrite']){// Delete folder deleteFolderRecursive(targetDir); console.log(chalk.yellow(`Module already existed , removing! `)); // Create a new module folder fs.mkdirsync (targetDir); copyTemplates(name); console.log(chalk.green(`Generate new module "${name}" finished! `)); } }) .catch(err=>{ console.log(chalk.red(err)); })} else{// Create a new module folder fs.mkdirsync (targetDir); copyTemplates(name); console.log(chalk.green(`Generate new module "${name}" finished! `)); } } module.exports = generateModule;Copy the code

The main logic is to traverse all folders and files in the templatePath path according to the templatePath and targetRootPath configured by the user, copy the files to targetRootPath, and prompt whether to overwrite the files if they already exist.

TemplatePath is a flexible path. The template can be in mei-CLI, or anywhere, as long as the correct path is specified, the same result can be achieved. This function can be used with any Web framework, any Web framework can prepare its Module template, which is to copy the template file to the specified location, that is, one-click template generation.

Look at this, there is no technical content.

meet publish

const chalk = require('chalk'); const inquirer = require('inquirer'); const shellHelper = require('.. /lib/shellHelper'); const upload = require('./upload'); let config = { autoPublish: false }; Function gitCommit(){// publish, Inquirer. Prompt ([{name:'message', type:'input', message:`Enter your publish message \n ` } ]) .then(answers=>{ let message = answers.message; shellHelper.series([ 'git pull', 'git add .', `git commit -m "${message}"`, 'git push', ], function(err){ if(err){ console.log(chalk.red(err)); process.exit(0); } console.log(chalk.green('Git push finished! ')); process.exit(0); }); }) .catch(err=>{ console.log(chalk.red(err)); }); } function publish(meetConfig){ Object.assign(config,meetConfig); upload(config.upload) .then(res=>{ console.log(chalk.green('Upload To CDN finished! ')); if(config.autoPublish === true){ gitCommit(); } }) .catch(err=>{ console.log(chalk.red(err)); }) } module.exports = publish;Copy the code

The principle of meet publish is to execute multiple Git commands in sequence using Node.js child_process to commit git, and use meet-ali-oss to upload resource files.

There are other similar functions, such as Build, Initial, upload, analysis, etc., which are not explained one by one by pasted codes. Readers can download meet-CLI to check

git clone https://github.com/linweiwei123/meet-cli.git
Copy the code

Add more features to your CLI, such as communicating with the server (using axios HTTP module requests), real-time communication, sharing cli command interface, etc. (some of them are very weak), as long as it is practical, think big and implement bold, make your CLI more powerful.

Functional logic is different, developers can use their own wisdom to develop their own CLI.

There are a few other areas of CLI development that developers need to be aware of.

Matters needing attention

  1. .gitignore,.npmignore Like the NPM module, the CLI also needs to pay attention to the submission of file content limits
  2. Note the difference between dependencies and devDependencies in package.json
  3. Good documentation

At this point the meet-CLI is developed and can be published to NPM for community use (as appropriate).

Future plans

todolist

  • Added the image processing command meet comp [path] for compression and webP generation
  • Generate the gitignore file command
  • Generate the ESLint configuration
  • Merge with multipages-Generator to form a complete H5 development workflow
  • Vue-cli replaces CLI interaction with web page operations

conclusion

This document describes the introduction to the CLI, common components, usage, and common CLI functions. CLI is an auxiliary tool for Web development, aiming to improve the efficiency of web work. I hope this article can bring some enlightenment and help to our work 😎!