— — — — — fostered fostered fostered — — — — —

Node series address:

  • Code repository: github.com/LiangJunron…
  • Article repository: github.com/LiangJunron…

— — — — — fostered fostered fostered — — — — —

A directory

What’s the difference between a free front end and a salted fish

directory
A directory
The preface
Three integrated Inquirer. Js
Four tips for using Inquirer. Js
 4.1 input box
 4.2 the radio
 4.3 more options
 4.4 confirmation box
 4.5 Verifying Input
5 dynamic questions
Vi References

The preface

Returns the directory

Thanks to the TypeScript environment and commander.js, we can now write directives in.ts files and run projects using NPM run XXX. However, there is a Bug with this approach:

  • When there are too many instructions, we can’t remember them at all!

Therefore, an intelligent prompt is needed to simplify and visualize instructions.

Three integrated Inquirer. Js

Returns the directory

One way jsliang wants to solve this problem is through the terminal type of question and answer form (may later arrange pages or Chrome plug-ins, etc.)

So, Here we go

First, install the necessary packages:

  • The installationInquirer.js:npm i inquirer
  • The installation@types/inquirer(Optional, TS is required) :npm i @types/inquirer -D

Then. We can start playing around with TypeScript and commander.js, and pick up index.ts and package.json to make a change:

src/index.ts

import program from 'commander';
import inquirer from 'inquirer';
import { sortCatalog } from './sortCatalog';

program
  .version('0.0.1')
  .description('Library of Tools')

program
  .command('jsliang')
  .description('Jsliang help instruction')
  .action(() = > {
    inquirer
    .prompt([
      { 
        type: 'rawlist'.name: 'question1'.message: A: May I help you? '.choices: ['Public Services'.'other']
      },
    ])
    .then((answers) = > {
      if (answers.question1 === 'Public Services') {
        inquirer.prompt([
          {
            type: 'rawlist'.name: 'question'.message: 'Current public services are:'.choices: ['File sort']
          }
        ]).then((answers) = > {
          if (answers.question === 'File sort') {
            inquirer.prompt([
              {
                type: 'input'.name: 'question'.message: 'Which folder do you want to sort? (Absolute path) '.default: 'D:/xx',
              }
            ]).then(async (answers) => {
              const result = await sortCatalog(answers.question);
              if (result) {
                console.log('Sort succeeded! ');
              }
            }).catch((error) = > {
              console.error('Wrong! ', error);
            });
          }
        }).catch((error) = > {
          console.error('Wrong! ', error);
        });
      } else if (answers === 'other') {
        // Do something else
      }
    }).catch((error) = > {
      console.error('Wrong! ', error);
    });
  });

program.parse(process.argv);
Copy the code

Notice that the sort has changed to jsliang.

package.json

{
  "name": "jsliang"."version": "1.0.0"."description": "Fe-util, Node Library"."main": "index.js"."scripts": {
    "jsliang": "ts-node ./src/index.ts jsliang"
  },
  "keywords": [
    "jsliang"."Node Tool Library"."Node"]."author": "jsliang"."license": "ISC"."devDependencies": {
    "@types/inquirer": "^ 7.3.1"."@types/node": "^ 15.12.2"."@typescript-eslint/eslint-plugin": "^ 4.26.1"."@typescript-eslint/parser": "^ 4.26.1"."eslint": "^ 7.28.0"."ts-node": "^ 10.0.0"."typescript": "^ 4.3.2." "
  },
  "dependencies": {
    "commander": "^ 7.2.0"."inquirer": "^ 8.1.0"}}Copy the code

And here’s the effect:

The same silky easy to use, but also can control the folder path ~

But! When you see the code above, do you feel like throwing up?

  • Question1: ah, what is this? What function did you write in this code?
  • Question2: It is disgusting that it is not supportedasync/await?

OK, let’s go through some of the operations in Inquirer.

Four tips for using Inquirer. Js

Returns the directory

In the above code, multiple questions can be passed through.prompt(Array), and then answers can be retrieved via a callback, as in an input box:

inquirer.prompt([
  { 
    type: 'input'.name: 'question'.message: A: May I help you? ',
  }
]).then((res) = > {
  console.log('success! ', res);
}).catch((err) = > {
  console.error('an error! ', err);
});
Copy the code

