手撸一个脚手架

懒人成就世界

最近总是忙于帮别人解决问题,大家隔着电脑屏幕聊啊聊,总是不够真实,驴头不对马嘴,浪费时间。得,还是请对方把代码端上来,跑一跑看看啥错误。服务到位的朋友会去掉node_modules丢个压缩包过来,不那么讲究的童鞋就整个项目全都丢过来,几十上百m,啊,100k的小水管要下到地老天荒,费事!么得办法,还是得想个办法,省点事。说来也巧,同事在搞个基于lerna的工具脚手架,为了方便,直接采用的vue-cli的模式,这提醒了我,我也可以搞个脚手架,方便你我他呀,那就干脆手撸一个脚手架吧。

说干咱就干了。首先说下想法,项目很多,那我肯定是希望一个命令就能帮我下好项目,但是这样也比较low,git也能做到,那就做的比git多一点吧,很多项目master分支都是个摆设,通常dev或develop才是项目的真实代码,那我这个脚手架就要支持分支选择,最后一想,干脆,node_modules我也给你下了吧。这样,这个脚手架的大致想法就齐活啦。

开干!

npm init肯定是first step,生成了package.json之后,就开始考虑需要啥依赖了,首先涉及到shell,不管是git操作还是下载依赖都需要执行sh,自然就想到了shell.js, 不过查阅了一下资料,node原生就提供sh执行工具child_process,允许我们创建子进程去操作,并且既有异步也有同步的,可以返回promise。说到脚手架,其实一直很好奇,那些和使用者进行的交互是如何做到的,以前C++java可以等待用户输入然后执行下一步,js这样还真的比较少见,查了资料,我发现,很多是使用co库去做的,乍看co,好像没看见过,但是一看用法,哎,不对,这哥们眼熟。

co(function* (){
   let data1 = yield readFile('path1')
   console.log(data1)//显示path1的文件的内容
   let data2 =  yield readFile('path2')
   console.log(data2)//显示path2的文件内容
})

co基于generator函数,相当于generator函数的一个自动执行器,如上,yield执行完之后,co自动执行了next() 指向下一个console函数,简单理解就很像async/await,阮一峰有几篇讲异步同步的文章,从头看到尾的话应该会很有收获,附在文尾。

有了异步转同步还不够,我们要能获取用户输入呀!

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

看到上面这块是不是就很眼熟啦,让你输入用户名,密码,多行描述,是否确认,获取用户输入的功能就是靠co-prompt来给我们提供的。co搭配co-prompt再加上child_process,我们执行构建的需求就差不多完成了。

'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 √ 构建完成!'))
    process.exit()  
  } 
}

module.exports = () => {
  // generator函数
   co(function *(){
    // 处理用户输入的交互
    let name = yield prompt('项目名:')
    let gitUrl = yield prompt('Git地址:')
    let branch = yield prompt('分支是(默认是master):') 
    let install = yield prompt('使用yarn还是npm或是其他进行install(默认是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 √ 构建完成!'))
        process.exit() 
      })

    })

    // nextSh(sh1, sh2)
  })
}

chalk是一个控制字体显示颜色的库,也可以使用另一个spin库,更美观一点,后续应该会加上。不过这不是重点,上面的代码中,实现了两种执行exec的方式,嵌套和async/await,不过async/await需要对child_process进行util.promisify包装,这样它的返回才是一个promise。理论上,co的这一套也是可以被async/await去取代的,正所谓万法皆通,正是这个道理。

执行文件我们写好了,怎么把它挂载到node命令上去呢?那来写个命令文件吧。目前Commandernode.js命令行界面的完整解决方案,具体它的用法可以去查阅官网。

#!/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('构建一个已有git项目')
    .alias('i')
    .action(()=>{
      // 执行init
      require('../command/init')()
    })

  //  必须加上这些,才可以执行commands
    program.parse(process.argv)

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

commander一定要执行parse命令,process.argv中包含program中传入的argsoptions,这个不被执行,那commander没有意义。

npm如何publish我就不在这里赘述了,网上很多。有一点要说下,我们开发的过程中npm指向的仓库可能是淘宝或是其他的源,发布时就会报错,执行下npm config set registry [http://registry.npmjs.org/](http://registry.npmjs.org/)就好了。
最后的最后,我们想像执行vue-cli init一样,直接initPack i就执行命令,还需要在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指向的是当前项目下bin目录下的文件,当我们下载cli时,就会自动把initPack挂载到全局路径上,就可以直接指向initPack i了。

一个完整的脚手架就撸好了,使用也非常简单,懒也要有懒的收获。


image.png

[ 代码传送门 ] (https://github.com/fatehuanchen/huanchen-cli.git)
相关文章: 阮一峰教学:http://www.ruanyifeng.com/blog/2015/05/async.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容