编写属于自己的脚手架

编写自己的脚手架,提高新建项目效率

脚手架

vue-cli, create-react-appreact-native-cli 等都是非常优秀的脚手架。

通过脚手架,我们可以快速初始化一个项目,无需自己从零配置,有效提升开发体验。

尽管社区的脚手架非常优秀,但是未必是符合我们的实际应用的,我们可以定制一个**属于自己的脚手架(或公司通用脚手架)**,来提升自己的开发效率。

脚手架的作用

  • 无需复制粘贴项目且删除无关代码,或从零创建一个项目和文件
  • 可以根据交互动态生成项目结构和配置文件
  • 减少重复性劳动
  • 统一使用规范(代码风格、文件结构等)

如果我们开发属于自己的脚手架,自己定制自己的模板,就可以把复制粘贴的人工流程转换为cli的自动化流程。

分析脚手架my-cli实现

一个能简单交互且能生成模板的cli,需要具备的功能

  • cli --version 可以查看 cli 版本
  • cli --help 可以查看帮助文档
  • cli create xxx 可以创建一个项目

脚手架my-cli开发流程

  1. 初始化项目
    • 创建项目my-cli,配置基本信息
    • npm link项目到全局,实现本地终端调试
  2. 项目开发
    • 基础指令配置:--help --version
    • 复杂指令配置:create指令
    • 命令行交互:基于方inquirer实现命令行交互
    • 获取项目模板、版本列表
    • 下载项目并在当前目录生成模板文件
  3. 发布npm包

初始化脚手架

1、创建my-cli文件夹,并初始化项目

1
npm init -y

2、创建bin文件夹,并创建cli.js。在package.json中指定命令路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "my-cli",
"version": "1.0.0",
"description": "",
"main": "bin/cli.js",
"bin": {
"my-cli": "bin/cli.js" // 指定命令的入口文件
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"inquirer": "^9.1.4"
}
}

3、编辑脚本文件cli.js

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

console.log("my-cli");

文件头部的#! /usr/bin/env node作用

  • #! 符号的名称叫 Shebang,用于指定脚本的解释程序
  • 开发 npm 包时,需要在入口文件指定该指令,否则会抛出 No such file or directory 错误

4、npm link到全局,就可以在全局使用命令

5、终端运行my-cli命令

image-20230215000410258

使用的脚手架工具库

commander 命令行指令配置

commander 可以自定义命令行指令

1
2
// 安装依赖
npm install commander
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#! /usr/bin/env node

const program = require('commander')

program
.version('0.1.0')
.command('create <name>')
.description('create a new project')
.action(name => {
// 打印命令行输入的值
console.log("project name is " + name)
})

program.parse()

命令行输出内容

1
2
3
4
5
6
7
8
9
10
11
~/Desktop/my-cli/my-cli

Usage: my-cli [options] [command]

Options:
-V, --version output the version number
-h, --help display help for command

Commands:
create <name> create a new project
help [command] display help for command

chalk 命令行美化工具

chalk 可以美化我们在命令行中输出内容的样式,例如实现多种颜色,不同样式的命令行提示

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

const program = require('commander')
const chalk = require('chalk')

program
.version('0.1.0')
.command('create <name>')
.description('create a new project')
.action(name => {
// 文本样式
console.log("project name is " + chalk.bold(name))
// 颜色
console.log("project name is " + chalk.cyan(name))
console.log("project name is " + chalk.green(name))
// 背景色
console.log("project name is " + chalk.bgRed(name))
// 使用RGB颜色输出
console.log("project name is " + chalk.rgb(4, 156, 219).underline(name));
console.log("project name is " + chalk.hex('#049CDB').bold(name));
console.log("project name is " + chalk.bgHex('#049CDB').bold(name))
})

program.parse()

image-20230215002709513

inquirer 命令行交互工具

inquirer 可以进行交互,可以帮助我们选择一些默认配置

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
#! /usr/bin/env node

import inquirer from 'inquirer';

inquirer.prompt([
{
name: "vue",
// 多选交互功能
// 单选将这里修改为 list 即可
type: "checkbox",
message: "Check the features needed for your project:",
choices: [
{
name: "Babel",
checked: true,
},
{
name: "TypeScript",
},
{
name: "Progressive Web App (PWA) Support",
},
{
name: "Router",
},
],
},
]).then((data) => {
console.log(data);
});

image-20230215003657262

ora 命令行loading效果

ora 文档

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

import ora from 'ora';
// 定义一个loading
const spinner = ora("Loading unicorns");
// 启动loading
spinner.start();
setTimeout(() => {
spinner.color = "yellow";
spinner.text = "Loading rainbows";

setTimeout(() => {
// 加载状态修改
spinner.stop() // 停止
spinner.succeed('Loading succeed'); // 成功 ✔
// spinner.fail(text?); 失败 ✖
// spinner.warn(text?); 提示 ⚠
// spinner.info(text?); 信息 ℹ
}, 2000);
}, 3000);


image-20230215004257717

download-git-repo 命令行下载工具

download-git-repo 可以从 git 中下载并提取一个 git repository

1
2
3
4
5
6
7
8
/**
* @param {String} repo 仓库地址
* @param {String} dest 仓库下载后存放路径
* @param {Object} opts 配置参数
* @param {Function} fn 回调函数
*/

function download(repo, dest, opts, fn) {}

download-git-repo不支持Promise,需要在外面包装一层