当前位置:网站首页>Cook a delicious cli

Cook a delicious cli

2022-06-24 10:53:00 Mr egg

Write at the top , I really want to write a recipe , Suffer from limited cooking power , So the title is a lie , ha-ha ^_~

Let's talk about command line tools today ( namely CLI:command-line interface, All of the following will use CLI Instead of long command line tool terms ) Development of .

After reading this article , You'll develop a CLI Have a more comprehensive understanding of .

You can also collect this article , When you want to develop a CLI when , Come back and turn it over , Always find what you want .

Daniel : Peanut Cola is ready , Wait for the start .

Heller , This is the beginning ,Let's go! <( ̄︶ ̄)GO!


> Take the first step : Initialize project

Create an empty project directory ( The next step is to cook-cli For example , So here we call it cook-cli), Then hit the command in the directory to initialize , The process is as follows :

$ mkdir cook-cli
$ cd cook-cli
$ npm init --yes

adopt npm init command , The directory will be initialized to a Node.js project , It will be cook-cli Generate under directory package.json file .

Add --yes Will automatically answer all questions asked during initialization , You can try to get rid of this parameter , Answer one question by one .


> Get through the main line :CLI Skeletal code

The project has been completed , Next we add skeleton code , Give Way CLI Fly for a while .

  • Implementer

We created src/index.js file , It is responsible for the realization of CLI The functional logic of , It's practical work . The code is as follows :

export function cli(args) {
    console.log('I like cooking');
}
  • spokesperson

Then create a bin/cook file , It is CLI The executable entry file for , yes CLI Spokesperson in the executable environment . The code is as follows :

#!/usr/bin/env node

require = require('esm')(module /*, options*/);
require('../src').cli(process.argv);

Carefully you will find that esm This module , Its function is to let us in js Use... Directly in the source code ECMAScript modules Specification loading module , I.e. direct use import and export. above src/index.js Can write directly in the code of export Thanks to this module .

( Please run... In the project root directory npm i esm To install the module )

  • Official announcement

We have spokesmen , But we have to publicize it . So in package.json add bin Statement of , Announce the existence of spokesmen . as follows :

{
  ...
  "bin": {
    "cook": "./bin/cook"
  },
  ...
}


> Rehearsal at all times : Local operation and debugging

stay CLI Before coming out , Local development debugging is essential , So it is very necessary to debug conveniently .

Daniel : Development Web application , I can debug the function through the browser . that CLI Yesterday ?

CLI It runs at the terminal , So we need to register it as a local command line first . The method is very simple , Run the following command in the root directory of the project :

$ npm link

This command registers a... In the local environment cook CLI, And link its execution logic code to your project directory , So every time you save a change, it will take effect immediately .

Try to run the following command :

$ cook

Daniel :Nice! But I have another question , I want to vscode Set breakpoints to debug , Sometimes it's easier to troubleshoot

You're right . The method is also very simple , stay vscode Add the following configuration , Path is : debugging > Add the configuration . According to the actual command parameters to be debugged , modify args The value of the can .

{
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Cook",
            "program": "${workspaceFolder}/bin/cook",
            "args": ["hello"] // Fill in the parameters you want to debug
        }
    ]
}


> Intention recognition : Enter parameter resolution

Insert a little interlude : Although you may often come into contact with all kinds of CLI, But it's still necessary to CLI A brief introduction to some of the terms involved :

  • command (command) And sub orders (subcommand)
# cook  It's an order 
$ cook

# start  That is to say  cook  Of   Sons command 
$ cook start
  • Command options (options)
# -V  For shorthand mode (short flag) The option to ( Be careful : Only one letter , Multiple letters represent multiple options )
$ cook -V

# --version  For full write mode (long name) The option to 
$ cook --version
  • Command parameter (argument)
# source.js  and  target.js  All for  cp  Arguments to the command 
$ cp source.js target.js

Actually , Subcommands are also parameters of commands

Ok, From the above introduction , We want to achieve one CLI, Right in ( Include subcommand, options, argument) We can't escape the analysis of , Let's face them .

