工作效率up! up! up! ,一起來實現一個Node.js-CLI開發工具吧。

前言

看如今火熱的前端框架,都有自己的CLI工具,例如Vue CLI,creat-react-app等等,搭建項目十分的方便。所以我也在想,要不也實現一個CLI工具,不一定要和前面幾個那樣高大上,但只要能提高工作的效率,就值得試一試。

初始化項目

首先,我們要打開CLI界面,用npm初始化一個項目:

npm init

package.json

{
  "name": "mycli",
  "version": "1.0.0",
  "description": "A cli-demo",
  "main": "index.js",
  "author": "xmanlin",
  "license": "MIT"
}

裏面一些用不上都已經去掉。

自定義命令

相信很多小夥伴在使用 npm 初始化項目的時候,或者在用 Vue CLI 搭建項目的時候,會發現它們有一個共同點——都有自己個性化的命令,感覺有點酷炫。那麼我們怎麼才能實現自己的命令呢,很簡單,在 package.json 添加 bin 

{
  "name": "mycli",
  "version": "1.0.0",
  "description": "A cli-demo",
  "main": "index.js",
  "bin": {
    "mycli": "./index.js"
  },
  "author": "xmanlin",
  "license": "MIT"
}

package.json中的 bin 的作用就是可以讓設置的 mycli 成爲一個可執行命令,而命令所執行的文件就是後面的 index.js ,這些都可以根據自己的想法來定。接着我們要繼續對 index.js 進行修改:

index.js

#!/usr/bin/env node

console.log("執行成功")

這裏的第一行很重要,這裏表明 index.js 是 node 可執行文件。

設置完成後,就可以全局安裝我們的CLI工具了:

npm install -g
+ mycli@1.0.0
added 1 package from 1 contributor in 0.12s

可以看見全局安裝成功,最後我們試試我們的命令:

mycli

輸出:

執行成功

交互式命令行

剛剛小夥伴們在進行命令行操作的時候,使用了不同選項的命令,例如 npm init  npm install -g ,其中包含一些交互式的,比如在初始化項目時, npm init 提供一些輸入問答形式的命令。接下來,我們就來爲自己的CLI工具添加這些類似的命令。

我們可以依賴兩個庫進行我們的開發:commander.jsInquirer.js

  • commander.js:完整的 node.js 命令行解決方案。我們可以利用它,快速的編寫我們的命令行,自定義化操作。
  • Inquirer.js:是常規交互式命令行用戶接口的集合,提供給 Node.js 一個方便嵌入,漂亮的命令行接口。我們可以用來快速進行交互式命令行的編寫。

這兩個庫的具體用法,這裏就過多的介紹,小夥伴們可以點擊上面名字的鏈接去熟悉一下,花不了太多時間,實際用起來也不難,後面那個沒有中文Readme,但是不妨礙大家會搜索呀~對不對。

定義選項

我們首先來實現類似於 npm -vnode -v這樣類似的命令選項。

首先安裝commander.js:

npm install commander

然後引入 commander.js 並對 index.js 進行修改

#!/usr/bin/env node

const { program } = require('commander');

// 字符串分割爲數組的方法
function strToArr(value, preValue){
    return value.split(',')
}
// cli版本
program.version(require('./package').version, '-v, --version', 'cli的最新版本');
// 設置選項
program
    .option('-d, --debug', '調試一下')
    .option('-l, --list <value>', '把字符串分割爲數組', strToArr)
    .action((options, command) => {
        // 進行邏輯處理
        if(options.debug) {
            console.log("調試成功")
        }
        if(options.list !== undefined) {
            console.log(options.list)
        }
    });

// 處理命令行輸入的參數
program.parse(process.argv);

我們來試試剛剛設置的命令選項:

mycli -d

輸出:

調試成功

輸入:

mycli -l 1,2,3

輸出:

[ '1', '2', '3' ]

在commander.js裏面已經給我們定義好了 --help 選項:

mycli -h

輸出:

Usage: index [options]

Options:
  -v, --version       cli的最新版本
  -d, --debug         調試一下
  -l, --list <value>  把字符串分割爲數組
  -h, --help          display help for command

利用 --help 選項,我們可以很清楚的瞭解到 mycli 中已有多少命令。

設置子命令

在項目開發中,我們有時會用到類似於 npm run xxx 這樣的命令,其中run就相當於npm的子命令。這裏我們也可以給mycli設置類似的子命令:

const { program } = require('commander');

...

// 創建文件命令行
program
    .command('create <filename>')
    .description('創建一個文件')
    .action((filename) => {
        console.log(filename)
    })
    
...
// 處理命令行輸入的參數
program.parse(process.argv);

command('create <filename>') 就是創建了一個 mycli 的 create 子命令,後面跟了一個必填參數。

輸入:

mycli create file

輸出

file

子命令創建成功。

利用命令行創建項目文件

