- Notifications
You must be signed in to change notification settings - Fork449
Description
脚手架
vue-cli,create-react-app、react-native-cli 等都是非常优秀的脚手架,通过脚手架,我们可以快速初始化一个项目,无需自己从零开始一步步配置,有效提升开发体验。尽管这些脚手架非常优秀,但是未必是符合我们的实际应用的,我们可以定制一个属于自己的脚手架(或公司通用脚手架),来提升自己的开发效率。
脚手架的作用
- 减少重复性的工作,不需要复制其他项目再删除无关代码,或者从零创建一个项目和文件。
- 可以根据交互动态生成项目结构和配置文件。
- 多人协作更为方便,不需要把文件传来传去。
实现的功能
在开始之前,我们需要明确自己的脚手架需要哪些功能。vue init template-name project-name 、create-react-app project-name。我们这次编写的脚手架(eos-cli)具备以下能力(脚手架的名字爱叫啥叫啥,我选用了Eos黎明女神):
eos init template-name project-name根据远程模板,初始化一个项目(远程模板可配置)eos config set <key> <value>修改配置信息eos config get [<key>]查看配置信息eos --version查看当前版本号eos -h
大家可以自行扩展其它的commander,本篇文章旨在教大家如何实现一个脚手架。
本项目完整代码请戳(建议先clone代码):https://github.com/YvetteLau/Blog/tree/master/eos-cli
效果展示
初始化一个项目
修改.eosrc文件,从 vuejs-template 下载模板
需要使用的第三方库
- babel-cli/babel-env: 语法转换
- commander: 命令行工具
- download-git-repo: 用来下载远程模板
- ini: 格式转换
- inquirer: 交互式命令行工具
- ora: 显示loading动画
- chalk: 修改控制台输出内容样式
- log-symbols: 显示出 √ 或 × 等的图标
关于这些第三方库的说明,可以直接npm上查看相应的说明,此处不一一展开。
初始化项目
创建一个空项目(eos-cli),使用npm init 进行初始化。
安装依赖
npm install babel-cli babel-env chalk commander download-git-repo ini inquirer log-symbols ora
目录结构
├──bin│└──www//可执行文件├──dist├──...//生成文件└──src├──config.js//管理eos配置文件├──index.js//主流程入口文件├──init.js//init command├──main.js//入口文件└──utils├──constants.js//定义常量├──get.js//获取模板└──rc.js//配置文件├──.babelrc//babel配置文件├──package.json├──README.md
babel 配置
开发使用了ES6语法,使用babel 进行转义,
.bablerc
{"presets": [ ["env", {"targets": {"node":"current" } } ] ]}eos 命令
node.js 内置了对命令行操作的支持,package.json 中的bin 字段可以定义命令名和关联的执行文件。在package.json 中添加bin 字段
package.json
{"name":"eos-cli","version":"1.0.0","description":"脚手架","main":"index.js","bin": {"eos":"./bin/www" },"scripts": {"compile":"babel src -d dist","watch":"npm run compile -- --watch" }}www 文件
行首加入一行#!/usr/bin/env node 指定当前脚本由node.js进行解析
#! /usr/bin/env noderequire('../dist/main.js');
链接到全局环境
开发过程中为了方便调试,在当前的eos-cli 目录下执行npm link,将eos 命令链接到全局环境。
启动项目
npm run watch
处理命令行
利用commander 来处理命令行。
main
importprogramfrom'commander';import{VERSION}from'./utils/constants';importapplyfrom'./index';importchalkfrom'chalk';/** * eos commands * - config * - init */letactionMap={init:{description:'generate a new project from a template',usages:['eos init templateName projectName']},config:{alias:'cfg',description:'config .eosrc',usages:['eos config set <k> <v>','eos config get <k>','eos config remove <k>']},//other commands}// 添加 init / config 命令Object.keys(actionMap).forEach((action)=>{program.command(action).description(actionMap[action].description).alias(actionMap[action].alias)//别名.action(()=>{switch(action){case'config'://配置apply(action, ...process.argv.slice(3));break;case'init':apply(action, ...process.argv.slice(3));break;default:break;}});});functionhelp(){console.log('\r\nUsage:');Object.keys(actionMap).forEach((action)=>{actionMap[action].usages.forEach(usage=>{console.log(' - '+usage);});});console.log('\r');}program.usage('<command> [options]');// eos -hprogram.on('-h',help);program.on('--help',help);// eos -V VERSION 为 package.json 中的版本号program.version(VERSION,'-V --version').parse(process.argv);// eos 不带参数时if(!process.argv.slice(2).length){program.outputHelp(make_green);}functionmake_green(txt){returnchalk.green(txt);}
下载模板
download-git-repo 支持从 Github、Gitlab 下载远程仓库到本地。
get.js
import{getAll}from'./rc';importdownloadGitfrom'download-git-repo';exportconstdownloadLocal=async(templateName,projectName)=>{letconfig=awaitgetAll();letapi=`${config.registry}/${templateName}`;returnnewPromise((resolve,reject)=>{//projectName 为下载到的本地目录downloadGit(api,projectName,(err)=>{if(err){reject(err);}resolve();});});}
init 命令
命令行交互
在用户执行 init 命令后,向用户提出问题,接收用户的输入并作出相应的处理。命令行交互利用inquirer 来实现:
inquirer.prompt([{name:'description',message:'Please enter the project description: '},{name:'author',message:'Please enter the author name: '}]).then((answer)=>{//...});
视觉美化
在用户输入之后,开始下载模板,这时候使用ora 来提示用户正在下载模板,下载结束之后,也给出提示。
importorafrom'ora';letloading=ora('downloading template ...');loading.start();//downloadloading.succeed();//或 loading.fail();
init.js
import{downloadLocal}from'./utils/get';importorafrom'ora';importinquirerfrom'inquirer';importfsfrom'fs';importchalkfrom'chalk';importsymbolfrom'log-symbols';letinit=async(templateName,projectName)=>{//项目不存在if(!fs.existsSync(projectName)){//命令行交互inquirer.prompt([{name:'description',message:'Please enter the project description: '},{name:'author',message:'Please enter the author name: '}]).then(async(answer)=>{//下载模板 选择模板//通过配置文件,获取模板信息letloading=ora('downloading template ...');loading.start();downloadLocal(templateName,projectName).then(()=>{loading.succeed();constfileName=`${projectName}/package.json`;if(fs.existsSync(fileName)){constdata=fs.readFileSync(fileName).toString();letjson=JSON.parse(data);json.name=projectName;json.author=answer.author;json.description=answer.description;//修改项目文件夹中 package.json 文件fs.writeFileSync(fileName,JSON.stringify(json,null,'\t'),'utf-8');console.log(symbol.success,chalk.green('Project initialization finished!'));}},()=>{loading.fail();});});}else{//项目已经存在console.log(symbol.error,chalk.red('The project already exists'));}}module.exports=init;
config 配置
eos configset registry vuejs-templatesconfig 配置,支持我们使用其它仓库的模板,例如,我们可以使用 vuejs-templates 中的仓库作为模板。这样有一个好处:更新模板无需重新发布脚手架,使用者无需重新安装,并且可以自由选择下载目标。
config.js
// 管理 .eosrc 文件 (当前用户目录下)import{get,set,getAll,remove}from'./utils/rc';letconfig=async(action,key,value)=>{switch(action){case'get':if(key){letresult=awaitget(key);console.log(result);}else{letobj=awaitgetAll();Object.keys(obj).forEach(key=>{console.log(`${key}=${obj[key]}`);})}break;case'set':set(key,value);break;case'remove':remove(key);break;default:break;}}module.exports=config;
rc.js
.eosrc 文件的增删改查
import{RC,DEFAULTS}from'./constants';import{decode,encode}from'ini';import{promisify}from'util';importchalkfrom'chalk';importfsfrom'fs';constexits=promisify(fs.exists);constreadFile=promisify(fs.readFile);constwriteFile=promisify(fs.writeFile);//RC 是配置文件//DEFAULTS 是默认的配置exportconstget=async(key)=>{constexit=awaitexits(RC);letopts;if(exit){opts=awaitreadFile(RC,'utf8');opts=decode(opts);returnopts[key];}return'';}exportconstgetAll=async()=>{constexit=awaitexits(RC);letopts;if(exit){opts=awaitreadFile(RC,'utf8');opts=decode(opts);returnopts;}return{};}exportconstset=async(key,value)=>{constexit=awaitexits(RC);letopts;if(exit){opts=awaitreadFile(RC,'utf8');opts=decode(opts);if(!key){console.log(chalk.red(chalk.bold('Error:')),chalk.red('key is required'));return;}if(!value){console.log(chalk.red(chalk.bold('Error:')),chalk.red('value is required'));return;}Object.assign(opts,{[key]:value});}else{opts=Object.assign(DEFAULTS,{[key]:value});}awaitwriteFile(RC,encode(opts),'utf8');}exportconstremove=async(key)=>{constexit=awaitexits(RC);letopts;if(exit){opts=awaitreadFile(RC,'utf8');opts=decode(opts);deleteopts[key];awaitwriteFile(RC,encode(opts),'utf8');}}
发布
npm publish 将本脚手架发布至npm上。其它用户可以通过npm install eos-cli -g 全局安装。
即可使用eos 命令。
项目地址
本项目完整代码请戳:https://github.com/YvetteLau/Blog/tree/master/eos-cli
编写本文,虽然花费了一定时间,但是在这个过程中,我也学习到了很多知识,谢谢各位小伙伴愿意花费宝贵的时间阅读本文,如果本文给了您一点帮助或者是启发,请不要吝啬你的赞和Star,您的肯定是我前进的最大动力。
https://github.com/YvetteLau/Blog
参考文章:
[1]珠峰架构课(墙裂推荐)
[2] npm依赖文档(https://www.npmjs.com/package/download-git-repo)
关注公众号,加入技术交流群。