乐趣区

关于前端:Node-系列-003-commanderjs

——————————☆☆☆——————————

Node 系列相应地址:

  • 代码仓库:https://github.com/LiangJunro…
  • 文章仓库:https://github.com/LiangJunro… 系列 - 前端材料 /Node

——————————☆☆☆——————————

commander.js —— 残缺的 Node.js 命令行解决方案。

本篇文章解说如何通过 commander.js 溜达 Node.js 命令行。

一 前言

当一个 Node.js 程序运行时,会有许多存在内存中的全局变量。

其中 process 作为过程对象,它有一个 argv 属性,能够查看到指令。

咱们轻易建一个 index.js 举例,终端执行命令:node index.js --name jsliang

index.js

console.log(process.argv);
/*
  [
    'C:\\Program Files\\nodejs\\node.exe',
    'F:\\jsliang\\index.js',
    '--name',
    'jsliang'
  ]
*/

看打印的数据:

  • Node 地位:C:\\Program Files\\nodejs\\node.exe
  • 以后代码门路:F:\\jsliang\\index.js
  • 参数 1:--name
  • 参数 2:jsliang

所以,在咱们写命令行程序的时候,只须要对 process.argv 这个数组的第三个元素及其之后的参数进行解析即可。

如果不嫌麻烦,齐全能够写出很多判断分支来做。

然而,有现成的为啥还要本人写,能偷懒就偷懒啊~

二 commander.js

commander.jstj 写的一个工具包,作用是让 Node 命令行程序的制作更加简略:

  • GitHub:commander

懂你,中文 README 奉上:https://github.com/tj/command…

上面咱们开始操作:

  • 初始化 package.jsonnpm init --yes

    • !!【注】如果是依照 Node 系列程序学习的,这个步骤能够省略
    • !!【注】上面代码能够建一个 test 空文件夹来耍耍
  • 安装包:npm i commander
  • 编写指令文件:

index.js

const program = require('commander');

program
  .version('0.0.1')
  .description('小工具指令清单')
  .option('-s, --sort <path>', '排序功能', '')

program.parse(process.argv);

package.json(主动生成)

{
  "name": "test",
  "version": "1.0.0",
  "description": "","main":"index.js","scripts": {"test":"echo \"Error: no test specified\" && exit 1"},"keywords": [],"author":"",
  "license": "ISC",
  "dependencies": {"commander": "^7.2.0"}
}

这份代码的逻辑是:

  1. 援用 commander
  2. 形容 commanderversion 等参数
  3. commander 掌控 process.argv

重点看看在这份代码中第 2 段的参数:

参数什么的看起来很焦躁,然而不理解又不晓得还能够怎么用

  • version:版本

    • 用法:.version('x.y.z')
  • description:形容

    • 用法:.description('小工具指令清单')
  • option:选项

    • 用法:.option('-n, --name <name>', 'your name', 'jsliang')
    • 第一个参数是选项定义,能够用 |,' ' 空格连贯
    • 第二个参数为选项形容
    • 第三个参数为选项参数默认值(可选)

所以上面咱们能够查看到一些信息。

  • 执行命令:node index.js -h

失去上面后果:

Usage: index [options]

小工具指令清单

Options:
  -V, --version           output the version number
  -s, --sort <path>       排序功能 (default: "")
  -h, --help              display help for command

这样咱们就实现了一些小指令,那么怎么操作呢?

  • 排序:node index.js -s "jsliang"

当然,这样感觉太怪了,能不能像日常开发一样,能够 npm run devnpm run xxx 的形式执行?

当然是能够的!

三 实际:文件重排性能

讲了那么多,做个小实际吧!


对于 Markdown 编写的文档库来说,如果你不设置文档程序,那么它就会依照零碎的规定来读取目录:

  • 1. 文章 1
  • 10. 文章 10
  • 2. 文章 2
  • ……

当一个文件夹内容过多的时候,咱们心愿依照本人的程序让用户浏览,所以不能找零碎的来,因而须要用 Node 写个小工具,读取的时候依照心愿的排序来读取,这就是咱们开发小工具的初衷。

当然,还有个很重要的性能,就是当咱们心愿在第 1 篇和第 2 篇文章两头插入一篇文章的时候,例如:

  • 1. 文章 1
  • 1-1. 文章 1-1
  • 2. 文章 2
  • ……

咱们还须要将这个目录构造进行从新排序,让新文章插入到指定地位。

3.1 实际目录

此时咱们的目录为:

+ docs
  + 3. 目录 c
    - 1. 目录 c1.md
    - 1-1. 目录 c2.md
    - 2. 目录 c3.md
  - 1. 文章 a.md
  - 2. 文章 b.md
  - 10. 文章 d.md
+ src
  - config.ts
  - index.ts【已有】- sortCatalog.ts
- .eslintrc.js【已有】- package.json【已有】- tsconfig.json【已有】

该目录的 .eslintrc.jspackage-lock.jsonpackage.jsontsconfig.json 是主动创立的,TypeScript 的配置看前置文章,这里不做累述。

docs 目录下创立几个空 Markdown 文件,文件名照抄即可。

为了防止小伙伴误操作,还是截个图吧

3.2 编写 commander

退出 commonder 只须要往 package.jsonindex.ts 上配置即可。

  • 初始化 package.jsonnpm init --yes(之前已配置)
  • 装置 commandernpm i commander

package.json

{
  "name": "jsliang",
  "version": "1.0.0",
  "description": "FE-util",
  "main": "index.js",
  "scripts": {"sort": "ts-node ./src/index.ts sort"},
  "keywords": [],
  "author": "","license":"ISC","devDependencies": {"@types/node":"^15.0.2","@typescript-eslint/eslint-plugin":"^4.23.0","@typescript-eslint/parser":"^4.23.0","eslint":"^7.26.0","ts-node":"^9.1.1","typescript":"^4.2.4"},"dependencies": {"commander":"^7.2.0"}
}

