命令行界面(command-line inteface),縮寫CLI,對於咱們大前端來講,cli的使用已經家常便飯了,如框架三巨頭的Angular CLI、create-react-app、Vue CLI,每個都坐擁成千上萬的死忠。html
當咱們建立一個新的項目的時候,每每是用三大佬的腳手架生成新的項目再進行開發,可是這樣的問題是初始化模版都是官方本身維護的,關於咱們常常使用的代碼片斷、發佈配置、layout佈局等等都得從新搞,(vue cli 可使用 --preset
參數指定模版路徑, react的沒有,angular沒有用過,暫時不知道)。前端
框架咱們採用dva和umi,它倆都是react的應用框架。vue
流程大概以下,接下來開始咱們的擼碼之旅。node
咱們建立個項目,來實現超級簡單的一個cli,獲取用戶輸入姓名,並向大佬問好。react
咱們的cli叫rollins-cli
,爲何叫rollins
, 由於Seth Rollins啊, 前the shield
成員。git
Burn it down!github
好了,繼續寫咱們的代碼npm
建立node項目三板斧json
mkdir rollins-cli
cd $_
npm init -y
複製代碼
touch index.js
, 建立index.js,寫入如下內容windows
#! /usr/bin/env node
const readline = require('readline')
/**
* @param { String } input 顯示的文案
* @returns { Promise } Promise對象
*/
const getCommandParams = input => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
return new Promise((resolve, reject) => {
rl.question(input, data => {
rl.close()
resolve(data)
})
})
}
const getUserInput = (input= '來了,老弟,你叫啥?\n') => {
getCommandParams(input).then(data => {
data ? console.log(`原來是${data} 大佬,失敬失敬`) : getUserInput('你不說我就一直問\n你叫啥?\n')
})
}
getUserInput()
複製代碼
運行效果以下
萬里長征第一步,以上代碼咱們就已經實現了一個簡易的CLI。
在package.json
文件中添加bin
字段, bin
字段用來指定各個內部命令對應的可執行文件的位置。
{
"bin": {
"rollins": "./bin/index.js"
}
}
複製代碼
上面代碼說明rollins
命令的執行文件在bin
文件夾下的index.js
# 建立bin目錄
mkdir bin
# 將index.js文件移動到bin目錄下
mv index.js ./bin
複製代碼
執行npm link
命令
會將定義的rollins
字段拷貝到全局安裝的node_modules文件夾內,並建立一個軟鏈接。
在全局node的bin
目錄中一樣也建立了軟鏈接
而後咱們就能夠在全局中使用rollins
這個命令了。
一樣是完美運行,接下來讓咱們招兵買馬繼續咱們的革命之旅。
執行npm i commander inquirer chalk ora -S
,來壯大咱們的勢力。
接下來,依次介紹登場的各位豪傑
commander 是一個node命令行的解決方案,能夠簡化咱們的開發流程。
#! /usr/bin/env node
const program = require('commander')
program
.version('0.0.1')
.command('init')
.alias('i')
.description('初始化')
.option('-a, --name <name>', '參數介紹', '我是默認值')
.action(option => {
console.log('init命令被調用')
})
program.parse(process.argv)
// 輸入爲空時 顯示幫助信息
if(!program.args.length){
program.help()
}
複製代碼
簡單說下上面命令
version
版本command
定義命令alias
定義命令的簡寫description
命令描述option
傳遞給命令的參數
-a
參數名簡寫--name
參數名<name>
或者 [name]
<>
表示強制用戶輸入 []
表示可選參數 用戶沒有輸入使用默認值或者undefinedaction
命令觸發時候的回調當咱們什麼命令也沒有輸入的時候,會觸發幫助信息,咱們直接調用grogram.help()
函數便可,grogram
會根據咱們定義的命令自動生成幫助信息
更多關於grogram的內容,請去官網查看,這點很少的東西,咱們就夠用了。
inquirer 給用戶提供了一個漂亮的界面和提出問題流的方式。
#! /usr/bin/env node
const { prompt } = require('inquirer')
const promptList = [
{
type: 'input',
name: 'name',
message: '請輸入文件夾名',
filter: function(val) {
return val.trim()
},
validate: function(val) {
return !!val.trim() ? true : '請輸入名稱'
}
},
{
type: 'list',
name: 'template',
message: '請選擇系統',
choices: [
'Mac',
'windows',
'Linux'
]
}
]
prompt(promptList).then(params => {
console.log(params)
})
複製代碼
chalk是命令行美化的模塊,而且支持模板輸出。
#! /usr/bin/env node
const chalk = require('chalk')
console.log(`${chalk.yellow(`${process.cwd()}`)}`)
console.log(`
${chalk.cyan(`
${chalk.gray('$')} cd test
${chalk.gray('$')} npm install
${chalk.gray('$')} npm start
`)}
`)
複製代碼
輸出以下
download-git-repo是支持從git倉庫中下載源碼的庫,細化到分支級別,支持GitHub, GitLab, Bitbucket。
download(repository, destination, options, callback)
{ clone: false }
默認爲fasle 表示是否使用git clone
,默認使用 http download 方式進行下載以github爲例,重點說下repository這個參數,倉庫地址是咱們的用戶名/項目名
代碼以下
const download = require('download-git-repo')
download('Ortonzhang/template-dva', './test', (err) => {
err ? console.log('err is: %s', err) : console.log('success')
})
複製代碼
首先建立咱們的模版,使用umi
和dva
建立好項目後,發送到github
, 也能夠基於別的框架進行建立項目。
建立template.json
, 寫入模版地址
{
"umi": "Ortonzhang/template-umi",
"dva": "Ortonzhang/template-dva"
}
複製代碼
將上面咱們的demo整合起來,完成腳手架的所有代碼。
#! /usr/bin/env node
const program = require('commander')
const { prompt } = require('inquirer')
const chalk = require('chalk')
const download = require('download-git-repo')
const fs = require('fs')
// 用來實現loading效果
const ora = require('ora')
const { promisify } = require('util');
const path = require('path')
// 使用 promisify 設置成promise
const readFileAsync = promisify(fs.readFile);
const writeFileAsync = promisify(fs.writeFile)
// 模版配置文件
const templateConfig = require(`${__dirname}/../template`)
// 獲取當前設備的路徑分割符號
const sep = path.sep
// 設置問題
const promptList = [
{
type: 'input',
name: 'name',
message: '請輸入文件夾名',
filter: function(val) {
return val.trim()
},
validate: function(val) {
return !!val.trim() ? true : '請輸入名稱'
}
},
{
type: 'list',
name: 'template',
message: '請選擇模版',
choices: [
'dva',
'umi'
]
}
]
const create = async () => {
// 獲取用戶輸入 拿到項目名和模版
let { name, template } = await prompt(promptList)
// 判斷Linux和windows 設置不一樣的分隔符
let step = sep === '/' ? '/' : '\\'
console.log(`
${chalk.white(`✨ Creating project in ${chalk.yellow(`${process.cwd()}${step}${name}`)}.`)}
`)
const spinner = ora('🗃 Initializing git repository...');
spinner.start()
// 下載模版
await downloadPromise(`${templateConfig[template]}`, `${name}`)
// 獲取模版的package.json文件數據
let stringPackage = await readFileAsync(`./${name}/package.json`, {encoding: 'utf8'})
// 轉化成json
let jsonPackage = JSON.parse(stringPackage)
// 設置名稱
jsonPackage.name = `${name}`
// 建立新的json數據
const updateJson = JSON.stringify(jsonPackage, null, 2)
// 寫入新的package.json
await writeFileAsync(`./${name}/package.json`, updateJson)
spinner.stop();
console.log(`
${chalk.green(`🎉 Successfully created project ${chalk.yellow(`${name}`)}.`)}
${chalk.white('👉 Get started with the following commands:')}
${chalk.cyan(`
${chalk.gray('$')} cd ${name}
${chalk.gray('$')} npm install
${chalk.gray('$')} npm start
`)}
`);
}
/**
* 下載模版
* @param {String} path 模版路徑
* @param {String} name 項目名稱
* @return {Promise} promise對象
*/
const downloadPromise = (path, name) => {
return new Promise((resolve, reject) => {
download(path, name, (err) => {
if(err){
console.log(chalk.red(err))
reject()
}
resolve()
})
})
}
program
// 版本號
.version(require('../package').version)
// 設置命令
.command('create')
//設置別名
.alias('c')
// 描述
.description('建立新項目')
// 動做
.action(option => {
create()
})
program.parse(process.argv)
// 輸入爲空時 顯示幫助信息
if(!program.args.length){
program.help()
}
複製代碼
以上就是rollins-cli
的所有代碼,發佈到npm
上就能夠了。
執行npm login
, 登錄npm輸入用戶名密碼
執行npm publish
,進行發佈操做。
npm config set registry https://registry.npmjs.org/
修改npm的registrynpm adduser
,輸入用戶名、密碼、郵箱以上內容,咱們就編寫發佈了一個屬於本身的npm包。麻雀雖小,五臟倒也全,本文代碼請戳github。