@quiteer/scripts 0.0.5 → 0.0.6
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/dist/index.mjs +172 -121
- 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.
|
|
15
|
+
var version = "0.0.5";
|
|
16
16
|
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region src/locales/changelog.ts
|
|
@@ -493,6 +493,137 @@ async function generateConfig() {
|
|
|
493
493
|
].join("\n"), "utf8");
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/commands/git-branches.ts
|
|
498
|
+
/**
|
|
499
|
+
* 列出远程仓库所有分支(含最新提交时间),并按时间倒序打印
|
|
500
|
+
* - 会先执行 `git fetch --prune --tags` 同步远程引用
|
|
501
|
+
* - 通过 `git for-each-ref` 获取 `refs/remotes/<remote>` 下的分支与提交时间
|
|
502
|
+
* @param remote 远程仓库名,默认 `origin`
|
|
503
|
+
* @returns Promise<void>
|
|
504
|
+
*/
|
|
505
|
+
async function gitRemoteBranches(urlOrRemote) {
|
|
506
|
+
const temp = "gb-temp";
|
|
507
|
+
const target = (urlOrRemote || "").trim();
|
|
508
|
+
let refBase = "";
|
|
509
|
+
try {
|
|
510
|
+
if (target) {
|
|
511
|
+
await execCommand("git", [
|
|
512
|
+
"fetch",
|
|
513
|
+
target,
|
|
514
|
+
`+refs/heads/*:refs/remotes/${temp}/*`,
|
|
515
|
+
"--prune",
|
|
516
|
+
"--no-tags"
|
|
517
|
+
]);
|
|
518
|
+
refBase = `refs/remotes/${temp}`;
|
|
519
|
+
} else {
|
|
520
|
+
const r = "origin";
|
|
521
|
+
await execCommand("git", [
|
|
522
|
+
"fetch",
|
|
523
|
+
r,
|
|
524
|
+
"--prune",
|
|
525
|
+
"--tags"
|
|
526
|
+
]);
|
|
527
|
+
refBase = `refs/remotes/${r}`;
|
|
528
|
+
}
|
|
529
|
+
} catch {
|
|
530
|
+
console.log("拉取远程分支失败,请检查 URL 或网络连接");
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const lines = (await execCommand("git", [
|
|
534
|
+
"for-each-ref",
|
|
535
|
+
"--format=%(refname:short) %(committerdate:relative) %(objectname:short) %(contents:subject)",
|
|
536
|
+
"--sort=-committerdate",
|
|
537
|
+
refBase
|
|
538
|
+
])).split("\n").filter(Boolean);
|
|
539
|
+
if (lines.length === 0) {
|
|
540
|
+
console.log("未找到远程分支,请确认已配置远程并有追踪分支");
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* 英文相对时间转中文
|
|
545
|
+
* @param rel 英文相对时间字符串(如:2 hours ago)
|
|
546
|
+
* @returns 中文相对时间(如:2小时前)
|
|
547
|
+
*/
|
|
548
|
+
function zhRelative(rel) {
|
|
549
|
+
const s = rel.toLowerCase().trim();
|
|
550
|
+
let m;
|
|
551
|
+
if (m = s.match(/^(\d+)\s+seconds?\s+ago$/)) return `${m[1]}秒前`;
|
|
552
|
+
if (m = s.match(/^(\d+)\s+minutes?\s+ago$/)) return `${m[1]}分钟前`;
|
|
553
|
+
if (m = s.match(/^(\d+)\s+hours?\s+ago$/)) return `${m[1]}小时前`;
|
|
554
|
+
if (m = s.match(/^(\d+)\s+days?\s+ago$/)) return `${m[1]}天前`;
|
|
555
|
+
if (m = s.match(/^(\d+)\s+weeks?\s+ago$/)) return `${m[1]}周前`;
|
|
556
|
+
if (m = s.match(/^(\d+)\s+months?\s+ago$/)) return `${m[1]}个月前`;
|
|
557
|
+
if (m = s.match(/^(\d+)\s+years?\s+ago$/)) return `${m[1]}年前`;
|
|
558
|
+
if (s === "yesterday") return "昨天";
|
|
559
|
+
if (s === "today") return "今天";
|
|
560
|
+
if (m = s.match(/^about\s+(\d+)\s+hours?\s+ago$/)) return `${m[1]}小时前`;
|
|
561
|
+
if (m = s.match(/^about\s+(\d+)\s+minutes?\s+ago$/)) return `${m[1]}分钟前`;
|
|
562
|
+
if (m = s.match(/^about\s+(\d+)\s+days?\s+ago$/)) return `${m[1]}天前`;
|
|
563
|
+
if (m = s.match(/^about\s+(\d+)\s+months?\s+ago$/)) return `${m[1]}个月前`;
|
|
564
|
+
if (m = s.match(/^about\s+(\d+)\s+years?\s+ago$/)) return `${m[1]}年前`;
|
|
565
|
+
return rel;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* 计算字符串在等宽终端中的显示宽度(简单处理全角字符为宽度 2)
|
|
569
|
+
* @param s 输入字符串(不含 ANSI 颜色码)
|
|
570
|
+
* @returns 可视宽度
|
|
571
|
+
*/
|
|
572
|
+
function displayWidth(s) {
|
|
573
|
+
let w = 0;
|
|
574
|
+
for (const ch of s) {
|
|
575
|
+
const code = ch.codePointAt(0) || 0;
|
|
576
|
+
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;
|
|
577
|
+
}
|
|
578
|
+
return w;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* 基于显示宽度的左对齐填充
|
|
582
|
+
* @param s 源字符串
|
|
583
|
+
* @param width 目标显示宽度
|
|
584
|
+
* @returns 左对齐并填充空格的字符串
|
|
585
|
+
*/
|
|
586
|
+
function padEndDisplay(s, width) {
|
|
587
|
+
const w = displayWidth(s);
|
|
588
|
+
if (w >= width) return s;
|
|
589
|
+
return s + " ".repeat(width - w);
|
|
590
|
+
}
|
|
591
|
+
const rows = lines.map((line) => {
|
|
592
|
+
const [nameRaw, relRaw, shaRaw, subjectRaw] = line.split(" ");
|
|
593
|
+
return {
|
|
594
|
+
name: nameRaw.replace(/^.+\//, ""),
|
|
595
|
+
relZh: zhRelative(relRaw || ""),
|
|
596
|
+
sha: (shaRaw || "").slice(0, 7),
|
|
597
|
+
subject: subjectRaw || ""
|
|
598
|
+
};
|
|
599
|
+
}).filter((item) => item.name && item.name !== "HEAD");
|
|
600
|
+
const nameWidth = Math.max(...rows.map((r) => displayWidth(r.name)));
|
|
601
|
+
const relValues = rows.map((r) => `(${r.relZh})`);
|
|
602
|
+
const relWidth = Math.max(...relValues.map((v) => displayWidth(v)), 0);
|
|
603
|
+
const label = " 最新 ";
|
|
604
|
+
const labelWidth = displayWidth(label);
|
|
605
|
+
const tag = bgGreen(white(label));
|
|
606
|
+
const pad = " ".repeat(labelWidth);
|
|
607
|
+
rows.forEach((row, i) => {
|
|
608
|
+
const relText = padEndDisplay(`(${row.relZh})`, relWidth);
|
|
609
|
+
const line = `${padEndDisplay(row.name, nameWidth)} ${relText} ${row.sha} ${row.subject}`;
|
|
610
|
+
if (i === 0) console.log(`${tag} ${lightGreen(line)}`);
|
|
611
|
+
else console.log(`${pad} ${white(line)}`);
|
|
612
|
+
});
|
|
613
|
+
if (target) {
|
|
614
|
+
const refnames = await execCommand("git", [
|
|
615
|
+
"for-each-ref",
|
|
616
|
+
"--format=%(refname)",
|
|
617
|
+
refBase
|
|
618
|
+
]);
|
|
619
|
+
for (const ref of refnames.split("\n").filter(Boolean)) await execCommand("git", [
|
|
620
|
+
"update-ref",
|
|
621
|
+
"-d",
|
|
622
|
+
ref
|
|
623
|
+
]);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
496
627
|
//#endregion
|
|
497
628
|
//#region src/commands/git-commit.ts
|
|
498
629
|
/**
|
|
@@ -670,133 +801,49 @@ async function updatePkg(args = ["--deep", "-u"]) {
|
|
|
670
801
|
}
|
|
671
802
|
|
|
672
803
|
//#endregion
|
|
673
|
-
//#region src/commands/
|
|
804
|
+
//#region src/commands/self-update.ts
|
|
674
805
|
/**
|
|
675
|
-
*
|
|
676
|
-
* -
|
|
677
|
-
* - 通过 `git for-each-ref` 获取 `refs/remotes/<remote>` 下的分支与提交时间
|
|
678
|
-
* @param remote 远程仓库名,默认 `origin`
|
|
679
|
-
* @returns Promise<void>
|
|
806
|
+
* 检查 @quiteer/scripts 是否有新版本并提示更新
|
|
807
|
+
* - 启动任意命令时调用,仅提示不执行安装
|
|
680
808
|
*/
|
|
681
|
-
async function
|
|
682
|
-
const temp = "gb-temp";
|
|
683
|
-
const target = (urlOrRemote || "").trim();
|
|
684
|
-
let refBase = "";
|
|
809
|
+
async function checkUpdateAndNotify() {
|
|
685
810
|
try {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
console.log("拉取远程分支失败,请检查 URL 或网络连接");
|
|
811
|
+
const latest = await execCommand("pnpm", [
|
|
812
|
+
"view",
|
|
813
|
+
"@quiteer/scripts",
|
|
814
|
+
"version"
|
|
815
|
+
]);
|
|
816
|
+
if (latest && latest !== version) console.info("quiteer-script :>> ", lightCyan(`检测到新版本 ${lightGreen(latest)},当前版本 ${lightBlue(version)},建议执行 ${bgGreen(white("qui su"))} 进行更新`));
|
|
817
|
+
} catch {}
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* 自更新到最新版本
|
|
821
|
+
* - 对比当前版本与远端版本,不一致则使用 pnpm 全局更新
|
|
822
|
+
*/
|
|
823
|
+
async function selfUpdate() {
|
|
824
|
+
const latest = await execCommand("pnpm", [
|
|
825
|
+
"view",
|
|
826
|
+
"@quiteer/scripts",
|
|
827
|
+
"version"
|
|
828
|
+
]);
|
|
829
|
+
if (!latest) {
|
|
830
|
+
console.info("quiteer-script :>> ", lightBlue("无法获取远端版本,请检查网络后重试"));
|
|
707
831
|
return;
|
|
708
832
|
}
|
|
709
|
-
|
|
710
|
-
"
|
|
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("未找到远程分支,请确认已配置远程并有追踪分支");
|
|
833
|
+
if (latest === version) {
|
|
834
|
+
console.info("quiteer-script :>> ", lightGreen(`已是最新版本 ${latest}`));
|
|
717
835
|
return;
|
|
718
836
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
-
]);
|
|
837
|
+
console.info("quiteer-script :>> ", lightCyan(`开始更新到最新版本 ${latest}(当前 ${version})`));
|
|
838
|
+
try {
|
|
839
|
+
await execa("pnpm", [
|
|
840
|
+
"add",
|
|
841
|
+
"-g",
|
|
842
|
+
`@quiteer/scripts@${latest}`
|
|
843
|
+
], { stdio: "inherit" });
|
|
844
|
+
console.info("quiteer-script :>> ", lightGreen("更新完成,请重新运行命令"));
|
|
845
|
+
} catch (e) {
|
|
846
|
+
console.info("quiteer-script :>> ", lightBlue(`更新失败:${e?.message || "未知错误"}`));
|
|
800
847
|
}
|
|
801
848
|
}
|
|
802
849
|
|
|
@@ -811,6 +858,7 @@ async function setupCli() {
|
|
|
811
858
|
*/
|
|
812
859
|
const cliOptions = await loadCliOptions();
|
|
813
860
|
const cli = cac(blue("quiteer"));
|
|
861
|
+
await checkUpdateAndNotify();
|
|
814
862
|
cli.command("generate-config", `${bgGreen(white("便捷命令"))} ${lightCyan("qui g")} 在项目根目录下生成配置文件`).alias("g").action(async () => {
|
|
815
863
|
await generateConfig();
|
|
816
864
|
});
|
|
@@ -827,6 +875,9 @@ async function setupCli() {
|
|
|
827
875
|
cli.command("update-pkg", `${bgGreen(white("便捷命令"))} ${lightBlue("qui u")} 更新 package.json 依赖版本`).alias("u").action(async () => {
|
|
828
876
|
await updatePkg(cliOptions.ncuCommandArgs);
|
|
829
877
|
});
|
|
878
|
+
cli.command("self-update", `${bgGreen(white("便捷命令"))} ${lightCyan("qui su")} 自更新:检查并更新 @quiteer/scripts 到最新版本`).alias("su").action(async () => {
|
|
879
|
+
await selfUpdate();
|
|
880
|
+
});
|
|
830
881
|
cli.command("git-commit", `${bgGreen(white("便捷命令"))} ${lightBlue("qui gc")} git 提交前后的操作和规范等`).alias("gc").option("--add", "添加所有变更文件到暂存区", { default: cliOptions.gitCommit.add }).option("-l ,--lang", "校验提交信息的语言", { default: cliOptions.lang }).action(async (args) => {
|
|
831
882
|
if (args?.add) await gitCommitAdd();
|
|
832
883
|
await gitCommit(args?.lang);
|