脚手架实现
前置准备
初始化
- 安装 node, 建议 16 以上的版本
新建一个文件夹,
npm init
初始化,并在 package.json 文件中添加bin
字段声明命令,指向命令执行的 js 文件1
2
3
4
5
6
7
8
9
10
11
12
13
14{
"name": "ny-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"bin": {
"ny": "bin/cli.js"
}
}bin/cli.js
添加
#!/usr/bin/env node
, 告知 os 此文件以 node 形式运行1
2
3
console.log("Hello World");
npm link
未发布到 npm 仓库之前,需要本地调试。npm link 之后,执行
ny-cli
正常输出即成功
- npm link: 链接到全局
- npm ls -g —depth=0: 查看全局已链接的包,检查是否 link 成功
- npm rm —global pkgName: 删除 link 的包
monorepo
npm link 调试有几个问题,推荐使用 monorepo 风格的脚手架
- 多个 Node.js 版本同时存在可能会出错
- 软连接错误删除
- link 失败不会报错并且会回退到直接从 npm 仓库查找同名的包进行安装,导致可能安装错误的包
- 子工程 package
1 | { |
- pnpm 中使用 workspace: pnpm 只会解析存在工作空间内的包,不会去下载安装 npm 上的包
- 在子工程中执行
pnpm i
, 将只会在子工程内部安装ny-cli
, 而不会在全局安装
准备
- 全局安装 pnpm:
npm install pnpm -g
新建文件夹并
pnpm init
初始化,创建 pnpm-workspace.yaml 文件1
2
3
4# 声明 packages 和 examples 文件夹中子工程是同属一个工作空间,可被其它子工程引用
packages:
- "packages/*"
- "examples/*"新建 packages 文件夹,并在此创建 ny-cli 文件夹,并
pnpm init
,在生成的 package.json 中配置bin
字段。同级创建 bin 文件夹及 cli 文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{
"name": "ny-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.6.2",
"bin": {
"ny": "./bin/cli.js"
}
}1
2
3
4// cli.js
#!/usr/bin/env node
console.log('Hello World');新建 examples 文件夹,并在此创建 app 文件夹,并
pnpm init
,在生成的 package.json 中配置dependencies
字段和scripts
脚本命令1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"ny": "ny"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.6.2",
"dependencies": {
"ny-cli": "workspace:*"
}
}在根文件目录执行
pnpm i
,完成后在 app 文件目录下执行pnpm ny
,终端输出Hello World
即说明搭建成功。整体目录如下
模块实现
命令参数
可以使用
commander
或yargs
来解析参数,本文档采取 commander 解析参数,yargs
方式可参考 monorepo 脚手架搭建教程
1 | const program = require("commander"); |
交互模块
使用
inquirer
处理询问式交互,建议安装 v8 版本,高版本使用会出一些奇怪的错误
1 | const Inquirer = require(inquirer); |
仓库模块
远程仓库拉取
download-git-repo
库
1 | const { library } = asws; |
直接创建
shell
命令
1 | const { exec, execSync } = require("child_process"); |
复制仓库文件
copy-dir
库
将 template 文件夹里的模板复制到项目里,比如packages/ny-cli/template
复制到examples/app/src/common
__dirname
: 脚本的物理地址,固定不变的路径process.cwd()
: 命令执行的当前目录路径
1 | const copydir = require("copy-dir"); |
后处理模块
模板下载完成后需要根据之前的创建参数调整模板相关配置项
通过方法修改
1 | function changeInfo(name) { |
动态配置模板
mustache
库,可参考 monorepo 脚手架搭建教程
美化模块
chalk
1 | const chalk = require("chalk"); |
figlet
1 | const figlet = require("figlet"); |
ora-loading
1 | const ora = require("ora"); |
封装 loading
1 | /** |
发布和安装
在
packeages/ny-cli
文件夹下运行发布命令,然后在项目工程中安装
1 | # 发布 |
完整示例代码
- cli.js
1 |
|
- create.js
1 | const path = require("path"); |
- util.js
1 | const ora = require("ora"); |