CLI介紹
命令行界面(英語:command-line interface,縮寫:CLI),是在圖形用戶界面得到普及之前使用最為廣泛的用戶界面,它通常不支持鼠標,用戶通過鍵盤輸入指令,計算機接收到指令后,予以執行。
目前前端開發中,CLI是常用的工具。前端三大框架Vue、React、Angular都有對應的CLI,包括現在最流行的前端工程化的打包工具Webpack,也有對應的webpack-cli。
在現代的前端開發中,CLI提高了開發的效率。讓相應的前端開發者免去了大量的重復性操作,節省了大量的時間。CLI可以完成的功能,包括但不限于初始化生成對應的項目模板、執行特定的腳本文件、在項目中創建新的模塊 。下面來介紹一下前端工程師如何使用node.js來完成一個CLI。
創建項目
打開終端,創建moka-cli的目錄
1
|
mkdir moka-cli |
進入目錄,初始化項目
1
2
|
cd moka-cli npm init -y |
根目錄下新建bin目錄,并新建index.js文件,此時目錄結構如下
1
2
3
4
|
|-- moka-cli |-- package.json |-- bin |-- index.js |
開發cli需要借助commander、inquirer、chalk三個庫
1
|
npm install commander inquirer chalk --save |
修改package.json文件,使用es moudle,并新增moka命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package.json { "name" : "moka-cli" , "version" : "1.0.0" , "description" : "" , "main" : "index.js" , + "type" : "module" , + "bin" : { + "moka" : "./bin/index.js" + }, "scripts" : { "test" : "echo \"Error: no test specified\" && exit 1" }, "keywords" : [], "author" : "" , "license" : "ISC" , "dependencies" : { "chalk" : "^4.1.0" , "commander" : "^6.2.1" , "inquirer" : "^7.3.3" } } |
接下來需要在/bin/index.js中編程,在第一行添加如下代碼,這行代碼是告訴系統要用node來執行這個文件。
1
|
#!/usr/bin/env node |
再添加下面代碼
1
2
3
|
#!/usr/bin/env node console.log( 'moka-cli run!' ) |
然后在項目根目錄下全局安裝項目
1
|
npm install -g |
到這里,一個簡單的CLI就完成了,可以打開終端,在任意目錄下輸入moka,控制臺就會打印出對應的消息。
1
2
|
$ moka moka-cli run! |
第三方庫介紹
moka-cli希望實現可以創建前端開發模板的功能,可以使用moka命令來創建一個vue或react項目。
先來介紹一下使用到到第三方的庫
commander
文檔地址
用來實現命令的庫,在moka-cli中會用來實現create命令,實現用下面的命令行來創建一個vue-demo的項目
1
|
moka create vue vue-demo |
inquirer
文檔地址
實現用戶和終端交互的庫,在moka-cli中使用create指令的時候會詢問是否覆蓋已有項目
1
2
|
$ moka create vue moka-demo ? Template named moka-demo is already existed, are you sure to overwrite? (Y/n) |
chalk
文檔地址
可以在終端界面顯示出多種顏色的文本和背景
核心功能
在根目錄創建actions和templates目錄,目錄結構如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
|-- moka-cli |-- package-lock.json |-- package.json + |-- actions + | |-- create.js |-- bin | |-- index.js + |-- templates + |-- react + | |-- app.js + | |-- index.js + | |-- src + | |-- index.js + |-- vue + |-- app.js + |-- index.js + |-- src + |-- index.js |
templats下面用來存放通過CLI要生成的項目的模板,在這里先隨便創建幾個文件展示一下。
修改/bin/index.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#!/usr/bin/env node import create from '../actions/create.js' ; import commander from 'commander' ; const { program } = commander; program.version( '0.0.1' ); program .command( 'create <template> [name]' ) .description( '新建項目' ) .action(create); program.parse(process.argv); |
修改./actions/create.js文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
import fs from 'fs' ; import path from 'path' ; import chalk from 'chalk' ; import inquirer from 'inquirer' ; import { fileURLToPath } from 'url' ; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const TEMPLATES = [ 'vue' , 'react' ]; const targetPath = process.cwd(); function createTemplate(template, name) { const templatePath = path.join(__dirname, '../' , `templates/${template}`); function readAndCopyFile(parentPath, tempPath) { let files = fs.readdirSync(parentPath); files.forEach((file) => { let curPath = `${parentPath}/${file}`; let stat = fs.statSync(curPath); let filePath = `${targetPath}/${tempPath}/${file}`; if (stat.isDirectory()) { fs.mkdirSync(filePath); readAndCopyFile(`${parentPath}/${file}`, `${tempPath}/${file}`); } else { const contents = fs.readFileSync(curPath, 'utf8' ); fs.writeFileSync(filePath, contents, 'utf8' ); } }); } readAndCopyFile(templatePath, name); } function deleteTemplate(path) { if (fs.existsSync(path)) { fs.readdirSync(path).forEach( function (file, index) { var curPath = path + '/' + file; if (fs.lstatSync(curPath).isDirectory()) { // recurse deleteTemplate(curPath); } else { // delete file fs.unlinkSync(curPath); } }); fs.rmdirSync(path); } } export default function create(template, name) { if (!TEMPLATES.includes(template)) { console.log(chalk.red(`No ${template} Template`)); return ; } const projectPath = path.resolve(targetPath, name); if (fs.existsSync(projectPath)) { inquirer .prompt([ { name: 'template-overwrite' , type: 'confirm' , message: `Template named ${name} is already existed, are you sure to overwrite?`, validate: function (input) { if (input.lowerCase !== 'y' && input.lowerCase !== 'n' ) { return 'Please input y/n !' ; } else { return true ; } }, }, ]) .then((answers) => { // 如果確定覆蓋 if (answers[ 'template-overwrite' ]) { // 刪除文件夾 deleteTemplate(projectPath); console.log(chalk.yellow(`Template already existed , removing!`)); //創建新模塊文件夾 fs.mkdirSync(projectPath); createTemplate(template, name); console.log( chalk.green(`${template} template has been created successfully!`) ); } }) . catch ((err) => { console.log(chalk.red(err)); }); } else { fs.mkdirSync(projectPath); createTemplate(template, name); console.log( chalk.green(`${template} template has been created successfully!`) ); } } |
最后在項目根目錄運行以下命令,重新安裝一下moka-cli
1
|
npm install -g |
隨便找一個路徑,運行moka create <template> [name]命令來生成項目
1
|
moka create react react-demo |
效果如下,會在該目錄下生成一個react-demo的文件夾,里面存放的就是moka-cli項目中/templates/react下的所有
1
2
|
$ moka create react react-demo react template has been created successfully! |
如果在該目錄下繼續創建一個同名的項目,就會提示是否覆蓋,輸入y后繼續執行
1
2
3
4
|
$ moka create react react-demo ? Template named react-demo is already existed, are you sure to overwrite? Yes Template already existed , removing! react template has been created successfully! |
create命令的核心邏輯是通過node的fs模塊來復制/templates下的文件,然后將其放到指定的路徑下,具體實現直接看代碼就可以來。
總結
CLI是提示前端開發效率的重要工具,如果有長期維護的項目,開發一個CLI來完成一些重復性的工作,是一個不錯的選擇。
moka-cli還沒有上傳github,等過段時間完善一些/templates下的項目模板,我會在文章里補充項目地址。
到此這篇關于利用node.js開發cli的文章就介紹到這了,更多相關node.js開發cli內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://juejin.cn/post/6910886628265295885