手?jǐn)]一個(gè)腳手架

懶人成就世界

最近總是忙于幫別人解決問題,大家隔著電腦屏幕聊啊聊绘搞,總是不夠真實(shí),驢頭不對(duì)馬嘴乍恐,浪費(fèi)時(shí)間默蚌。得,還是請(qǐng)對(duì)方把代碼端上來漱办,跑一跑看看啥錯(cuò)誤这刷。服務(wù)到位的朋友會(huì)去掉node_modules丟個(gè)壓縮包過來,不那么講究的童鞋就整個(gè)項(xiàng)目全都丟過來娩井,幾十上百m暇屋,啊,100k的小水管要下到地老天荒洞辣,費(fèi)事咐刨!么得辦法,還是得想個(gè)辦法屋彪,省點(diǎn)事所宰。說來也巧,同事在搞個(gè)基于lerna的工具腳手架畜挥,為了方便仔粥,直接采用的vue-cli的模式,這提醒了我蟹但,我也可以搞個(gè)腳手架躯泰,方便你我他呀,那就干脆手?jǐn)]一個(gè)腳手架吧华糖。

說干咱就干了麦向。首先說下想法,項(xiàng)目很多客叉,那我肯定是希望一個(gè)命令就能幫我下好項(xiàng)目诵竭,但是這樣也比較low,git也能做到兼搏,那就做的比git多一點(diǎn)吧卵慰,很多項(xiàng)目master分支都是個(gè)擺設(shè),通常dev或develop才是項(xiàng)目的真實(shí)代碼佛呻,那我這個(gè)腳手架就要支持分支選擇裳朋,最后一想,干脆吓著,node_modules我也給你下了吧鲤嫡。這樣送挑,這個(gè)腳手架的大致想法就齊活啦。

開干暖眼!

npm init肯定是first step惕耕,生成了package.json之后,就開始考慮需要啥依賴了罢荡,首先涉及到shell赡突,不管是git操作還是下載依賴都需要執(zhí)行sh,自然就想到了shell.js区赵, 不過查閱了一下資料惭缰,node原生就提供sh執(zhí)行工具child_process,允許我們創(chuàng)建子進(jìn)程去操作笼才,并且既有異步也有同步的漱受,可以返回promise。說到腳手架骡送,其實(shí)一直很好奇昂羡,那些和使用者進(jìn)行的交互是如何做到的,以前C++java可以等待用戶輸入然后執(zhí)行下一步摔踱,js這樣還真的比較少見虐先,查了資料,我發(fā)現(xiàn)派敷,很多是使用co庫去做的蛹批,乍看co,好像沒看見過篮愉,但是一看用法腐芍,哎,不對(duì)试躏,這哥們眼熟猪勇。

co(function* (){
   let data1 = yield readFile('path1')
   console.log(data1)//顯示path1的文件的內(nèi)容
   let data2 =  yield readFile('path2')
   console.log(data2)//顯示path2的文件內(nèi)容
})

co基于generator函數(shù),相當(dāng)于generator函數(shù)的一個(gè)自動(dòng)執(zhí)行器颠蕴,如上泣刹,yield執(zhí)行完之后,co自動(dòng)執(zhí)行了next() 指向下一個(gè)console函數(shù)犀被,簡單理解就很像async/await项玛,阮一峰有幾篇講異步同步的文章,從頭看到尾的話應(yīng)該會(huì)很有收獲弱判,附在文尾。

有了異步轉(zhuǎn)同步還不夠锥惋,我們要能獲取用戶輸入呀昌腰!

var name = yield prompt('username: ');
var pass = yield password('password: ');
var desc = yield multiline('description: ');
var ok = yield confirm('are you sure? ');

看到上面這塊是不是就很眼熟啦开伏,讓你輸入用戶名,密碼遭商,多行描述固灵,是否確認(rèn),獲取用戶輸入的功能就是靠co-prompt來給我們提供的劫流。co搭配co-prompt再加上child_process巫玻,我們執(zhí)行構(gòu)建的需求就差不多完成了。

'use strict'

// const exec = require('child_process').exec
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const co = require('co')
const prompt = require('co-prompt')
const chalk = require('chalk')


async function nextSh(sh1, sh2) {
  console.log(chalk.white('\n 開始拉取代碼...'))
  const { error } = await exec(sh1);
  if(error) {
    console.log(error)
    process.exit()
  }else {
    console.log(chalk.green('\n √ 拉取代碼成功!'))
    console.log(chalk.green('\n 開始install...'))
  }
  const { error : error1  } = await exec(sh2);
  if(error1) {
    console.log(error1)
    process.exit()
  }else {
    console.log(chalk.green('\n √ 構(gòu)建完成!'))
    process.exit()  
  } 
}

module.exports = () => {
  // generator函數(shù)
   co(function *(){
    // 處理用戶輸入的交互
    let name = yield prompt('項(xiàng)目名:')
    let gitUrl = yield prompt('Git地址:')
    let branch = yield prompt('分支是(默認(rèn)是master):') 
    let install = yield prompt('使用yarn還是npm或是其他進(jìn)行install(默認(rèn)是npm):')

    branch = branch || 'master'

    install = install || 'npm'

    let sh1 = `git clone -b ${branch} ${gitUrl} ${name}`
    let sh2 = `cd ${name} && ${install} install`

    console.log(chalk.white('\n 開始拉取代碼...'))

    exec(sh1, (error) => {
      if(error){
        console.log(error)
        process.exit()
      }
      console.log(chalk.green('\n √ 拉取代碼成功!'))
      console.log(chalk.white('\n 開始install...'))

      exec(sh2, (error) => {
        if(error){
          console.log(error)
          process.exit()
        }
        console.log(chalk.green('\n √ 構(gòu)建完成!'))
        process.exit() 
      })

    })

    // nextSh(sh1, sh2)
  })
}

