@quiteer/scripts 0.0.4 → 0.0.5

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.
Files changed (2) hide show
  1. package/dist/index.mjs +135 -1
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -12,7 +12,7 @@ import enquirer from "enquirer";
12
12
  import { versionBump } from "bumpp";
13
13
 
14
14
  //#region package.json
15
- var version = "0.0.3";
15
+ var version = "0.0.4";
16
16
 
17
17
  //#endregion
18
18
  //#region src/locales/changelog.ts
@@ -669,6 +669,137 @@ async function updatePkg(args = ["--deep", "-u"]) {
669
669
  execCommand("npx", ["npm-check-updates", ...args], { stdio: "inherit" });
670
670
  }
671
671
 
672
+ //#endregion
673
+ //#region src/commands/git-branches.ts
674
+ /**
675
+ * 列出远程仓库所有分支(含最新提交时间),并按时间倒序打印
676
+ * - 会先执行 `git fetch --prune --tags` 同步远程引用
677
+ * - 通过 `git for-each-ref` 获取 `refs/remotes/<remote>` 下的分支与提交时间
678
+ * @param remote 远程仓库名,默认 `origin`
679
+ * @returns Promise<void>
680
+ */
681
+ async function gitRemoteBranches(urlOrRemote) {
682
+ const temp = "gb-temp";
683
+ const target = (urlOrRemote || "").trim();
684
+ let refBase = "";
685
+ try {
686
+ if (target) {
687
+ await execCommand("git", [
688
+ "fetch",
689
+ target,
690
+ `+refs/heads/*:refs/remotes/${temp}/*`,
691
+ "--prune",
692
+ "--no-tags"
693
+ ]);
694
+ refBase = `refs/remotes/${temp}`;
695
+ } else {
696
+ const r = "origin";
697
+ await execCommand("git", [
698
+ "fetch",
699
+ r,
700
+ "--prune",
701
+ "--tags"
702
+ ]);
703
+ refBase = `refs/remotes/${r}`;
704
+ }
705
+ } catch {
706
+ console.log("拉取远程分支失败,请检查 URL 或网络连接");
707
+ return;
708
+ }
709
+ const lines = (await execCommand("git", [
710
+ "for-each-ref",
711
+ "--format=%(refname:short) %(committerdate:relative) %(objectname:short) %(contents:subject)",
712
+ "--sort=-committerdate",
713
+ refBase
714
+ ])).split("\n").filter(Boolean);
715
+ if (lines.length === 0) {
716
+ console.log("未找到远程分支,请确认已配置远程并有追踪分支");
717
+ return;
718
+ }
719
+ /**
720
+ * 英文相对时间转中文
721
+ * @param rel 英文相对时间字符串(如:2 hours ago)
722
+ * @returns 中文相对时间(如:2小时前)
723
+ */
724
+ function zhRelative(rel) {
725
+ const s = rel.toLowerCase().trim();
726
+ let m;
727
+ if (m = s.match(/^(\d+)\s+seconds?\s+ago$/)) return `${m[1]}秒前`;
728
+ if (m = s.match(/^(\d+)\s+minutes?\s+ago$/)) return `${m[1]}分钟前`;
729
+ if (m = s.match(/^(\d+)\s+hours?\s+ago$/)) return `${m[1]}小时前`;
730
+ if (m = s.match(/^(\d+)\s+days?\s+ago$/)) return `${m[1]}天前`;
731
+ if (m = s.match(/^(\d+)\s+weeks?\s+ago$/)) return `${m[1]}周前`;
732
+ if (m = s.match(/^(\d+)\s+months?\s+ago$/)) return `${m[1]}个月前`;
733
+ if (m = s.match(/^(\d+)\s+years?\s+ago$/)) return `${m[1]}年前`;
734
+ if (s === "yesterday") return "昨天";
735
+ if (s === "today") return "今天";
736
+ if (m = s.match(/^about\s+(\d+)\s+hours?\s+ago$/)) return `${m[1]}小时前`;
737
+ if (m = s.match(/^about\s+(\d+)\s+minutes?\s+ago$/)) return `${m[1]}分钟前`;
738
+ if (m = s.match(/^about\s+(\d+)\s+days?\s+ago$/)) return `${m[1]}天前`;
739
+ if (m = s.match(/^about\s+(\d+)\s+months?\s+ago$/)) return `${m[1]}个月前`;
740
+ if (m = s.match(/^about\s+(\d+)\s+years?\s+ago$/)) return `${m[1]}年前`;
741
+ return rel;
742
+ }
743
+ /**
744
+ * 计算字符串在等宽终端中的显示宽度(简单处理全角字符为宽度 2)
745
+ * @param s 输入字符串(不含 ANSI 颜色码)
746
+ * @returns 可视宽度
747
+ */
748
+ function displayWidth(s) {
749
+ let w = 0;
750
+ for (const ch of s) {
751
+ const code = ch.codePointAt(0) || 0;
752
+ w += code >= 4352 && code <= 4447 || code >= 11904 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65049 || code >= 65072 && code <= 65135 || code >= 65280 && code <= 65376 || code >= 65504 && code <= 65510 ? 2 : 1;
753
+ }
754
+ return w;
755
+ }
756
+ /**
757
+ * 基于显示宽度的左对齐填充
758
+ * @param s 源字符串
759
+ * @param width 目标显示宽度
760
+ * @returns 左对齐并填充空格的字符串
761
+ */
762
+ function padEndDisplay(s, width) {
763
+ const w = displayWidth(s);
764
+ if (w >= width) return s;
765
+ return s + " ".repeat(width - w);
766
+ }
767
+ const rows = lines.map((line) => {
768
+ const [nameRaw, relRaw, shaRaw, subjectRaw] = line.split(" ");
769
+ return {
770
+ name: nameRaw.replace(/^.+\//, ""),
771
+ relZh: zhRelative(relRaw || ""),
772
+ sha: (shaRaw || "").slice(0, 7),
773
+ subject: subjectRaw || ""
774
+ };
775
+ }).filter((item) => item.name && item.name !== "HEAD");
776
+ const nameWidth = Math.max(...rows.map((r) => displayWidth(r.name)));
777
+ const relValues = rows.map((r) => `(${r.relZh})`);
778
+ const relWidth = Math.max(...relValues.map((v) => displayWidth(v)), 0);
779
+ const label = " 最新 ";
780
+ const labelWidth = displayWidth(label);
781
+ const tag = bgGreen(white(label));
782
+ const pad = " ".repeat(labelWidth);
783
+ rows.forEach((row, i) => {
784
+ const relText = padEndDisplay(`(${row.relZh})`, relWidth);
785
+ const line = `${padEndDisplay(row.name, nameWidth)} ${relText} ${row.sha} ${row.subject}`;
786
+ if (i === 0) console.log(`${tag} ${lightGreen(line)}`);
787
+ else console.log(`${pad} ${white(line)}`);
788
+ });
789
+ if (target) {
790
+ const refnames = await execCommand("git", [
791
+ "for-each-ref",
792
+ "--format=%(refname)",
793
+ refBase
794
+ ]);
795
+ for (const ref of refnames.split("\n").filter(Boolean)) await execCommand("git", [
796
+ "update-ref",
797
+ "-d",
798
+ ref
799
+ ]);
800
+ }
801
+ }
802
+
672
803
  //#endregion
673
804
  //#region src/index.ts
674
805
  async function setupCli() {
@@ -703,6 +834,9 @@ async function setupCli() {
703
834
  cli.command("git-commit-verify", `${bgGreen(white("便捷命令"))} ${lightBlue("qui gv")} 校验提交信息是否符合 Conventional Commits 标准`).alias("gv").option("-l --lang", "校验提交信息的语言", { default: cliOptions.lang }).action(async (args) => {
704
835
  await gitCommitVerify(args?.lang, cliOptions.gitCommitVerifyIgnores);
705
836
  });
837
+ cli.command("git-branches [url]", `${bgGreen(white("便捷命令"))} ${lightBlue("qui gb")} 查看远程仓库所有分支(分支名 + 最新时间),倒序排序`).alias("gb").action(async (url) => {
838
+ await gitRemoteBranches(url);
839
+ });
706
840
  cli.command("release", `${bgGreen(white("便捷命令"))} ${lightBlue("qui r")} 版本管理:选择包并提升版本,创建自定义前缀标签并生成 changelog`).alias("r").option("--tag-prefix <prefix>", "标签前缀(可选,留空则交互输入)").action(async (args) => {
707
841
  await release(args?.tagPrefix);
708
842
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@quiteer/scripts",
3
3
  "type": "module",
4
- "version": "0.0.4",
4
+ "version": "0.0.5",
5
5
  "homepage": "https://taiaiac.github.io/web/ci/scripts.html",
6
6
  "publishConfig": {
7
7
  "access": "public",