创建简单的npm脚手架

前言

vue-cli, webpack-cli 等脚手架是不是用起来爱不释手?自己写了个模版每次来回复制粘贴代码是不是很难维护?如果你是对前端、Node操作有一定的了解,同时也存在以上疑问,那就请尽情阅读尝试吧!

本篇文章按照al-block-cli举例, al-block-cli是一个基于vueelementUI而集成的一个开发模版,可安装进行使用

依赖

  1. Commander.js 命令行工具
  2. download-git-repo git仓库代码下载
  3. chalk 命令行输出样式美化
  4. Inquirer.js 命令行交互
  5. ora命令行加载中效果

根据上方的依赖插件即可以看出,其实脚手架就是一个利用终端命令将仓库中的代码拉取到本地的工具所以还没有模版代码的同学赶紧去创建个

项目准备

初始化

1
$ npm init

根据提示完成初始化搭建,如果不清楚如何配置可以直接回车

安装依赖

1
$ npm install commander download-git-repo chalk inquirer ora --save

构建结构

创建bincommands文件夹以及配置文件templates.json。bin文件夹为可执行命令入口目录,commands则负责编写一些命令交互

最终目录结构

1
2
3
4
5
6
- al-block-cli
| - bin
| - commands
| - node_modules
| - package.json
| - templates.json

编写代码

配置文件

输入默认需要的配置,如这里需要github的仓库地址和命令行的名称

1
2
3
4
5
6
{
"init": {
"name": "init",
"path": "Alisdon/al-block-template"
}
}

入口文件

新建al-block-cli文件,并在其第一行加入

1
#! /usr/bin/env node

此行为了防止操作系统用户没有将node装在默认的/usr/bin路径里。当系统看到这一行的时候,首先会到env设置里查找node的安装路径,再调用对应路径下的解释器程序完成操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env node

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

const program = require('commander');

program
.version(require('../package').version);

program
.usage('<command>');

program.command('init')
.description('create a new project')
.alias('i')
.action(() => {
require('../commands/init')
});

program.parse(process.argv);

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

命令交互

新建init.js文件表示命令init

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
const { prompt } = require('inquirer');
const program = require('commander');
const chalk = require('chalk');
const download = require('download-git-repo');
const ora = require('ora');
const fs = require('fs');

const option = program.parse(process.argv).args[0];
const question = [
{
type: 'input',
name: 'name',
message: 'Project name',
default: typeof option === 'string' ? option : 'al-block-template',
filter (val) {
return val.trim()
},
validate (val) {
const validate = (val.trim().split(" ")).length === 1;
return validate || 'Project name is not allowed to have spaces ';
},
transformer (val) {
return val;
}
},
{
type: 'input',
name: 'description',
message: 'Project description',
default: 'Vue project',
validate () {
return true;
},
transformer(val) {
return val;
}
},
{
type: 'input',
name: 'author',
message: 'Author',
default: '',
validate () {
return true;
},
transformer(val) {
return val;
}
}
];

module.exports = prompt(question).then(({name, description, author}) => {
const gitPlace = require('../templates').init.path;
const projectName = name;
const spinner = ora('Downloading please wait...');

spinner.start();
download(`${gitPlace}`, `./${projectName}`, (err) => {
if (err) {
console.log(chalk.red(err));
process.exit()
}

fs.readFile(`./${projectName}/package.json`, 'utf8', function (err, data) {
if(err) {
spinner.stop();
console.error(err);
return;
}

const packageJson = JSON.parse(data);
packageJson.name = name;
packageJson.description = description;
packageJson.author = author;

fs.writeFile(`./${projectName}/package.json`, JSON.stringify(packageJson, null, 2), 'utf8', function (err) {
if(err) {
spinner.stop();
console.error(err);
} else {
spinner.stop();
console.log(chalk.green('project init successfully!'))
console.log(`
${chalk.yellow(`cd ${name}`)}
${chalk.yellow('npm install')}
${chalk.yellow('npm run dev')}
`);
}
});
});
})
});

测试发布

测试

至此,一个简单的脚手架(壳)就已经完成了,为了查看在编写过程中是否出错,我们现在本地进行测试

1
$ node bin/al-block-cli

如果没有报错,出现了熟悉的命令行,那就说明成功了

发布

发布之前我们需要做个小调整,观察其他脚手架工具他们都是以自己独特的key值进行搭建,对此我们可以在package.json里面配置bin对象

1
2
3
"bin": {
"al-block-cli": "bin/al-block-cli"
}

这里需要注意bin/后面的al-block-cli,这个路径是由入口文件的路径确定,如果你是建的al-block-cli.js则此处应该配置bin/al-block-cli.js,本篇是创建的没有后缀名的文件

修改后生成的最终package.json

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
{
"name": "al-block-cli",
"version": "1.0.0",
"description": "al-block-cli",
"keywords": [
"vue",
"al-block",
"al-block-cli"
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"al-block-cli": "bin/al-block-cli"
},
"preferGlobal": true,
"author": "Alisdon [920124512@qq.com]",
"license": "MIT",
"dependencies": {
"chalk": "^2.4.1",
"commander": "^2.19.0",
"download-git-repo": "^1.1.0",
"inquirer": "^6.2.1",
"ora": "^3.0.0"
}
}

对比文件内容,如果没有问题我们就开始发布了

1
2
$ npm login
$ npm publish