@peiyanlu/release 0.0.14 → 0.0.15

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
@@ -122,7 +122,9 @@ export default defineConfig({
122
122
  packages: [],
123
123
  getPkgDir: (pkg) => `.`,
124
124
  toTag: (pkg: string, version: string) => `v${ version }`,
125
- tagPrefix: undefined,
125
+ changelog: {
126
+ tagPrefix: undefined,
127
+ },
126
128
 
127
129
  git: {
128
130
  commit: true,
@@ -157,7 +159,9 @@ export default defineConfig({
157
159
  packages: [ 'demo-a', 'demo-b' ],
158
160
  getPkgDir: (pkg) => `packages/${ pkg }`,
159
161
  toTag: (pkg: string, version: string) => `${ pkg }@${ version }`,
160
- tagPrefix: (pkg: string) => `${ pkg }@`,
162
+ changelog: {
163
+ tagPrefix: (pkg: string) => `${ pkg }@`,
164
+ },
161
165
 
162
166
  git: {
163
167
  commit: true,
package/dist/cli.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  import path, { join, resolve } from "node:path";
2
2
  import { fileURLToPath, pathToFileURL } from "node:url";
3
- import { bumpPackageVersion, coloredChangeset, eol, execAsync, getAllRemotes, getAuthenticatedUser, getCurrentBranch, getGithubReleaseUrl, getGithubUrl, getLatestTag, getLog, getPackageInfo, getPackageUrl, getPublishedVersion, getRemote, getStatus, gitAddAll, gitAddTracked, gitCommit, gitTagAnnotated, hasWriteAccess, isGitRepo, isOtpError, isPrerelease, isValid, isWorkingDirClean, parseGitHubRepo, parseVersion, pingRegistry, publishPackage, pushBranch, pushTag, readJsonFile, resolveChangelogRange, resolvePublishTag } from "@peiyanlu/cli-utils";
3
+ import { bumpPackageVersion, coloredChangeset, eol, execAsync, getAllRemotes, getAuthenticatedUser, getCurrentBranch, getGithubReleaseUrl, getGithubUrl, getLatestTag, getLog, getPackageInfo, getPackageUrl, getPublishedVersion, getRemote, getStatus, gitAddAll, gitAddTracked, gitCommit, gitTagAnnotated, hasWriteAccess, isGitRepo, isOtpError, isPrerelease, isValid, isWorkingDirClean, parseGitHubRepo, parseVersion, pingRegistry, publishPackage, pushBranch, pushTag, readJsonFile, registryArg, resolveChangelogRange, resolvePublishTag, runNpm } from "@peiyanlu/cli-utils";
4
4
  import { dim, green, red, rgb, underline, yellow } from "ansis";
5
5
  import { program } from "commander";
6
6
  import { createReadStream, createWriteStream, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
7
7
  import { cancel, confirm, group, intro, isCancel, log, outro, select, tasks, text } from "@clack/prompts";
8
+ import { castArray, isFunction, isPlainObject, isUndefined, isZero } from "@peiyanlu/ts-utils";
8
9
  import { publint } from "publint";
9
10
  import { formatMessage } from "publint/utils";
10
11
  import { inc, neq } from "semver";
11
- import { castArray, isFunction, isPlainObject, isUndefined } from "@peiyanlu/ts-utils";
12
12
  import { spawnSync } from "node:child_process";
13
13
  import { inspect } from "node:util";
14
14
  import { ConventionalChangelog } from "conventional-changelog";
@@ -54,13 +54,12 @@ const resolveConfig = async (cwd) => {
54
54
  };
55
55
  };
56
56
  const mergeConfig = (defaults, overrides) => {
57
- if (!overrides) return structuredClone(defaults);
58
- if (Object.keys(defaults).length === 0) return structuredClone(overrides);
59
57
  const result = {};
60
- for (const key in defaults) {
58
+ for (const key of Object.keys(defaults)) {
61
59
  const defaultValue = defaults[key];
62
60
  const overrideValue = overrides[key];
63
- if (overrideValue === void 0) if (isFunction(defaultValue)) result[key] = defaultValue;
61
+ if (isUndefined(overrideValue)) if (isFunction(defaultValue)) result[key] = defaultValue;
62
+ else if (isPlainObject(defaultValue)) result[key] = mergeConfig(defaultValue, {});
64
63
  else result[key] = structuredClone(defaultValue);
65
64
  else if (isPlainObject(defaultValue) && isPlainObject(overrideValue)) result[key] = mergeConfig(defaultValue, overrideValue);
66
65
  else if (isFunction(overrideValue)) result[key] = overrideValue;
@@ -134,13 +133,14 @@ const MSG = {
134
133
  GIT_WORKDIR: `[git] Working dir must be clean`,
135
134
  GIT_REGISTRY: (name) => `[git] Repository not found for ${dim(name)}`,
136
135
  GIT_REMOTE: (name) => `[git] Remote "${dim(name)}" not found`,
137
- GITHUB_TAG_EXIT: (tag) => `[github] Release or tag "${tag}" already exists`,
136
+ GITHUB_TAG_EXIT: (tag) => `[github] Release for tag "${tag}" already exists and is not a draft`,
138
137
  GITHUB_TOKEN: (tokenRef) => `[github] Missing GitHub token. Please set ${yellow(tokenRef ?? "GITHUB_TOKEN")}`,
139
138
  GITHUB_AUTH: "[github] Invalid GitHub token or insufficient permissions",
140
139
  GITHUB_USER: "[github] User does not have permission to create releases",
141
140
  NPM_REGISTRY: (registry) => `[npm] Unable to reach npm registry ${underline(dim(registry))}`,
142
- NPM_AUTH: `[npm] Not authenticated with npm. Please run ${yellow("npm login")} and try again.`,
143
- NPM_USER: (user, name) => `[npm] User ${dim(user)} is not a collaborator of ${dim(name)}.`
141
+ NPM_AUTH: `[npm] Not authenticated with npm. Please run ${yellow("npm login")} and try again`,
142
+ NPM_PERMISSION: `[npm] Missing publish permission. Please use a token with publish access and try again`,
143
+ NPM_USER: (user, name) => `[npm] User ${dim(user)} is not a collaborator of ${dim(name)}`
144
144
  },
145
145
  ABORT: {
146
146
  CANCEL: "Operation cancelled",
@@ -444,8 +444,11 @@ const createDefaultConfig = (isCI) => {
444
444
  toTag(pkg, version) {
445
445
  return `v${version}`;
446
446
  },
447
- tagPrefix(pkg) {
448
- return "";
447
+ changelog: {
448
+ tagPrefix(pkg) {
449
+ return "";
450
+ },
451
+ releaseCount: 1
449
452
  },
450
453
  ignoreGit: false,
451
454
  ignoreNpm: false,
@@ -521,42 +524,42 @@ const defaultTypes = [
521
524
  },
522
525
  {
523
526
  type: "chore",
524
- section: "🎫 其他更新",
527
+ section: "🧹 其他更新",
525
528
  description: "非功能性更改"
526
529
  },
527
530
  {
528
531
  type: "refactor",
529
- section: "♻ 代码重构",
532
+ section: "♻️ 代码重构",
530
533
  description: "重构代码"
531
534
  },
532
535
  {
533
536
  type: "test",
534
- section: "✅ 测试",
537
+ section: "🧪 测试",
535
538
  description: "测试更新"
536
539
  },
537
540
  {
538
541
  type: "build",
539
- section: "👷‍ 构建系统",
542
+ section: "🏗️ 构建系统",
540
543
  description: "构建工具和系统修改"
541
544
  },
542
545
  {
543
546
  type: "ci",
544
- section: "🔧 CI 配置",
547
+ section: "🤖 CI",
545
548
  description: "持续集成配置"
546
549
  },
547
550
  {
548
551
  type: "config",
549
- section: "🔨 配置",
552
+ section: "⚙️ 配置",
550
553
  description: "配置文件更新"
551
554
  },
552
555
  {
553
556
  type: "deps",
554
- section: "🔗 依赖更新",
557
+ section: "📦 依赖更新",
555
558
  description: "依赖版本变更"
556
559
  },
557
560
  {
558
561
  type: "security",
559
- section: "🔒 安全修复",
562
+ section: "🛡️ 安全修复",
560
563
  description: "安全性更新"
561
564
  },
562
565
  {
@@ -566,13 +569,18 @@ const defaultTypes = [
566
569
  },
567
570
  {
568
571
  type: "ux",
569
- section: "🖥️ 用户体验",
572
+ section: "🎨 用户体验",
570
573
  description: "用户体验改进"
571
574
  },
572
575
  {
573
576
  type: "hotfix",
574
- section: "🔥 热修复",
577
+ section: "🚑 热修复",
575
578
  description: "紧急修复"
579
+ },
580
+ {
581
+ type: "tool",
582
+ section: "🛠️ 工具链",
583
+ description: "工具链变更"
576
584
  }
577
585
  ];
578
586
  //#endregion
@@ -632,32 +640,37 @@ const parsePreset = async () => {
632
640
  {{/each}}`.trim();
633
641
  return preset;
634
642
  };
635
- const createGenerator = async ({ getPkgDir, tagPrefix }) => {
643
+ const createGenerator = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
636
644
  const pkgDir = getPkgDir();
637
645
  const preset = await parsePreset();
638
- return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount: 1 }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
646
+ return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
639
647
  };
640
- const getChangelog = async ({ getPkgDir, tagPrefix }) => {
648
+ const getChangelog = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
641
649
  const generator = await createGenerator({
642
650
  getPkgDir,
643
- tagPrefix
651
+ tagPrefix,
652
+ releaseCount
644
653
  });
645
654
  let changelog = "";
646
655
  for await (const chunk of generator.write()) changelog += chunk;
647
656
  return changelog;
648
657
  };
649
- const generateChangelog = async ({ getPkgDir, tagPrefix }) => {
658
+ const generateChangelog = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
650
659
  const infile = join(getPkgDir(), "CHANGELOG.md");
651
660
  if (!existsSync(infile)) await writeFile(infile, "");
652
661
  const exist = readFileSync(infile, "utf-8");
662
+ const writeStream = createWriteStream(infile);
653
663
  const generator = await createGenerator({
654
664
  getPkgDir,
655
- tagPrefix
665
+ tagPrefix,
666
+ releaseCount
656
667
  });
657
- const writeStream = createWriteStream(infile);
658
668
  for await (const chunk of generator.write()) writeStream.write(chunk);
659
- if (exist.trim().length) writeStream.write(eol(2));
660
- writeStream.write(exist);
669
+ const override = isZero(releaseCount);
670
+ if (!(isZero(exist.trim().length) || override)) {
671
+ writeStream.write(eol());
672
+ writeStream.write(exist);
673
+ }
661
674
  writeStream.end();
662
675
  await finished(writeStream);
663
676
  };
@@ -776,7 +789,7 @@ const createCReleaseByTag = async (octokit, options) => {
776
789
  const existing = await getReleaseByTagSafe(octokit, options);
777
790
  if (!existing) return createCRelease(octokit, { ...options });
778
791
  const { draft, id: releaseId } = existing;
779
- if (!draft) throw new Error(`Release for tag "${options.tag}" already exists and is not a draft`);
792
+ if (!draft) throw new Error(MSG.ERROR.GITHUB_TAG_EXIT(options.tag));
780
793
  return updateCRelease(octokit, {
781
794
  releaseId,
782
795
  ...options
@@ -919,15 +932,32 @@ const runNpmOptPrompts = async (ctx, config) => {
919
932
  };
920
933
  //#endregion
921
934
  //#region src/npm/publish.ts
935
+ const canPublish = async (registry) => {
936
+ const res = await runNpm([
937
+ "publish",
938
+ "--dry-run",
939
+ "--no-git-checks",
940
+ "--access",
941
+ "public",
942
+ ...registryArg(registry)
943
+ ], { error: "throw" }).catch((err) => err);
944
+ if (!(res instanceof Error)) return true;
945
+ return [/previously published versions/i, /cannot publish over/i].some((reg) => reg.test(res.message));
946
+ };
922
947
  const npmCheck = async (ctx, config) => {
923
948
  const { pkg: { name, current, publishConfig: { registry } } } = ctx;
924
949
  const { npm: { skipChecks } } = config;
925
950
  const tag = await resolvePublishTag(name, current);
926
951
  Object.assign(ctx.npm, { tag });
927
952
  if (skipChecks) return "";
928
- const [pinged, username] = await Promise.all([pingRegistry(registry), getAuthenticatedUser(registry)]);
953
+ const [pinged, username, canPublished] = await Promise.all([
954
+ pingRegistry(registry),
955
+ getAuthenticatedUser(registry),
956
+ canPublish(registry)
957
+ ]);
929
958
  if (!pinged) throw new Error(MSG.ERROR.NPM_REGISTRY(registry));
930
959
  if (!username) throw new Error(MSG.ERROR.NPM_AUTH);
960
+ if (!canPublished) throw new Error(MSG.ERROR.NPM_PERMISSION);
931
961
  Object.assign(ctx.npm, { username });
932
962
  const latest = await getPublishedVersion(`${name}@${tag}`);
933
963
  if (latest) {
@@ -1077,7 +1107,7 @@ var Action = class {
1077
1107
  }
1078
1108
  async bumpTask(ctx, config) {
1079
1109
  const { pkg: { current }, dryRun, isCI, showRelease, showChangelog, selectedPkg, noGit } = ctx;
1080
- const { isMonorepo, hooks, getPkgDir, tagPrefix } = config;
1110
+ const { isMonorepo, hooks, getPkgDir, changelog: { tagPrefix, releaseCount } } = config;
1081
1111
  const need = (ctx) => {
1082
1112
  const { pkg: { next }, isIncrement } = ctx;
1083
1113
  return !next && isIncrement;
@@ -1107,11 +1137,16 @@ var Action = class {
1107
1137
  }]).catch((err) => abortOnError(err, ctx));
1108
1138
  await runLifeCycleHook(hooks, "after:bump", dryRun);
1109
1139
  if (noGit) return;
1110
- const { from, to } = await resolveChangelogRange(isIncrement, isMonorepo ? `${tagPrefix?.(selectedPkg)}*` : "*");
1140
+ const match = isMonorepo ? `${tagPrefix?.(selectedPkg)}*` : "*";
1141
+ const { from, to } = isZero(releaseCount) ? {
1142
+ from: "",
1143
+ to: "HEAD"
1144
+ } : await resolveChangelogRange(isIncrement, match);
1111
1145
  const commits = await getLog(from, to, getPkgDir(selectedPkg));
1112
1146
  const changelog = await getChangelog({
1113
1147
  getPkgDir: () => getPkgDir(selectedPkg),
1114
- tagPrefix: tagPrefix?.(selectedPkg)
1148
+ tagPrefix: tagPrefix?.(selectedPkg),
1149
+ releaseCount
1115
1150
  });
1116
1151
  Object.assign(ctx.github, { changelog });
1117
1152
  if (commits) {
@@ -1122,14 +1157,15 @@ var Action = class {
1122
1157
  }
1123
1158
  async changelogTask(ctx, config) {
1124
1159
  const { isIncrement, dryRun, selectedPkg, noGit } = ctx;
1125
- const { getPkgDir, tagPrefix } = config;
1160
+ const { getPkgDir, changelog: { tagPrefix, releaseCount } } = config;
1126
1161
  if (noGit) return;
1127
1162
  await tasks([{
1128
1163
  title: MSG.TASK.CHANGELOG.START,
1129
1164
  task: async () => {
1130
1165
  await generateChangelog({
1131
1166
  getPkgDir: () => getPkgDir(selectedPkg),
1132
- tagPrefix: tagPrefix?.(selectedPkg)
1167
+ tagPrefix: tagPrefix?.(selectedPkg),
1168
+ releaseCount
1133
1169
  });
1134
1170
  await gitAddAll();
1135
1171
  return success(MSG.TASK.CHANGELOG.END, dryRun);
@@ -1222,7 +1258,9 @@ program.command("init").description("Create a release configuration file").optio
1222
1258
  packages: [ 'demo-a', 'demo-b' ],
1223
1259
  getPkgDir: (pkg) => \`packages/\${ pkg }\`,
1224
1260
  toTag: (pkg, version) => \`\${ pkg }@\${ version }\`,
1225
- tagPrefix: (pkg) => \`\${ pkg }@\`,
1261
+ changelog: {
1262
+ tagPrefix: (pkg) => \`\${ pkg }@\`,
1263
+ },
1226
1264
  }`;
1227
1265
  writeFileSync(configPath, [`import { defineConfig } from '@peiyanlu/release'`, `export default defineConfig(${monorepo ? monoConfigToString() : "{}"})`].join(eol(2)), "utf-8");
1228
1266
  console.log(`${green`✔`} ${infile} created successfully`);
package/dist/index.d.mts CHANGED
@@ -23,14 +23,20 @@ interface ReleaseConfig {
23
23
  getPkgDir: (pkg: string) => string;
24
24
  /** 使用 monorepo 时使用 toTag,例如 `${pkg}@${version}`,默认为 `v${version}` 作为单仓库 */
25
25
  toTag: (pkg: string, version: string) => string;
26
- /** 作为 CHANGELOG 标题的 Tag 前缀,多包场景下需要 `${pkg}@` */
27
- tagPrefix: ((pkg: string) => string) | undefined;
26
+ /**
27
+ * CHANGELOG 相关配置
28
+ * 影响生成的日志数量、日志标题(monorepo)。
29
+ */
30
+ changelog: {
31
+ /** 作为 CHANGELOG 标题的 Tag 前缀,多包场景下需要 `${pkg}@` */tagPrefix: ((pkg: string) => string); /** 生成多少个版本的变更日志,详见 {@link Options} */
32
+ releaseCount: number;
33
+ };
28
34
  /** 忽略 git、github 环境检查以及后续操作 */
29
- ignoreGit: boolean | undefined;
35
+ ignoreGit: boolean;
30
36
  /** 忽略 npm 环境检查以及后续操作 */
31
- ignoreNpm: boolean | undefined;
37
+ ignoreNpm: boolean;
32
38
  /** 忽略 github 环境检查以及后续操作 */
33
- ignoreGithub: boolean | undefined;
39
+ ignoreGithub: boolean;
34
40
  /**
35
41
  * Git 相关配置
36
42
  * 影响提交、打 tag、推送等本地/远程 Git 行为。
@@ -156,27 +162,32 @@ declare function defineConfig(config: UserConfigFnObject): UserConfigFnObject;
156
162
  declare function defineConfig(config: UserConfigFnPromise): UserConfigFnPromise;
157
163
  declare function defineConfig(config: UserConfigFn): UserConfigFn;
158
164
  declare function defineConfig(config: UserConfigExport): UserConfigExport;
159
- declare const mergeConfig: <T extends object>(defaults: DeepRequired<T>, overrides?: DeepPartial<T>) => DeepRequired<T>;
165
+ declare const mergeConfig: <T extends object>(defaults: DeepRequired<T>, overrides: DeepPartial<T>) => DeepRequired<T>;
160
166
  //#endregion
161
167
  //#region src/git/changelog.d.ts
162
- interface Options {
168
+ interface GenerateOptions {
163
169
  /** @example () => `packages/${pkg}` */
164
170
  getPkgDir: () => string;
165
171
  /** @example `${pkg}@` */
166
172
  tagPrefix?: string;
173
+ /** 生成多少个变更日志,详见 {@link Options} */
174
+ releaseCount?: number;
167
175
  }
168
176
  declare const createGenerator: ({
169
177
  getPkgDir,
170
- tagPrefix
171
- }: Options) => Promise<ConventionalChangelog>;
178
+ tagPrefix,
179
+ releaseCount
180
+ }: GenerateOptions) => Promise<ConventionalChangelog>;
172
181
  declare const getChangelog: ({
173
182
  getPkgDir,
174
- tagPrefix
175
- }: Options) => Promise<string>;
183
+ tagPrefix,
184
+ releaseCount
185
+ }: GenerateOptions) => Promise<string>;
176
186
  declare const generateChangelog: ({
177
187
  getPkgDir,
178
- tagPrefix
179
- }: Options) => Promise<void>;
188
+ tagPrefix,
189
+ releaseCount
190
+ }: GenerateOptions) => Promise<void>;
180
191
  //#endregion
181
192
  //#region src/npm/publish.d.ts
182
193
  interface PublishTagOptions {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { isFunction, isPlainObject } from "@peiyanlu/ts-utils";
1
+ import { isFunction, isPlainObject, isUndefined, isZero } from "@peiyanlu/ts-utils";
2
2
  import { createWriteStream, existsSync, readFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { eol, getPackageInfo, publishPackage, resolvePublishTag } from "@peiyanlu/cli-utils";
@@ -12,13 +12,12 @@ function defineConfig(config) {
12
12
  return config;
13
13
  }
14
14
  const mergeConfig = (defaults, overrides) => {
15
- if (!overrides) return structuredClone(defaults);
16
- if (Object.keys(defaults).length === 0) return structuredClone(overrides);
17
15
  const result = {};
18
- for (const key in defaults) {
16
+ for (const key of Object.keys(defaults)) {
19
17
  const defaultValue = defaults[key];
20
18
  const overrideValue = overrides[key];
21
- if (overrideValue === void 0) if (isFunction(defaultValue)) result[key] = defaultValue;
19
+ if (isUndefined(overrideValue)) if (isFunction(defaultValue)) result[key] = defaultValue;
20
+ else if (isPlainObject(defaultValue)) result[key] = mergeConfig(defaultValue, {});
22
21
  else result[key] = structuredClone(defaultValue);
23
22
  else if (isPlainObject(defaultValue) && isPlainObject(overrideValue)) result[key] = mergeConfig(defaultValue, overrideValue);
24
23
  else if (isFunction(overrideValue)) result[key] = overrideValue;
@@ -66,42 +65,42 @@ const defaultTypes = [
66
65
  },
67
66
  {
68
67
  type: "chore",
69
- section: "🎫 其他更新",
68
+ section: "🧹 其他更新",
70
69
  description: "非功能性更改"
71
70
  },
72
71
  {
73
72
  type: "refactor",
74
- section: "♻ 代码重构",
73
+ section: "♻️ 代码重构",
75
74
  description: "重构代码"
76
75
  },
77
76
  {
78
77
  type: "test",
79
- section: "✅ 测试",
78
+ section: "🧪 测试",
80
79
  description: "测试更新"
81
80
  },
82
81
  {
83
82
  type: "build",
84
- section: "👷‍ 构建系统",
83
+ section: "🏗️ 构建系统",
85
84
  description: "构建工具和系统修改"
86
85
  },
87
86
  {
88
87
  type: "ci",
89
- section: "🔧 CI 配置",
88
+ section: "🤖 CI",
90
89
  description: "持续集成配置"
91
90
  },
92
91
  {
93
92
  type: "config",
94
- section: "🔨 配置",
93
+ section: "⚙️ 配置",
95
94
  description: "配置文件更新"
96
95
  },
97
96
  {
98
97
  type: "deps",
99
- section: "🔗 依赖更新",
98
+ section: "📦 依赖更新",
100
99
  description: "依赖版本变更"
101
100
  },
102
101
  {
103
102
  type: "security",
104
- section: "🔒 安全修复",
103
+ section: "🛡️ 安全修复",
105
104
  description: "安全性更新"
106
105
  },
107
106
  {
@@ -111,13 +110,18 @@ const defaultTypes = [
111
110
  },
112
111
  {
113
112
  type: "ux",
114
- section: "🖥️ 用户体验",
113
+ section: "🎨 用户体验",
115
114
  description: "用户体验改进"
116
115
  },
117
116
  {
118
117
  type: "hotfix",
119
- section: "🔥 热修复",
118
+ section: "🚑 热修复",
120
119
  description: "紧急修复"
120
+ },
121
+ {
122
+ type: "tool",
123
+ section: "🛠️ 工具链",
124
+ description: "工具链变更"
121
125
  }
122
126
  ];
123
127
  //#endregion
@@ -177,32 +181,37 @@ const parsePreset = async () => {
177
181
  {{/each}}`.trim();
178
182
  return preset;
179
183
  };
180
- const createGenerator = async ({ getPkgDir, tagPrefix }) => {
184
+ const createGenerator = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
181
185
  const pkgDir = getPkgDir();
182
186
  const preset = await parsePreset();
183
- return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount: 1 }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
187
+ return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
184
188
  };
185
- const getChangelog = async ({ getPkgDir, tagPrefix }) => {
189
+ const getChangelog = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
186
190
  const generator = await createGenerator({
187
191
  getPkgDir,
188
- tagPrefix
192
+ tagPrefix,
193
+ releaseCount
189
194
  });
190
195
  let changelog = "";
191
196
  for await (const chunk of generator.write()) changelog += chunk;
192
197
  return changelog;
193
198
  };
194
- const generateChangelog = async ({ getPkgDir, tagPrefix }) => {
199
+ const generateChangelog = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
195
200
  const infile = join(getPkgDir(), "CHANGELOG.md");
196
201
  if (!existsSync(infile)) await writeFile(infile, "");
197
202
  const exist = readFileSync(infile, "utf-8");
203
+ const writeStream = createWriteStream(infile);
198
204
  const generator = await createGenerator({
199
205
  getPkgDir,
200
- tagPrefix
206
+ tagPrefix,
207
+ releaseCount
201
208
  });
202
- const writeStream = createWriteStream(infile);
203
209
  for await (const chunk of generator.write()) writeStream.write(chunk);
204
- if (exist.trim().length) writeStream.write(eol(2));
205
- writeStream.write(exist);
210
+ const override = isZero(releaseCount);
211
+ if (!(isZero(exist.trim().length) || override)) {
212
+ writeStream.write(eol());
213
+ writeStream.write(exist);
214
+ }
206
215
  writeStream.end();
207
216
  await finished(writeStream);
208
217
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peiyanlu/release",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "description": "Release project tool.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,7 +20,7 @@
20
20
  "scripts": {
21
21
  "dev": "tsdown -w",
22
22
  "build": "tsdown",
23
- "prepublish": "tsdown",
23
+ "prepublishOnly": "tsdown",
24
24
  "release": "node index.js",
25
25
  "test": "vitest run",
26
26
  "test:cov": "vitest run --coverage"
@@ -30,8 +30,8 @@
30
30
  "@actions/github": "^9.1.1",
31
31
  "@clack/prompts": "^1.5.0",
32
32
  "@octokit/rest": "^22.0.1",
33
- "@peiyanlu/cli-utils": "^0.0.8",
34
- "@peiyanlu/ts-utils": "^0.0.5",
33
+ "@peiyanlu/cli-utils": "^0.0.9",
34
+ "@peiyanlu/ts-utils": "^0.0.7",
35
35
  "ansis": "^4.3.1",
36
36
  "commander": "^15.0.0",
37
37
  "conventional-changelog": "^7.2.0",