手写脚手架并发布

准备工作

首先准备一个模板,上传至github或者gitlab,本文以github为例

需要使用的第三方库

  • babel-cli/babel-env: 语法转换
  • commander: 命令行工具
  • download-git-repo: 用来下载远程模板
  • inquirer: 交互式命令行工具
  • ora: 显示loading动画
  • chalk: 修改控制台输出内容样式
  • log-symbols: 显示出 √ 或 × 等的图标

实现功能

  • xiami、xiami -h、xiami –help可显示帮助信息
  • xiami init templateName projectName 可从xiamijun/templateName 的远程仓库拉取模板

手写脚手架

新建空项目,npm init

安装依赖

npm install babel-cli babel-env chalk commander download-git-repo ini inquirer log-symbols ora –save

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
├── bin
│ └── www // 可执行文件
├── dist
├── ... // 生成文件
└── src
├── index.js // 主流程入口文件
├── init.js // init command
├── main.js //入口文件
└── utils
├── constants.js // 定义常量
├── get.js // 获取模板
├── .babelrc // babel配置文件
├── package.json
├── README.md

package.json添加如下字段:

1
2
3
4
5
6
7
"bin": {
"xiami": "./bin/www"
},
"scripts": {
"compile": "babel src -d dist",
"watch": "npm run compile -- --watch"
},

babel 配置

.bablerc文件:

1
2
3
4
5
6
7
8
9
10
11
12
{
"presets": [
[
"env",
{
"targets": {
"node": "current"
}
}
]
]
}

src/main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import program from 'commander'
import {
VERSION
} from './utils/constants'
import apply from './index'
import chalk from 'chalk'

// 定义action
let actionMap = {
init: {
description: 'generate a new project form a template',
usages: [
'xiami init templateName projectName'
]
}
}

// 描述、别名、回调
Object.keys(actionMap).forEach(action => {
program.command(action).description(actionMap[action].description).alias(actionMap[action].alias).action(() => {
switch (action) {
case 'init':
apply(action, ...process.argv.slice(3))
break;

default:
break;
}
})
})

// 帮助信息
function help() {
console.log('\r\nUsage:');
Object.keys(actionMap).forEach(action=>{
actionMap[action].usages.forEach(usage=>{
console.log(`-${usage}`);
})
})
console.log('\r');
}

program.usage('<command>[options]')

program.on('-h',help)
program.on('--help',help)

// 版本号
program.version(VERSION,'-v --version').parse(process.argv)

// 不带参数时
if (!process.argv.slice(2).length) {
program.outputHelp(make_green)
}

function make_green(text) {
return chalk.green(text)
}

src/index.js

1
2
3
4
5
6
// 主的流程控制
let apply = (action, ...args) => {
require(`./${action}`)(...args)
}

export default apply

src/init.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import {
downloadLocal
} from './utils/get'
import ora from 'ora'
import inquirer from 'inquirer'
import fs from 'fs'
import chalk from 'chalk'
import symbol from 'log-symbols'

let init = 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 => {
// 下载模板 选择模板
let loading = ora('downloading template...')
loading.start()
downloadLocal(templateName, projectName).then(() => {
loading.succeed()
const fileName = `${projectName}/package.json`
if (fs.existsSync(fileName)) {
const data = fs.readFileSync(fileName).toString()
let json = 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

src/utils/get.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {URL} from './constants'
import downloadGit from 'download-git-repo'

export const downloadLocal = (templateName, projectName) => {
let api = `${URL}/${templateName}`
return new Promise((resolve, reject) => {
//projectName 为下载到的本地目录
downloadGit(api, projectName, err => {
if (err) {
reject(err)
}
resolve()
})
})
}

src/utils/constants.js

1
2
3
4
5
import {version} from '../../package.json'

export const VERSION=version

export const URL = 'xiamijun'

bin/www

1
2
3
#! /usr/bin/env node

require('../dist/main.js');

调试

运行npm run watch 进行打包

运行npm link将当前的xiami命令链接到全局环境

发布

  • 首先切换至 https://registry.npmjs.org/ ,使用nrm或者npm config set
  • 登录npm,输入npm login,输入用户名、密码、邮箱
  • npm publish

项目地址

github地址:https://github.com/xiamijun/xiami-vue-cli

npm地址:https://www.npmjs.com/package/xiami-vue-cli