Where Object can be inserted:

  • type: [String] Indicates the type of the prompt, defaultinput, includinginput,number,confirm,list,rawlist,expand,checkbox,password,editor
  • name: [String] Stores the variable that answers the current question
  • messageContent: [String | Function 】 the questions
  • default: 【 String | Number | Boolean | Array | Function 】 the default values
  • choices: 【 Array | Function 】 list option
  • validate: [Function] Verify method, verify the input value is feasible, valid returntrueOtherwise, a string is returned indicating an error message (returnfalseIs the default error message.
  • filter: [Function] Filter the answer and return the processed value
  • transformer: [Function] display effect of operation answer
  • when: 【 Function | Boolean 】 to accept the answer, according to the content in front of the judge whether need to show the problem
  • pageSize: 【Number】 inlist,rawlist,expand,checkboxThis multiple options, paging split
  • prefix: [String] Changes the default prefix
  • suffix: [String] Changes the default suffix
  • askAnswered: [Boolean] Specifies whether the existing answer is mandatory
  • loop: “Boolean”listWhether you can scroll through the selection loop, defaulttrue

I’m sure you don’t understand, so let’s write down some possible use cases.

The subsequent code for shorthand, all written about the following code as shown, behind not shiver

import program from 'commander';
import inquirer from 'inquirer';

program
  .version('0.0.1')
  .description('Library of Tools')

program
  .command('jsliang')
  .description('Jsliang help instruction')
  .action(() = > {
    inquirer
    .prompt([
      { 
        type: 'rawlist'.name: 'question'.message: A: May I help you? '.choices: ['Public Services'.'other']
      },
    ])
    .then((answers) = > {
      console.log('Answer:', answers);
    }).catch((error) = > {
      console.error('Wrong! ', error);
    });
  });

program.parse(process.argv);
Copy the code

Note: ① You can also find the following examples in inquires.js, but Jsliang wants to carry them into its own article for subsequent retrieval. ② If there is a comment about jsliang copying someone else’s README without seeing this comment, then Jsliang has nothing to say, just a few times, write a little comment

4.1 input box

Returns the directory

Enter text:

With parameters: type, name, message[, default, filter, validate, Transformer]

inquirer.prompt([
  { 
    type: 'input'.name: 'question'.message: 'the problem? '.default: 'liangjunrong',}]);Copy the code

Input numbers:

With parameters: type, name, message[, default, filter, validate, Transformer]

inquirer.prompt([
  { 
    type: 'number'.name: 'question'.message: 'the problem? '.default: '1',}]);Copy the code

Enter your password:

Available parameters:type, name, message, mask,[, default, filter, validate]

inquirer.prompt([
  { 
    type: 'password'.name: 'question'.message: 'the problem? ',}]);Copy the code

4.2 the radio

Returns the directory

Radio option without subscript:

Optional parameters: type, name, message, choices[, default, filter, loop]

inquirer.prompt([
  { 
    type: 'list'.name: 'question'.message: 'the problem? '.default: 'jsliang'.choices: ['liangjunrong'.'jsliang']}]);Copy the code

Add separator:

inquirer.prompt([
  { 
    type: 'list'.name: 'question'.message: 'the problem? '.default: 'jsliang'.choices: [
      'liangjunrong'.new inquirer.Separator(), // Add a delimiter
      'jsliang',]}]);Copy the code

Radio option with subscript:

Optional parameters: type, name, message, choices[, default, filter, loop]

inquirer.prompt([
  { 
    type: 'rawlist'.name: 'question'.message: 'the problem? '.default: 'jsliang'.choices: ['liangjunrong'.'jsliang']}]);Copy the code

4.3 more options

Returns the directory

Optional parameters: type, name, message, choices[, filter, validate, default, loop]

inquirer.prompt([
  { 
    type: 'checkbox'.name: 'question'.message: 'the problem? '.choices: ['liangjunrong'.'jsliang']}]);Copy the code

4.4 confirmation box

Returns the directory

Possible parameters: type, name, message, [default]

inquirer.prompt([
  { 
    type: 'confirm'.name: 'question'.message: 'the problem? ',}]);Copy the code

4.5 Verifying Input

Returns the directory

inquirer.prompt([
  { 
    type: 'input'.name: 'phone'.message: 'Please enter your mobile phone number'.validate: (val) = > {
      if (val.match(/\d{11}/g)) {
        return true;
      }
      return 'Please enter 11 digits'; }}]);Copy the code

5 dynamic questions

Returns the directory

Above we said 2 questions:

  • Question1: ah, what is this? What function did you write in this code?
  • Question2: It is disgusting that it is not supportedasync/await?

Now that we’ve solved problem 1 (which is supported by the inquires.js feature), let’s see how problem 2 works.

To solve this problem, we need to install rx.js, rx.js as recommended in inquires.js.

  • Making: RXJS
  • RxJS Chinese document

Start installation:

  • The installationrxjs:npm i rxjs@5

The current version is V7.1.0, but after checking the Inquirer. Js example is v5.x, I can’t find the use of the new version, so I can only use this

Second jsliang is really lazy and doesn’t want to know what rx.js does, I just want the project to run in async/await mode

import program from 'commander';
import Rx from 'rxjs/Rx';
import inquirer from 'inquirer';

const prompts = new Rx.Subject();

// Ruthless information processor
inquirer.prompt(prompts).ui.process.subscribe((result) = > {
  console.log('Success:', result);
}, (error: unknown) = > {
  console.error('failure', error);
}, () = > {
  console.log('complete');
});

program
  .version('0.0.1')
  .description('Library of Tools')

program
  .command('jsliang')
  .description('Jsliang help instruction')
  .action(() = > {
    prompts.next({
      type: 'confirm'.name: 'question'.message: 'the problem? '}); prompts.complete(); }); program.parse(process.argv);Copy the code

This completes the encapsulation and makes it easier to process the information. (Imagine a bunch of switches behind it… case… Judgment)

However, unexpected problems occurred after inquire.js was plugged into multiple modules.

Multiple module examples

+ src
  - index.ts
  + base
    - config.ts
  + common
    - inquirer.ts
  + jsliang
    - inquirer.ts
Copy the code

You do not need to change the interface according to this directory. The following directory shall prevail

Personally, I suspect rx.js is a singleton thing

Error message:

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] test: `ts-node ./src/index.ts test`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] test script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\wps\AppData\Roaming\npm-cache\_logs\2021-06-08T11_46_58_005Z-debug.log
Copy the code

