使用 typescript 快速開發一個 cli

Jason Long發表於2020-12-08

cli 的全稱 command-line interface(命令列介面),也就是前端同學常用的腳手架,比如 yovue clireact cli 等。

cli 可以方便我們快速建立專案,下圖是引用 vue cli 的介紹:
vue cli guide

建立專案

執行下面的命令,建立一個專案:

npm init

執行命令完成後,可以看到專案根目錄只有一個 package.json 檔案。

demo1

在 package.json 檔案增加 bin 物件,並指定入口檔案 dist/index.js。

在命令列執行需要在入口檔案的第一行增加 #!/usr/bin/env node,告訴系統用 node 執行這個檔案。

{
  "name": "cli-demo",
  "version": "0.0.1",
  "description": "cli demo",
  "keywords": [
    "cli"
  ],
  "bin": {
    "cli-demo": "dist/index.js"
  }
 ...
}

安裝依賴

命令列工具,也會涉及到使用者互動的動作,那麼 node.js 是怎麼實現呢?早有大佬提供了非常好的庫,我們只要拿過來用,主要有兩個庫:

  • commander:完整的 node.js 命令列解決方案。
  • inquirer:互動式命令列工具。

將這兩個庫安裝到專案裡:

yarn add commander inquirer

由於是用 typescript 開發,再通過 rollup 打包,先安裝相關的依賴庫:

yarn add typescript rollup rollup-plugin-terser rollup-plugin-typescript2 @types/inquirer -D

配置

由於是用 typescript 開發,首先需要配置一下 tsconfig.json。

{
  "compilerOptions": {
    "target": "ES6",
    "module": "ESNext",
    "sourceMap": false,
    "declaration": false,
    "outDir": "./dist",
    "moduleResolution": "Node",
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "removeComments": false,
    "importHelpers": true,
    "strict": true,
    "lib": ["ES6", "DOM"]
  },
  "include": ["src"]
}

接下來在根目錄增加一個 rollup.config.js,把 typescript 程式碼編譯成 javascript 程式碼。前面提到的要在第一行增加 #!/usr/bin/env node 來告訴系統用 node 執行,那麼可以在 rollup.config.js 的 banner 選項,把 #!/usr/bin/env node 寫在最前面。

import typescript from 'typescript'
import json from '@rollup/plugin-json'
import { terser } from 'rollup-plugin-terser'
import typescript2 from 'rollup-plugin-typescript2'

import { dependencies } from './package.json'

const external = Object.keys(dependencies || '')
const globals = external.reduce((prev, current) => {
  const newPrev = prev

  newPrev[current] = current
  return newPrev
}, {})

const defaultConfig = {
  input: './src/index.ts',
  output: {
    file: './dist/index.js',
    format: 'cjs',
    banner: '#!/usr/bin/env node',
    globals
  },
  external,
  plugins: [
    typescript2({
      exclude: 'node_modules/**',
      useTsconfigDeclarationDir: true,
      typescript,
      tsconfig: './tsconfig.json'
    }),
    json(),
    terser()
  ]
}

export default defaultConfig

實現一個簡單的 cli

在根目錄建立一個 src 資料夾,然後再建立一個 index.ts

新增引用

新增引用並例項化 Command 物件。

import { Command } from 'commander'
import pkg from '../package.json'

const program = new Command(pkg.name)

自定義命令

實現一個可互動的自定義命令,模擬在終端(命令列)的登入功能。使用 command 方法建立一個命令,description 可以用來描述這個命令的作用,登入處理邏輯則寫在 action 方法裡。最後使用 parse(process.argv) 方法,解析命令。更多詳細介紹和使用,可移步:https://github.com/tj/commander.js/blob/master/Readme_zh-CN.md

program
    .command('login')
    .description('模擬登入。')
    .action(() => {
       handleLogin()
    })

program.parse(process.argv)

互動的話,用到前面說的 inquirer 庫,接收輸入的使用者名稱和密碼。選項的 type 的值有 inputpasswordnumbercheckboxeditorlistrawListexpandconfirm,選項 nameinquirer.prompt 方法返回的物件,選項 validate 可用來驗證輸入是否符合規則。更多詳細介紹和使用,可移步:https://github.com/SBoudrias/Inquirer.js/blob/master/README.md

如果選項 typepassword,可通過 mask 設定掩碼。

const handleLogin = () => {
  // 配置互動的使用者名稱和密碼
 const prompt = [
    {
      type: 'input',
      name: 'userName',
      message: '使用者名稱:',
      validate: (value: string) => value.length > 0 || '使用者名稱不能為空'
    },
    {
      type: 'password',
      name: 'password',
      message: '密碼:',
      mask: '? ',
      validate: (value: string) => value.length > 0 || '密碼不能為空'
    }
  ]

  inquirer.prompt(prompt).then(({ userName, password }) => {
    if (userName === 'demo' || password === '123456') {
      console.log('登入成功')
      return
    }
    console.log('使用者名稱或密碼錯誤')
  })
}

其他

一個 cli 工具,幫助資訊也是必須的,可以通過 on('--help') 修改自定義幫助資訊。

必須在 parse 方法之前。

program.on('--help', () => {
   console.log('\n執行 cli-demo -h | --help 檢視命令使用。\n')
})

然後再來修改一下,沒有輸入任何引數的時候,會出現錯誤,可以使用 exitOverride 方法重新退出,在終端(命令列)輸出幫助資訊。

program.exitOverride()

try {
  program.parse(process.argv)
} catch (error) {
  program.outputHelp()
}

到這裡,一個簡單的 cli 工具完成了,先本地來測試下看看。在終端(命令列)輸入 npm link,生成一個全域性軟連線,可以方便除錯和測試。

show demo

轉載請標註來源: https://www.cnblogs.com/JasonLong/p/14075724.html

相關文章