我們現在能夠定義選項,設置命令了。接下來我們就可以實際做點東西,利用命令行來創建項目的文件。

簡單的設計一個流程:
創建文件流程

創建模板文件

我們先來創建一個templates文件夾,然後在裏面寫幾個常見的模板文件,這裏的模板文件運用到了模板字符串,結構如下:

模板文件

reactClass.js

module.exports = function (className) {
    return `
import * as React from 'react';

export class ${className} extends React.Component{
    constructor(props){
        super(props);

        this.state = {}
    }

    componentDidMount(){

    }

    render() {
        return (
            <div></div>
        )
    }
}
    ` 
}

vueTemplate.js

module.exports = function () {
    return `
    <template>
        <div></div>
    </template>
    <script>
    export default {
        data() {
            return {}
        }
        methods: {

        }
    }
    </sctipt>
    <style lang="scss" scoped>
    
    </style>
    `
}

index.js

const reactClass = require('./reactClass');
const vueTemplate = require('./vueTemplate');

module.exports = [
    { name: 'reactClass', src: reactClass },
    { name: 'vueTemplate', src: vueTemplate }
]

模板文件創建完成後,我們先把它放在一邊,待會兒才用的上。

創建交互命令行以及調用模板

當我們輸入 mycli create file 命令後,需要得到下圖中的效果,我們可以手動上下進行選擇,也就是可以被稱爲交互式的命令。

選擇

這裏就要用到我們上文中提到的另外一個庫——Inquirer.js

首先肯定需要進行安裝

npm install inquirer

引入並修改我們根目錄下的index.js:

#!/usr/bin/env node

const { program } = require('commander');
const inquirer = require('inquirer');

// 引入模板文件
const templates = require('./templates/index');

// 命令行選擇列表
let prompList = [
    {
        type:'list',
        name: 'template',
        message: '請選擇你想要生成的模板?',
        choices: templates,
        default: templates[0]
    }
]

...

// 創建文件命令行
program
    .command('create <filename>')
    .description('創建一個文件')
    .action(async (filename) => {
        const res = await inquirer.prompt(prompList)
        console.log(res)
    })

// 處理命令行輸入的參數
program.parse(process.argv);

接下來我們在命令行中輸入:

mycli create file

就可以得到上面所展示的效果

選擇

然後選擇第一個後回車:

選擇一

可以看到輸出了我們所選擇模板的名字。接下來就是進行實際文件的創建。

創建項目文件

創建文件則需要調用node.js的fs相關api,然後修改index.js:

// 處理文件
const fs = require("fs");

...

// 創建文件命令行
program
    .command('create <filename>')
    .description('創建一個文件')
    .action(async (filename) => {
        const res = await inquirer.prompt(prompList)
        if(res.template === 'reactClass') {
            templates.forEach((item) => {
                if(item.name === 'reactClass') {
                    fs.writeFile(`./${filename}.jsx`, item.src(filename), function(err) {
                        if(err) {
                            console.log('創建失敗:', err)
                        } else {
                            console.log(`創建文件成功!${filename}.jsx`);
                        }
                    })
                }
            })
        }
        if(res.template === 'vueTemplate') {
            templates.forEach((item) => {
                if(item.name === 'vueTemplate') {
                    fs.writeFile(`./${filename}.vue`, item.src(), function(err) {
                        if(err) {
                            console.log('創建失敗:', err)
                        } else {
                            console.log(`文件創建成功!${filename}`);
                        }
                    })
                }
            })
        } 
    })
    
...

我們再次在命令行中輸入mycli create file,然後選擇一個模板。

輸出:

創建文件成功!file.jsx

同時我們可以看見項目根目錄下面新增了一個file.jsx文件:

生成文件

打開file.jsx可以看見文件的類名也進行了相應的填寫。不僅如此,因爲我們的mycli是全局安裝的,所以可以說在電腦中任何位置,只要我們輸入 mycli create file ,我們在當前目錄下都能獲取到file.jsx文件。也就意味着,我們在開發一些項目的時候,不用在複製粘貼,刪刪改改。

直接一行命令就能搞定了~

更多功能

既然能創建文件,那麼能否創建文件夾?答案是肯定的,只需要繼續添加命令就行:

...

// 創建文件夾命令行
program
    .command('create-f <folder>')
    .description('創建一個文件夾')
    .action((folder) => {
        if(fs.existsSync(folder)) {
            console.log('文件夾已存在')
        } else {
            fs.mkdirSync(folder);
            console.log('文件夾創建成功')
        }
    });

...

然後在命令行中輸入mycli create-f xxx ,同樣可以創建想要命名文件夾。

最後

到此我們的 mycli 已經能進行創建文件,創建文件夾等等操作了。跟着敲一遍,一定會有收穫的。後續我們可以根據項目的實際情況編寫模板文件,自定義想要的命令,進行必要的拓展。這個就留給大家盡情的發揮啦~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章