@varlet/release 1.0.2 → 1.1.0

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 CHANGED
@@ -13,6 +13,11 @@
13
13
 
14
14
  `Varlet Release` is a tool used for publishing all packages, generating change logs, and checking `commit messages`, relying on `pnpm`.
15
15
 
16
+ - 📦 **Out of the box**: Zero-configuration release experience
17
+ - 🤖 **Interactive CLI**: Friendly interactive terminal prompts
18
+ - 🛠 **Standardization**: Validates Git Commit messages and generates standard changelogs
19
+ - 🔗 **Extensibility**: Supports both CLI commands and Node.js API for deep customization
20
+
16
21
  > `Varlet Release` requires `Node.js` ^20.19.0 || >=22.12.0 and `esm` only.
17
22
 
18
23
  ## Installation
@@ -23,10 +28,21 @@ pnpm add @varlet/release -D
23
28
 
24
29
  ## Usage
25
30
 
31
+ ### Core Workflow
32
+
33
+ When executing `vr release`, the following sequence of lifecycles occurs automatically:
34
+
35
+ 1. Select/Confirm the **version** to publish interactively
36
+ 2. Execute the user-defined `task` function (optional, e.g., to rebuild projects based on the new version)
37
+ 3. Update the `package.json` **version** programmatically
38
+ 4. Generate the **Changelog**
39
+ 5. **Git Commit** & **Git Tag**
40
+ 6. **Publish** to npm
41
+
26
42
  ### Using Command
27
43
 
28
44
  ```shell
29
- # Release all packages and generate changelogs
45
+ # Release all packages and run the full workflow
30
46
  npx vr release
31
47
 
32
48
  # Specify remote name
@@ -49,26 +65,38 @@ npx vr lint-commit -p .git/COMMIT_EDITMSG
49
65
  npx vr publish
50
66
  ```
51
67
 
68
+ ### Git Hooks Integration (Best Practice)
69
+
70
+ It is highly recommended to use `commit-lint` with `simple-git-hooks` or `husky` in `package.json` to automatically check developers' commit messages before committing:
71
+
72
+ ```json
73
+ {
74
+ "simple-git-hooks": {
75
+ "commit-msg": "npx vr lint-commit -p $1"
76
+ }
77
+ }
78
+ ```
79
+
52
80
  ### Configuration
53
81
 
54
82
  #### release
55
83
 
56
- | Params | Instructions |
57
- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
58
- | -r --remote \<remote\> | Specify remote name |
59
- | -s --skip-npm-publish | Skip npm publish |
60
- | -c --check-remote-version | Check if the remote version of the npm package is the same as the one you want to publish locally, if so, stop execution. |
61
- | -sc --skip-changelog | Skip generate changelog |
62
- | -sgt --skip-git-tag | Skip git tag |
63
- | -nt --npm-tag \<npmTag\> | npm tag |
84
+ | Params | Instructions | Default |
85
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------- |
86
+ | -r --remote \<remote\> | Specify remote name | `origin` |
87
+ | -s --skip-npm-publish | Skip npm publish | `false` |
88
+ | -c --check-remote-version | Check if the remote version of the npm package is the same as the one you want to publish locally, if so, stop execution. | `false` |
89
+ | -sc --skip-changelog | Skip generate changelog | `false` |
90
+ | -sgt --skip-git-tag | Skip git tag | `false` |
91
+ | -nt --npm-tag \<npmTag\> | npm tag | `-` |
64
92
 
65
93
  #### changelog
66
94
 
67
- | Params | Instructions |
68
- | ----------------------------------- | -------------------------- |
69
- | -f --file \<filename\> | Specify changelog filename |
70
- | -rc --releaseCount \<releaseCount\> | Release count |
71
- | -p --preset \<preset\> | Specify changelog preset |
95
+ | Params | Instructions | Default |
96
+ | ----------------------------------- | -------------------------- | -------------- |
97
+ | -f --file \<filename\> | Specify changelog filename | `CHANGELOG.md` |
98
+ | -rc --releaseCount \<releaseCount\> | Release count | `0` |
99
+ | -p --preset \<preset\> | Specify changelog preset | `angular` |
72
100
 
73
101
  #### lint-commit
74
102
 
@@ -81,28 +109,30 @@ npx vr publish
81
109
 
82
110
  #### publish
83
111
 
84
- | Params | Instructions |
85
- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
86
- | -c --check-remote-version | Detects whether the remote version of the npm package is the same as the package version to be published locally, and if it is, skip the release |
87
- | -nt --npm-tag \<npmTag\> | npm tag |
112
+ | Params | Instructions | Default |
113
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------- |
114
+ | -c --check-remote-version | Detects whether the remote version of the npm package is the same as the package version to be published locally, and if it is, skip the release | `false` |
115
+ | -nt --npm-tag \<npmTag\> | npm tag | `-` |
88
116
 
89
- ### Custom Handle
117
+ ### Node API Custom Handle
118
+
119
+ You can write your own release scripts with Internal Node.js API instead of CLI.
90
120
 
91
121
  #### Example
92
122
 
93
123
  ```js
94
124
  import { changelog, release } from '@varlet/release'
95
125
 
96
- // Do what you want to do...
126
+ // Run the core release workflow directly
97
127
  release()
98
128
  ```
99
129
 
100
- You can pass in a task that will be called before the publish after the package version is changed.
130
+ You can pass in a custom `task` function that will be called after the package version is updated but before the remaining publish steps.
101
131
 
102
132
  ```js
103
133
  import { changelog, release } from '@varlet/release'
104
134
 
105
- async function task() {
135
+ async function task(newVersion, oldVersion) {
106
136
  await doSomething1()
107
137
  await doSomething2()
108
138
  }
@@ -110,54 +140,6 @@ async function task() {
110
140
  release({ task })
111
141
  ```
112
142
 
113
- #### Types
114
-
115
- ```ts
116
- interface PublishCommandOptions {
117
- preRelease?: boolean
118
- checkRemoteVersion?: boolean
119
- npmTag?: string
120
- }
121
- function publish({ preRelease, checkRemoteVersion, npmTag }: PublishCommandOptions): Promise<void>
122
- function updateVersion(version: string): void
123
- interface ReleaseCommandOptions {
124
- remote?: string
125
- skipNpmPublish?: boolean
126
- skipChangelog?: boolean
127
- skipGitTag?: boolean
128
- npmTag?: string
129
- task?(newVersion: string, oldVersion: string): Promise<void>
130
- }
131
- function release(options: ReleaseCommandOptions): Promise<void>
132
-
133
- interface ChangelogCommandOptions {
134
- file?: string
135
- releaseCount?: number
136
- preset?:
137
- | 'angular'
138
- | 'atom'
139
- | 'codemirror'
140
- | 'conventionalcommits'
141
- | 'ember'
142
- | 'eslint'
143
- | 'express'
144
- | 'jquery'
145
- | 'jshint'
146
- }
147
- function changelog({ releaseCount, file, preset }?: ChangelogCommandOptions): Promise<void>
148
-
149
- const COMMIT_MESSAGE_RE: RegExp
150
- function isVersionCommitMessage(message: string): string | false | null
151
- function getCommitMessage(commitMessagePath: string): string
152
- interface CommitLintCommandOptions {
153
- commitMessagePath: string
154
- commitMessageRe?: string | RegExp
155
- errorMessage?: string
156
- warningMessage?: string
157
- }
158
- function commitLint(options: CommitLintCommandOptions): void
159
- ```
160
-
161
143
  ## License
