@soybeanjs/cli 1.7.4 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -29
- package/dist/index.d.ts +65 -1
- package/dist/index.js +185 -123
- package/package.json +2 -6
package/README.md
CHANGED
|
@@ -1,51 +1,277 @@
|
|
|
1
1
|
# @soybeanjs/cli
|
|
2
2
|
|
|
3
|
-
SoybeanJS
|
|
3
|
+
SoybeanJS 的命令行工具集,提供 Git 提交规范、依赖更新、Changelog 生成、版本发布等便捷功能。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 特性
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- 📝 **符合 Conventional Commits 规范的 Git 提交** - 交互式选择提交类型和范围
|
|
8
|
+
- ✅ **提交信息校验** - 自动校验 commit message 是否符合规范
|
|
9
|
+
- 🧹 **清理功能** - 一键清理 node_modules、dist 等目录
|
|
10
|
+
- 📦 **依赖更新** - 基于 npm-check-updates 快速升级依赖
|
|
11
|
+
- 📋 **Changelog 生成** - 根据 Git tags 自动生成变更日志
|
|
12
|
+
- 🚀 **版本发布** - 一键完成版本更新、Changelog 生成、代码提交和推送
|
|
13
|
+
- 🌍 **多语言支持** - 支持中文和英文,可持久化设置默认语言
|
|
14
|
+
- ⚙️ **灵活配置** - 支持项目级和全局配置,可自定义提交类型和范围
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
8
17
|
|
|
9
18
|
```bash
|
|
10
|
-
pnpm
|
|
19
|
+
pnpm add -D @soybeanjs/cli
|
|
20
|
+
# 或者
|
|
21
|
+
npm i -D @soybeanjs/cli
|
|
22
|
+
# 或者
|
|
23
|
+
yarn add -D @soybeanjs/cli
|
|
11
24
|
```
|
|
12
25
|
|
|
13
|
-
|
|
26
|
+
安装后可通过 `pnpm soy` 或 `npx soy` 使用 CLI 命令。
|
|
14
27
|
|
|
15
|
-
|
|
28
|
+
## 快速开始
|
|
16
29
|
|
|
17
30
|
```bash
|
|
31
|
+
# 查看帮助
|
|
18
32
|
pnpm soy -h
|
|
33
|
+
|
|
34
|
+
# 交互式 Git 提交
|
|
35
|
+
pnpm soy git-commit
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 命令说明
|
|
39
|
+
|
|
40
|
+
### 全局选项
|
|
41
|
+
|
|
42
|
+
| 选项 | 说明 | 默认值 |
|
|
43
|
+
| ------------------------- | --------------------------------------------- | --------------------------------------- |
|
|
44
|
+
| `-v, --version` | 显示版本号 | - |
|
|
45
|
+
| `-e, --execute [command]` | 版本发布时在 bump 之后、commit 之前执行的命令 | `pnpm soy changelog` |
|
|
46
|
+
| `-p, --push` | 发布时是否推送 Git 提交和标签 | - |
|
|
47
|
+
| `-t, --total` | 生成 Changelog 时基于所有标签 | - |
|
|
48
|
+
| `-c, --cleanupDir <dir>` | 指定清理目录,多个目录用逗号分隔 | 见配置 |
|
|
49
|
+
| `-l, --lang <lang>` | CLI 显示语言 (`zh-cn` / `en-us`) | `en-us`(可通过 config 命令持久化修改) |
|
|
50
|
+
| `-h, --help` | 显示帮助信息 | - |
|
|
51
|
+
|
|
52
|
+
### 命令列表
|
|
53
|
+
|
|
54
|
+
| 命令 | 说明 |
|
|
55
|
+
| ------------------- | ---------------------------------------------------------------- |
|
|
56
|
+
| `cleanup` | 清理目录(node_modules、dist、lock 文件等) |
|
|
57
|
+
| `ncu` | 升级 package.json 中的依赖到最新版本 |
|
|
58
|
+
| `update-pkg` | [已废弃] 请使用 `ncu` 代替 |
|
|
59
|
+
| `git-commit` | 交互式生成符合 Conventional Commits 规范的 Git 提交信息 |
|
|
60
|
+
| `git-commit-verify` | 校验 Git 提交信息是否符合规范(通常配合 Git hooks 使用) |
|
|
61
|
+
| `changelog` | 根据 Git tags 生成 Changelog |
|
|
62
|
+
| `release` | 一键发布:更新版本号 → 生成 Changelog → 提交代码 → 打标签 → 推送 |
|
|
63
|
+
| `config <cmd>` | 管理 CLI 配置 |
|
|
64
|
+
|
|
65
|
+
### config 子命令
|
|
66
|
+
|
|
67
|
+
| 命令 | 说明 |
|
|
68
|
+
| ------------------------------ | -------------------------------------------------------------- |
|
|
69
|
+
| `config set --set-lang <lang>` | 设置默认语言(`zh-cn` 或 `en-us`),配置会持久化到全局配置文件 |
|
|
70
|
+
| `config get` | 查看当前配置信息 |
|
|
71
|
+
| `config path` | 显示全局配置文件路径 |
|
|
72
|
+
|
|
73
|
+
#### 使用示例
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# 设置默认语言为中文
|
|
77
|
+
pnpm soy config set --set-lang zh-cn
|
|
78
|
+
|
|
79
|
+
# 设置默认语言为英文
|
|
80
|
+
pnpm soy config set --set-lang en-us
|
|
81
|
+
|
|
82
|
+
# 查看当前配置
|
|
83
|
+
pnpm soy config get
|
|
84
|
+
|
|
85
|
+
# 查看全局配置文件位置
|
|
86
|
+
pnpm soy config path
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### git-commit 命令详解
|
|
90
|
+
|
|
91
|
+
使用 `pnpm soy git-commit` 进行交互式提交:
|
|
92
|
+
|
|
93
|
+
1. **选择提交类型 (type)** - 如 feat、fix、docs、style 等
|
|
94
|
+
2. **选择提交范围 (scope)** - 如 projects、components、utils 等
|
|
95
|
+
3. **输入提交描述** - 在描述前加 `!` 表示破坏性变更(Breaking Change)
|
|
96
|
+
|
|
97
|
+
生成的提交信息格式:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
<type>(<scope>)<!>: <description>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
示例:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
feat(components): add new Button component
|
|
107
|
+
fix(utils)!: change API signature
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 配置
|
|
111
|
+
|
|
112
|
+
支持两层配置:
|
|
113
|
+
|
|
114
|
+
1. **全局配置** - `~/.soybeanrc`(JSON 格式),通过 `config set` 命令管理
|
|
115
|
+
2. **项目配置** - 项目根目录下的配置文件(支持 TS/JS 等格式),优先级高于全局配置
|
|
116
|
+
|
|
117
|
+
### 配置文件
|
|
118
|
+
|
|
119
|
+
在项目根目录创建配置文件(支持以下名称):
|
|
120
|
+
|
|
121
|
+
- `soybean.config.ts`(推荐)
|
|
122
|
+
- `soybean.config.js`
|
|
123
|
+
- `soybean.config.mjs`
|
|
124
|
+
- `soybean.config.json`
|
|
125
|
+
- 在 `package.json` 的 `soybean` 字段中配置
|
|
126
|
+
|
|
127
|
+
使用 TypeScript 配置时,使用 `defineConfig` 获得类型提示:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
// soybean.config.ts
|
|
131
|
+
import { defineConfig } from '@soybeanjs/cli';
|
|
132
|
+
|
|
133
|
+
export default defineConfig({
|
|
134
|
+
// 配置项
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 配置项
|
|
139
|
+
|
|
140
|
+
| 配置项 | 类型 | 说明 | 默认值 |
|
|
141
|
+
| ------------------------ | -------------------------- | -------------------------------------- | ---------------------------------------------------------- |
|
|
142
|
+
| `cwd` | `string` | 项目根目录 | `process.cwd()` |
|
|
143
|
+
| `lang` | `'zh-cn' \| 'en-us'` | 默认显示语言 | `'en-us'` |
|
|
144
|
+
| `cleanupDirs` | `string[]` | 需要清理的目录 glob 模式 | `['**/dist', '**/node_modules', '**/pnpm-lock.yaml', ...]` |
|
|
145
|
+
| `ncuCommandArgs` | `string[]` | ncu 命令参数 | 自动解析 workspace |
|
|
146
|
+
| `changelogOptions` | `Partial<ChangelogOption>` | Changelog 生成选项 | `{}` |
|
|
147
|
+
| `gitCommitVerifyIgnores` | `RegExp[]` | 提交信息校验忽略规则(追加到默认规则) | 见源码 |
|
|
148
|
+
| `gitCommit` | `GitCommitConfig` | Git 提交配置 | 见下方 |
|
|
149
|
+
|
|
150
|
+
### Git 提交配置 (gitCommit)
|
|
151
|
+
|
|
152
|
+
用于自定义 commit types 和 scopes:
|
|
153
|
+
|
|
154
|
+
| 配置项 | 类型 | 说明 | 默认值 |
|
|
155
|
+
| ------------------------ | -------------------- | --------------------------------------------------------- | ----------- |
|
|
156
|
+
| `gitCommit.types` | `[string, string][]` | 自定义提交类型 | 默认 types |
|
|
157
|
+
| `gitCommit.typesAppend` | `boolean` | 自定义 types 是否追加到默认 types(`false` 则完全覆盖) | `true` |
|
|
158
|
+
| `gitCommit.scopes` | `[string, string][]` | 自定义提交范围 | 默认 scopes |
|
|
159
|
+
| `gitCommit.scopesAppend` | `boolean` | 自定义 scopes 是否追加到默认 scopes(`false` 则完全覆盖) | `true` |
|
|
160
|
+
|
|
161
|
+
**默认提交类型 (types):**
|
|
162
|
+
|
|
163
|
+
| type | 说明 (中文) | 说明 (English) |
|
|
164
|
+
| ---------- | ------------------------------------- | ------------------------------------------------------------- |
|
|
165
|
+
| `feat` | 新功能 | A new feature |
|
|
166
|
+
| `fix` | 修复 Bug | A bug fix |
|
|
167
|
+
| `docs` | 文档更新 | Documentation only changes |
|
|
168
|
+
| `style` | 代码风格修改(不影响代码含义) | Changes that do not affect the meaning of the code |
|
|
169
|
+
| `refactor` | 代码重构(既不修复 bug 也不添加功能) | A code change that neither fixes a bug nor adds a feature |
|
|
170
|
+
| `perf` | 性能优化 | A code change that improves performance |
|
|
171
|
+
| `optimize` | 代码质量优化 | A code change that optimizes code quality |
|
|
172
|
+
| `test` | 测试相关 | Adding missing tests or correcting existing tests |
|
|
173
|
+
| `build` | 构建系统或外部依赖变更 | Changes that affect the build system or external dependencies |
|
|
174
|
+
| `ci` | CI 配置变更 | Changes to CI configuration files and scripts |
|
|
175
|
+
| `chore` | 其他不修改 src/测试文件的变更 | Other changes that don't modify src or test files |
|
|
176
|
+
| `revert` | 还原先前的提交 | Reverts a previous commit |
|
|
177
|
+
|
|
178
|
+
**默认提交范围 (scopes):**
|
|
179
|
+
|
|
180
|
+
| scope | 说明 (中文) | 说明 (English) |
|
|
181
|
+
| ------------ | -------------- | -------------------- |
|
|
182
|
+
| `projects` | 项目 | project |
|
|
183
|
+
| `packages` | 包 | packages |
|
|
184
|
+
| `components` | 组件 | components |
|
|
185
|
+
| `hooks` | 钩子函数 | hook functions |
|
|
186
|
+
| `utils` | 工具函数 | utils functions |
|
|
187
|
+
| `types` | TS 类型声明 | TS declaration |
|
|
188
|
+
| `styles` | 代码风格 | style |
|
|
189
|
+
| `deps` | 项目依赖 | project dependencies |
|
|
190
|
+
| `release` | 发布项目新版本 | release project |
|
|
191
|
+
| `other` | 其他变更 | other changes |
|
|
192
|
+
|
|
193
|
+
### 配置示例
|
|
194
|
+
|
|
195
|
+
#### 1. 追加自定义提交类型和范围
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
// soybean.config.ts
|
|
199
|
+
import { defineConfig } from '@soybeanjs/cli';
|
|
200
|
+
|
|
201
|
+
export default defineConfig({
|
|
202
|
+
lang: 'zh-cn', // 项目默认使用中文
|
|
203
|
+
|
|
204
|
+
gitCommit: {
|
|
205
|
+
// 追加自定义 types(在默认 types 基础上添加)
|
|
206
|
+
types: [
|
|
207
|
+
['wip', '工作进行中'],
|
|
208
|
+
['merge', '合并分支'],
|
|
209
|
+
['i18n', '国际化相关']
|
|
210
|
+
],
|
|
211
|
+
|
|
212
|
+
// 追加自定义 scopes
|
|
213
|
+
scopes: [
|
|
214
|
+
['ui', 'UI 组件'],
|
|
215
|
+
['store', '状态管理'],
|
|
216
|
+
['api', 'API 接口']
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
});
|
|
19
220
|
```
|
|
20
221
|
|
|
21
|
-
|
|
222
|
+
#### 2. 完全覆盖默认提交类型和范围
|
|
22
223
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
| git-commit | 生成符合 Angular 规范的 git 提交信息 (在提交信息添加前缀`!`可以表示破坏性更新的提交) |
|
|
27
|
-
| git-commit-verify | 校验 git 的提交信息是否符合 Angular 规范 |
|
|
28
|
-
| cleanup | 清空依赖和构建产物 |
|
|
29
|
-
| ncu | 命令 npm-check-updates, 升级依赖 |
|
|
30
|
-
| changelog | 根据两次 tag 生成 changelog (--total: 根据所有 tag 生成 changelog) |
|
|
31
|
-
| release | 发布:更新版本号、生成 changelog、提交代码 |
|
|
224
|
+
```ts
|
|
225
|
+
// soybean.config.ts
|
|
226
|
+
import { defineConfig } from '@soybeanjs/cli';
|
|
32
227
|
|
|
33
|
-
|
|
228
|
+
export default defineConfig({
|
|
229
|
+
gitCommit: {
|
|
230
|
+
// 完全覆盖默认 types
|
|
231
|
+
types: [
|
|
232
|
+
['feat', '新功能'],
|
|
233
|
+
['fix', '修复'],
|
|
234
|
+
['docs', '文档']
|
|
235
|
+
],
|
|
236
|
+
typesAppend: false, // 不追加,完全覆盖
|
|
34
237
|
|
|
35
|
-
|
|
238
|
+
// 完全覆盖默认 scopes
|
|
239
|
+
scopes: [
|
|
240
|
+
['frontend', '前端'],
|
|
241
|
+
['backend', '后端'],
|
|
242
|
+
['shared', '共享代码']
|
|
243
|
+
],
|
|
244
|
+
scopesAppend: false
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
```
|
|
36
248
|
|
|
37
|
-
|
|
249
|
+
#### 3. 修改默认清理目录
|
|
38
250
|
|
|
39
|
-
|
|
40
|
-
|
|
251
|
+
```ts
|
|
252
|
+
// soybean.config.ts
|
|
253
|
+
import { defineConfig } from '@soybeanjs/cli';
|
|
41
254
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
255
|
+
export default defineConfig({
|
|
256
|
+
cleanupDirs: ['**/dist', '**/.cache', '**/coverage', '**/node_modules', '!node_modules/**']
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### package.json 脚本配置示例
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"scripts": {
|
|
265
|
+
"commit": "soy git-commit",
|
|
266
|
+
"commit:zh": "soy git-commit -l=zh-cn",
|
|
267
|
+
"cleanup": "soy cleanup",
|
|
268
|
+
"ncu": "soy ncu",
|
|
269
|
+
"changelog": "soy changelog",
|
|
270
|
+
"release": "soy release"
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
46
274
|
|
|
47
|
-
|
|
275
|
+
## License
|
|
48
276
|
|
|
49
|
-
|
|
50
|
-
import type { CliOption } from '@soybeanjs/cli';
|
|
51
|
-
```
|
|
277
|
+
[MIT](./LICENSE)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
import { ChangelogOption } from "@soybeanjs/changelog";
|
|
2
|
+
//#region src/locales/index.d.ts
|
|
3
|
+
type Lang = 'zh-cn' | 'en-us';
|
|
4
|
+
//#endregion
|
|
2
5
|
//#region src/types/index.d.ts
|
|
6
|
+
type CommitItem = [string, string];
|
|
7
|
+
interface GitCommitConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Commit types
|
|
10
|
+
*
|
|
11
|
+
* If not set, it will use the default types
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* {
|
|
16
|
+
* types: [
|
|
17
|
+
* ['feat', '新功能'],
|
|
18
|
+
* ['wip', '工作进行中']
|
|
19
|
+
* ],
|
|
20
|
+
* typesAppend: true // append to defaults, set to false to overwrite
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
types?: CommitItem[];
|
|
25
|
+
/**
|
|
26
|
+
* Whether to append custom types to defaults or overwrite
|
|
27
|
+
*
|
|
28
|
+
* @default true
|
|
29
|
+
*/
|
|
30
|
+
typesAppend?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Commit scopes
|
|
33
|
+
*
|
|
34
|
+
* If not set, it will use the default scopes
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* {
|
|
39
|
+
* scopes: [
|
|
40
|
+
* ['ui', 'UI 相关'],
|
|
41
|
+
* ['api', 'API 相关']
|
|
42
|
+
* ],
|
|
43
|
+
* scopesAppend: true // append to defaults, set to false to overwrite
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
scopes?: CommitItem[];
|
|
48
|
+
/**
|
|
49
|
+
* Whether to append custom scopes to defaults or overwrite
|
|
50
|
+
*
|
|
51
|
+
* @default true
|
|
52
|
+
*/
|
|
53
|
+
scopesAppend?: boolean;
|
|
54
|
+
}
|
|
3
55
|
interface CliOption {
|
|
4
56
|
/** The project root directory */
|
|
5
57
|
cwd: string;
|
|
@@ -15,7 +67,7 @@ interface CliOption {
|
|
|
15
67
|
*/
|
|
16
68
|
cleanupDirs: string[];
|
|
17
69
|
/**
|
|
18
|
-
*
|
|
70
|
+
* npm-check-updates command args
|
|
19
71
|
*
|
|
20
72
|
* If not set, soybean-cli will resolve workspace package.json files automatically and fall back to package.json in single-package repos.
|
|
21
73
|
*/
|
|
@@ -28,6 +80,18 @@ interface CliOption {
|
|
|
28
80
|
changelogOptions: Partial<ChangelogOption>;
|
|
29
81
|
/** The ignore pattern list of git commit verify */
|
|
30
82
|
gitCommitVerifyIgnores: RegExp[];
|
|
83
|
+
/**
|
|
84
|
+
* Git commit config for types and scopes
|
|
85
|
+
*/
|
|
86
|
+
gitCommit?: GitCommitConfig;
|
|
87
|
+
/**
|
|
88
|
+
* Default display language of cli
|
|
89
|
+
*
|
|
90
|
+
* Can be 'zh-cn' or 'en-us'
|
|
91
|
+
*
|
|
92
|
+
* @default 'en-us'
|
|
93
|
+
*/
|
|
94
|
+
lang?: Lang;
|
|
31
95
|
}
|
|
32
96
|
//#endregion
|
|
33
97
|
//#region src/index.d.ts
|
package/dist/index.js
CHANGED
|
@@ -1,61 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { cac } from "cac";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
4
|
import process$1 from "node:process";
|
|
4
5
|
import { loadConfig } from "c12";
|
|
5
|
-
import { readFileSync } from "node:fs";
|
|
6
|
-
import path, { join, posix } from "node:path";
|
|
7
|
-
import enquirer from "enquirer";
|
|
8
6
|
import { bgRed, green, red, yellow } from "kolorist";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import enquirer from "enquirer";
|
|
9
9
|
import { rimraf } from "rimraf";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { existsSync } from "fs";
|
|
13
|
-
import { join as join$1 } from "path";
|
|
10
|
+
import { existsSync as existsSync$1 } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
14
12
|
import { generateChangelog, generateTotalChangelog } from "@soybeanjs/changelog";
|
|
15
13
|
import { versionBump } from "bumpp";
|
|
14
|
+
import { consola } from "consola";
|
|
16
15
|
//#region package.json
|
|
17
|
-
var version = "1.
|
|
18
|
-
//#endregion
|
|
19
|
-
//#region src/config/index.ts
|
|
20
|
-
const defaultOptions = {
|
|
21
|
-
cwd: process$1.cwd(),
|
|
22
|
-
cleanupDirs: [
|
|
23
|
-
"**/dist",
|
|
24
|
-
"**/package-lock.json",
|
|
25
|
-
"**/yarn.lock",
|
|
26
|
-
"**/pnpm-lock.yaml",
|
|
27
|
-
"**/node_modules",
|
|
28
|
-
"!node_modules/**"
|
|
29
|
-
],
|
|
30
|
-
ncuCommandArgs: [],
|
|
31
|
-
changelogOptions: {},
|
|
32
|
-
gitCommitVerifyIgnores: [
|
|
33
|
-
/^((Merge pull request)|(Merge (.*?) into (.*?)|(Merge branch (.*?)))(?:\r?\n)*$)/m,
|
|
34
|
-
/^(Merge tag (.*?))(?:\r?\n)*$/m,
|
|
35
|
-
/^(R|r)evert (.*)/,
|
|
36
|
-
/^(amend|fixup|squash)!/,
|
|
37
|
-
/^(Merged (.*?)(in|into) (.*)|Merged PR (.*): (.*))/,
|
|
38
|
-
/^Merge remote-tracking branch(\s*)(.*)/,
|
|
39
|
-
/^Automatic merge(.*)/,
|
|
40
|
-
/^Auto-merged (.*?) into (.*)/
|
|
41
|
-
]
|
|
42
|
-
};
|
|
43
|
-
async function loadCliOptions(overrides, cwd = process$1.cwd()) {
|
|
44
|
-
const { config } = await loadConfig({
|
|
45
|
-
name: "soybean",
|
|
46
|
-
defaults: defaultOptions,
|
|
47
|
-
overrides,
|
|
48
|
-
cwd,
|
|
49
|
-
packageJson: true
|
|
50
|
-
});
|
|
51
|
-
return config;
|
|
52
|
-
}
|
|
53
|
-
//#endregion
|
|
54
|
-
//#region src/shared/index.ts
|
|
55
|
-
async function execCommand(cmd, args, options) {
|
|
56
|
-
const { execa } = await import("execa");
|
|
57
|
-
return ((await execa(cmd, args, options))?.stdout)?.trim() || "";
|
|
58
|
-
}
|
|
16
|
+
var version = "1.8.1";
|
|
59
17
|
//#endregion
|
|
60
18
|
//#region src/locales/index.ts
|
|
61
19
|
const locales = {
|
|
@@ -129,21 +87,125 @@ const locales = {
|
|
|
129
87
|
}
|
|
130
88
|
};
|
|
131
89
|
//#endregion
|
|
90
|
+
//#region src/config/index.ts
|
|
91
|
+
const defaultGitCommitVerifyIgnores = [
|
|
92
|
+
/^((Merge pull request)|(Merge (.*?) into (.*?)|(Merge branch (.*?)))(?:\r?\n)*$)/m,
|
|
93
|
+
/^(Merge tag (.*?))(?:\r?\n)*$/m,
|
|
94
|
+
/^(R|r)evert (.*)/,
|
|
95
|
+
/^(amend|fixup|squash)!/,
|
|
96
|
+
/^(Merged (.*?)(in|into) (.*)|Merged PR (.*): (.*))/,
|
|
97
|
+
/^Merge remote-tracking branch(\s*)(.*)/,
|
|
98
|
+
/^Automatic merge(.*)/,
|
|
99
|
+
/^Auto-merged (.*?) into (.*)/
|
|
100
|
+
];
|
|
101
|
+
const defaultOptions = {
|
|
102
|
+
cwd: process$1.cwd(),
|
|
103
|
+
cleanupDirs: [
|
|
104
|
+
"**/dist",
|
|
105
|
+
"**/package-lock.json",
|
|
106
|
+
"**/yarn.lock",
|
|
107
|
+
"**/pnpm-lock.yaml",
|
|
108
|
+
"**/node_modules",
|
|
109
|
+
"!node_modules/**"
|
|
110
|
+
],
|
|
111
|
+
ncuCommandArgs: [],
|
|
112
|
+
changelogOptions: {},
|
|
113
|
+
gitCommitVerifyIgnores: defaultGitCommitVerifyIgnores,
|
|
114
|
+
gitCommit: {
|
|
115
|
+
typesAppend: true,
|
|
116
|
+
scopesAppend: true
|
|
117
|
+
},
|
|
118
|
+
lang: "en-us"
|
|
119
|
+
};
|
|
120
|
+
function mergeCommitItems(defaults, custom, append = true) {
|
|
121
|
+
if (!custom || custom.length === 0) return defaults;
|
|
122
|
+
if (!append) return custom;
|
|
123
|
+
const existing = new Map(defaults);
|
|
124
|
+
for (const [key, value] of custom) existing.set(key, value);
|
|
125
|
+
return Array.from(existing.entries());
|
|
126
|
+
}
|
|
127
|
+
function resolveGitCommitConfig(config, lang) {
|
|
128
|
+
const gitCommitConfig = config.gitCommit || {};
|
|
129
|
+
const defaultLocale = locales[lang] || locales["en-us"];
|
|
130
|
+
const defaultTypes = defaultLocale.gitCommitTypes;
|
|
131
|
+
const defaultScopes = defaultLocale.gitCommitScopes;
|
|
132
|
+
return {
|
|
133
|
+
types: mergeCommitItems(defaultTypes, gitCommitConfig.types, gitCommitConfig.typesAppend !== false),
|
|
134
|
+
scopes: mergeCommitItems(defaultScopes, gitCommitConfig.scopes, gitCommitConfig.scopesAppend !== false),
|
|
135
|
+
typesAppend: gitCommitConfig.typesAppend !== false,
|
|
136
|
+
scopesAppend: gitCommitConfig.scopesAppend !== false
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function readGlobalConfig$1() {
|
|
140
|
+
const globalConfigPath = getGlobalConfigPath();
|
|
141
|
+
if (!existsSync(globalConfigPath)) return {};
|
|
142
|
+
try {
|
|
143
|
+
const content = readFileSync(globalConfigPath, "utf8");
|
|
144
|
+
return JSON.parse(content);
|
|
145
|
+
} catch {
|
|
146
|
+
return {};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function isPlainObject(value) {
|
|
150
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof RegExp) && !(value instanceof Date);
|
|
151
|
+
}
|
|
152
|
+
function deepMerge(target, source) {
|
|
153
|
+
const result = { ...target };
|
|
154
|
+
for (const key of Object.keys(source)) {
|
|
155
|
+
const sourceVal = source[key];
|
|
156
|
+
const targetVal = result[key];
|
|
157
|
+
if (sourceVal !== void 0) if (isPlainObject(sourceVal) && isPlainObject(targetVal)) result[key] = deepMerge(targetVal, sourceVal);
|
|
158
|
+
else result[key] = sourceVal;
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
async function loadCliOptions(overrides, cwd = process$1.cwd()) {
|
|
163
|
+
const { config: projectConfig } = await loadConfig({
|
|
164
|
+
name: "soybean",
|
|
165
|
+
defaults: {},
|
|
166
|
+
overrides,
|
|
167
|
+
cwd,
|
|
168
|
+
packageJson: true,
|
|
169
|
+
rcFile: false
|
|
170
|
+
});
|
|
171
|
+
const globalConfig = readGlobalConfig$1();
|
|
172
|
+
let mergedConfig = deepMerge({ ...defaultOptions }, globalConfig);
|
|
173
|
+
mergedConfig = deepMerge(mergedConfig, projectConfig);
|
|
174
|
+
const resolvedConfig = mergedConfig;
|
|
175
|
+
if (resolvedConfig.gitCommitVerifyIgnores) resolvedConfig.gitCommitVerifyIgnores = [...defaultGitCommitVerifyIgnores, ...resolvedConfig.gitCommitVerifyIgnores.filter((item) => !defaultGitCommitVerifyIgnores.some((def) => def.toString() === item.toString()))];
|
|
176
|
+
const lang = resolvedConfig.lang || "en-us";
|
|
177
|
+
resolvedConfig.gitCommit = resolveGitCommitConfig(resolvedConfig, lang);
|
|
178
|
+
resolvedConfig.lang = lang;
|
|
179
|
+
return resolvedConfig;
|
|
180
|
+
}
|
|
181
|
+
function getGlobalConfigPath() {
|
|
182
|
+
return `${process$1.env.HOME || process$1.env.USERPROFILE || "."}/.soybeanrc`;
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/shared/index.ts
|
|
186
|
+
async function execCommand(cmd, args, options) {
|
|
187
|
+
const { execa } = await import("execa");
|
|
188
|
+
return ((await execa(cmd, args, options))?.stdout)?.trim() || "";
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
132
191
|
//#region src/command/git-commit.ts
|
|
133
192
|
/**
|
|
134
193
|
* Git commit with Conventional Commits standard
|
|
135
194
|
*
|
|
136
|
-
* @param
|
|
195
|
+
* @param options
|
|
137
196
|
*/
|
|
138
|
-
async function gitCommit(
|
|
139
|
-
const
|
|
140
|
-
const
|
|
197
|
+
async function gitCommit(options = {}) {
|
|
198
|
+
const lang = options.lang || "en-us";
|
|
199
|
+
const { gitCommitMessages } = locales[lang];
|
|
200
|
+
const types = options.types || locales[lang].gitCommitTypes;
|
|
201
|
+
const scopes = options.scopes || locales[lang].gitCommitScopes;
|
|
202
|
+
const typesChoices = types.map(([value, msg]) => {
|
|
141
203
|
return {
|
|
142
204
|
name: value,
|
|
143
205
|
message: `${`${value}:`.padEnd(12)}${msg}`
|
|
144
206
|
};
|
|
145
207
|
});
|
|
146
|
-
const scopesChoices =
|
|
208
|
+
const scopesChoices = scopes.map(([value, msg]) => ({
|
|
147
209
|
name: value,
|
|
148
210
|
message: `${value.padEnd(30)} (${msg})`
|
|
149
211
|
}));
|
|
@@ -191,73 +253,13 @@ async function cleanup(paths) {
|
|
|
191
253
|
}
|
|
192
254
|
//#endregion
|
|
193
255
|
//#region src/command/ncu.ts
|
|
194
|
-
async function ncu(args = []) {
|
|
195
|
-
await execCommand("pnpm", ["npm-check-updates", ...args
|
|
196
|
-
}
|
|
197
|
-
function parsePnpmWorkspacePackages(content) {
|
|
198
|
-
const lines = content.split(/\r?\n/u);
|
|
199
|
-
const packages = [];
|
|
200
|
-
let inPackages = false;
|
|
201
|
-
for (const line of lines) {
|
|
202
|
-
const trimmedLine = line.trim();
|
|
203
|
-
if (!inPackages) {
|
|
204
|
-
if (trimmedLine === "packages:") inPackages = true;
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (!trimmedLine || trimmedLine.startsWith("#")) continue;
|
|
208
|
-
if (/^\S/u.test(line)) break;
|
|
209
|
-
const match = line.match(/^\s*-\s*['"]?(.+?)['"]?\s*$/u);
|
|
210
|
-
if (match?.[1]) packages.push(match[1]);
|
|
211
|
-
}
|
|
212
|
-
return packages;
|
|
213
|
-
}
|
|
214
|
-
function resolveWorkspacePatterns(packageJson, pnpmWorkspaceContent) {
|
|
215
|
-
const workspaces = Array.isArray(packageJson?.workspaces) ? packageJson.workspaces : packageJson?.workspaces?.packages ?? [];
|
|
216
|
-
const pnpmWorkspacePackages = pnpmWorkspaceContent ? parsePnpmWorkspacePackages(pnpmWorkspaceContent) : [];
|
|
217
|
-
return [.../* @__PURE__ */ new Set([...workspaces, ...pnpmWorkspacePackages])];
|
|
218
|
-
}
|
|
219
|
-
function toPackageFilePattern(pattern) {
|
|
220
|
-
const normalizedPattern = pattern.trim();
|
|
221
|
-
if (!normalizedPattern) return "";
|
|
222
|
-
const isNegated = normalizedPattern.startsWith("!");
|
|
223
|
-
const cleanPattern = (isNegated ? normalizedPattern.slice(1) : normalizedPattern).replace(/\/$/u, "");
|
|
224
|
-
const packageFilePattern = cleanPattern.endsWith("package.json") ? cleanPattern : posix.join(cleanPattern, "package.json");
|
|
225
|
-
return isNegated ? `!${packageFilePattern}` : packageFilePattern;
|
|
226
|
-
}
|
|
227
|
-
async function resolveDefaultNcuArgs(cwd = process$1.cwd()) {
|
|
228
|
-
const [packageJsonContent, pnpmWorkspaceContent] = await Promise.all([readFile(join(cwd, "package.json"), "utf8").catch(() => null), readFile(join(cwd, "pnpm-workspace.yaml"), "utf8").catch(() => null)]);
|
|
229
|
-
const workspacePatterns = resolveWorkspacePatterns(packageJsonContent ? JSON.parse(packageJsonContent) : null, pnpmWorkspaceContent).map(toPackageFilePattern).filter(Boolean);
|
|
230
|
-
if (!workspacePatterns.length) return [
|
|
231
|
-
"-u",
|
|
232
|
-
"--packageFile",
|
|
233
|
-
"package.json"
|
|
234
|
-
];
|
|
235
|
-
const packageFiles = await glob(["package.json", ...workspacePatterns], {
|
|
236
|
-
cwd,
|
|
237
|
-
followSymbolicLinks: false,
|
|
238
|
-
onlyFiles: true
|
|
239
|
-
});
|
|
240
|
-
const uniquePackageFiles = [...new Set(packageFiles)].sort((left, right) => {
|
|
241
|
-
if (left === "package.json") return -1;
|
|
242
|
-
if (right === "package.json") return 1;
|
|
243
|
-
return left.localeCompare(right);
|
|
244
|
-
});
|
|
245
|
-
if (!uniquePackageFiles.length) return [
|
|
246
|
-
"-u",
|
|
247
|
-
"--packageFile",
|
|
248
|
-
"package.json"
|
|
249
|
-
];
|
|
250
|
-
if (uniquePackageFiles.length === 1) return [
|
|
251
|
-
"-u",
|
|
252
|
-
"--packageFile",
|
|
253
|
-
uniquePackageFiles[0]
|
|
254
|
-
];
|
|
255
|
-
return ["-u", ...uniquePackageFiles.flatMap((file) => ["--packageFile", file])];
|
|
256
|
+
async function ncu(args = ["-u", "-w"]) {
|
|
257
|
+
await execCommand("pnpm", ["npm-check-updates", ...args], { stdio: "inherit" });
|
|
256
258
|
}
|
|
257
259
|
//#endregion
|
|
258
260
|
//#region src/command/changelog.ts
|
|
259
261
|
async function genChangelog(options, total = false) {
|
|
260
|
-
const hasChangelog = existsSync
|
|
262
|
+
const hasChangelog = existsSync$1(join(process.cwd(), "CHANGELOG.md"));
|
|
261
263
|
if (total) await generateTotalChangelog(options);
|
|
262
264
|
else await generateChangelog(options);
|
|
263
265
|
if (!hasChangelog) await execCommand("git", ["add", "CHANGELOG.md"]);
|
|
@@ -275,15 +277,54 @@ async function release(execute = "pnpm soy changelog", push = true) {
|
|
|
275
277
|
});
|
|
276
278
|
}
|
|
277
279
|
//#endregion
|
|
280
|
+
//#region src/command/config.ts
|
|
281
|
+
function readGlobalConfig() {
|
|
282
|
+
const filePath = getGlobalConfigPath();
|
|
283
|
+
if (!existsSync(filePath)) return {};
|
|
284
|
+
try {
|
|
285
|
+
const content = readFileSync(filePath, "utf8");
|
|
286
|
+
return JSON.parse(content);
|
|
287
|
+
} catch {
|
|
288
|
+
consola.warn(`Failed to parse global config at ${filePath}, will create new one.`);
|
|
289
|
+
return {};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function writeGlobalConfig(config) {
|
|
293
|
+
writeFileSync(getGlobalConfigPath(), JSON.stringify(config, null, 2), "utf8");
|
|
294
|
+
}
|
|
295
|
+
async function configSet(options) {
|
|
296
|
+
const config = readGlobalConfig();
|
|
297
|
+
if (options.lang) {
|
|
298
|
+
if (options.lang !== "zh-cn" && options.lang !== "en-us") {
|
|
299
|
+
consola.error(`Invalid language: ${options.lang}. Supported values: 'zh-cn', 'en-us'`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
config.lang = options.lang;
|
|
303
|
+
writeGlobalConfig(config);
|
|
304
|
+
consola.success(`Default language set to ${green(options.lang)}`);
|
|
305
|
+
consola.info(`Config file: ${yellow(getGlobalConfigPath())}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async function configGet() {
|
|
309
|
+
const config = await loadCliOptions();
|
|
310
|
+
consola.log("\nCurrent configuration:");
|
|
311
|
+
consola.log(` ${green("lang")}: ${config.lang}`);
|
|
312
|
+
consola.log(` ${green("cwd")}: ${config.cwd}`);
|
|
313
|
+
consola.log(` ${green("global config")}: ${getGlobalConfigPath()}\n`);
|
|
314
|
+
}
|
|
315
|
+
async function configPath() {
|
|
316
|
+
consola.log(getGlobalConfigPath());
|
|
317
|
+
}
|
|
318
|
+
//#endregion
|
|
278
319
|
//#region src/index.ts
|
|
279
320
|
async function setupCli() {
|
|
280
321
|
const cliOptions = await loadCliOptions();
|
|
281
322
|
const cli = cac("soybean");
|
|
282
|
-
cli.version(version).option("-e, --execute [command]", "Execute additional command after bumping and before git commit. Defaults to 'pnpm soy changelog'").option("-p, --push", "Indicates whether to push the git commit and tag").option("-t, --total", "Generate changelog by total tags").option("-c, --cleanupDir <dir>", "The glob pattern of dirs to cleanup, If not set, it will use the default value, Multiple values use \",\" to separate them").option("-l, --lang <lang>",
|
|
283
|
-
default:
|
|
323
|
+
cli.version(version).option("-e, --execute [command]", "Execute additional command after bumping and before git commit. Defaults to 'pnpm soy changelog'").option("-p, --push", "Indicates whether to push the git commit and tag").option("-t, --total", "Generate changelog by total tags").option("-c, --cleanupDir <dir>", "The glob pattern of dirs to cleanup, If not set, it will use the default value, Multiple values use \",\" to separate them").option("-l, --lang <lang>", `display lang of cli (default: ${cliOptions.lang})`, {
|
|
324
|
+
default: cliOptions.lang,
|
|
284
325
|
type: [String]
|
|
285
326
|
}).help();
|
|
286
|
-
const
|
|
327
|
+
const simpleCommands = {
|
|
287
328
|
cleanup: {
|
|
288
329
|
desc: "delete dirs: node_modules, dist, etc.",
|
|
289
330
|
action: async (args) => {
|
|
@@ -307,13 +348,17 @@ async function setupCli() {
|
|
|
307
348
|
"git-commit": {
|
|
308
349
|
desc: "git commit, generate commit message which match Conventional Commits standard",
|
|
309
350
|
action: async (args) => {
|
|
310
|
-
await gitCommit(
|
|
351
|
+
await gitCommit({
|
|
352
|
+
lang: args?.lang || cliOptions.lang,
|
|
353
|
+
types: cliOptions.gitCommit?.types,
|
|
354
|
+
scopes: cliOptions.gitCommit?.scopes
|
|
355
|
+
});
|
|
311
356
|
}
|
|
312
357
|
},
|
|
313
358
|
"git-commit-verify": {
|
|
314
359
|
desc: "verify git commit message, make sure it match Conventional Commits standard",
|
|
315
360
|
action: async (args) => {
|
|
316
|
-
await gitCommitVerify(args?.lang, cliOptions.gitCommitVerifyIgnores);
|
|
361
|
+
await gitCommitVerify(args?.lang || cliOptions.lang, cliOptions.gitCommitVerifyIgnores);
|
|
317
362
|
}
|
|
318
363
|
},
|
|
319
364
|
changelog: {
|
|
@@ -329,7 +374,24 @@ async function setupCli() {
|
|
|
329
374
|
}
|
|
330
375
|
}
|
|
331
376
|
};
|
|
332
|
-
for
|
|
377
|
+
for (const [cmdName, { desc, action }] of Object.entries(simpleCommands)) cli.command(cmdName, desc).action(action);
|
|
378
|
+
cli.command("config <cmd>", "manage cli configuration (set|get|path)").option("--set-lang <lang>", "Set default language (zh-cn or en-us)").action(async (cmd, options) => {
|
|
379
|
+
switch (cmd) {
|
|
380
|
+
case "set":
|
|
381
|
+
await configSet({ lang: options.setLang });
|
|
382
|
+
break;
|
|
383
|
+
case "get":
|
|
384
|
+
await configGet();
|
|
385
|
+
break;
|
|
386
|
+
case "path":
|
|
387
|
+
await configPath();
|
|
388
|
+
break;
|
|
389
|
+
default:
|
|
390
|
+
console.error(`Unknown config subcommand: ${cmd}`);
|
|
391
|
+
console.log("Available subcommands: set, get, path");
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
333
395
|
cli.parse();
|
|
334
396
|
}
|
|
335
397
|
setupCli();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soybeanjs/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "SoybeanJS's command line tools",
|
|
5
5
|
"homepage": "https://github.com/soybeanjs/cli",
|
|
6
6
|
"bugs": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"registry": "https://registry.npmjs.org/"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@soybeanjs/changelog": "^0.
|
|
40
|
+
"@soybeanjs/changelog": "^0.5.2",
|
|
41
41
|
"bumpp": "^12.0.0",
|
|
42
42
|
"c12": "^3.3.4",
|
|
43
43
|
"cac": "^7.0.0",
|
|
@@ -53,9 +53,6 @@
|
|
|
53
53
|
"@soybeanjs/cli": "link:",
|
|
54
54
|
"@soybeanjs/oxc-config": "^0.2.3",
|
|
55
55
|
"@types/node": "^26.1.1",
|
|
56
|
-
"oxfmt": "^0.60.0",
|
|
57
|
-
"oxlint": "^1.75.0",
|
|
58
|
-
"tsdown": "^0.22.14",
|
|
59
56
|
"tsx": "^4.23.1",
|
|
60
57
|
"typescript": "^7.0.2",
|
|
61
58
|
"vite-plus": "0.2.6"
|
|
@@ -63,7 +60,6 @@
|
|
|
63
60
|
"scripts": {
|
|
64
61
|
"build": "vp pack",
|
|
65
62
|
"commit": "soy git-commit",
|
|
66
|
-
"commit:zh": "soy git-commit -l=zh-cn",
|
|
67
63
|
"fmt": "vp fmt",
|
|
68
64
|
"lint": "vp lint",
|
|
69
65
|
"publish-pkg": "pnpm -r publish --access public",
|