commander: well , brother , Don't panic , There's me !

Yes , brother , it's wonderful to have you . Next we use commander This module parses the input parameters , The process and examples are as follows :

  • Module installation
$ npm i commander
  • src/index.js Example
......
import program from 'commander';

export function cli(args) {
    program.parse(args);
}

In a word , It's so crisp .

Daniel : Join in ? How to do? ?

In the next example , We will use these parsed input parameters . therefore , Please take a moment .


> Can't be without you : Version and help

Version and help information is a CLI The parts that must be provided , Otherwise, it would be unprofessional . Let's see how to achieve it .

modify src/index.js , The code is as follows :

import program from 'commander';
import pkg from '../package.json';

export function cli(args) {
    program.version(pkg.version, '-V, --version').usage('<command> [options]');
    
    program.parse(args);
}

adopt program.version and usage The chain call is done , Still so cold .

Try to run the following command :

$ cook -V
$ cook -h

> Add generals : Add subcommand

Now we start to enrich CLI The function of , From add a subcommand start Start .

It has a parameter food and One option --fruit, The code is as follows :

......
export function cli(args) {
  .....

  program
    .command('start <food>')
    .option('-f, --fruit <name>', 'Fruit to be added')
    .description('Start cooking food')
    .action(function(food, option) {
      console.log(`run start command`);
      console.log(`argument: ${food}`);
      console.log(`option: fruit = ${option.fruit}`);
    });

  program.parse(args);
}

The above example shows how to get the parsed input parameters , stay action You can get everything you want , What do you want to do , It's entirely up to you .

Try running subcommands :

$ cook start pizza -f apple

> Seek foreign aid : Call external command

Sometimes , We need to be in CLI To call external commands , Such as npm And so on. .

execa: It's time for me to play .┏ (\^ω^)=*

  • Module installation
$ npm i execa
  • src/index.js Example
......
import execa from 'execa';

export function cli(args) {
  .....

  program
    .command('npm-version')
    .description('Display npm version')
    .action(async function() {
      const { stdout } = await execa('npm -v');
      console.log('Npm version:', stdout);
    });

  program.parse(args);
}

Passed above execa To call external commands npm -v. Come on , A print npm Version number of :

$ cook npm-version

> Promote communication : Provide human-computer interaction

Sometimes we want to CLI Be able to interact with users through question and answer , Users can provide the information we want by inputting or selecting .

At this point , A strong wind blew through , See only Inquirer.js Come running with colorful clouds .

  • Module installation
$ npm i inquirer

The most common scenario is : Text input , Is it an option , check , The radio . Examples are as follows :

  • src/index.js Example
......
import inquirer from 'inquirer';

export function cli(args) {
  ......

  program
    .command('ask')
    .description('Ask some questions')
    .action(async function(option) {
      const answers = await inquirer.prompt([
        {
          type: 'input',
          name: 'name',
          message: 'What is your name?'
        },
        {
          type: 'confirm',
          name: 'isAdult',
          message: 'Are you over 18 years old?'
        },
        {
          type: 'checkbox',
          name: 'favoriteFrameworks',
          choices: ['Vue', 'React', 'Angular'],
          message: 'What are you favorite frameworks?'
        },
        {
          type: 'list',
          name: 'favoriteLanguage',
          choices: ['Chinese', 'English', 'Japanese'],
          message: 'What is you favorite language?'
        }
      ]);
      console.log('your answers:', answers);
    });

  program.parse(args);
}

The code is simple , Go straight to the renderings :


> Reduce anxiety : Wait for a reminder

Human computer interaction experience is very important , If you can't finish the work immediately , We need to timely feedback the progress of users' current work , This can reduce users' waiting anxiety .

ora and listr Shoulder to shoulder , Take a neat step , Come face to face .

The first thing to play is ora

  • Module installation
$ npm i ora
  • src/index.js Example
......
import ora from 'ora';