162
144
 
163
145
  [MIT](https://github.com/varletjs/release/blob/main/LICENSE)
package/README.zh-CN.md CHANGED
@@ -6,14 +6,19 @@
6
6
  </p>
7
7
  <p align="center">
8
8
  <a href="https://www.npmjs.com/package/@varlet/release" target="_blank" rel="noopener noreferrer"><img src="https://badgen.net/npm/v/@varlet/release" alt="NPM Version" /></a>
9
- <a href="https://github.com/valetjs/release/blob/master/LICENSE" target="_blank" rel="noopener noreferrer"><img src="https://badgen.net/github/license/varletjs/release" alt="License" /></a>
9
+ <a href="https://github.com/varletjs/release/blob/main/LICENSE" target="_blank" rel="noopener noreferrer"><img src="https://badgen.net/github/license/varletjs/release" alt="License" /></a>
10
10
  </p>
11
11
 
12
12
  ## 介绍
13
13
 
14
- `Varlet Release` 是一个用于发布所有包、生成变更日志和检测 `commit message` 的工具,依赖于 `pnpm`。
14
+ `Varlet Release` 是一个用于发布所有包、生成变更日志和检测 `commit message` 的辅助工具,依赖于 `pnpm`。
15
15
 
16
- `Varlet Release` 需要 `Node.js` ^20.19.0 || >=22.12.0,并且仅支持 `esm`。
16
+ - 📦 **开箱即用**:零配置的极简发布体验
17
+ - 🤖 **直观交互**:提供非常友好的交互式终端提示
18
+ - 🛠 **规范驱动**:自动校验 Git Commit 并生成标准 Changelog
19
+ - 🔗 **深度扩展**:支持命令行调用与 Node.js API 无缝扩展
20
+
21
+ > `Varlet Release` 需要 `Node.js` ^20.19.0 || >=22.12.0,并且仅支持 `esm`。
17
22
 
18
23
  ## 安装
19
24
 
@@ -23,15 +28,26 @@ pnpm add @varlet/release -D
23
28
 
24
29
  ## 使用
25
30
 
26
- ### 使用命令
31
+ ### 核心工作流
32
+
33
+ 执行 `vr release` 时,背后会自动完成以下流程(保障每一步的严谨性):
34
+
35
+ 1. 交互式选择/确认要发布的版本号
36
+ 2. 执行用户自定义的额外 `task` 操作(可选,如重新构建以注入新版本号)
37
+ 3. 自动修改工程中的版本号信息
38
+ 4. 自动生成符合规范的 Changelog
39
+ 5. Git 提交 (Commit) 与打标签 (Tag)
40
+ 6. 发布至 npm
41
+
42
+ ### 命令行使用
27
43
 
28
44
  ```shell
29
- # 发布所有包并生成变更日志
45
+ # 发布所有包并执行完整工作流程
30
46
  npx vr release
31
47
 
32
48
  # 指定远程仓库名称
33
49
  npx vr release -r https://github.com/varletjs/varlet-release
34
- # or
50
+ #
35
51
  npx vr release --remote https://github.com/varletjs/varlet-release
36
52
 
37
53
  # 仅生成变更日志
@@ -39,36 +55,48 @@ npx vr changelog
39
55
 
40
56
  # 指定变更日志文件名
41
57
  npx vr changelog -f changelog.md
42
- # or
58
+ #
43
59
  npx vr changelog --file changelog.md
44
60
 
45
- # 检测 commit message
61
+ # 检测 commit message 是否符合规范
46
62
  npx vr lint-commit -p .git/COMMIT_EDITMSG
47
63
 
48
- # 发布到 npm,可以在 ci 中执行
64
+ # 发布到 npm(通常在 CI/CD 中执行)
49
65
  npx vr publish
50
66
  ```
51
67
 
52
- ### 配置
68
+ ### Git Hooks 集成 (推荐最佳实践)
69
+
70
+ 建议在 `package.json` 中配合 `simple-git-hooks` 或 `husky` 使用 `commit-lint`,在开发者提交代码时自动触发校验:
71
+
72
+ ```json
73
+ {
74
+ "simple-git-hooks": {
75
+ "commit-msg": "npx vr lint-commit -p $1"
76
+ }
77
+ }
78
+ ```
79
+
80
+ ### 配置参考
53
81
 
54
82
  #### release
55
83
 
56
- | 参数 | 说明 |
57
- | ------------------------- | ----------------------------------------------------------------------- |
58
- | -r --remote \<remote\> | 指定远程仓库名称 |
59
- | -s --skip-npm-publish | 跳过 npm 发布 |
60
- | -c --check-remote-version | 检测 npm 包的远程版本是否与要在本地发布的包版本相同,如果是,则停止执行 |
61
- | -sc --skip-changelog | 跳过生成变更日志 |
62
- | -sgt --skip-git-tag | 跳过 git tag |
63
- | -nt --npm-tag \<npmTag\> | npm tag |
84
+ | 参数 | 说明 | 默认值 |
85
+ | ------------------------- | ----------------------------------------------------------------------- | -------- |
86
+ | -r --remote \<remote\> | 指定远程仓库名称 | `origin` |
87
+ | -s --skip-npm-publish | 跳过 npm 发布 | `false` |
88
+ | -c --check-remote-version | 检测 npm 包的远程版本是否与要在本地发布的包版本相同,如果是,则停止执行 | `false` |
89
+ | -sc --skip-changelog | 跳过生成变更日志 | `false` |
90
+ | -sgt --skip-git-tag | 跳过 git tag | `false` |
91
+ | -nt --npm-tag \<npmTag\> | npm tag | `-` |
64
92
 
65
93
  #### changelog
66
94
 
67
- | 参数 | 说明 |
68
- | ----------------------------------- | ------------------ |
69
- | -f --file \<filename\> | 指定变更日志文件名 |
70
- | -rc --releaseCount \<releaseCount\> | 发布数量 |
71
- | -p --preset \<preset\> | 指定变更预设 |
95
+ | 参数 | 说明 | 默认值 |
96
+ | ----------------------------------- | ------------------ | -------------- |
97
+ | -f --file \<filename\> | 指定变更日志文件名 | `CHANGELOG.md` |
98
+ | -rc --releaseCount \<releaseCount\> | 发布数量 | `0` |
99
+ | -p --preset \<preset\> | 指定变更预设 | `angular` |
72
100
 
73
101
  #### lint-commit
74
102
 
@@ -81,28 +109,30 @@ npx vr publish
81
109
 
82
110
  #### publish
83
111
 
84
- | 参数 | 说明 |
85
- | ------------------------- | --------------------------------------------------------------------- |
86
- | -c --check-remote-version | 检测npm包的远程版本是否与要在本地发布的包版本相同,如果是,则跳过发布 |
87
- | -nt --npm-tag \<npmTag\> | npm tag |
112
+ | 参数 | 说明 | 默认值 |
113
+ | ------------------------- | --------------------------------------------------------------------- | ------- |
114
+ | -c --check-remote-version | 检测npm包的远程版本是否与要在本地发布的包版本相同,如果是,则跳过发布 | `false` |
115
+ | -nt --npm-tag \<npmTag\> | npm tag | `-` |
88
116
 
89
- ### 自定义处理
117
+ ### Node API 自定义处理
90
118
 
91
- #### 示例
119
+ 除了命令行,你也可以使用 Node.js API 结合内部逻辑编写发布脚本。
120
+
121
+ #### 示例代码
92
122
 
93
123
  ```js
94
124
  import { changelog, release } from '@varlet/release'
95
125
 
96
- // Do what you want to do...
126
+ // 执行默认发布流程
97
127
  release()
98
128
  ```
99
129
 
100
- 你可以传入一个 `task`,在包版本更改后,在发布之前会调用 `task`。
130
+ 你可以传入一个 `task` 函数,在版本号变更之后、发布到 npm 等后续操作之前被调用。
101
131
 
102
132
  ```js
103
133
  import { changelog, release } from '@varlet/release'
104
134
 
105
- async function task() {
135
+ async function task(newVersion, oldVersion) {
106
136
  await doSomething1()
107
137
  await doSomething2()
108
138
  }
@@ -110,54 +140,6 @@ async function task() {
110
140
  release({ task })
111
141
  ```
112
142
 
113
- #### 类型
114
-
115
- ```ts
116
- interface PublishCommandOptions {
117
- preRelease?: boolean
118
- checkRemoteVersion?: boolean
119
- npmTag?: string
120
- }
121
- function publish({ preRelease, checkRemoteVersion, npmTag }: PublishCommandOptions): Promise<void>
122
- function updateVersion(version: string): void
123
- interface ReleaseCommandOptions {
124
- remote?: string
125
- skipNpmPublish?: boolean
126
- skipChangelog?: boolean
127
- skipGitTag?: boolean
128
- npmTag?: string
129
- task?(newVersion: string, oldVersion: string): Promise<void>
130
- }
131
- function release(options: ReleaseCommandOptions): Promise<void>
132
-
133
- interface ChangelogCommandOptions {
134
- file?: string
135
- releaseCount?: number
136
- preset?:
137
- | 'angular'
138
- | 'atom'
139
- | 'codemirror'
140
- | 'conventionalcommits'
141
- | 'ember'
142
- | 'eslint'
143
- | 'express'
144
- | 'jquery'
145
- | 'jshint'
146
- }
147
- function changelog({ releaseCount, file, preset }?: ChangelogCommandOptions): Promise<void>
148
-
149
- const COMMIT_MESSAGE_RE: RegExp
150
- function isVersionCommitMessage(message: string): string | false | null
151
- function getCommitMessage(commitMessagePath: string): string
152
- interface CommitLintCommandOptions {
153
- commitMessagePath: string
154
- commitMessageRe?: string | RegExp
155
- errorMessage?: string
156
- warningMessage?: string
157
- }
158
- function commitLint(options: CommitLintCommandOptions): void
159
- ```
160
-
161
143
  ## 许可证
162
144
 
163
145
  [MIT](https://github.com/varletjs/release/blob/main/LICENSE)
package/dist/cli.js CHANGED
@@ -1,10 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { changelog, commitLint, publish, release } from "./index.js";
2
+ import { i as release, l as commitLint, o as changelog, r as publish } from "./src-BWrnR5U-.js";
3
3
  import { Command } from "commander";
4
-
5
4
  //#region package.json
6
- var version = "1.0.2";
7
-
5
+ var version = "1.1.0";
8
6
  //#endregion
9
7
  //#region src/cli.ts
10
8
  const program = new Command();
@@ -14,6 +12,5 @@ program.command("publish").option("-c --check-remote-version", "Check remote ver
14
12
  program.command("changelog").option("-rc --releaseCount <releaseCount>", "Release count").option("-f --file <file>", "Changelog filename").option("-p --preset <preset>", "Changelog preset").description("Generate changelog").action((options) => changelog(options));
15
13
  program.command("commit-lint").option("-p --commitMessagePath <path>", "Git commit message path").option("-r --commitMessageRe <reg>", "Validate the regular of whether the commit message passes").option("-e --errorMessage <message>", "Validation failed to display error messages").option("-w --warningMessage <message>", "Validation failed to display warning messages").description("Lint commit message").action((option) => commitLint(option));
16
14
  program.parse();
17
-
18
15
  //#endregion
19
- export { };
16
+ export {};
package/dist/index.d.ts CHANGED
@@ -12,7 +12,14 @@ declare function publish({
12
12
  checkRemoteVersion,
13
13
  npmTag
14
14
  }: PublishCommandOptions): Promise<void>;
15
- declare function getAllPackageJsons(): string[];
15
+ declare function getPackageJsons(): {
16
+ filePath: string;
17
+ config: {
18
+ name: string;
19
+ version: string;
20
+ private: boolean;
21
+ };
22
+ }[];
16
23
  declare function updateVersion(version: string): void;
17
24
  interface ReleaseCommandOptions {
18
25
  remote?: string;
@@ -52,4 +59,4 @@ interface CommitLintCommandOptions {
52
59
  }
53
60
  declare function commitLint(options: CommitLintCommandOptions): void;
54
61
  //#endregion
55
- export { COMMIT_HEADER_RE, COMMIT_MESSAGE_RE, ChangelogCommandOptions, CommitLintCommandOptions, PublishCommandOptions, ReleaseCommandOptions, changelog, commitLint, getAllPackageJsons, getCommitMessage, isSameVersion, isVersionCommitMessage, publish, release, updateVersion };
62
+ export { COMMIT_HEADER_RE, COMMIT_MESSAGE_RE, ChangelogCommandOptions, CommitLintCommandOptions, PublishCommandOptions, ReleaseCommandOptions, changelog, commitLint, getCommitMessage, getPackageJsons, isSameVersion, isVersionCommitMessage, publish, release, updateVersion };
package/dist/index.js CHANGED
@@ -1,431 +1,2 @@
1
- import { resolve } from "node:path";
2
- import { styleText } from "node:util";
3
- import { cancel, confirm, isCancel, select, spinner } from "@clack/prompts";
4
- import fse from "fs-extra";
5
- import { logger } from "rslog";
6
- import semver from "semver";
7
- import { x } from "tinyexec";
8
- import conventionalChangelog from "conventional-changelog";
9
-
10
- //#region src/commitLint.ts
11
- const { readFileSync } = fse;
12
- const COMMIT_HEADER_RE = /^(revert|fix|feat|docs|perf|test|types|style|build|chore|release|refactor|merge|wip)(?:\(([^)]+)\))?(!)?:\s(.+)$/;
13
- const COMMIT_MESSAGE_RE = new RegExp(COMMIT_HEADER_RE.source.replace(/\$$/, "") + "(?:\\n[\\s\\S]+)?$");
14
- const ERROR_MESSAGE = "Commit message invalid.";
15
- const WARNING_MESSAGE = `\
16
- The rules for commit messages are as follows
17
-
18
- Example:
19
-
20
- feat: add a new feature
21
- feat(ui/button): add a new feature in the ui/button scope
22
-
23
- fix: fix a bug
24
- fix(ui/button): fix a bug in the ui/button scope
25
-
26
- docs: fix an error in the documentation
27
- docs(ui/button): fix a documentation error in the ui/button scope
28
-
29
- Allowed types:
30
- - fix
31
- - feat
32
- - docs
33
- - perf
34
- - test
35
- - types
36
- - style
37
- - build
38
- - chore
39
- - release
40
- - refactor
41
- - revert
42
- - merge
43
- - wip
44
-
45
- Commit message reference: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y
46
- 参考阮一峰Commit message编写指南: https://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html`;
47
- function isVersionCommitMessage(message) {
48
- return Boolean(message.startsWith("v") && semver.valid(message.slice(1)));
49
- }
50
- function getCommitMessage(commitMessagePath) {
51
- return readFileSync(commitMessagePath, "utf-8").trim();
52
- }
53
- function commitLint(options) {
54
- const { commitMessagePath, commitMessageRe = COMMIT_MESSAGE_RE, errorMessage = ERROR_MESSAGE, warningMessage = WARNING_MESSAGE } = options;
55
- if (!commitMessagePath) {
56
- logger.error("commitMessagePath is required");
57
- process.exit(1);
58
- }
59
- const commitMessage = getCommitMessage(commitMessagePath);
60
- const isValidCommitMessage = new RegExp(commitMessageRe).test(commitMessage);
61
- if (!isVersionCommitMessage(commitMessage) && !isValidCommitMessage) {
62
- logger.error(errorMessage);
63
- logger.warn(warningMessage);
64
- process.exit(1);
65
- }
66
- }
67
-
68
- //#endregion
69
- //#region src/changelog.ts
70
- const { createWriteStream } = fse;
71
- const COMMIT_TYPE_MAP = {
72
- feat: "Features",
73
- fix: "Bug Fixes",
74
- perf: "Performance Improvements",
75
- revert: "Reverts",
76
- refactor: "Code Refactoring",
77
- docs: "Documentation",
78
- style: "Styles",
79
- test: "Tests",
80
- build: "Build System",
81
- ci: "Continuous Integration"
82
- };
83
- const ALWAYS_SHOW_TYPES = new Set([
84
- "feat",
85
- "fix",
86
- "perf",
87
- "revert",
88
- "refactor"
89
- ]);
90
- const BREAKING_CHANGE_RE = /BREAKING CHANGES?:\s*([\s\S]+)/;
91
- const MAIN_TEMPLATE = `{{> header}}
92
-
93
- {{> footer}}
94
- {{#each commitGroups}}
95
-
96
- {{#if title}}
97
- ### {{title}}
98
-
99
- {{/if}}
100
- {{#each commits}}
101
- {{> commit root=@root}}
102
- {{/each}}
103
-
104
- {{/each}}
105
- `;
106
- function linkify(text, context, issues) {
107
- let result = text;
108
- const repoUrl = context.repository ? `${context.host}/${context.owner}/${context.repository}` : context.repoUrl;
109
- if (repoUrl) {
110
- const issueBaseUrl = `${repoUrl}/issues/`;
111
- result = result.replace(/#([0-9]+)/g, (_, issue) => {
112
- issues.push(issue);
113
- return `[#${issue}](${issueBaseUrl}${issue})`;
114
- });
115
- }
116
- if (context.host) result = result.replace(/\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g, (_, username) => username.includes("/") ? `@${username}` : `[@${username}](${context.host}/${username})`);
117
- return result;
118
- }
119
- function extractBreakingText(commit) {
120
- const body = typeof commit.body === "string" ? commit.body : "";
121
- const footer = typeof commit.footer === "string" ? commit.footer : "";
122
- const match = BREAKING_CHANGE_RE.exec(`${footer}\n${body}`);
123
- if (match?.[1]) return match[1].trim();
124
- if (typeof commit.subject === "string" && commit.subject) return commit.subject;
125
- if (typeof commit.header === "string" && commit.header) return commit.header;
126
- return "";
127
- }
128
- function tryParseHeader(commit) {
129
- if (typeof commit.header !== "string") return null;
130
- const match = COMMIT_HEADER_RE.exec(commit.header.trim());
131
- if (!match) return null;
132
- return {
133
- type: match[1],
134
- scope: match[2] || void 0,
135
- subject: match[4] || void 0,
136
- isBreaking: Boolean(match[3])
137
- };
138
- }
139
- function processBreakingChanges(commit, context, issues) {
140
- let discard = true;
141
- commit.notes.forEach((note) => {
142
- note.title = "BREAKING CHANGES";
143
- discard = false;
144
- });
145
- const hadBreakingNotes = commit.notes.length > 0;
146
- const addBreakingNote = () => {
147
- if (!hadBreakingNotes) {
148
- const text = linkify(extractBreakingText(commit), context, issues);
149
- commit.notes.push({
150
- title: "BREAKING CHANGES",
151
- text
152
- });
153
- }
154
- discard = false;
155
- };
156
- const parsed = tryParseHeader(commit);
157
- if (parsed) {
158
- if (!commit.type) commit.type = parsed.type;
159
- if (!commit.scope && parsed.scope) commit.scope = parsed.scope;
160
- if (!commit.subject && parsed.subject) commit.subject = parsed.subject;
161
- if (parsed.isBreaking) addBreakingNote();
162
- }
163
- if (typeof commit.type === "string" && commit.type.endsWith("!")) {
164
- commit.type = commit.type.slice(0, -1);
165
- addBreakingNote();
166
- }
167
- return discard;
168
- }
169
- function mapCommitType(commit, discard) {
170
- if (commit.revert) {
171
- commit.type = "Reverts";
172
- return true;
173
- }
174
- const mapped = COMMIT_TYPE_MAP[commit.type];
175
- if (mapped) {
176
- if (ALWAYS_SHOW_TYPES.has(commit.type) || !discard) {
177
- commit.type = mapped;
178
- return true;
179
- }
180
- }
181
- return !discard;
182
- }
183
- function createDefaultWriterOpts() {
184
- return {
185
- mainTemplate: MAIN_TEMPLATE,
186
- transform(commit, context) {
187
- const issues = [];
188
- if (!mapCommitType(commit, processBreakingChanges(commit, context, issues))) return false;
189
- if (commit.scope === "*") commit.scope = "";
190
- if (typeof commit.hash === "string") commit.shortHash = commit.hash.substring(0, 7);
191
- if (typeof commit.subject === "string") commit.subject = linkify(commit.subject, context, issues);
192
- commit.references = commit.references.filter((ref) => !issues.includes(ref.issue));
193
- return commit;
194
- }
195
- };
196
- }
197
- function changelog({ releaseCount = 0, file = "CHANGELOG.md", preset = "angular", writerOpts = createDefaultWriterOpts() } = {}) {
198
- const s = spinner();
199
- s.start("Generating changelog");
200
- return new Promise((resolve$1) => {
201
- conventionalChangelog({
202
- preset,
203
- releaseCount
204
- }, void 0, void 0, void 0, writerOpts).pipe(createWriteStream(resolve(process.cwd(), file))).on("close", () => {
205
- s.stop("Changelog generated successfully!");
206
- resolve$1();
207
- });
208
- });
209
- }
210
-
211
- //#endregion
212
- //#region src/release.ts
213
- const cwd = process.cwd();
214
- const { writeFileSync, readJSONSync, existsSync, readdirSync } = fse;
215
- const releaseTypes = [
216
- "patch",
217
- "minor",
218
- "major",
219
- "prepatch",
220
- "preminor",
221
- "premajor"
222
- ];
223
- const BACK_HINT = "Back to previous step";
224
- async function isWorktreeEmpty() {
225
- return !(await x("git", ["status", "--porcelain"])).stdout;
226
- }
227
- async function isSameVersion(version) {
228
- const s = spinner();
229
- s.start("Check remote version...");
230
- const packageJsones = getPackageJsons();
231
- const packageJson = packageJsones.find((packageJson) => !packageJson.config.private) || packageJsones[0];
232
- if (packageJson) {
233
- const { config } = packageJson;
234
- try {
235
- await x("npm", [
236
- "view",
237
- `${config.name}@${version ?? config.version}`,
238
- "version"
239
- ], { throwOnError: true });
240
- s.cancel();
241
- logger.warn(`The npm package has a same remote version ${version ?? config.version}.`);
242
- return true;
243
- } catch {
244
- s.stop();
245
- return false;
246
- }
247
- }
248
- }
249
- async function publish({ preRelease, checkRemoteVersion, npmTag }) {
250
- const s = spinner();
251
- s.start("Publishing all packages");
252
- const args = [
253
- "-r",
254
- "publish",
255
- "--no-git-checks",
256
- "--access",
257
- "public"
258
- ];
259
- if (checkRemoteVersion && await isSameVersion()) {
260
- logger.error("publishing automatically skipped.");
261
- return;
262
- }
263
- if (preRelease) args.push("--tag", "alpha");
264
- else if (npmTag) args.push("--tag", npmTag);
265
- try {
266
- const ret = await x("pnpm", args, { throwOnError: true });
267
- s.stop("Publish all packages successfully");
268
- ret.stdout && logger.log(ret.stdout);
269
- } catch (error) {
270
- s.cancel("Publish all packages failed");
271
- throw error?.output?.stderr ?? error;
272
- }
273
- }
274
- async function pushGit(version, remote = "origin", skipGitTag = false) {
275
- const s = spinner();
276
- s.start("Pushing to remote git repository");
277
- await x("git", ["add", "."], { throwOnError: true });
278
- await x("git", [
279
- "commit",
280
- "-m",
281
- `v${version}`
282
- ], { throwOnError: true });
283
- if (!skipGitTag) {
284
- await x("git", ["tag", `v${version}`], { throwOnError: true });
285
- await x("git", [
286
- "push",
287
- remote,
288
- `v${version}`
289
- ], { throwOnError: true });
290
- }
291
- const ret = await x("git", ["push"], { throwOnError: true });
292
- s.stop("Push remote repository successfully");
293
- ret.stdout && logger.log(ret.stdout);
294
- }
295
- function getAllPackageJsons() {
296
- const result = [resolve(cwd, "package.json")];
297
- const packagesDir = resolve(cwd, "packages");
298
- if (existsSync(packagesDir)) for (const name of readdirSync(packagesDir)) {
299
- const pkgPath = resolve(packagesDir, name, "package.json");
300
- if (existsSync(pkgPath)) result.push(pkgPath);
301
- }
302
- return result;
303
- }
304
- function getPackageJsons() {
305
- return getAllPackageJsons().map((path) => {
306
- return {
307
- config: readJSONSync(path),
308
- filePath: path
309
- };
310
- });
311
- }
312
- function updateVersion(version) {
313
- getPackageJsons().forEach(({ config, filePath }) => {
314
- config.version = version;
315
- writeFileSync(filePath, JSON.stringify(config, null, 2));
316
- });
317
- }
318
- async function confirmRegistry() {
319
- const registry = (await x("npm", [
320
- "config",
321
- "get",
322
- "registry"
323
- ])).stdout;
324
- const ret = await confirm({ message: `Current registry is: ${registry}` });
325
- if (isCancel(ret)) {
326
- cancel("Operation cancelled.");
327
- process.exit(0);
328
- }
329
- return ret;
330
- }
331
- async function confirmVersion(currentVersion, expectVersion) {
332
- const ret = await select({
333
- message: "Version confirm",
334
- options: [`All packages version ${currentVersion} -> ${expectVersion}`, BACK_HINT].map((value) => ({
335
- label: value,
336
- value
337
- }))
338
- });
339
- if (isCancel(ret)) {
340
- cancel("Operation cancelled.");
341
- process.exit(0);
342
- }
343
- return ret;
344
- }
345
- async function confirmRefs(remote = "origin") {
346
- const { stdout } = await x("git", ["remote", "-v"]);
347
- const reg = new RegExp(`${remote}\t(.*) \\(push`);
348
- const repo = stdout.match(reg)?.[1];
349
- const { stdout: branch } = await x("git", ["branch", "--show-current"]);
350
- const ret = await confirm({ message: `Current refs ${repo}:refs/for/${styleText("blue", branch)}` });
351
- if (isCancel(ret)) {
352
- cancel("Operation cancelled.");
353
- process.exit(0);
354
- }
355
- return ret;
356
- }
357
- async function getReleaseType() {
358
- const releaseType = await select({
359
- message: "Please select release type",
360
- options: releaseTypes.map((type) => ({
361
- label: type,
362
- value: type
363
- }))
364
- });
365
- if (isCancel(releaseType)) {
366
- cancel("Operation cancelled.");
367
- process.exit(0);
368
- }
369
- return releaseType;
370
- }
371
- async function getReleaseVersion(currentVersion) {
372
- let isPreRelease = false;
373
- let expectVersion = "";
374
- let confirmVersionRet = "";
375
- do {
376
- const type = await getReleaseType();
377
- isPreRelease = type.startsWith("pre");
378
- expectVersion = semver.inc(currentVersion, type, `alpha.${Date.now()}`);
379
- expectVersion = isPreRelease ? expectVersion.slice(0, -2) : expectVersion;
380
- confirmVersionRet = await confirmVersion(currentVersion, expectVersion);
381
- } while (confirmVersionRet === BACK_HINT);
382
- return {
383
- isPreRelease,
384
- expectVersion
385
- };
386
- }
387
- async function release(options) {
388
- try {
389
- const currentVersion = readJSONSync(resolve(cwd, "package.json")).version;
390
- if (!currentVersion) {
391
- logger.error("Your package is missing the version field");
392
- return;
393
- }
394
- if (!await isWorktreeEmpty()) {
395
- logger.error("Git worktree is not empty, please commit changed");
396
- return;
397
- }
398
- if (!await confirmRefs(options.remote)) return;
399
- if (!options.skipNpmPublish && !await confirmRegistry()) return;
400
- const { isPreRelease, expectVersion } = await getReleaseVersion(currentVersion);
401
- if (options.checkRemoteVersion && await isSameVersion(expectVersion)) {
402
- logger.error("Please check remote version.");
403
- return;
404
- }
405
- updateVersion(expectVersion);
406
- if (options.task) await options.task(expectVersion, currentVersion);
407
- if (!options.skipNpmPublish) await publish({
408
- preRelease: isPreRelease,
409
- npmTag: options.npmTag
410
- });
411
- if (!isPreRelease) {
412
- if (!options.skipChangelog) await changelog();
413
- await pushGit(expectVersion, options.remote, options.skipGitTag);
414
- }
415
- logger.success(`Release version ${expectVersion} successfully!`);
416
- if (isPreRelease) {
417
- try {
418
- await x("git", ["restore", "**/package.json"], { throwOnError: true });
419
- } catch {}
420
- try {
421
- await x("git", ["restore", "package.json"], { throwOnError: true });
422
- } catch {}
423
- }
424
- } catch (error) {
425
- logger.error(error);
426
- process.exit(1);
427
- }
428
- }
429
-
430
- //#endregion
431
- export { COMMIT_HEADER_RE, COMMIT_MESSAGE_RE, changelog, commitLint, getAllPackageJsons, getCommitMessage, isSameVersion, isVersionCommitMessage, publish, release, updateVersion };
1
+ import { a as updateVersion, c as COMMIT_MESSAGE_RE, d as isVersionCommitMessage, i as release, l as commitLint, n as isSameVersion, o as changelog, r as publish, s as COMMIT_HEADER_RE, t as getPackageJsons, u as getCommitMessage } from "./src-BWrnR5U-.js";
2
+ export { COMMIT_HEADER_RE, COMMIT_MESSAGE_RE, changelog, commitLint, getCommitMessage, getPackageJsons, isSameVersion, isVersionCommitMessage, publish, release, updateVersion };
@@ -0,0 +1,432 @@
1
+ import { createWriteStream, existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { styleText } from "node:util";
4
+ import { cancel, confirm, isCancel, select, spinner } from "@clack/prompts";
5
+ import { logger } from "rslog";
6
+ import semver from "semver";
7
+ import { x } from "tinyexec";
8
+ import conventionalChangelog from "conventional-changelog";
9
+ //#region src/commitLint.ts
10
+ const COMMIT_HEADER_RE = /^(revert|fix|feat|docs|perf|test|types|style|build|chore|release|refactor|merge|wip)(?:\(([^)]+)\))?(!)?:\s(.+)$/;
11
+ const COMMIT_MESSAGE_RE = new RegExp(COMMIT_HEADER_RE.source.replace(/\$$/, "") + "(?:\\n[\\s\\S]+)?$");
12
+ const ERROR_MESSAGE = "Commit message invalid.";
13
+ const WARNING_MESSAGE = `\
14
+ The rules for commit messages are as follows
15
+
16
+ Example:
17
+
18
+ feat: add a new feature
19
+ feat(ui/button): add a new feature in the ui/button scope
20
+
21
+ fix: fix a bug
22
+ fix(ui/button): fix a bug in the ui/button scope
23
+
24
+ docs: fix an error in the documentation
25
+ docs(ui/button): fix a documentation error in the ui/button scope
26
+
27
+ Allowed types:
28
+ - fix
29
+ - feat
30
+ - docs
31
+ - perf
32
+ - test
33
+ - types
34
+ - style
35
+ - build
36
+ - chore
37
+ - release
38
+ - refactor
39
+ - revert
40
+ - merge
41
+ - wip
42
+
43
+ Commit message reference: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y
44
+ 参考阮一峰Commit message编写指南: https://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html`;
45
+ function isVersionCommitMessage(message) {
46
+ return Boolean(message.startsWith("v") && semver.valid(message.slice(1)));
47
+ }
48
+ function getCommitMessage(commitMessagePath) {
49
+ return readFileSync(commitMessagePath, "utf-8").trim();
50
+ }
51
+ function commitLint(options) {
52
+ const { commitMessagePath, commitMessageRe = COMMIT_MESSAGE_RE, errorMessage = ERROR_MESSAGE, warningMessage = WARNING_MESSAGE } = options;
53
+ if (!commitMessagePath) {
54
+ logger.error("commitMessagePath is required");
55
+ process.exit(1);
56
+ }
57
+ const commitMessage = getCommitMessage(commitMessagePath);
58
+ const isValidCommitMessage = new RegExp(commitMessageRe).test(commitMessage);
59
+ if (!isVersionCommitMessage(commitMessage) && !isValidCommitMessage) {
60
+ logger.error(errorMessage);
61
+ logger.warn(warningMessage);
62
+ process.exit(1);
63
+ }
64
+ }
65
+ //#endregion
66
+ //#region src/changelog.ts
67
+ const COMMIT_TYPE_MAP = {
68
+ feat: "Features",
69
+ fix: "Bug Fixes",
70
+ perf: "Performance Improvements",
71
+ revert: "Reverts",
72
+ refactor: "Code Refactoring",
73
+ docs: "Documentation",
74
+ style: "Styles",
75
+ test: "Tests",
76
+ build: "Build System",
77
+ ci: "Continuous Integration"
78
+ };
79
+ const ALWAYS_SHOW_TYPES = new Set([
80
+ "feat",
81
+ "fix",
82
+ "perf",
83
+ "revert",
84
+ "refactor"
85
+ ]);
86
+ const BREAKING_CHANGE_RE = /BREAKING CHANGES?:\s*([\s\S]+)/;
87
+ const MAIN_TEMPLATE = `{{> header}}
88
+
89
+ {{> footer}}
90
+ {{#each commitGroups}}
91
+
92
+ {{#if title}}
93
+ ### {{title}}
94
+
95
+ {{/if}}
96
+ {{#each commits}}
97
+ {{> commit root=@root}}
98
+ {{/each}}
99
+
100
+ {{/each}}
101
+ `;
102
+ function linkify(text, context, issues) {
103
+ let result = text;
104
+ const repoUrl = context.repository ? `${context.host}/${context.owner}/${context.repository}` : context.repoUrl;
105
+ if (repoUrl) {
106
+ const issueBaseUrl = `${repoUrl}/issues/`;
107
+ result = result.replace(/#([0-9]+)/g, (_, issue) => {
108
+ issues.push(issue);
109
+ return `[#${issue}](${issueBaseUrl}${issue})`;
110
+ });
111
+ }
112
+ if (context.host) result = result.replace(/\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g, (_, username) => username.includes("/") ? `@${username}` : `[@${username}](${context.host}/${username})`);
113
+ return result;
114
+ }
115
+ function extractBreakingText(commit) {
116
+ const body = typeof commit.body === "string" ? commit.body : "";
117
+ const footer = typeof commit.footer === "string" ? commit.footer : "";
118
+ const match = BREAKING_CHANGE_RE.exec(`${footer}\n${body}`);
119
+ if (match?.[1]) return match[1].trim();
120
+ if (typeof commit.subject === "string" && commit.subject) return commit.subject;
121
+ if (typeof commit.header === "string" && commit.header) return commit.header;
122
+ return "";
123
+ }
124
+ function tryParseHeader(commit) {
125
+ if (typeof commit.header !== "string") return null;
126
+ const match = COMMIT_HEADER_RE.exec(commit.header.trim());
127
+ if (!match) return null;
128
+ return {
129
+ type: match[1],
130
+ scope: match[2] || void 0,
131
+ subject: match[4] || void 0,
132
+ isBreaking: Boolean(match[3])
133
+ };
134
+ }
135
+ function processBreakingChanges(commit, context, issues) {
136
+ let discard = true;
137
+ commit.notes.forEach((note) => {
138
+ note.title = "BREAKING CHANGES";
139
+ discard = false;
140
+ });
141
+ const hadBreakingNotes = commit.notes.length > 0;
142
+ const addBreakingNote = () => {
143
+ if (!hadBreakingNotes) {
144
+ const text = linkify(extractBreakingText(commit), context, issues);
145
+ commit.notes.push({
146
+ title: "BREAKING CHANGES",
147
+ text
148
+ });
149
+ }
150
+ discard = false;
151
+ };
152
+ const parsed = tryParseHeader(commit);
153
+ if (parsed) {
154
+ if (!commit.type) commit.type = parsed.type;
155
+ if (!commit.scope && parsed.scope) commit.scope = parsed.scope;
156
+ if (!commit.subject && parsed.subject) commit.subject = parsed.subject;
157
+ if (parsed.isBreaking) addBreakingNote();
158
+ }
159
+ if (typeof commit.type === "string" && commit.type.endsWith("!")) {
160
+ commit.type = commit.type.slice(0, -1);
161
+ addBreakingNote();
162
+ }
163
+ return discard;
164
+ }
165
+ function mapCommitType(commit, discard) {
166
+ if (commit.revert) {
167
+ commit.type = "Reverts";
168
+ return true;
169
+ }
170
+ const mapped = COMMIT_TYPE_MAP[commit.type];
171
+ if (mapped) {
172
+ if (ALWAYS_SHOW_TYPES.has(commit.type) || !discard) {
173
+ commit.type = mapped;
174
+ return true;
175
+ }
176
+ }
177
+ return !discard;
178
+ }
179
+ function createDefaultWriterOpts() {
180
+ return {
181
+ mainTemplate: MAIN_TEMPLATE,
182
+ transform(commit, context) {
183
+ const issues = [];
184
+ if (!mapCommitType(commit, processBreakingChanges(commit, context, issues))) return false;
185
+ if (commit.scope === "*") commit.scope = "";
186
+ if (typeof commit.hash === "string") commit.shortHash = commit.hash.substring(0, 7);
187
+ if (typeof commit.subject === "string") commit.subject = linkify(commit.subject, context, issues);
188
+ commit.references = commit.references.filter((ref) => !issues.includes(ref.issue));
189
+ return commit;
190
+ }
191
+ };
192
+ }
193
+ function changelog({ releaseCount = 0, file = "CHANGELOG.md", preset = "angular", writerOpts = createDefaultWriterOpts() } = {}) {
194
+ const s = spinner();
195
+ s.start("Generating changelog");
196
+ return new Promise((resolve$1) => {
197
+ conventionalChangelog({
198
+ preset,
199
+ releaseCount
200
+ }, void 0, void 0, void 0, writerOpts).pipe(createWriteStream(resolve(process.cwd(), file))).on("close", () => {
201
+ s.stop("Changelog generated successfully!");
202
+ resolve$1();
203
+ });
204
+ });
205
+ }
206
+ //#endregion
207
+ //#region src/utils.ts
208
+ function readJSONSync(path) {
209
+ const content = readFileSync(path, "utf-8").replace(/^\uFEFF/, "");
210
+ try {
211
+ return JSON.parse(content);
212
+ } catch (err) {
213
+ err.message = `${path}: ${err.message}`;
214
+ throw err;
215
+ }
216
+ }
217
+ //#endregion
218
+ //#region src/release.ts
219
+ const cwd = () => process.cwd();
220
+ const releaseTypes = [
221
+ "patch",
222
+ "minor",
223
+ "major",
224
+ "prepatch",
225
+ "preminor",
226
+ "premajor"
227
+ ];
228
+ const BACK_HINT = "Back to previous step";
229
+ async function isWorktreeEmpty() {
230
+ return !(await x("git", ["status", "--porcelain"])).stdout;
231
+ }
232
+ async function isSameVersion(version) {
233
+ const s = spinner();
234
+ s.start("Check remote version...");
235
+ const packageJsones = getPackageJsons();
236
+ const packageJson = packageJsones.find((packageJson) => !packageJson.config.private) || packageJsones[0];
237
+ if (packageJson) {
238
+ const { config } = packageJson;
239
+ try {
240
+ await x("npm", [
241
+ "view",
242
+ `${config.name}@${version ?? config.version}`,
243
+ "version"
244
+ ], { throwOnError: true });
245
+ s.cancel();
246
+ logger.warn(`The npm package has a same remote version ${version ?? config.version}.`);
247
+ return true;
248
+ } catch {
249
+ s.stop();
250
+ return false;
251
+ }
252
+ }
253
+ }
254
+ async function publish({ preRelease, checkRemoteVersion, npmTag }) {
255
+ const s = spinner();
256
+ s.start("Publishing all packages");
257
+ const args = [
258
+ "-r",
259
+ "publish",
260
+ "--no-git-checks",
261
+ "--access",
262
+ "public"
263
+ ];
264
+ if (checkRemoteVersion && await isSameVersion()) {
265
+ logger.error("publishing automatically skipped.");
266
+ return;
267
+ }
268
+ if (preRelease) args.push("--tag", "alpha");
269
+ else if (npmTag) args.push("--tag", npmTag);
270
+ try {
271
+ const ret = await x("pnpm", args, { throwOnError: true });
272
+ s.stop("Publish all packages successfully");
273
+ ret.stdout && logger.log(ret.stdout);
274
+ } catch (error) {
275
+ s.cancel("Publish all packages failed");
276
+ throw error?.output?.stderr ?? error;
277
+ }
278
+ }
279
+ async function pushGit(version, remote = "origin", skipGitTag = false) {
280
+ const s = spinner();
281
+ s.start("Pushing to remote git repository");
282
+ await x("git", ["add", "."], { throwOnError: true });
283
+ await x("git", [
284
+ "commit",
285
+ "-m",
286
+ `v${version}`
287
+ ], { throwOnError: true });
288
+ if (!skipGitTag) {
289
+ await x("git", ["tag", `v${version}`], { throwOnError: true });
290
+ await x("git", [
291
+ "push",
292
+ remote,
293
+ `v${version}`
294
+ ], { throwOnError: true });
295
+ }
296
+ const ret = await x("git", ["push"], { throwOnError: true });
297
+ s.stop("Push remote repository successfully");
298
+ ret.stdout && logger.log(ret.stdout);
299
+ }
300
+ function getPackageJsons() {
301
+ const packageJsons = [resolve(cwd(), "package.json")];
302
+ const packagesDir = resolve(cwd(), "packages");
303
+ if (existsSync(packagesDir)) for (const name of readdirSync(packagesDir)) {
304
+ const pkgPath = resolve(packagesDir, name, "package.json");
305
+ if (existsSync(pkgPath)) packageJsons.push(pkgPath);
306
+ }
307
+ return packageJsons.map((path) => {
308
+ return {
309
+ filePath: path,
310
+ config: readJSONSync(path)
311
+ };
312
+ });
313
+ }
314
+ function updateVersion(version) {
315
+ getPackageJsons().forEach(({ config, filePath }) => {
316
+ config.version = version;
317
+ writeFileSync(filePath, JSON.stringify(config, null, 2));
318
+ });
319
+ }
320
+ async function confirmRegistry() {
321
+ const registry = (await x("npm", [
322
+ "config",
323
+ "get",
324
+ "registry"
325
+ ])).stdout;
326
+ const ret = await confirm({ message: `Current registry is: ${registry}` });
327
+ if (isCancel(ret)) {
328
+ cancel("Operation cancelled.");
329
+ process.exit(0);
330
+ }
331
+ return ret;
332
+ }
333
+ async function confirmVersion(currentVersion, expectVersion) {
334
+ const ret = await select({
335
+ message: "Version confirm",
336
+ options: [`All packages version ${currentVersion} -> ${expectVersion}`, BACK_HINT].map((value) => ({
337
+ label: value,
338
+ value
339
+ }))
340
+ });
341
+ if (isCancel(ret)) {
342
+ cancel("Operation cancelled.");
343
+ process.exit(0);
344
+ }
345
+ return ret;
346
+ }
347
+ async function confirmRefs(remote = "origin") {
348
+ const { stdout } = await x("git", ["remote", "-v"]);
349
+ const reg = new RegExp(`${remote}\t(.*) \\(push`);
350
+ const repo = stdout.match(reg)?.[1];
351
+ const { stdout: branch } = await x("git", ["branch", "--show-current"]);
352
+ const ret = await confirm({ message: `Current refs ${repo}:refs/for/${styleText("blue", branch)}` });
353
+ if (isCancel(ret)) {
354
+ cancel("Operation cancelled.");
355
+ process.exit(0);
356
+ }
357
+ return ret;
358
+ }
359
+ async function getReleaseType() {
360
+ const releaseType = await select({
361
+ message: "Please select release type",
362
+ options: releaseTypes.map((type) => ({
363
+ label: type,
364
+ value: type
365
+ }))
366
+ });
367
+ if (isCancel(releaseType)) {
368
+ cancel("Operation cancelled.");
369
+ process.exit(0);
370
+ }
371
+ return releaseType;
372
+ }
373
+ async function getReleaseVersion(currentVersion) {
374
+ let isPreRelease = false;
375
+ let expectVersion = "";
376
+ let confirmVersionRet = "";
377
+ do {
378
+ const type = await getReleaseType();
379
+ isPreRelease = type.startsWith("pre");
380
+ expectVersion = semver.inc(currentVersion, type, `alpha.${Date.now()}`);
381
+ expectVersion = isPreRelease ? expectVersion.slice(0, -2) : expectVersion;
382
+ confirmVersionRet = await confirmVersion(currentVersion, expectVersion);
383
+ } while (confirmVersionRet === BACK_HINT);
384
+ return {
385
+ isPreRelease,
386
+ expectVersion
387
+ };
388
+ }
389
+ async function release(options) {
390
+ try {
391
+ const currentVersion = readJSONSync(resolve(cwd(), "package.json")).version;
392
+ if (!currentVersion) {
393
+ logger.error("Your package is missing the version field");
394
+ return;
395
+ }
396
+ if (!await isWorktreeEmpty()) {
397
+ logger.error("Git worktree is not empty, please commit changed");
398
+ return;
399
+ }
400
+ if (!await confirmRefs(options.remote)) return;
401
+ if (!options.skipNpmPublish && !await confirmRegistry()) return;
402
+ const { isPreRelease, expectVersion } = await getReleaseVersion(currentVersion);
403
+ if (options.checkRemoteVersion && await isSameVersion(expectVersion)) {
404
+ logger.error("Please check remote version.");
405
+ return;
406
+ }
407
+ updateVersion(expectVersion);
408
+ if (options.task) await options.task(expectVersion, currentVersion);
409
+ if (!options.skipNpmPublish) await publish({
410
+ preRelease: isPreRelease,
411
+ npmTag: options.npmTag
412
+ });
413
+ if (!isPreRelease) {
414
+ if (!options.skipChangelog) await changelog();
415
+ await pushGit(expectVersion, options.remote, options.skipGitTag);
416
+ }
417
+ logger.success(`Release version ${expectVersion} successfully!`);
418
+ if (isPreRelease) {
419
+ try {
420
+ await x("git", ["restore", "**/package.json"], { throwOnError: true });
421
+ } catch {}
422
+ try {
423
+ await x("git", ["restore", "package.json"], { throwOnError: true });
424
+ } catch {}
425
+ }
426
+ } catch (error) {
427
+ logger.error(error);
428
+ process.exit(1);
429
+ }
430
+ }
431
+ //#endregion
432
+ export { updateVersion as a, COMMIT_MESSAGE_RE as c, isVersionCommitMessage as d, release as i, commitLint as l, isSameVersion as n, changelog as o, publish as r, COMMIT_HEADER_RE as s, getPackageJsons as t, getCommitMessage as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varlet/release",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "publish all packages, generate changelogs and check commit messages",
5
5
  "keywords": [
6
6
  "changelog",
@@ -42,33 +42,30 @@
42
42
  "package.json": "prettier --write"
43
43
  },
44
44
  "dependencies": {
45
- "@clack/prompts": "^1.0.0",
45
+ "@clack/prompts": "^1.1.0",
46
46
  "commander": "^11.1.0",
47
47
  "conventional-changelog": "^5.1.0",
48
- "fs-extra": "^11.3.3",
49
- "rslog": "^2.0.0",
48
+ "rslog": "^2.0.1",
50
49
  "semver": "^7.5.4",
51
- "tinyexec": "^1.0.2"
50
+ "tinyexec": "^1.0.4"
52
51
  },
53
52
  "devDependencies": {
54
53
  "@configurajs/eslint": "^0.1.2",
55
54
  "@configurajs/prettier": "^0.1.4",
56
55
  "@ianvs/prettier-plugin-sort-imports": "^4.7.1",
57
56
  "@types/conventional-changelog": "^3.1.5",
58
- "@types/fs-extra": "^11.0.4",
59
57
  "@types/node": "^22.14.0",
60
58
  "@types/semver": "^7.5.5",
61
- "@vitest/coverage-v8": "^4.0.18",
59
+ "@vitest/coverage-v8": "^4.1.0",
62
60
  "eslint": "^9.39.2",
63
61
  "lint-staged": "^16.2.7",
64
62
  "prettier": "^3.8.1",
65
63
  "prettier-plugin-packagejson": "^3.0.0",
66
64
  "prettier-plugin-tailwindcss": "^0.7.2",
67
- "rimraf": "^6.1.2",
68
65
  "simple-git-hooks": "^2.13.1",
69
- "tsdown": "^0.20.3",
66
+ "tsdown": "^0.21.4",
70
67
  "typescript": "^5.9.3",
71
- "vitest": "^4.0.18"
68
+ "vitest": "^4.1.0"
72
69
  },
73
70
  "engines": {
74
71
  "node": "^20.19.0 || >=22.12.0",
@@ -76,7 +73,6 @@
76
73
  },
77
74
  "scripts": {
78
75
  "build": "tsdown",
79
- "clean": "rimraf node_modules dist",
80
76
  "commit-lint": "node dist/cli.js commit-lint",
81
77
  "dev": "tsdown --watch",
82
78
  "format": "eslint . --fix && prettier --write .",