I’m not familiar with RX. Js, so I thought I could update it:

Change folders/files according to this directory

SRC + -- -- -- -- -- -- -- -- -- -- -- - index SRC folder. Ts -- -- -- -- -- -- -- - main entrance + base -- -- -- -- -- -- -- -- -- - base folder, For example the config/math - config. Ts -- -- -- -- -- -- commonly used configuration items - inquirer. Ts -- -- -- -- -- inquirer total processing, Unified encapsulation async/ await-interface. ts ————————— temporarily put all generic interface.ts here + common ————————————————— generic function-index.ts -- -- -- -- -- -- - common entrance to deal with problems - sortCatalog. Ts - - - the inquirer calls the function of the specific file + jsliang -- -- -- -- -- -- -- -- business functions - xx. Ts ———————————————— Service function fileCopy the code

Here is a picture of the catalogue:

src/base/inquirer.ts

import * as myInquirer from 'inquirer';
import Rx from 'rxjs/Rx';
import { Question } from './interface';

export const inquirer = (questions: Question[], answers: any): void= > {
  const prompts = new Rx.Subject();

  // Determine the length
  if(questions.length ! == answers.length) {console.error('Question and answer length inconsistent! ');
  }

  // List of problems
  const questionList = questions.map((item, index) = > {
    return () = > {
      prompts.next(Object.assign({}, item, {
        name: String(index),
      }));
    };
  });

  // Problem handler
  myInquirer.prompt(prompts).ui.process.subscribe(async (res) => {
    console.log('Execute successfully, enter:', res);
    const index = Number(res.name);
    
    Prompts (whether they need to stop)
    answers[index](res, questionList, prompts);

    // The last question is automatically terminated by default
    if (index === answers.length - 1) {
      prompts.complete(); // The callback function can manually control the termination of the query}},(error: unknown) = > {
    console.error('Execution failed with error message:', error);
  }, () = > {
    // console.log(' done '); // The code that must be executed
  });

  // Execute the first question
  questionList[0] (); };Copy the code

src/base/interface.ts

export interface Question {
  type: string, name? : string,message: string,
  default? : string, choices? : string[], validate? (): boolean, }export interface Result {
  name: string,
  answer: string,
}
Copy the code

After setting it like this, you can have fun in other places:

src/common/index.ts

import { inquirer } from '.. /base/inquirer';
import { Result } from '.. /base/interface';
import { sortCatalog } from './sortCatalog';

const common = (): void= > {
  // Test new features
  const questionList = [
    {
      type: 'list'.message: A: May I help you? '.choices: ['Public Services'.'other'] {},type: 'list'.message: 'Current public services are:'.choices: ['File sort'] {},type: 'input'.message: 'Which folder do you want to sort? (Absolute path) '.default: 'D:/xx',},];const answerList = [
    async (result: Result, questions: any) => {
      if (result.answer === 'Public Services') {
        questions[1] (); }else if (result.answer === 'other') {
        // Do something else
        console.log('The service is not available yet'); }},async (result: Result, questions: any) => {
      console.log(result);
      if (result.answer === 'File sort') {
        questions[2]();
      }
    },
    async (result: Result) => {
      const sortResult = await sortCatalog(result.answer);
      if (sortResult) {
        console.log('Sort succeeded! '); }},]; inquirer(questionList, answerList); };export default common;
Copy the code

Pass in the array of questions, and then the callback handles the content for my current needs, and I won’t change it anymore.

Other detailed documents are as follows:

src/index.ts

import program from 'commander';
import common from './common';

program
  .version('0.0.1')
  .description('Library of Tools')

program
  .command('jsliang')
  .description('Jsliang help instruction')
  .action(() = > {
    common();
  });

program.parse(process.argv);
Copy the code

src/base/config.ts

/ * * *@name Default global configuration *@time The 2021-05-22 16:12:21 * /
import path from 'path';

// Base directory
export const BASE_PATH = path.join(__dirname, './docs');

// Ignore the directory
export const IGNORE_PATH = [
  '.vscode'.'node_modules',];Copy the code

src/common/sortCatalog.ts

/ * * *@name File sorting function *@time The 2021-05-22 16:08:06 *@description Rule 1. System order 1/10/2/21/3, want to order 1/2/3/10/21 2. Insert file 1/2/1-1, want to sort by 1/2/2/3 */
import fs from 'fs';
import path from 'path';
import { IGNORE_PATH } from '.. /base/config';

const recursion = (filePath: string, level = 0) = > {
  const files = fs.readdirSync(filePath);

  files
    .filter((item= >! IGNORE_PATH.includes(item)))// Filter ignores files/folders
    .sort((a, b) = >
      Number((a.split('. ') [0]).replace(The '-'.'. '))
      - Number((b.split('. ') [0]).replace(The '-'.'. ')))// Sort folders
    .forEach((item, index) = > { // Walk through the folder
      // Set the old file name and new file name
      const oldFileName = item;
      const newFileName = `${index + 1}.${oldFileName.slice(oldFileName.indexOf('. ') + 1)}`;

      // Set the old file path and new file path
      const oldPath = `${filePath}/${oldFileName}`;
      const newPath = `${filePath}/${newFileName}`;

      // Determine the file format
      const stat = fs.statSync(oldPath);

      // Determine whether it is a folder or a file
      if (stat.isFile()) {
        fs.renameSync(oldPath, newPath); // Rename the file
      } else if (stat.isDirectory()) {
        fs.renameSync(oldPath, newPath); // Rename the folder
        recursion(newPath, level + 1); // Recursive folder}}); };export const sortCatalog = (filePath: string): boolean= > {
  // Absolute path
  if (path.isAbsolute(filePath)) {
    recursion(filePath);
  } else { // Relative path
    recursion(path.join(__dirname, filePath));
  }

  return true;
};
Copy the code

Then, Inquirer. Js access is done, try our NPM run jsliang, it works!

Back can be happy to write function ~

Vi References

Returns the directory

  • Making: SBoudrias/Inquirer. Js
  • Making: RXJS
  • RxJS Chinese document
  • CSDN: Inquirer. Js – a tool for user interaction with the command line

Jsliang’s document library is licensed by Junrong Liang under the Creative Commons Attribution – Non-commercial – Share alike 4.0 International License. Based on the github.com/LiangJunron… On the creation of works. Outside of this license agreement authorized access can be from creativecommons.org/licenses/by… Obtained.