export function cli(args) {

  ......

  program
    .command('wait')
    .description('Wait 5 secords')
    .action(async function(option) {
      const spinner = ora('Waiting 5 seconds').start();
      let count = 5;
      
      await new Promise(resolve => {
        let interval = setInterval(() => {
          if (count <= 0) {
            clearInterval(interval);
            spinner.stop();
            resolve();
          } else {
            count--;
            spinner.text = `Waiting ${count} seconds`;
          }
        }, 1000);
      });
    });

  program.parse(args);
}

Don't talk much , Directly above :

listr Then came .

  • Module installation
$ npm i listr
  • src/index.js Example
......
import Listr from 'listr';

export function cli(args) {
  ......

  program
    .command('steps')
    .description('some steps')
    .action(async function(option) {
      const tasks = new Listr([
        {
          title: 'Run step 1',
          task: () =>
            new Promise(resolve => {
              setTimeout(() => resolve('1 Done'), 1000);
            })
        },
        {
          title: 'Run step 2',
          task: () =>
            new Promise((resolve) => {
              setTimeout(() => resolve('2 Done'), 1000);
            })
        },
        {
          title: 'Run step 3',
          task: () =>
            new Promise((resolve, reject) => {
              setTimeout(() => reject(new Error('Oh, my god')), 1000);
            })
        }
      ]);

      await tasks.run().catch(err => {
        console.error(err);
      });
    });

  program.parse(args);
}

Still don't say much , Still directly above :


> Add some color : Let life be no longer monotonous

chalk: I am a young man of art , I live for art , It's up to me .<( ̄ˇ ̄)/

  • Module installation
$ npm i chalk
  • src/index.js Example
.....
import chalk from 'chalk';


export function cli(args) {

  console.log(chalk.yellow('I like cooking'));
  
  .....
  
}

With color CLI, Does it make you feel more happy :


> Facade decoration : Add a border

boxen: This is my favorite , It's time to show me !<(ˉ^ˉ)>

  • Module installation
$ npm i boxen
  • src/index.js Example
......
import boxen from 'boxen';

export function cli(args) {

  console.log(boxen(chalk.yellow('I like cooking'), { padding: 1 }));
  
  ......
}  

Um. , It looks more professional :


> Publish results : It can be published

If you are scope Way to publish , for example @daniel-dx/cook-cli. So in package.json Add the following configuration to make your release smooth ( Of course , If you are npm Paid members of , Then this configuration can be saved )

{
  "publishConfig": {
    "access": "public"
  },
}

finishing , launch :

$ npm publish

OK, Your... Has been released to the world CLI 了 , Now you can go to https://www.npmjs.com/ Go and find out what you published CLI 了 .


> Warm reminder : It's time to upgrade

update-notifier: It's finally me , I wait until the flowers are gone . X﹏X

  • Module installation
$ npm i update-notifier
  • src/index.js Example
......

import updateNotifier from 'update-notifier';

import pkg from '../package.json';

export function cli(args) {
  checkVersion();
  
  ......
}

function checkVersion() {
  const notifier = updateNotifier({ pkg, updateCheckInterval: 0 });

  if (notifier.update) {
    notifier.notify();
  }
}

For local debugging , We'll have local CLI Drop a version , hold package.json Of version It is amended as follows 0.0.9, And then run cook See the effect :

o( ̄︶ ̄)o perfect !


The development of an CLI Some necessary or common steps of .

Of course , If you just want to quickly develop one CLI, As some leaders often say : Don't tell me the process , I just want the result . It can be used as oclif These are designed for the development of CLI And the frame of life , Open the box .

And we as programmers , The context of the solution , Understanding of the past and the present , Still need to pay some time and energy for , This will make us more practical , go further .

Okay , I'll talk about it today , Goodbye to my friends !

Almost forgot , Attach the source code of the sample :https://github.com/daniel-dx/cook-cli

┏(^0^)┛ ByeBye!

Oh , Forget to say , My latest article will be published in the official account as soon as possible , If you are interested, please pay attention

原网站

版权声明
本文为[Mr egg]所创,转载请带上原文链接,感谢
https://yzsam.com/2021/06/20210617110308576w.html