@zjex/git-workflow 0.0.1 → 0.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.
@@ -42,6 +42,14 @@ interface TagChoice {
42
42
  value: string;
43
43
  }
44
44
 
45
+ // 获取指定前缀的最新 tag(不依赖 shell 管道)
46
+ function getLatestTag(prefix: string): string {
47
+ const tags = execOutput(`git tag -l "${prefix}*" --sort=-v:refname`)
48
+ .split("\n")
49
+ .filter(Boolean);
50
+ return tags[0] || "";
51
+ }
52
+
45
53
  export async function createTag(inputPrefix?: string): Promise<void> {
46
54
  const config = getConfig();
47
55
  const fetchSpinner = ora("正在获取 tags...").start();
@@ -60,13 +68,68 @@ export async function createTag(inputPrefix?: string): Promise<void> {
60
68
 
61
69
  if (!prefix) {
62
70
  const allTags = execOutput("git tag -l").split("\n").filter(Boolean);
71
+
72
+ // 仓库没有任何 tag 的情况
73
+ if (allTags.length === 0) {
74
+ prefix = await input({
75
+ message: "当前仓库没有 tag,请输入前缀 (如 v):",
76
+ default: "v",
77
+ theme,
78
+ });
79
+ if (!prefix) {
80
+ console.log(colors.yellow("已取消"));
81
+ return;
82
+ }
83
+
84
+ // 选择初始版本号
85
+ const initialVersion = await select({
86
+ message: "选择初始版本号:",
87
+ choices: [
88
+ { name: `${prefix}0.0.1`, value: "0.0.1" },
89
+ { name: `${prefix}0.1.0`, value: "0.1.0" },
90
+ { name: `${prefix}1.0.0`, value: "1.0.0" },
91
+ { name: "自定义...", value: "__custom__" },
92
+ ],
93
+ theme,
94
+ });
95
+
96
+ let version = initialVersion;
97
+ if (initialVersion === "__custom__") {
98
+ version = await input({
99
+ message: "请输入版本号 (如 0.0.1):",
100
+ theme,
101
+ });
102
+ if (!version) {
103
+ console.log(colors.yellow("已取消"));
104
+ return;
105
+ }
106
+ }
107
+
108
+ const newTag = `${prefix}${version}`;
109
+ const ok = await select({
110
+ message: `确认创建 ${newTag}?`,
111
+ choices: [
112
+ { name: "是", value: true },
113
+ { name: "否", value: false },
114
+ ],
115
+ theme,
116
+ });
117
+ if (ok) {
118
+ doCreateTag(newTag);
119
+ }
120
+ return;
121
+ }
122
+
123
+ // 从现有 tag 中提取前缀
63
124
  const prefixes = [
64
125
  ...new Set(allTags.map((t) => t.replace(/[0-9].*/, "")).filter(Boolean)),
65
126
  ];
66
127
 
67
128
  if (prefixes.length === 0) {
129
+ // 有 tag 但无法提取前缀(比如纯数字 tag)
68
130
  prefix = await input({
69
- message: "当前仓库没有 tag,请输入新前缀 (如 v):",
131
+ message: "请输入 tag 前缀 (如 v):",
132
+ default: "v",
70
133
  theme,
71
134
  });
72
135
  if (!prefix) {
@@ -75,9 +138,7 @@ export async function createTag(inputPrefix?: string): Promise<void> {
75
138
  }
76
139
  } else {
77
140
  const prefixWithDate: PrefixInfo[] = prefixes.map((p) => {
78
- const latest = execOutput(
79
- `git tag -l "${p}*" --sort=-v:refname | head -1`
80
- );
141
+ const latest = getLatestTag(p);
81
142
  const date = latest
82
143
  ? execOutput(`git log -1 --format=%ct "${latest}" 2>/dev/null`)
83
144
  : "0";
@@ -108,9 +169,7 @@ export async function createTag(inputPrefix?: string): Promise<void> {
108
169
  }
109
170
  }
110
171
 
111
- const latestTag = execOutput(
112
- `git tag -l "${prefix}*" --sort=-v:refname | head -1`
113
- );
172
+ const latestTag = getLatestTag(prefix);
114
173
 
115
174
  if (!latestTag) {
116
175
  const newTag = `${prefix}1.0.0`;
package/src/config.ts CHANGED
@@ -18,6 +18,24 @@ export interface GwConfig {
18
18
  defaultTagPrefix?: string;
19
19
  // 创建分支后是否自动推送,默认询问
20
20
  autoPush?: boolean;
21
+ // commit 时是否自动暂存所有更改,默认 true
22
+ autoStage?: boolean;
23
+ // commit 时是否使用 emoji,默认 true
24
+ useEmoji?: boolean;
25
+ // 自定义 commit 类型的 emoji
26
+ commitEmojis?: {
27
+ feat?: string;
28
+ fix?: string;
29
+ docs?: string;
30
+ style?: string;
31
+ refactor?: string;
32
+ perf?: string;
33
+ test?: string;
34
+ build?: string;
35
+ ci?: string;
36
+ chore?: string;
37
+ revert?: string;
38
+ };
21
39
  }
22
40
 
23
41
  const defaultConfig: GwConfig = {
@@ -26,6 +44,8 @@ const defaultConfig: GwConfig = {
26
44
  requireId: false,
27
45
  featureIdLabel: "Story ID",
28
46
  hotfixIdLabel: "Issue ID",
47
+ autoStage: true,
48
+ useEmoji: true,
29
49
  };
30
50
 
31
51
  function getGitRoot(): string {
package/src/index.ts CHANGED
@@ -1,18 +1,146 @@
1
1
  // @ts-nocheck shebang handled by tsup banner
2
2
 
3
3
  import { cac } from "cac";
4
- import { checkGitRepo } from "./utils.js";
4
+ import { select } from "@inquirer/prompts";
5
+ import { ExitPromptError } from "@inquirer/core";
6
+ import { checkGitRepo, theme, colors } from "./utils.js";
5
7
  import { createBranch, deleteBranch } from "./commands/branch.js";
6
8
  import { listTags, createTag } from "./commands/tag.js";
7
9
  import { release } from "./commands/release.js";
8
10
  import { init } from "./commands/init.js";
9
11
  import { stash } from "./commands/stash.js";
12
+ import { commit } from "./commands/commit.js";
10
13
  import { showHelp } from "./commands/help.js";
11
14
 
12
- declare const __VERSION__: string;
15
+ // 捕获 Ctrl+C 退出,静默处理
16
+ process.on("uncaughtException", (err) => {
17
+ if (err instanceof ExitPromptError) {
18
+ process.exit(0);
19
+ }
20
+ console.error(err);
21
+ process.exit(1);
22
+ });
23
+
24
+ declare const __VERSION__: string | undefined;
25
+
26
+ // 开发环境下从 package.json 读取版本号
27
+ const version: string =
28
+ typeof __VERSION__ !== "undefined"
29
+ ? __VERSION__
30
+ : (await import("../package.json", { with: { type: "json" } })).default
31
+ .version;
32
+
33
+ // 交互式主菜单
34
+ async function mainMenu(): Promise<void> {
35
+ // ASCII Art Logo
36
+ console.log(
37
+ colors.green(`
38
+ ███████╗ ██╗███████╗██╗ ██╗
39
+ ╚══███╔╝ ██║██╔════╝╚██╗██╔╝
40
+ ███╔╝ ██║█████╗ ╚███╔╝
41
+ ███╔╝ ██ ██║██╔══╝ ██╔██╗
42
+ ███████╗╚█████╔╝███████╗██╔╝ ██╗
43
+ ╚══════╝ ╚════╝ ╚══════╝╚═╝ ╚═╝
44
+ `)
45
+ );
46
+ console.log(colors.dim(` git-workflow v${version}\n`));
47
+
48
+ const action = await select({
49
+ message: "选择操作:",
50
+ choices: [
51
+ {
52
+ name: `[1] ✨ 创建 feature 分支 ${colors.dim("gw f")}`,
53
+ value: "feature",
54
+ },
55
+ {
56
+ name: `[2] 🐛 创建 hotfix 分支 ${colors.dim("gw h")}`,
57
+ value: "hotfix",
58
+ },
59
+ {
60
+ name: `[3] 🗑️ 删除分支 ${colors.dim("gw d")}`,
61
+ value: "delete",
62
+ },
63
+ {
64
+ name: `[4] 📝 提交代码 ${colors.dim("gw c")}`,
65
+ value: "commit",
66
+ },
67
+ {
68
+ name: `[5] 🏷️ 创建 tag ${colors.dim("gw t")}`,
69
+ value: "tag",
70
+ },
71
+ {
72
+ name: `[6] 📋 列出 tags ${colors.dim("gw ts")}`,
73
+ value: "tags",
74
+ },
75
+ {
76
+ name: `[7] 📦 发布版本 ${colors.dim("gw r")}`,
77
+ value: "release",
78
+ },
79
+ {
80
+ name: `[8] 💾 管理 stash ${colors.dim("gw s")}`,
81
+ value: "stash",
82
+ },
83
+ {
84
+ name: `[9] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
85
+ value: "init",
86
+ },
87
+ { name: "[0] ❓ 帮助", value: "help" },
88
+ { name: "[q] 退出", value: "exit" },
89
+ ],
90
+ loop: false,
91
+ theme,
92
+ });
93
+
94
+ switch (action) {
95
+ case "feature":
96
+ checkGitRepo();
97
+ await createBranch("feature");
98
+ break;
99
+ case "hotfix":
100
+ checkGitRepo();
101
+ await createBranch("hotfix");
102
+ break;
103
+ case "delete":
104
+ checkGitRepo();
105
+ await deleteBranch();
106
+ break;
107
+ case "tag":
108
+ checkGitRepo();
109
+ await createTag();
110
+ break;
111
+ case "tags":
112
+ checkGitRepo();
113
+ await listTags();
114
+ break;
115
+ case "commit":
116
+ checkGitRepo();
117
+ await commit();
118
+ break;
119
+ case "release":
120
+ await release();
121
+ break;
122
+ case "stash":
123
+ checkGitRepo();
124
+ await stash();
125
+ break;
126
+ case "init":
127
+ await init();
128
+ break;
129
+ case "help":
130
+ console.log(showHelp());
131
+ break;
132
+ case "exit":
133
+ break;
134
+ }
135
+ }
13
136
 
14
137
  const cli = cac("gw");
15
138
 
139
+ // 默认命令 - 显示交互式菜单
140
+ cli.command("", "显示交互式菜单").action(() => {
141
+ return mainMenu();
142
+ });
143
+
16
144
  cli
17
145
  .command("feature", "创建 feature 分支")
18
146
  .alias("feat")
@@ -78,11 +206,20 @@ cli
78
206
  return stash();
79
207
  });
80
208
 
209
+ cli
210
+ .command("commit", "交互式提交 (Conventional Commits + Gitmoji)")
211
+ .alias("c")
212
+ .alias("cm")
213
+ .action(() => {
214
+ checkGitRepo();
215
+ return commit();
216
+ });
217
+
81
218
  cli.help((sections) => {
82
219
  sections.push({
83
220
  body: showHelp(),
84
221
  });
85
222
  });
86
- cli.version(__VERSION__);
223
+ cli.version(version);
87
224
 
88
225
  cli.parse();