前面的文章分享了組件庫的開發、example、組件庫文檔,本文分享組件庫 cli 開發。 1 為什麼要開發組件庫 cli 回顧一個新組件的完整開發步驟: 1 在 packages 目錄下創建組件目錄 xxx: 1.1 使用 pnpm 初始化 package.json,修改 name 屬性; 1.2 ...
前面的文章分享了組件庫的開發、example、組件庫文檔,本文分享組件庫 cli 開發。
1 為什麼要開發組件庫 cli
回顧一個新組件的完整開發步驟:
1 在 packages 目錄下創建組件目錄 xxx:
1.1 使用 pnpm 初始化 package.json,修改 name 屬性;
1.2 在該目錄中創建 src 目錄和 index.ts 文件;
1.3 在 src 目錄下創建 types.ts 文件和 index.tsx / index.vue 文件;
2 在組件庫的入口模塊 packages/yyg-demo-ui:
2.1 使用 pnpm install 安裝 1.1 創建的 xxx;
2.2 在 packages/xxx-ui/index.ts 中引入 xxx,並添加到 components 數組中;
3 packages/scss/components/ 目錄:
3.1 在該目錄下創建 _xxx.module.scss;
3.2 在該目錄中的 index.scss 中引入 _xxx.module.scss;
4 組件庫文檔:
4.1 在 docs/components 目錄下創建 xxx.md 文件;
4.2 在 docs/demos 目錄下創建 xxx 目錄,併在該目錄中創建 xxx.vue 文件;
4.3 在 docs/components.ts 中添加組件菜單項;
該步驟是一個機械化的流程操作,涉及新建或修改十多個文件,費事費力,純屬體力活。這種情況下就可以使用工具替代咱們完成這些操作,開發一個 cli,執行命令、輸入組件名就自動創建組件,完成上述操作,如此便可以將註意力集中到組件和業務的開發中。
2 開發 cli 使用到的技術
開發 cli 的庫有很多,優雅哥在這裡使用的還是最傳統的技術棧,在下麵使用的這些庫時要註意版本號:
庫 | 版本 | 作用 |
---|---|---|
commander | ^9.4.1 | 接收輸入的命令,解析命令參數 |
chalk | 4.1.2 | 控制台輸出的文字顏色 |
inquirer | 8.2.5 | 命令行交互,在命令行提示用戶輸入,獲取到用戶輸入的內容 |
log-symbols | 4.1.0 | 控制台輸出的圖標,如 success、failure 等狀態 |
ora | 5.4.1 | 在控制台顯示 loading |
shelljs | ^0.8.5 | 執行 cmd 命令,如 cd、pnpm install 等 |
3 搭建 cli 開發框架
有了上面的知識準備,接下來就進入實戰 cli:
3.1 初始化 cli 模塊
在命令行中進入 cli 目錄,依舊使用 pnpm 初始化:
pnpm init
修改生成的 package.json 文件 name 屬性:
{
"name": "@yyg-demo-ui/cli",
"version": "1.0.0",
"description": "命令行工具",
"author": "程式員優雅哥",
"license": "ISC"
}
在 cli 目錄下創建 ts 配置文件 tsconfig.json:
{
"compilerOptions": {
"target": "es2015",
"lib": [
"es2015"
],
"module": "commonjs",
"rootDir": "./",
"allowJs": true,
"isolatedModules": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
在 cli 目錄下創建 index.ts 文件作為入口:
#!/usr/bin/env node
console.log('hello cli!')
該文件第一行不能省略,這句話的意思是使用 node 來執行這個文件,並且在 /use/bin/env 環境變數中去找 node 執行器。
3.2 ts-node 執行 ts 文件
有了入口文件,怎麼去執行它呢?當前 index.ts 中沒有任何 TypeScript 語法,可以使用 node index.js 來執行,但有 TypeScript 語法時,就需要 tsc 先編譯 ts 文件,再使用 node 命令來執行。這樣每次運行比較麻煩,慶幸可以使用 ts-node 來執行。
在 cli 目錄下按照 ts-node 為開發依賴:
pnpm install ts-node -D
可以嘗試在命令行中執行 ts-node index.ts。
直接這麼執行不優雅,優雅哥更寧願在 cli 的 package.json 添加 scripts:
"scripts": {
"gen": "ts-node ./index.ts create"
},
在上面的 gen 命令中,添加了一個參數 create,在後面會解析出這個參數。
重新在命令行執行:
pnpm run gen
控制台能正常輸出 hello cli!,ts 文件可以正常執行。
3.3 源碼目錄
上面創建的 index.ts 是命令執行的入口,現在咱們在 cli 中創建 src 目錄存放源碼,併在 src 中創建 index.ts 作為源碼的入口,首先在該文件中定義一個入口函數:
src/index.ts:
export const mainEntry = () => {
console.log('hello cli mainEntry')
}
在外層的 index.ts 中(cli/index.ts)調用該函數:
#!/usr/bin/env node
import { mainEntry } from './src'
mainEntry()
執行 pnpm run gen 測試程式是否正常運行。
3.4 參數解析
前面定義的 gen 命令攜帶了參數 create,要怎麼解析出這個參數呢?可以使用 commander 庫來完成。
在 cli 中安裝 commander:
pnpm install commander -D
修改 cli/src/index.ts 文件,使用 commander 來解析參數:
import { program } from 'commander'
export const mainEntry = () => {
console.log('hello cli mainEntry')
program.version(require('../package').version)
.usage('<command> [arguments]')
program.command('create')
.description('create a new component')
.alias('c')
.action(() => {
console.log('創建組件')
})
program.parse(process.argv)
if (!program.args.length) {
program.help()
}
}
如果直接執行 ts-node index.ts,會輸出命令使用幫助:
hello cli mainEntry
Usage: index <command> [arguments]
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
create|c create a new component
help [command] display help for command
執行 pnpm run gen (即 ts-node index.ts create),則會進入 create 命令的 action 回調函數中:
hello cli mainEntry
創建組件
在 cli/src/ 目錄下創建目錄 command,併在該目錄中創建 create-component.ts 文件,該文件用於處理參數為 create 時執行的內容(即創建組件相關的目錄文件等):
export const createComponent = () => {
console.log('創建新組建')
}
該文件導出了函數 createComponent,該函數的內部實現邏輯咱們在下一篇文章實現,本文先將 cli 架子搭起來。
修改 cli/src/index.ts 文件,首先引入 createComponent 函數,然後在 create 命令的 action 中調用它:
...
import { createComponent } from './command/create-component'
export const mainEntry = () => {
...
program.command('create')
...
action(createComponent)
...
}
執行 gen 命令時,就會調用到 createComponent 函數了。
3.5 用戶交互
在 createComponent 中,首先要提示組件開發人員輸入組件的名稱、中文名、組件類型(tsx、vue),這時候可以使用 inquirer 來實現。先在 cli 下安裝依賴,為了省事,咱把其他依賴一起安裝了:
pnpm install [email protected] [email protected] @types/[email protected] [email protected] [email protected] shelljs @types/shelljs -D
接著在 create-component.ts 中定義交互提示和變數名:
import inquirer, { QuestionCollection } from 'inquirer'
// 交互提示
const questions: QuestionCollection = [
{
name: 'componentName',
message: 'Input the component name: ',
default: ''
},
{
name: 'description',
message: 'Input the component description: ',
default: ''
},
{
type: 'list',
name: 'componentType',
message: 'Choose the component type: ',
choices: [
'tsx', 'vue'
]
}
]
最後在 createComponent 函數中使用 inquirer 實現交互提示信息:
const createNewComponent = (componentName: string, description: string, componentType: string) => {
console.log(componentName, description, componentType)
}
export const createComponent = () => {
inquirer.prompt(questions).then(({ componentName, description, componentType }) => {
createNewComponent(componentName, description, componentType)
})
}
執行 pnpm run gen 運行效果如下:
到這裡,組件庫 cli 的架子咱們就搭建起來了,後面只需要實現 createNewComponent 函數即可,在該函數中創建目錄、文件、執行命令等。
3.6 美化日誌
本文最後咱們玩點優雅的東西。如果直接使用 console.log 輸出,只有黑白色,不優雅,咱可以使用 chalk 改變輸出的文字的顏色,並通過 log-symbols 加些圖標。首先在 src 下創建 util 目錄,在該目錄中創建 log-utils.ts 文件,用來封裝優雅版的 console.log:
import chalk from 'chalk'
import logSymbols from 'log-symbols'
export const r = (msg: string, showIcon = true): void => {
if (showIcon) {
console.log(logSymbols.error, chalk.red(msg))
} else {
console.log(chalk.red(msg))
}
}
export const g = (msg: string, showIcon = true): void => {
if (showIcon) {
console.log(logSymbols.success, chalk.green(msg))
} else {
console.log(chalk.green(msg))
}
}
export const c = (msg: string): void => {
console.log(logSymbols.info, chalk.cyan(msg))
}
該文件導出了 r、g、c 三個函數,其他文件使用時非常簡便:
c('yyg-demo-ui cli 工具')
本文搭建好 cli 的架子,下文將完成 createNewComponent 函數,實現組件創建的全過程。
感謝閱讀,公號同名“程式員優雅哥”。