留神 scripts 扭转了,记得复制过来

而后简略写写 index.ts 外面内容

src/index.ts

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

program
  .version('0.0.1')
  .description('工具库')

program
  .command('sort <path>')
  .description('文件排序功能。示例:npm run sort"docs"或者 npm run sort" C:/code/jsliang/src/docs"')
  .action((path: string) => {sortCatalog(`../${path}`); // 为了更便捷,先退一层到外边
  });

program.parse(process.argv);

3.3 编写排序功能

筹备好根底配置之后,只须要往 sortCatalog.ts 外面增加内容即可:

src/sortCatalog.ts

/**
 * @name 文件排序功能
 * @time 2021-05-22 16:08:06
 * @description 规定
   1. 零碎程序 1/10/2/21/3,心愿排序 1/2/3/10/21
   2. 插入文件 1/2/1-1,心愿排序 1/2/3(将 1-1 变成 2,2 变成 3)*/
import fs from 'fs';
import path from 'path';
import {IGNORE_PATH} from './config';

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

  files
    .filter((item => !IGNORE_PATH.includes(item))) // 过滤疏忽文件 / 文件夹
    .sort((a, b) =>
      Number((a.split('.')[0]).replace('-', '.'))
      - Number((b.split('.')[0]).replace('-', '.'))
    ) // 排序文件夹
    .forEach((item, index) => { // 遍历文件夹
      // 设置旧文件名称和新文件名称
      const oldFileName = item;
      const newFileName = `${index + 1}.${oldFileName.slice(oldFileName.indexOf('.') + 1)}`;

      // 设置旧文件门路和新文件门路
      const oldPath = `${filePath}/${oldFileName}`;
      const newPath = `${filePath}/${newFileName}`;

      // 判断文件格式
      const stat = fs.statSync(oldPath);

      // 判断是文件夹还是文件
      if (stat.isFile()) {fs.renameSync(oldPath, newPath); // 重命名文件
      } else if (stat.isDirectory()) {fs.renameSync(oldPath, newPath); // 重命名文件夹
        recursion(newPath, level + 1); // 递归文件夹
      }
    });
};

export const sortCatalog = (filePath: string): boolean => {
  // 绝对路径
  if (path.isAbsolute(filePath)) {recursion(filePath);
  } else { // 相对路径
    recursion(path.join(__dirname, filePath));
  }

  return true;
};

有小伙伴看完必定好奇 config.ts 是什么,其实就是全局配置而已:

/**
 * @name 默认的全局配置
 * @time 2021-05-22 16:12:21
 */
import path from 'path';

// 根底目录
export const BASE_PATH = path.join(__dirname, './docs');

// 疏忽目录
export const IGNORE_PATH = [
  '.vscode',
  'node_modules',
];

当咱们没有配置的时候,就给默认的配置。

3.4 运行内容

OK,筹备结束,就能够耍起来了。

以后 docs 下目录构造为:

- 1. 文章 a.md
- 10. 文章 d.md
- 2. 文章 b.md
- 3. 目录 c
  - 1-1. 目录 c2.md
  - 1. 目录 c1.md
  - 2. 目录 c3.md

而咱们运行 npm run sort "docs" 后,新目录列表变成了:

- 1. 文章 a.md
- 2. 文章 b.md
- 3. 目录 c
  - 1. 目录 c1.md
  - 2. 目录 c2.md
  - 3. 目录 c3.md
- 4. 文章 d.md

这样,咱们简略的案例就做好啦!是不是非常简单!

四 罕用 commander 配置

上面例举下咱们十分不想看,然而失常应用又常见的 commander 配置:

  • version:版本。用来设置命令程序的版本号

    • 用法:.version('x.y.z')
  • description:形容。用来设置命令的形容

    • 用法:.description('小工具指令清单')
  • option:选项。

    • 用法:.option('-n, --name <name>', 'your name', 'jsliang')
    • 第一个参数是选项定义,能够用 |,' ' 空格连贯,参数能够用 <>(必填)或者 [](选填)润饰
    • 第二个参数为选项形容
    • 第三个参数为选项参数默认值(可选)
  • command:命令

    • 用法:.command('init <path>', 'description')
    • command 用法略微简单,原则上承受 3 个参数,第一个为命令定义,第二个命令形容,第三个为命令辅助润饰对象
    • 第一个参数能够应用 <> 或者 [] 润饰命令参数
    • 第二个参数可选

      • 当没有第二个参数时,commander.js 将返回 Command 对象
      • 当带有第二个参数,将返回原型对象,并且没有显示调用 action(fn) 时,将会应用子命令模式
      • 子命令模式:./pm./pm-install./pm-search 等,这些子命令跟主命令在不同的文件中
    • 第三个参数个别不同,他能够设置是否显示应用的子命令模式
  • action:动作。用来设置命令执行的相干回调

    • 用法:.action(fn)
    • fn 能够接受命令的参数为函数形参,程序与 command() 中定义的程序统一
  • parse:解析 process.argv

    • 用法:program.parse(process.argv)
    • 这个 API 个别在最初调用,用来解析 process.argv

OK,commander 的简略介绍到此结束,咱们下期见!

五 参考文献

  • Github:commander
  • W3CSchool:应用 commander.js 做一个 Nodejs 命令行程序

jsliang 的文档库由 梁峻荣 采纳 常识共享 署名 - 非商业性应用 - 雷同形式共享 4.0 国内 许可协定 进行许可。<br/> 基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/> 本许可协定受权之外的应用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处取得。

退出移动版