本篇是前端工程化之依托模板搭建企业级脚手架(cli)。通过脚手架,我们可以快速初始化一个项目,无需自己从零开始一步步…
一、背景
1.为什么
通过脚手架,我们可以快速初始化一个项目,无需自己从零开始一步步配置,有效提升开发体验。
当然,社区已经贡献了vue-cli
、create-react-app
、react-native-cli
等非常优秀的脚手架,
但是,这些脚手架未必完全符合我们的实际应用,特别是作为企业通用脚手架而言,所以我们需要定制自己的脚手架,来提升开发效率。
2.脚手架的作用
- 减少重复性工作:不需要复制其他项目再删除无关代码,或者从零创建一个项目和文件。
- 多套模板:可以根据交互动态生成项目结构和配置文件。
- 协作:多人协作更为方便,不需要把文件传来传去。
本项目完整代码地址,在文章末尾。
二、提供的功能
明确脚手架(@lianpf/create-app-cli
)需要提供的功能:
ca init <template-name> <project-name>
根据远程模板,初始化一个项目(远程模板可配置)ca config set <template-name> <repository>
设置模板信息(暂未实现)ca config get templates
查看模板配置信息ca cfg get templates
同上ca -v
查看当前版本号ca -h
帮助
如果有其他需求,可根据需求,自行拓展commander
三、配置搭建cli
1.依赖的第三方库
@babel/cli
、@babel/core
和@babel/preset-env
: 语法转换commander
: 命令行工具download-git-repo
: 用来下载远程模板- ini: 配置项格式转换
- 『暂时没用』一般用于物理机本地配置项
inquirer
: 交互式命令行工具ora
: 显示node
命令环境loading
动画chalk
: 修改控制台输出内容样式log-symbols
: 显示出√
或×
等的图标ascii-table
: 以表格形式展示数据,比如:模板列表
关于以上第三方
package
的介绍,可直接npm.org
上查看相应的说明
2.搭建cli
2.1 初始化项目
创建一个空项目(
create-app-cli
)
create-app-cli $ npm init // 项目初始化
安装依赖
create-app-cli $ npm install @babel/cli @babel/core @babel/preset-env chalk commander download-git-repo ini inquirer log-symbols ora -D
目录结构
.
├── bin
│ └── www // 可执行文件
├── lib
├── ... // 生成文件
├── src
│ ├── config.js // 管理 @lianpf/create-app-cli 配置文件
│ ├── index.js // 主流程入口文件
│ ├── init.js // init command
│ ├── main.js // 入口文件
│ └── utils
│ ├── constants.js // 定义常量
│ ├── download.js // 模板远程仓库下载
│ └── rc.js // 配置文件
├── templates.json // 模板配置文件
├── .babelrc // babel配置文件
├── package.json
└── README.md
babel
配置:开发使用了ES6语法,使用 babel 进行转义
.bablerc
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
},
"@babel/preset-react" // react 语法
]
],
"plugins": [
"transform-class-properties",
"transform-decorators",
"transform-react-constant-elements",
"transform-react-inline-elements"
]
}
2.2 @lianpf/create-app-cli
开发环境搭建
2.2.1 脚手架开发:启动命令配置
node.js
内置了对命令行操作的支持,package.json
中的bin
字段可以定义命令名和关联的执行文件。在package.json
中添加 bin
字段
package.json
{
"name": "@lianpf/create-app-cli",
"version": "0.2.1",
"description": "A cli to help create a project",
"main": "index.js",
"bin": {
"ca": "./bin/www"
},
"scripts": {
"compile": "babel src -d lib",
"watch": "npm run compile -- --watch"
}
}
www 文件
行首加入一行#!/usr/bin/env node
指定当前脚本由node.js
进行解析
#! /usr/bin/env node
require('../lib/main.js');
2.2.2 脚手架测试:链接到全局环境
开发过程中为了方便调试,在当前的create-app-cli
目录下执行 npm link
,将ca
命令链接到全局环境
2.2.3 脚手架开发:启动项目
create-app-cli $ npm run watch
2.3 @lianpf/create-app-cli
命令行处理
利用commander
来处理命令行
/create-app-cli/src/main.js
import program from 'commander';
import { VERSION, make_success, make_fail } from './utils/constants';
import apply from './index';
/**
* ca commands
* - config
* - init
* - v
* - h
*/
let actionMap = {
// init 命令配置
init: {
...
},
// config 命令配置
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;
}
});
});
function help() {
// -h 参数是,遍历列出所有命令
...
}
program.usage('<command> [options]');
program.on('-h', help);
program.on('--help', help);
program.version(VERSION, '-v, --version').parse(process.argv);
// ca 不带参数时
if (!process.argv.slice(2).length) {
program.outputHelp(make_success);
}
2.4 @lianpf/create-app-cli
下载模板
download-git-repo
支持从Github、Gitlab
下载远程仓库到本地
/create-app-cli/src/util/download.js
import downloadGit from 'download-git-repo';
import { templateConfig, hasTemplate } from './constants';
export const downloadLocal = async (templateName, projectName) => {
const _hasTemplate = hasTemplate(templateName)
let api = ''
// 判断是否存在模板
if (_hasTemplate) {
// 获取 模板下载地址
api = `${templateConfig[templateName].type}:${templateConfig[templateName].user}/${templateConfig[templateName].repository}#${templateConfig[templateName].branch}`;
}
return new Promise((resolve, reject) => {
// projectName 为下载到的本地目录
downloadGit(api, projectName, { clone: true }, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
constants.js
获取template
配置
/create-app-cli/src/util/constants.js
...
import templates from '../../templates.json'
...
// template config
export const templateConfig = templates
// 判断是否存在当前模板
export const hasTemplate = (templateName) => {
return Object.keys(templateConfig).indexOf(templateName) > -1
}
...
静态template
配置
/create-app-cli/template.json
{
"react-template": {
"type": "https://github.com",
"user": "xxx",
"repository": "xxx",
"branch": "master",
"templateName": "react-template",
"keyWords": "react、webpack"
},
...
}
注意,这里没有实现
ca config set <template-name> <repository>
设置模板命令,且企业级脚手架对应模板相对固定,故我们将模板预先内置到template.json
文件中
2.5 @lianpf/create-app-cli
交互式命令和美化
2.5.1 命令行交互
用户执行init
命令后,向用户提出问题,接收用户的输入并作出相应的处理。命令行交互利用inquirer
实现:
/create-app-cli/src/init.js
inquirer.prompt([
{
name: 'description',
message: 'Please enter the project description: '
},
{
name: 'author',
message: 'Please enter the author name: '
}
]).then((answer) => {
//...
});
类似于:
npm init
命令执行后,项目初始化,会问询你作者、版本信息、描述等添加到package.json
配置文件的交互操作。
2.5.2 视觉美化
在上一步,用户输入后,开始下载模板,此时使用ora
提示用户正在下载模板,以及下载结束后给出相应提示。
/create-app-cli/src/init.js
...
import ora from 'ora';
...
let loading = ora('downloading template ...');
loading.start();
//download
loading.succeed(); //或 loading.fail();
2.6 @lianpf/create-app-cli
获取模板配置
config配置
支持使用其它仓库作为模板。这样使用者可以自由选择下载目标
/create-app-cli/src/config.js
import { get } from './utils/rc';
import { make_success, make_fail, make_warn } from './utils/constants';
let config = async (action, key) => {
switch (action) {
case 'get':
if (key) {
let result = await get(key);
if (result.code === 0) {
console.log(make_success(result.message));
} else {
console.log(make_fail(result.message));
}
} else {
console.log(make_warn('Command does not exist!'));
}
break;
// set 模板命令暂未实现,需通过 template.json 配置
// case 'set':
// set(key, value);
// break;
// case 'remove':
// remove(key);
// break;
default:
console.log(make_warn('Command does not exist!'));
break;
}
}
module.exports = config;
rc.js
负责对本地物理机『模板配置文件』的增删改查,这里改造为对脚手架模板配置文件template.json
的查询
/create-app-cli/src/util/rc.js
import { configCommand, make_success, make_warn, templateConfig } from './constants';
import AsciiTable from 'ascii-table'
// constants 配置文件
export const get = async (key) => {
...
if (opts.indexOf(key) !== -1) {
switch (key) {
case 'templates':
// code、message、data处理
...
break;
default:
...
break;
}
}
...
console.log(make_success(templateTable.toString()))
return {
code,
data: {},
message
};
}
四、发布cli
npm publish
脚手架发布。- 其它用户可通过
npm install @lianpf/create-app-cli -g
全局安装,即可使用ca
命令。
仓库源码
谢谢各位花费宝贵的时间阅读本文,以下是源码仓库地址,如果本文给了您一点帮助或者是启发,请动动小手点进Github仓库,不要吝啬你的Star,您的肯定是我前进的最大动力
参考
最后, 希望大家早日实现:成为编程高手的伟大梦想!
欢迎交流~
本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!