@peiyanlu/release 1.1.1 → 1.2.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
@@ -1,19 +1,15 @@
1
1
  # Release Project
2
2
 
3
- <p>
4
- <a href="https://www.npmjs.com/package/@peiyanlu/release" target="_blank">
5
- <img src="https://img.shields.io/badge/npm-@peiyanlu/release-blue.svg?logo=npm" alt="NPM Package" />
6
- </a>
7
- <a href="https://www.npmjs.com/package/@peiyanlu/release" target="_blank">
8
- <img src="https://img.shields.io/npm/v/@peiyanlu/release.svg" alt="NPM Version" />
9
- </a>
10
- <a href="https://www.npmjs.com/package/@peiyanlu/release" target="_blank">
11
- <img src="https://img.shields.io/npm/l/@peiyanlu/release.svg" alt="Package License" />
12
- </a>
13
- <a href="https://www.npmjs.com/package/@peiyanlu/release" target="_blank">
14
- <img src="https://img.shields.io/npm/dm/@peiyanlu/release.svg" alt="NPM Downloads" />
15
- </a>
16
- </p>
3
+
4
+ [![module](https://img.shields.io/badge/package-ESM--only-FFE536.svg?logo=npm)](https://nodejs.org/api/esm.html)
5
+ [![npm](https://img.shields.io/npm/v/@peiyanlu/release.svg)](https://npmjs.com/package/@peiyanlu/release)
6
+ [![license](https://img.shields.io/npm/l/@peiyanlu/release.svg)](https://npmjs.com/package/@peiyanlu/release)
7
+ [![downloads](https://img.shields.io/npm/dm/@peiyanlu/release.svg)](https://npmjs.com/package/@peiyanlu/release)
8
+ [![node](https://img.shields.io/node/v/@peiyanlu/release.svg)](https://nodejs.org)
9
+ [![size](https://packagephobia.com/badge?p=@peiyanlu/release)](https://packagephobia.com/result?p=@peiyanlu/release)
10
+ [![deps](https://img.shields.io/librariesio/release/npm/@peiyanlu/release)](https://libraries.io/npm/%40peiyanlu%2Frelease)
11
+ [![build](https://img.shields.io/github/actions/workflow/status/peiyanlu/release/test.yaml?branch=master&logo=github)](https://github.com/peiyanlu/release/actions)
12
+ [![coverage](https://coveralls.io/repos/github/peiyanlu/release/badge.svg?branch=master)](https://coveralls.io/github/peiyanlu/release?branch=master)
17
13
 
18
14
  🚀 **一个用于自动化版本管理与包发布的通用 CLI 工具**
19
15
 
@@ -282,6 +278,23 @@ COMMIT_TYPES:
282
278
 
283
279
  适用于 CI/CD 场景下的**安全、无 Token** 自动化发布。
284
280
 
281
+ 支持配置发布前清理:
282
+
283
+ - 移除 `package.json` 中对发布无意义的字段
284
+ - `README.md` 只保留主要内容
285
+
286
+ ```ts
287
+ // 默认配置
288
+ export default defineConfig({
289
+ npm: {
290
+ cleanup: {
291
+ packageJson: true,
292
+ readme: false,
293
+ removeTempDir: true,
294
+ }
295
+ },
296
+ })
297
+ ```
285
298
 
286
299
  ## GitHub Releases
287
300
 
@@ -320,10 +333,13 @@ GitHub Release 可基于 Git Tag 自动创建,并支持附带:
320
333
  ### 示例用法
321
334
 
322
335
  - 发布到 npm
336
+
323
337
  ```ts
324
338
  // publishCI.ts
325
339
 
326
340
  import { publishTagToNpm } from '@peiyanlu/release'
341
+
342
+
327
343
  await publishTagToNpm({})
328
344
  ```
329
345
 
@@ -353,6 +369,7 @@ jobs:
353
369
  ```
354
370
 
355
371
  - 创建 GitHub Release
372
+
356
373
  ```yaml
357
374
  # .github/workflows/release.yml
358
375
 
package/dist/cli.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  import path, { join, resolve } from "node:path";
2
2
  import { fileURLToPath, pathToFileURL } from "node:url";
3
- import { bumpPackageVersion, canPublish, coloredStatus, eol, execAsync, getAuthenticatedUser, getGithubReleaseUrl, getGithubUrl, getLatestTag, getLogSince, getPackageInfo, getPackageUrl, getPublishedVersion, getRemoteList, getRemoteNames, getShortStatus, gitAddAll, gitAddSync, gitAddTracked, gitBranchCurrent, gitCommitMessage, gitPushBranch, gitPushTag, gitResetHardSync, gitTagAnnotated, gitTagDeleteSync, hasWriteAccess, isGitRepo, isOtpError, isPrerelease, isValidVersion, isWorkingTreeClean, parseGitHubRepo, parseVersion, pingRegistry, publishPackage, readJsonFile, resolveChangelogRange, resolvePublishTag } from "@peiyanlu/cli-utils";
3
+ import { bumpPackageVersion, canPublish, coloredStatus, eol, execAsync, getAuthenticatedUser, getGithubReleaseUrl, getGithubUrl, getLatestTag, getLogSince, getPackageInfo, getPackageUrl, getPublishedVersion, getRemoteList, getRemoteNames, getShortStatus, gitAddAll, gitAddSync, gitAddTracked, gitBranchCurrent, gitCommitMessage, gitPushBranch, gitPushTag, gitResetHardSync, gitTagAnnotated, gitTagDeleteSync, hasWriteAccess, isGitRepo, isOtpError, isPrerelease, isValidVersion, isWorkingTreeClean, parseGitHubRepo, parseVersion, pingRegistry, publishPackage, resolveChangelogRange, resolvePublishTag } from "@peiyanlu/cli-utils";
4
+ import { copyDir, createMatcher, createTempDir, editFile, readJsonFile, readJsonFileSync, writeJsonFile } from "@peiyanlu/node-utils";
4
5
  import { dim, green, red, rgb, underline, yellow } from "ansis";
5
6
  import { program } from "commander";
6
7
  import { createReadStream, createWriteStream, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
7
8
  import { cancel, confirm, group, intro, isCancel, log, outro, select, tasks, text } from "@clack/prompts";
8
- import { castArray, isFunction, isPlainObject, isUndefined, isZero, mapObjectValues } from "@peiyanlu/ts-utils";
9
+ import { castArray, isEmptyObject, isFunction, isPlainObject, isUndefined, isZero, mapObject, omit, partition, unset } from "@peiyanlu/ts-utils";
9
10
  import { publint } from "publint";
10
11
  import { formatMessage } from "publint/utils";
11
12
  import { inc, neq } from "semver";
@@ -20,7 +21,7 @@ import { Octokit } from "@octokit/rest";
20
21
  import { contentType } from "mime-types";
21
22
  import open from "open";
22
23
  import { glob } from "tinyglobby";
23
- //#region node_modules/.pnpm/tsdown@0.22.3_publint@0.3.21_typescript@6.0.3/node_modules/tsdown/esm-shims.js
24
+ //#region node_modules/.pnpm/tsdown@0.22.13_publint@0.3.21_typescript@6.0.3/node_modules/tsdown/esm-shims.js
24
25
  const getFilename = () => fileURLToPath(import.meta.url);
25
26
  const getDirname = () => path.dirname(getFilename());
26
27
  const __dirname = /* @__PURE__ */ getDirname();
@@ -133,7 +134,7 @@ const MSG = {
133
134
  },
134
135
  ERROR: {
135
136
  GIT_WORKDIR: `[git] Working tree must be clean`,
136
- GIT_REGISTRY: (name) => `[git] Repository not found for ${dim(name)}`,
137
+ GIT_REGISTRY: (name) => `[git] Repository not found for package "${dim(name)}"`,
137
138
  GIT_REMOTE: (name) => `[git] Remote "${dim(name)}" not found`,
138
139
  GITHUB_TAG_EXIT: (tag) => `[github] Release for tag "${tag}" already exists and is not a draft`,
139
140
  GITHUB_TOKEN: (tokenRef) => `[github] Missing GitHub token. Please set ${yellow(tokenRef ?? "GITHUB_TOKEN")}`,
@@ -141,7 +142,6 @@ const MSG = {
141
142
  GITHUB_USER: "[github] User does not have permission to create releases",
142
143
  NPM_REGISTRY: (registry) => `[npm] Unable to reach npm registry ${underline(dim(registry))}`,
143
144
  NPM_AUTH: `[npm] Not authenticated with npm. Please run ${yellow("npm login")} and try again`,
144
- NPM_PERMISSION: `[npm] Missing publish permission. Please use a token with publish access and try again`,
145
145
  NPM_USER: (user, name) => `[npm] User ${dim(user)} is not a collaborator of ${dim(name)}`
146
146
  },
147
147
  ABORT: {
@@ -175,8 +175,9 @@ const gitCheck = async (ctx, config) => {
175
175
  ctx.noGitHub = true;
176
176
  return;
177
177
  }
178
- const { name: remoteName, url: remoteUrl } = (await getRemoteList())[0];
179
- if (!remoteUrl) if (requireRemote) throw new Error(MSG.ERROR.GIT_REMOTE(name));
178
+ const remote = (await getRemoteList())[0];
179
+ const { name: remoteName = "origin", url: remoteUrl } = remote ?? {};
180
+ if (!remote || !remoteUrl) if (requireRemote) throw new Error(MSG.ERROR.GIT_REMOTE(remoteName));
180
181
  else {
181
182
  ctx.noGitHub = true;
182
183
  return;
@@ -186,9 +187,11 @@ const gitCheck = async (ctx, config) => {
186
187
  remoteUrl
187
188
  });
188
189
  const [owner, repo] = parseGitHubRepo(remoteUrl);
190
+ const url = getGithubUrl(owner, repo);
189
191
  Object.assign(ctx.github, {
190
192
  owner,
191
- repo
193
+ repo,
194
+ url
192
195
  });
193
196
  };
194
197
  const commitAndTag = async (ctx, config) => {
@@ -401,6 +404,7 @@ const createDefaultContext = () => {
401
404
  tagMessage: ""
402
405
  },
403
406
  github: {
407
+ url: "",
404
408
  username: "",
405
409
  repo: "",
406
410
  changelog: "",
@@ -464,7 +468,12 @@ const createDefaultConfig = (isCI) => {
464
468
  npm: {
465
469
  publish: isCI,
466
470
  publishArgs: [],
467
- skipChecks: false
471
+ skipChecks: false,
472
+ cleanup: {
473
+ packageJson: true,
474
+ readme: false,
475
+ removeTempDir: true
476
+ }
468
477
  },
469
478
  github: {
470
479
  release: isCI,
@@ -553,12 +562,6 @@ const defaultTypes = [
553
562
  effect: "hidden",
554
563
  description: "持续集成配置"
555
564
  },
556
- {
557
- type: "deps",
558
- section: "📦 依赖更新",
559
- effect: "changelog",
560
- description: "依赖版本变更"
561
- },
562
565
  {
563
566
  type: "security",
564
567
  section: "🛡️ 安全修复",
@@ -584,16 +587,22 @@ const defaultTypes = [
584
587
  description: "用户体验改进"
585
588
  },
586
589
  {
587
- type: "config",
588
- section: "⚙️ 配置",
589
- effect: "hidden",
590
- description: "配置文件更新"
590
+ type: "deps",
591
+ section: "📦 依赖更新",
592
+ effect: "changelog",
593
+ description: "依赖版本变更"
591
594
  },
592
595
  {
593
596
  type: "tool",
594
597
  section: "🛠️ 工具链",
595
598
  effect: "hidden",
596
599
  description: "工具链变更"
600
+ },
601
+ {
602
+ type: "config",
603
+ section: "⚙️ 配置",
604
+ effect: "hidden",
605
+ description: "配置文件更新"
597
606
  }
598
607
  ];
599
608
  //#endregion
@@ -922,6 +931,107 @@ const runNpmOptPrompts = async (ctx, config) => {
922
931
  return res;
923
932
  };
924
933
  //#endregion
934
+ //#region src/npm/cleanup.ts
935
+ const PUBLISH_CONFIG_FIELDS = [
936
+ "bin",
937
+ "main",
938
+ "exports",
939
+ "types",
940
+ "typings",
941
+ "module",
942
+ "browser",
943
+ "esnext",
944
+ "es2015",
945
+ "unpkg",
946
+ "umd:main",
947
+ "typesVersions",
948
+ "cpu",
949
+ "os",
950
+ "engines"
951
+ ];
952
+ const IGNORE_FIELDS = [
953
+ "babel",
954
+ "browserslist",
955
+ "c8",
956
+ "commitlint",
957
+ "devDependencies",
958
+ "eslintConfig",
959
+ "eslintIgnore",
960
+ "husky",
961
+ "jest",
962
+ "lint-staged",
963
+ "nano-staged",
964
+ "pre-commit",
965
+ "prettier",
966
+ "pwmetrics",
967
+ "remarkConfig",
968
+ "renovate",
969
+ "resolutions",
970
+ "sharec",
971
+ "simple-git-hooks",
972
+ "simple-pre-commit",
973
+ "size-limit",
974
+ "typeCoverage",
975
+ "yaspeller",
976
+ "pnpm",
977
+ "packageManager"
978
+ ];
979
+ const KEEP_SCRIPT_NAMES = [
980
+ "preinstall",
981
+ "install",
982
+ "postinstall"
983
+ ];
984
+ const IGNORE_FILES = [
985
+ ".DS_Store",
986
+ ".git",
987
+ ".vscode",
988
+ ".idea",
989
+ "node_modules"
990
+ ];
991
+ const clearPackageJSON = async (dir, options) => {
992
+ const file = join(dir, "package.json");
993
+ const json = await readJsonFile(file);
994
+ const { ignoreFields = [], keepScripts = [] } = options ?? {};
995
+ const mergePublishConfig = (json) => {
996
+ if (!json.publishConfig) return json;
997
+ const publishConfig = { ...json.publishConfig };
998
+ PUBLISH_CONFIG_FIELDS.forEach((field) => {
999
+ if (publishConfig[field]) {
1000
+ json[field] = publishConfig[field];
1001
+ delete publishConfig[field];
1002
+ }
1003
+ });
1004
+ return {
1005
+ ...json,
1006
+ publishConfig
1007
+ };
1008
+ };
1009
+ const removeIgnoredFields = (json) => {
1010
+ const [matched, unmatched] = partition([...ignoreFields, ...IGNORE_FIELDS], (i) => i.includes("."));
1011
+ matched.forEach((field) => unset(json, field));
1012
+ return omit(json, unmatched);
1013
+ };
1014
+ const cleanScripts = (json) => {
1015
+ if (!json.scripts) return json;
1016
+ const keep = [...keepScripts, ...KEEP_SCRIPT_NAMES];
1017
+ const excludes = Object.keys(json.scripts).filter((k) => !keep.includes(k));
1018
+ json.scripts = omit(json.scripts, excludes);
1019
+ return json;
1020
+ };
1021
+ const removeEmptyObjects = (json) => {
1022
+ return omit(json, Object.entries(json).filter(([k, v]) => isEmptyObject(v)).map(([k, v]) => k));
1023
+ };
1024
+ await writeJsonFile(file, removeEmptyObjects(cleanScripts(removeIgnoredFields(structuredClone(mergePublishConfig(json))))));
1025
+ };
1026
+ const cleanReadme = async (dir, homepage) => {
1027
+ const file = join(dir, "README.md");
1028
+ homepage && await editFile(file, (str) => {
1029
+ return str.split(/^##\s+\S.*$/m)[0] + `## Docs
1030
+
1031
+ Read full docs **[here](${homepage})**.`;
1032
+ });
1033
+ };
1034
+ //#endregion
925
1035
  //#region src/npm/publish.ts
926
1036
  const npmCheck = async (ctx, config) => {
927
1037
  const { pkg: { name, current, publishConfig: { registry } } } = ctx;
@@ -929,14 +1039,13 @@ const npmCheck = async (ctx, config) => {
929
1039
  const tag = await resolvePublishTag(name, current);
930
1040
  Object.assign(ctx.npm, { tag });
931
1041
  if (skipChecks) return "";
932
- const [pinged, username, canPublished] = await Promise.all([
1042
+ const [pinged, username] = await Promise.all([
933
1043
  pingRegistry(registry),
934
1044
  getAuthenticatedUser(registry),
935
1045
  canPublish(registry)
936
1046
  ]);
937
1047
  if (!pinged) throw new Error(MSG.ERROR.NPM_REGISTRY(registry));
938
1048
  if (!username) throw new Error(MSG.ERROR.NPM_AUTH);
939
- if (!canPublished) throw new Error(MSG.ERROR.NPM_PERMISSION);
940
1049
  Object.assign(ctx.npm, { username });
941
1050
  const latest = await getPublishedVersion(`${name}@${tag}`);
942
1051
  if (latest) {
@@ -945,18 +1054,34 @@ const npmCheck = async (ctx, config) => {
945
1054
  return latest ? `(${tag} → ${latest})` : `(${tag} → no published version)`;
946
1055
  };
947
1056
  const publishNpm = async (ctx, config) => {
948
- const { dryRun, selectedPkg, npm: { otp, tag } } = ctx;
949
- const { npm: { publish, publishArgs }, getPkgDir } = config;
1057
+ const { dryRun, selectedPkg, npm: { otp, tag }, github: { url } } = ctx;
1058
+ const { npm: { publish, publishArgs, cleanup: { packageJson, readme, removeTempDir } }, getPkgDir } = config;
950
1059
  if (!publish) return;
951
1060
  const otpArgs = otp ? ["--otp", otp] : [];
952
1061
  const dryRunArg = dryRun ? ["--dry-run"] : [];
953
- return publishPackage({
1062
+ const args = [
1063
+ ...otpArgs,
1064
+ ...publishArgs,
1065
+ ...dryRunArg
1066
+ ];
1067
+ if (packageJson || readme) {
1068
+ const parent = resolve(getPkgDir(selectedPkg));
1069
+ const temp = await createTempDir(parent);
1070
+ await copyDir(parent, temp.path, { ignore: [(name) => {
1071
+ return [temp.name, ...IGNORE_FILES].map((pattern) => createMatcher(pattern)).some((matcher) => matcher(name));
1072
+ }] });
1073
+ const options = typeof packageJson === "boolean" ? void 0 : packageJson;
1074
+ packageJson && await clearPackageJSON(temp.path, options);
1075
+ readme && await cleanReadme(temp.path, `${url}#readme`);
1076
+ await publishPackage({
1077
+ tag,
1078
+ args,
1079
+ cwd: temp.path
1080
+ });
1081
+ removeTempDir && await temp.remove();
1082
+ } else await publishPackage({
954
1083
  tag,
955
- args: [
956
- ...otpArgs,
957
- ...publishArgs,
958
- ...dryRunArg
959
- ],
1084
+ args,
960
1085
  cwd: getPkgDir(selectedPkg)
961
1086
  });
962
1087
  };
@@ -984,16 +1109,16 @@ var Action = class {
984
1109
  taskEnd(MSG.OUTRO_PREPARE(ctx.dryRun));
985
1110
  }
986
1111
  async createContext(cmdArgs, options, prepare = false) {
987
- const { version: cVersion, name: cName } = readJsonFile(join(__dirname, "../package.json"));
1112
+ const { version: cVersion, name: cName } = readJsonFileSync(join(__dirname, "../package.json"));
988
1113
  const { otp, package: defPkg, releaseCount, ...others } = options;
989
- const { showChangelog, showRelease, ci, dryRun, onlyChangelog } = mapObjectValues(others, (v) => Boolean(v));
1114
+ const { showChangelog, showRelease, ci, dryRun, onlyChangelog } = mapObject(others, (k, v) => [k, Boolean(v)]);
990
1115
  process.env["dryRun"] = String(dryRun);
991
1116
  const { configFile, config: local } = await resolveConfig(process.cwd());
992
1117
  const config = mergeConfig(createDefaultConfig(ci ? true : void 0), local);
993
1118
  info(MSG.INFO.TOOL(cName, cVersion));
994
1119
  info(MSG.INFO.CONFIG(configFile));
995
1120
  {
996
- const { requireCleanWorkingTree, skipGit, skipNpm, skipGithub, isMonorepo, includeHidden } = mapObjectValues(others, (v) => Boolean(v));
1121
+ const { requireCleanWorkingTree, skipGit, skipNpm, skipGithub, isMonorepo, includeHidden } = mapObject(others, (k, v) => [k, Boolean(v)]);
997
1122
  const count = Number(releaseCount);
998
1123
  config.changelog.releaseCount = isNaN(count) ? 0 : count;
999
1124
  config.changelog.includeHidden = includeHidden;
@@ -1042,7 +1167,7 @@ var Action = class {
1042
1167
  showRelease,
1043
1168
  process.env.GITHUB_ACTIONS,
1044
1169
  process.env.CI
1045
- ].some((k) => Boolean(k)),
1170
+ ].some(Boolean),
1046
1171
  pkg: {
1047
1172
  name: pkgName,
1048
1173
  isPrivate: pkgPrivate,
@@ -1245,7 +1370,7 @@ var Action = class {
1245
1370
  };
1246
1371
  //#endregion
1247
1372
  //#region src/cli.ts
1248
- const pkg = readJsonFile(join(__dirname, "..", "package.json"));
1373
+ const pkg = readJsonFileSync(join(__dirname, "..", "package.json"));
1249
1374
  program.name("release").description(pkg.description).version(pkg.version, "-v, --version", "Print the tool version and exit.").usage("[release-type] [options]").argument("[release-type]", "Version bump type: patch | minor | major").option("-n, --dry-run", "Run in dry mode", false).option("-m, --is-monorepo", "Mono-repo project", false).option("-p, --package <pkg>", "Specify package name (Mono-repo CI only).", "").option("--otp <code>", "One-time password for npm publish.", "").option("--prepare", "Prepare a release.", false).option("--ci", "Enable CI mode.", false).option("--show-changelog", "Print changelog and exit.", false).option("--show-release", "Print release version and exit.", false).option("-r, --release-count <count>", "Release count for release.", (value) => Number(value), 1).option("--include-hidden", "Include hidden commit types in the changelog.", false).option("--only-changelog", "Only update the changelog.", false).option("-C, --no-require-clean-working-tree", "Allow releasing with uncommitted changes.", true).option("--skip-git", "Skip all Git-related checks and operations.", false).option("--skip-npm", "Skip all npm-related checks and operations.", false).option("--skip-github", "Skip all GitHub-related checks and operations.", false).helpOption("-h, --help", "Display help information.").action(async (releaseType, options) => {
1250
1375
  const { prepare } = options;
1251
1376
  const action = new Action();
@@ -1254,7 +1379,7 @@ program.name("release").description(pkg.description).version(pkg.version, "-v, -
1254
1379
  program.command("init").description("Create a release configuration file").option("-f, --force", "Overwrite existing config file", false).option("-m, --monorepo", "Mono-repo project", false).option("-a, --add", "Automatically stage the config file.", false).action((options) => {
1255
1380
  const { force = false, monorepo = false, add = false } = options;
1256
1381
  const cwd = process.cwd();
1257
- const { type } = readJsonFile(join(cwd, "package.json"));
1382
+ const { type } = readJsonFileSync(join(cwd, "package.json"));
1258
1383
  const isESM = type === "module";
1259
1384
  const isTS = existsSync(join(cwd, "tsconfig.json"));
1260
1385
  const infile = `release.config.${isESM ? "" : "m"}${isTS ? "ts" : "js"}`;
@@ -1278,7 +1403,7 @@ program.command("init").description("Create a release configuration file").optio
1278
1403
  if (add) gitAddSync([infile]);
1279
1404
  console.log(`\n${content}`);
1280
1405
  });
1281
- program.addHelpText("afterAll", "\nThanks for using!");
1406
+ program.addHelpText("afterAll", "\nCASE: release -C -r0 --include-hidden --only-changelog\n\nThanks for using!");
1282
1407
  program.parse(process.argv);
1283
1408
  program.on("command:*", () => {
1284
1409
  console.error(`\n${red`Error`} Invalid command: ${red`%s`}`, program.args.join(" "));
package/dist/index.d.mts CHANGED
@@ -1,15 +1,21 @@
1
- import { DeepPartial, DeepRequired } from "@peiyanlu/ts-utils";
1
+ import { DeepPartial, DeepRequired, Simplify } from "@peiyanlu/ts-utils";
2
2
  import { ConventionalChangelog } from "conventional-changelog";
3
- import { CommitType } from "conventional-changelog-conventionalcommits";
3
+ import { CommitType as CommitType$1 } from "conventional-changelog-conventionalcommits";
4
4
  import { log } from "@clack/prompts";
5
-
6
5
  //#region src/types.d.ts
6
+ interface CommitType {
7
+ type: string;
8
+ section?: string;
9
+ effect?: "bump" | "changelog" | "hidden";
10
+ scope?: string;
11
+ description?: string;
12
+ }
7
13
  type Logger = typeof log;
8
- type HookTiming = 'before' | 'after';
9
- type HookAction = 'bump' | 'publish' | 'push' | 'release';
14
+ type HookTiming = "before" | "after";
15
+ type HookAction = "bump" | "publish" | "push" | "release";
10
16
  type ReleaseHookKey = `${HookTiming}:${HookAction}`;
11
17
  type ReleaseHookValue = string | string[] | ((logger: Logger) => Promise<void> | void);
12
- type HookConfig = { [key in ReleaseHookKey]: ReleaseHookValue };
18
+ type HookConfig = { [key in ReleaseHookKey]: ReleaseHookValue; };
13
19
  interface ReleaseConfig {
14
20
  /**
15
21
  * 生命周期钩子配置
@@ -122,11 +128,42 @@ interface ReleaseConfig {
122
128
  publishArgs: string[];
123
129
  /**
124
130
  * 是否跳过发布前检查(如 ping、whoami 等校验)
131
+ *
132
+ * 启用后可减少发布前的网络请求,但可能无法提前发现登录状态异常或 Registry 不可用等问题。
125
133
  */
126
134
  skipChecks: boolean;
135
+ /**
136
+ * 发布前的文件清理配置
137
+ *
138
+ * 用于控制复制到临时发布目录后的清理行为,不会修改源项目文件。
139
+ */
140
+ cleanup: {
141
+ /**
142
+ * 是否清理 package.json
143
+ *
144
+ * 包括移除开发相关字段、应用 publishConfig,并保留发布所需内容。
145
+ */
146
+ packageJson: boolean | {
147
+ /** 需要忽略的字段,支持 `a.b.c` */
148
+ ignoreFields: string[];
149
+ /** 需要保留的脚本,默认:`preinstall | install | postinstall` */
150
+ keepScripts: string[];
151
+ };
152
+ /**
153
+ * 是否清理 README.md
154
+ *
155
+ * 仅保留主要内容,并移除或精简不需要随包发布的文档内容。
156
+ */
157
+ readme: boolean;
158
+ /**
159
+ * 是否在发布之后删除临时目录
160
+ */
161
+ removeTempDir: boolean;
162
+ };
127
163
  };
128
164
  /**
129
165
  * GitHub Release 相关配置
166
+ *
130
167
  * 控制是否创建 Release、发布方式以及上传资源。
131
168
  */
132
169
  github: {
@@ -192,11 +229,12 @@ interface ParsePresetOptions {
192
229
  /** 生成 CHANGELOG 时包含默认隐藏的 commit 类型 */
193
230
  includeHidden?: boolean;
194
231
  /** 对默认 commit types 进行转换 */
195
- transformTypes?: (types: CommitType[]) => CommitType[];
232
+ transformTypes?: (types: CommitType$1[]) => CommitType$1[];
196
233
  }
197
- declare const createGenerator: (options: GenerateOptions & ParsePresetOptions) => Promise<ConventionalChangelog>;
198
- declare const getChangelog: (options: GenerateOptions & ParsePresetOptions) => Promise<string>;
199
- declare const generateChangelog: (options: GenerateOptions & ParsePresetOptions) => Promise<void>;
234
+ type GOptions = Simplify<GenerateOptions & ParsePresetOptions>;
235
+ declare const createGenerator: (options: GOptions) => Promise<ConventionalChangelog>;
236
+ declare const getChangelog: (options: GOptions) => Promise<string>;
237
+ declare const generateChangelog: (options: GOptions) => Promise<void>;
200
238
  //#endregion
201
239
  //#region src/npm/publish.d.ts
202
240
  interface PublishTagOptions {
@@ -222,4 +260,4 @@ interface PublishTagOptions {
222
260
  }
223
261
  declare const publishTagToNpm: (options: PublishTagOptions) => Promise<void>;
224
262
  //#endregion
225
- export { type Logger, type UserConfig, createGenerator, defineConfig, generateChangelog, getChangelog, mergeConfig, publishTagToNpm };
263
+ export { type CommitType, type Logger, type UserConfig, createGenerator, defineConfig, generateChangelog, getChangelog, mergeConfig, publishTagToNpm };
package/dist/index.mjs CHANGED
@@ -8,6 +8,7 @@ import "conventional-recommended-bump";
8
8
  import { writeFile } from "node:fs/promises";
9
9
  import { finished } from "node:stream/promises";
10
10
  import { bold, compareUrl, each, heading, link, list, newline, segments, small, words } from "@conventional-changelog/template";
11
+ import "@peiyanlu/node-utils";
11
12
  import { blue, cyan, green, magenta, red, underline, yellow } from "ansis";
12
13
  //#region src/config.ts
13
14
  function defineConfig(config) {
@@ -102,12 +103,6 @@ const defaultTypes = [
102
103
  effect: "hidden",
103
104
  description: "持续集成配置"
104
105
  },
105
- {
106
- type: "deps",
107
- section: "📦 依赖更新",
108
- effect: "changelog",
109
- description: "依赖版本变更"
110
- },
111
106
  {
112
107
  type: "security",
113
108
  section: "🛡️ 安全修复",
@@ -133,16 +128,22 @@ const defaultTypes = [
133
128
  description: "用户体验改进"
134
129
  },
135
130
  {
136
- type: "config",
137
- section: "⚙️ 配置",
138
- effect: "hidden",
139
- description: "配置文件更新"
131
+ type: "deps",
132
+ section: "📦 依赖更新",
133
+ effect: "changelog",
134
+ description: "依赖版本变更"
140
135
  },
141
136
  {
142
137
  type: "tool",
143
138
  section: "🛠️ 工具链",
144
139
  effect: "hidden",
145
140
  description: "工具链变更"
141
+ },
142
+ {
143
+ type: "config",
144
+ section: "⚙️ 配置",
145
+ effect: "hidden",
146
+ description: "配置文件更新"
146
147
  }
147
148
  ];
148
149
  //#endregion
@@ -218,7 +219,6 @@ const publishTagToNpm = async (options) => {
218
219
  [pkgName, version] = tag.split(tagSeparator);
219
220
  console.log(`🔍 Parsed tag → package: "${yellow(pkgName)}", version: "${green(version)}"`);
220
221
  } else {
221
- pkgName = ".";
222
222
  version = tag;
223
223
  console.log(`🔍 Parsed tag → version only: "${green(version)}"`);
224
224
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peiyanlu/release",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Release project tool.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -17,42 +17,27 @@
17
17
  },
18
18
  "./package.json": "./package.json"
19
19
  },
20
- "scripts": {
21
- "dev": "tsdown -w",
22
- "build": "tsdown",
23
- "prepublishOnly": "tsdown",
24
- "release": "node index.js",
25
- "test": "vitest run",
26
- "test:cov": "vitest run --coverage"
27
- },
28
20
  "dependencies": {
29
21
  "@actions/core": "^3.0.1",
30
22
  "@actions/github": "^9.1.1",
31
- "@clack/prompts": "^1.5.0",
23
+ "@clack/prompts": "^1.7.0",
24
+ "@conventional-changelog/template": "^1.2.1",
32
25
  "@octokit/rest": "^22.0.1",
33
- "@peiyanlu/cli-utils": "^0.0.12",
34
- "@peiyanlu/ts-utils": "^0.0.8",
26
+ "@peiyanlu/cli-utils": "^0.0.14",
27
+ "@peiyanlu/node-utils": "^0.0.2",
28
+ "@peiyanlu/ts-utils": "^0.0.11",
35
29
  "ansis": "^4.3.1",
36
30
  "commander": "^15.0.0",
37
- "conventional-changelog": "^8.0.0",
38
- "conventional-changelog-conventionalcommits": "^10.2.0",
39
- "conventional-recommended-bump": "^12.0.0",
40
- "@conventional-changelog/template": "^1.2.0",
31
+ "conventional-changelog": "^8.1.0",
32
+ "conventional-changelog-conventionalcommits": "^10.2.1",
33
+ "conventional-recommended-bump": "^12.1.0",
41
34
  "mime-types": "^3.0.2",
35
+ "minimatch": "^10.2.5",
42
36
  "open": "^11.0.0",
43
37
  "publint": "^0.3.21",
44
38
  "semver": "^7.8.5",
45
39
  "tinyglobby": "^0.2.17"
46
40
  },
47
- "devDependencies": {
48
- "@types/mime-types": "^3.0.1",
49
- "@types/node": "^24.12.4",
50
- "@types/semver": "^7.7.1",
51
- "@vitest/coverage-v8": "^4.1.8",
52
- "tsdown": "^0.22.3",
53
- "typescript": "^6.0.3",
54
- "vitest": "^4.1.9"
55
- },
56
41
  "author": {
57
42
  "name": "YanluPei",
58
43
  "email": "peiyanlu@vip.qq.com"