1. 程式人生 > >使用 typescript 快速開發一個 cli

使用 typescript 快速開發一個 cli

cli 的全稱 command-line interface(命令列介面),也就是前端同學常用的腳手架,比如 [yo](https://yeoman.io/)、[vue cli](https://cli.vuejs.org/zh/guide/)、[react cli](https://github.com/facebook/create-react-app) 等。 cli 可以方便我們快速建立專案,下圖是引用 vue cli 的介紹: ![vue cli guide](https://img2020.cnblogs.com/blog/423657/202012/423657-20201202150232018-1502238969.png) ## 建立專案 執行下面的命令,建立一個專案: ```sh npm init ``` 執行命令完成後,可以看到專案根目錄只有一個 package.json 檔案。 ![demo1](https://img2020.cnblogs.com/blog/423657/202012/423657-20201202185344046-40286989.png) 在 package.json 檔案增加 bin 物件,並指定入口檔案 dist/index.js。 > 在命令列執行需要在入口檔案的第一行增加 `#!/usr/bin/env node`,告訴系統用 node 執行這個檔案。 ```json { "name": "cli-demo", "version": "0.0.1", "description": "cli demo", "keywords": [ "cli" ], "bin": { "cli-demo": "dist/index.js" } ... } ``` ## 安裝依賴 命令列工具,也會涉及到使用者互動的動作,那麼 node.js 是怎麼實現呢?早有大佬提供了非常好的庫,我們只要拿過來用,主要有兩個庫: - [commander](https://github.com/tj/commander.js):完整的 node.js 命令列解決方案。 - [inquirer](https://github.com/SBoudrias/Inquirer.js):互動式命令列工具。 將這兩個庫安裝到專案裡: ```sh yarn add commander inquirer ``` 由於是用 typescript 開發,再通過 rollup 打包,先安裝相關的依賴庫: ```sh yarn add typescript rollup rollup-plugin-terser rollup-plugin-typescript2 @types/inquirer -D ``` ## 配置 由於是用 typescript 開發,首先需要配置一下 tsconfig.json。 ```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` 寫在最前面。 ```js 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` 物件。 ```ts 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](https://github.com/tj/commander.js/blob/master/Readme_zh-CN.md)。 ```ts program .command('login') .description('模擬登入。') .action(() => { handleLogin() }) program.parse(process.argv) ``` 互動的話,用到前面說的 `inquirer` 庫,接收輸入的使用者名稱和密碼。選項的 `type` 的值有 `input`、`password`、`number`、`checkbox`、`editor`、`list`、`rawList`、`expand`、`confirm`,選項 `name` 是 `inquirer.prompt` 方法返回的物件,選項 `validate` 可用來驗證輸入是否符合規則。更多詳細介紹和使用,可移步:[https://github.com/SBoudrias/Inquirer.js/blob/master/README.md](https://github.com/SBoudrias/Inquirer.js/blob/master/README.md) > 如果選項 `type` 是 `password`,可通過 `mask` 設定掩碼。 ```ts const handleLogin = () => { // 配置互動的使用者名稱和密碼 const prompt = [ { type: 'input', name: 'userName', message: '使用者名稱:', validate: (value: string) => value.length > 0 || '使用者名稱不能為空' }, { type: 'password', name: 'password', message: '密碼:', mask: '