chalk是一個(gè)控制字體顯示顏色的庫祠汇,也可以使用另一個(gè)spin庫仍秤,更美觀一點(diǎn),后續(xù)應(yīng)該會(huì)加上可很。不過這不是重點(diǎn)诗力,上面的代碼中,實(shí)現(xiàn)了兩種執(zhí)行exec的方式我抠,嵌套和async/await苇本,不過async/await需要對(duì)child_process進(jìn)行util.promisify包裝,這樣它的返回才是一個(gè)promise菜拓。理論上瓣窄,co的這一套也是可以被async/await去取代的,正所謂萬法皆通纳鼎,正是這個(gè)道理俺夕。

執(zhí)行文件我們寫好了,怎么把它掛載到node命令上去呢喷橙?那來寫個(gè)命令文件吧啥么。目前Commandernode.js命令行界面的完整解決方案,具體它的用法可以去查閱官網(wǎng)贰逾。

#!/usr/bin/env node --harmony

'use strict'

process.env.NODE_PATH = __dirname + '/../node_modules/'

const program = require('commander')

// 獲取version
program.version(require('../package').version)

program.usage('<command>')

program
    .command('init')
    .description('構(gòu)建一個(gè)已有g(shù)it項(xiàng)目')
    .alias('i')
    .action(()=>{
      // 執(zhí)行init
      require('../command/init')()
    })

  //  必須加上這些悬荣,才可以執(zhí)行commands
    program.parse(process.argv)

    if (!program.args.length) {
        program.help()
    }

commander一定要執(zhí)行parse命令,process.argv中包含program中傳入的argsoptions疙剑,這個(gè)不被執(zhí)行氯迂,那commander沒有意義。

npm如何publish我就不在這里贅述了言缤,網(wǎng)上很多嚼蚀。有一點(diǎn)要說下,我們開發(fā)的過程中npm指向的倉庫可能是淘寶或是其他的源管挟,發(fā)布時(shí)就會(huì)報(bào)錯(cuò)轿曙,執(zhí)行下npm config set registry [http://registry.npmjs.org/](http://registry.npmjs.org/)就好了。
最后的最后守谓,我們想像執(zhí)行vue-cli init一樣,直接initPack i就執(zhí)行命令斋荞,還需要在package .json中修改bin

{
  "name": "huanchen-cli",
  "version": "1.0.3",
  "description": "自制clidemo",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "cli"
  ],
  "author": "1540226204@qq.com",
  "license": "ISC",
  "repository": {
    "type": "git",
    "url": "https://github.com/fatehuanchen/huanchen-cli.git"
  },
  "bin": {
    "initPack": "bin/initPack"
  },
  "dependencies": {
    "chalk": "^2.4.2",
    "co": "^4.6.0",
    "co-prompt": "^1.0.0",
    "commander": "^2.19.0"
  }
}

可以看的平酿,bin下的initPack指向的是當(dāng)前項(xiàng)目下bin目錄下的文件,當(dāng)我們下載cli時(shí)悦陋,就會(huì)自動(dòng)把initPack掛載到全局路徑上,就可以直接指向initPack i了叨恨。

一個(gè)完整的腳手架就擼好了,使用也非常簡單痒钝,懶也要有懶的收獲。


image.png

[ 代碼傳送門 ] (https://github.com/fatehuanchen/huanchen-cli.git)
相關(guān)文章: 阮一峰教學(xué):http://www.ruanyifeng.com/blog/2015/05/async.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末送矩,一起剝皮案震驚了整個(gè)濱河市蚕甥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌栋荸,老刑警劉巖菇怀,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異晌块,居然都是意外死亡爱沟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門匆背,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呼伸,“玉大人,你說我怎么就攤上這事钝尸±ㄏ恚” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵珍促,是天一觀的道長铃辖。 經(jīng)常有香客問我,道長猪叙,這世上最難降的妖魔是什么娇斩? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任仁卷,我火速辦了婚禮,結(jié)果婚禮上成洗,老公的妹妹穿的比我還像新娘五督。我一直安慰自己,他們只是感情好瓶殃,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著副签,像睡著了一般遥椿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上淆储,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天冠场,我揣著相機(jī)與錄音,去河邊找鬼本砰。 笑死碴裙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的点额。 我是一名探鬼主播舔株,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼还棱!你這毒婦竟也來了载慈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對(duì)情侶失蹤珍手,失蹤者是張志新(化名)和其女友劉穎办铡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寡具,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡童叠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年拯钻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撰豺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡亩歹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亭姥,到底是詐尸還是另有隱情顾稀,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布粮揉,位于F島的核電站抚笔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏辐宾。R本人自食惡果不足惜膨蛮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一鸽疾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧制肮,春花似錦、人聲如沸综液。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽附帽。三九已至井誉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喳钟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國打工奔则, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酬蹋。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓除嘹,卻偏偏與公主長得像岸蜗,于是被迫代替她去往敵國和親叠蝇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容