【不要說話】假前甜品腳手架

CLI

命令行界面(command-line inteface),縮寫CLI,對於咱們大前端來講,cli的使用已經家常便飯了,如框架三巨頭的Angular CLIcreate-react-appVue CLI,每個都坐擁成千上萬的死忠。html

需求

當咱們建立一個新的項目的時候,每每是用三大佬的腳手架生成新的項目再進行開發,可是這樣的問題是初始化模版都是官方本身維護的,關於咱們常常使用的代碼片斷、發佈配置、layout佈局等等都得從新搞,(vue cli 可使用 --preset參數指定模版路徑, react的沒有,angular沒有用過,暫時不知道)。前端

框架咱們採用dvaumi,它倆都是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。

bin字段

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

commander 是一個node命令行的解決方案,能夠簡化咱們的開發流程。

demo

#! /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] <>表示強制用戶輸入 []表示可選參數 用戶沒有輸入使用默認值或者undefined
  • action 命令觸發時候的回調

當咱們什麼命令也沒有輸入的時候,會觸發幫助信息,咱們直接調用grogram.help()函數便可,grogram會根據咱們定義的命令自動生成幫助信息

更多關於grogram的內容,請去官網查看,這點很少的東西,咱們就夠用了。

inquirer

inquirer 給用戶提供了一個漂亮的界面和提出問題流的方式。

demo

#! /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

chalk是命令行美化的模塊,而且支持模板輸出。

demo

#! /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

download-git-repo是支持從git倉庫中下載源碼的庫,細化到分支級別,支持GitHub, GitLab, Bitbucket。

api

download(repository, destination, options, callback)

  • repository 倉庫地址
  • destination 項目保存地址
  • options 可選參數
    • { clone: false } 默認爲fasle 表示是否使用git clone,默認使用 http download 方式進行下載
  • callback 回調函數

demo

以github爲例,重點說下repository這個參數,倉庫地址是咱們的用戶名/項目名

代碼以下

const download = require('download-git-repo')

download('Ortonzhang/template-dva', './test', (err) => {
    err ? console.log('err is: %s', err) : console.log('success')
})
複製代碼

整合

首先建立咱們的模版,使用umidva建立好項目後,發送到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的registry
  • 版本號重複
    • 修改package.json裏面的version字段
  • 沒有權限
    • 執行npm adduser,輸入用戶名、密碼、郵箱

參考

inquirer.js —— 一個用戶與命令行交互的工具

跟着老司機玩轉Node命令行

總結

以上內容,咱們就編寫發佈了一個屬於本身的npm包。麻雀雖小,五臟倒也全,本文代碼請戳github

相關文章
相關標籤/搜索