@simon_he/pi 0.1.24 → 0.1.26
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 +36 -0
- package/dist/index.cjs +275 -31
- package/dist/index.mjs +275 -31
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -114,6 +114,42 @@ export PI_Lang=en
|
|
|
114
114
|
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
## Shell Integration (prun)
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
# zsh
|
|
121
|
+
eval "$(prun --init zsh)"
|
|
122
|
+
|
|
123
|
+
# bash
|
|
124
|
+
eval "$(prun --init bash)"
|
|
125
|
+
|
|
126
|
+
# fish
|
|
127
|
+
eval (prun --init fish)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
> Note: This lets the command selected by prun be available immediately in your shell history (press ↑ to recall).
|
|
131
|
+
|
|
132
|
+
Auto integration (built-in):
|
|
133
|
+
|
|
134
|
+
- In interactive shells, the first `prun` run will append the right line to your shell rc (zsh: `~/.zshrc`, bash: `~/.bashrc`, fish: `~/.config/fish/config.fish`).
|
|
135
|
+
- Disable with `PI_NO_AUTO_INIT=1` (or set `PI_AUTO_INIT=0`).
|
|
136
|
+
- Open a new terminal (or source the rc file) after the first run.
|
|
137
|
+
|
|
138
|
+
Make it persistent:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
# zsh
|
|
142
|
+
echo 'eval "$(prun --init zsh)"' >> ~/.zshrc
|
|
143
|
+
|
|
144
|
+
# bash
|
|
145
|
+
echo 'eval "$(prun --init bash)"' >> ~/.bashrc
|
|
146
|
+
|
|
147
|
+
# fish
|
|
148
|
+
echo 'prun --init fish | source' >> ~/.config/fish/config.fish
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Reload your shell config (or open a new terminal) after adding the line.
|
|
152
|
+
|
|
117
153
|
## Power
|
|
118
154
|
|
|
119
155
|
The current environment is npm | yarn | pnpm, and it supports passing some args --silent
|
package/dist/index.cjs
CHANGED
|
@@ -44,39 +44,39 @@ let node_module = require("node:module");
|
|
|
44
44
|
let node_url = require("node:url");
|
|
45
45
|
|
|
46
46
|
//#region package.json
|
|
47
|
-
var version = "0.1.
|
|
47
|
+
var version = "0.1.26";
|
|
48
48
|
|
|
49
49
|
//#endregion
|
|
50
50
|
//#region src/installDeps.ts
|
|
51
|
-
const isZh$
|
|
51
|
+
const isZh$7 = node_process.default.env.PI_Lang === "zh";
|
|
52
52
|
const gumDocUrl = "https://github.com/charmbracelet/gum";
|
|
53
53
|
const niDocUrl = "https://github.com/antfu/ni";
|
|
54
54
|
async function installDeps(options = {}) {
|
|
55
55
|
const { gum = true, ni = true, strict = true } = options;
|
|
56
56
|
const platform = node_process.default.platform;
|
|
57
57
|
if (gum && !await (0, lazy_js_utils_node.isInstallPkg)("gum")) if (platform === "darwin") {
|
|
58
|
-
console.log(picocolors.default.cyan(isZh$
|
|
58
|
+
console.log(picocolors.default.cyan(isZh$7 ? "正在为您安装必要的依赖gum..." : "Installing gum..."));
|
|
59
59
|
const { status } = await (0, lazy_js_utils_node.jsShell)("brew install gum", [
|
|
60
60
|
"inherit",
|
|
61
61
|
"pipe",
|
|
62
62
|
"inherit"
|
|
63
63
|
]);
|
|
64
|
-
if (status === 0) console.log(picocolors.default.cyan(isZh$
|
|
64
|
+
if (status === 0) console.log(picocolors.default.cyan(isZh$7 ? "gum 安装成功!" : "gum installed successfully!"));
|
|
65
65
|
else {
|
|
66
|
-
console.log(picocolors.default.red(isZh$
|
|
66
|
+
console.log(picocolors.default.red(isZh$7 ? `gum 安装失败,请尝试从官网解决安装问题! ${gumDocUrl}` : `gum installation failed, please try manual install: ${gumDocUrl}`));
|
|
67
67
|
if (strict) node_process.default.exit(1);
|
|
68
68
|
}
|
|
69
|
-
} else console.log(picocolors.default.yellow(isZh$
|
|
69
|
+
} else console.log(picocolors.default.yellow(isZh$7 ? `未检测到 gum,请根据系统手动安装: ${gumDocUrl}` : `gum not found, please install it manually: ${gumDocUrl}`));
|
|
70
70
|
if (ni && !await (0, lazy_js_utils_node.isInstallPkg)("ni")) {
|
|
71
|
-
console.log(picocolors.default.cyan(isZh$
|
|
71
|
+
console.log(picocolors.default.cyan(isZh$7 ? "正在为您安装必要的依赖ni..." : "Installing ni..."));
|
|
72
72
|
const { status } = await (0, lazy_js_utils_node.jsShell)("npm i -g @antfu/ni", [
|
|
73
73
|
"inherit",
|
|
74
74
|
"pipe",
|
|
75
75
|
"inherit"
|
|
76
76
|
]);
|
|
77
|
-
if (status === 0) console.log(picocolors.default.cyan(isZh$
|
|
77
|
+
if (status === 0) console.log(picocolors.default.cyan(isZh$7 ? "ni 安装成功!" : "ni installed successfully!"));
|
|
78
78
|
else {
|
|
79
|
-
console.log(picocolors.default.red(isZh$
|
|
79
|
+
console.log(picocolors.default.red(isZh$7 ? `ni 安装失败,请尝试从官网解决安装问题! ${niDocUrl}` : `ni installation failed, please try manual install: ${niDocUrl}`));
|
|
80
80
|
if (strict) node_process.default.exit(1);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -84,7 +84,7 @@ async function installDeps(options = {}) {
|
|
|
84
84
|
|
|
85
85
|
//#endregion
|
|
86
86
|
//#region src/help.ts
|
|
87
|
-
const isZh$
|
|
87
|
+
const isZh$6 = node_process.default.env.PI_Lang === "zh";
|
|
88
88
|
async function ensureGum() {
|
|
89
89
|
if (await (0, lazy_js_utils_node.isInstallPkg)("gum")) return true;
|
|
90
90
|
await installDeps({
|
|
@@ -95,9 +95,9 @@ async function ensureGum() {
|
|
|
95
95
|
return await (0, lazy_js_utils_node.isInstallPkg)("gum");
|
|
96
96
|
}
|
|
97
97
|
function printPlainVersion() {
|
|
98
|
-
console.log(isZh$
|
|
99
|
-
console.log(isZh$
|
|
100
|
-
console.log(isZh$
|
|
98
|
+
console.log(isZh$6 ? `pi 版本: ${version}` : `pi version: ${version}`);
|
|
99
|
+
console.log(isZh$6 ? "请为我的努力点一个行 🌟" : "Please give me a 🌟 for my efforts");
|
|
100
|
+
console.log(isZh$6 ? "谢谢 🤟" : "Thank you 🤟");
|
|
101
101
|
}
|
|
102
102
|
function printPlainHelp() {
|
|
103
103
|
console.log([
|
|
@@ -118,7 +118,7 @@ function printPlainHelp() {
|
|
|
118
118
|
async function help(argv) {
|
|
119
119
|
const arg = argv[0];
|
|
120
120
|
if (arg === "-v" || arg === "--version") {
|
|
121
|
-
if (await ensureGum()) await (0, lazy_js_utils_node.jsShell)(isZh$
|
|
121
|
+
if (await ensureGum()) await (0, lazy_js_utils_node.jsShell)(isZh$6 ? `gum style \
|
|
122
122
|
--foreground 212 --border-foreground 212 --border double \
|
|
123
123
|
--align center --width 50 --margin "1 2" --padding "2 4" \
|
|
124
124
|
"pi 版本: ${version}" "请为我的努力点一个行 🌟" "谢谢 🤟"` : `gum style \
|
|
@@ -146,7 +146,7 @@ function pa() {
|
|
|
146
146
|
|
|
147
147
|
//#endregion
|
|
148
148
|
//#region src/detectNode.ts
|
|
149
|
-
const isZh$
|
|
149
|
+
const isZh$5 = node_process.default.env.PI_Lang === "zh";
|
|
150
150
|
async function detectNode() {
|
|
151
151
|
let pkg;
|
|
152
152
|
try {
|
|
@@ -165,7 +165,7 @@ async function detectNode() {
|
|
|
165
165
|
const hasFnm = await (0, lazy_js_utils_node.isInstallPkg)("fnm");
|
|
166
166
|
if (!hasGum || !hasFnm) {
|
|
167
167
|
const missing = [!hasGum ? "gum" : "", !hasFnm ? "fnm" : ""].filter(Boolean).join(", ");
|
|
168
|
-
console.log(picocolors.default.yellow(isZh$
|
|
168
|
+
console.log(picocolors.default.yellow(isZh$5 ? `当前 node 版本不满足 ${pkg.engines.node},未检测到 ${missing},请手动切换版本。` : `Current Node version does not satisfy ${pkg.engines.node}. Missing ${missing}. Please switch manually.`));
|
|
169
169
|
return;
|
|
170
170
|
}
|
|
171
171
|
const { result, status } = await (0, lazy_js_utils_node.jsShell)(`echo "yes\nno" | gum filter --placeholder=" 当前node版本不满足 ${pkg.engines.node},是否切换node版本"`, [
|
|
@@ -197,7 +197,7 @@ const Dw = /\s-Dw/g;
|
|
|
197
197
|
const w = /\s-w/g;
|
|
198
198
|
const D = /\s-D(?!w)/g;
|
|
199
199
|
const d = /\s-d(?!w)/g;
|
|
200
|
-
const isZh$
|
|
200
|
+
const isZh$4 = node_process.default.env.PI_Lang === "zh";
|
|
201
201
|
const log$1 = console.log;
|
|
202
202
|
async function getParams(params) {
|
|
203
203
|
const root = node_process.default.cwd();
|
|
@@ -240,7 +240,7 @@ async function getParams(params) {
|
|
|
240
240
|
default: return d.test(params) ? params.replace(d, " -D") : params;
|
|
241
241
|
}
|
|
242
242
|
} catch {
|
|
243
|
-
console.log(picocolors.default.red(`${isZh$
|
|
243
|
+
console.log(picocolors.default.red(`${isZh$4 ? "package.json并不存在,在以下目录中:" : "package.json has not been found in"} ${node_process.default.cwd()}`));
|
|
244
244
|
node_process.default.exit(1);
|
|
245
245
|
}
|
|
246
246
|
}
|
|
@@ -280,7 +280,7 @@ async function getLatestVersion(pkg, isZh = true) {
|
|
|
280
280
|
return `${data.join(" ")}${isZh ? " 安装成功! 😊" : " successfully! 😊"}`;
|
|
281
281
|
}
|
|
282
282
|
async function pushHistory(command) {
|
|
283
|
-
log$1(picocolors.default.bold(picocolors.default.blue(`${isZh$
|
|
283
|
+
log$1(picocolors.default.bold(picocolors.default.blue(`${isZh$4 ? "快捷指令" : "shortcut command"}: ${command}`)));
|
|
284
284
|
const shellName = (node_process.default.env.SHELL || "/bin/bash").split("/").pop() || "bash";
|
|
285
285
|
let historyFile = "";
|
|
286
286
|
let historyFormat = "bash";
|
|
@@ -304,7 +304,7 @@ async function pushHistory(command) {
|
|
|
304
304
|
}
|
|
305
305
|
try {
|
|
306
306
|
if (!node_fs.default.existsSync(historyFile)) {
|
|
307
|
-
log$1(picocolors.default.yellow(`${isZh$
|
|
307
|
+
log$1(picocolors.default.yellow(`${isZh$4 ? `未找到 ${shellName} 历史文件` : `${shellName} history file not found`}`));
|
|
308
308
|
return;
|
|
309
309
|
}
|
|
310
310
|
const raw = node_fs.default.readFileSync(historyFile, "utf8");
|
|
@@ -393,22 +393,22 @@ async function pushHistory(command) {
|
|
|
393
393
|
node_fs.default.writeFileSync(tmpPath, finalContent, "utf8");
|
|
394
394
|
node_fs.default.renameSync(tmpPath, historyFile);
|
|
395
395
|
} catch (err) {
|
|
396
|
-
log$1(picocolors.default.red(`${isZh$
|
|
396
|
+
log$1(picocolors.default.red(`${isZh$4 ? `❌ 添加到 ${shellName} 历史记录失败` : `❌ Failed to add to ${shellName} history`}${err ? `: ${String(err)}` : ""}`));
|
|
397
397
|
}
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
//#endregion
|
|
401
401
|
//#region src/pi.ts
|
|
402
|
-
const isZh$
|
|
402
|
+
const isZh$3 = node_process.default.env.PI_Lang === "zh";
|
|
403
403
|
async function pi(params, pkg, executor = "ni") {
|
|
404
404
|
await detectNode();
|
|
405
405
|
const text = pkg ? `Installing ${params} ...` : "Updating dependency ...";
|
|
406
406
|
const isLatest = executor === "pil";
|
|
407
407
|
const start = Date.now();
|
|
408
408
|
let successMsg = "";
|
|
409
|
-
if (isLatest) successMsg = await getLatestVersion(pkg, isZh$
|
|
410
|
-
else successMsg = pkg ? isZh$
|
|
411
|
-
const failMsg = pkg ? isZh$
|
|
409
|
+
if (isLatest) successMsg = await getLatestVersion(pkg, isZh$3);
|
|
410
|
+
else successMsg = pkg ? isZh$3 ? `${pkg} 安装成功! 😊` : `Installed ${pkg} successfully! 😊` : isZh$3 ? "依赖更新成功! 😊" : "Updated dependency successfully! 😊";
|
|
411
|
+
const failMsg = pkg ? isZh$3 ? `${params} 安装失败 😭` : `Failed to install ${params} 😭` : isZh$3 ? "依赖更新失败 😭" : "Failed to update dependency 😭";
|
|
412
412
|
const isSilent = node_process.default.env.PI_SILENT === "true";
|
|
413
413
|
let stdio = isSilent ? "inherit" : [
|
|
414
414
|
"inherit",
|
|
@@ -452,7 +452,7 @@ async function pi(params, pkg, executor = "ni") {
|
|
|
452
452
|
let { status, result } = await runCommands(cmdList);
|
|
453
453
|
if (result && result.includes("pnpm versions with respective Node.js version support")) {
|
|
454
454
|
(0, node_console.log)(result);
|
|
455
|
-
(0, node_console.log)(picocolors.default.yellow(isZh$
|
|
455
|
+
(0, node_console.log)(picocolors.default.yellow(isZh$3 ? "正在尝试使用 npm 再次执行..." : "Trying to use npm to run again..."));
|
|
456
456
|
const fallbackCommands = isLatest ? latestParams.map((p) => `npm install ${p}`) : [`npm install${newParams ? ` ${newParams}` : runSockets}`];
|
|
457
457
|
const fallbackResults = await Promise.all(fallbackCommands.map((command) => (0, lazy_js_utils_node.jsShell)(command, { stdio })));
|
|
458
458
|
const fallbackFailed = fallbackResults.find((r) => r.status !== 0);
|
|
@@ -468,7 +468,7 @@ async function pi(params, pkg, executor = "ni") {
|
|
|
468
468
|
pushHistory(runCmd);
|
|
469
469
|
} else if (result && result.includes("Not Found - 404")) {
|
|
470
470
|
const _pkg = result.match(/\/[^/:]+:/)?.[0].slice(1, -1);
|
|
471
|
-
const _result = isZh$
|
|
471
|
+
const _result = isZh$3 ? `${_pkg} 包名可能有误或者版本号不存在,并不能在npm中搜索到,请检查` : `${_pkg} the package name may be wrong, and cannot be found in npm, please check`;
|
|
472
472
|
loading_status.fail(picocolors.default.red(result ? `${failMsg}\n${_result}` : failMsg));
|
|
473
473
|
} else loading_status.fail(picocolors.default.red(result ? `${failMsg}\n${result}` : failMsg));
|
|
474
474
|
if (result) {
|
|
@@ -498,9 +498,24 @@ function getCcommand() {
|
|
|
498
498
|
|
|
499
499
|
//#endregion
|
|
500
500
|
//#region src/pfind.ts
|
|
501
|
-
function
|
|
501
|
+
function isNoHistory$1(value) {
|
|
502
|
+
if (!value) return false;
|
|
503
|
+
const normalized = value.toLowerCase();
|
|
504
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
505
|
+
}
|
|
506
|
+
async function pfind(params) {
|
|
507
|
+
const hadNoHistoryEnv = node_process.default.env.CCOMMAND_NO_HISTORY != null || node_process.default.env.NO_HISTORY != null;
|
|
508
|
+
const initialNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY ?? node_process.default.env.NO_HISTORY;
|
|
509
|
+
const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory$1(initialNoHistory));
|
|
502
510
|
const { ccommand } = getCcommand();
|
|
503
|
-
|
|
511
|
+
const prevNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY;
|
|
512
|
+
if (shouldWriteHistory) delete node_process.default.env.CCOMMAND_NO_HISTORY;
|
|
513
|
+
try {
|
|
514
|
+
await ccommand(`find ${params}`);
|
|
515
|
+
} finally {
|
|
516
|
+
if (prevNoHistory == null) delete node_process.default.env.CCOMMAND_NO_HISTORY;
|
|
517
|
+
else node_process.default.env.CCOMMAND_NO_HISTORY = prevNoHistory;
|
|
518
|
+
}
|
|
504
519
|
}
|
|
505
520
|
|
|
506
521
|
//#endregion
|
|
@@ -615,9 +630,234 @@ async function pix(params) {
|
|
|
615
630
|
|
|
616
631
|
//#endregion
|
|
617
632
|
//#region src/prun.ts
|
|
618
|
-
function prun(params) {
|
|
633
|
+
async function prun(params) {
|
|
634
|
+
ensurePrunAutoInit();
|
|
635
|
+
const hadNoHistoryEnv = node_process.default.env.CCOMMAND_NO_HISTORY != null || node_process.default.env.NO_HISTORY != null;
|
|
636
|
+
const initialNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY ?? node_process.default.env.NO_HISTORY;
|
|
619
637
|
const { ccommand } = getCcommand();
|
|
620
|
-
|
|
638
|
+
const prevNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY;
|
|
639
|
+
const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory(initialNoHistory));
|
|
640
|
+
const { lines, restore } = captureOutput();
|
|
641
|
+
node_process.default.env.CCOMMAND_NO_HISTORY = "1";
|
|
642
|
+
try {
|
|
643
|
+
await ccommand(params);
|
|
644
|
+
} finally {
|
|
645
|
+
restore();
|
|
646
|
+
if (prevNoHistory == null) delete node_process.default.env.CCOMMAND_NO_HISTORY;
|
|
647
|
+
else node_process.default.env.CCOMMAND_NO_HISTORY = prevNoHistory;
|
|
648
|
+
}
|
|
649
|
+
const shortcut = extractShortcutCommand(lines());
|
|
650
|
+
if (shortcut && shouldWriteHistory) writeShellHistory(shortcut);
|
|
651
|
+
}
|
|
652
|
+
const isZh$2 = node_process.default.env.PI_Lang === "zh";
|
|
653
|
+
const safeShellValue = /^[\w./:@%+=,-]+$/;
|
|
654
|
+
const ansiEscape = String.fromCharCode(27);
|
|
655
|
+
const ansiRegex = new RegExp(`${ansiEscape}\\[[0-9;]*m`, "g");
|
|
656
|
+
function stripAnsi(value) {
|
|
657
|
+
return value.replace(ansiRegex, "");
|
|
658
|
+
}
|
|
659
|
+
function captureOutput() {
|
|
660
|
+
const stdoutWrite = node_process.default.stdout.write.bind(node_process.default.stdout);
|
|
661
|
+
const stderrWrite = node_process.default.stderr.write.bind(node_process.default.stderr);
|
|
662
|
+
let buffer = "";
|
|
663
|
+
const output = [];
|
|
664
|
+
const pushBufferLines = () => {
|
|
665
|
+
let idx = buffer.indexOf("\n");
|
|
666
|
+
while (idx !== -1) {
|
|
667
|
+
output.push(buffer.slice(0, idx));
|
|
668
|
+
buffer = buffer.slice(idx + 1);
|
|
669
|
+
idx = buffer.indexOf("\n");
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
node_process.default.stdout.write = ((chunk, encoding, cb) => {
|
|
673
|
+
const text = typeof chunk === "string" ? chunk : chunk?.toString(encoding || "utf8");
|
|
674
|
+
if (text) {
|
|
675
|
+
buffer += text;
|
|
676
|
+
pushBufferLines();
|
|
677
|
+
}
|
|
678
|
+
return stdoutWrite(chunk, encoding, cb);
|
|
679
|
+
});
|
|
680
|
+
node_process.default.stderr.write = ((chunk, encoding, cb) => {
|
|
681
|
+
const text = typeof chunk === "string" ? chunk : chunk?.toString(encoding || "utf8");
|
|
682
|
+
if (text) {
|
|
683
|
+
buffer += text;
|
|
684
|
+
pushBufferLines();
|
|
685
|
+
}
|
|
686
|
+
return stderrWrite(chunk, encoding, cb);
|
|
687
|
+
});
|
|
688
|
+
const restore = () => {
|
|
689
|
+
if (buffer.trim()) output.push(buffer);
|
|
690
|
+
buffer = "";
|
|
691
|
+
node_process.default.stdout.write = stdoutWrite;
|
|
692
|
+
node_process.default.stderr.write = stderrWrite;
|
|
693
|
+
};
|
|
694
|
+
return {
|
|
695
|
+
lines: () => output.slice(),
|
|
696
|
+
restore
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
function isNoHistory(value) {
|
|
700
|
+
if (!value) return false;
|
|
701
|
+
const normalized = value.toLowerCase();
|
|
702
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
703
|
+
}
|
|
704
|
+
function stripTrailingNonAscii(value) {
|
|
705
|
+
let end = value.length;
|
|
706
|
+
while (end > 0) {
|
|
707
|
+
if (value.charCodeAt(end - 1) <= 127) break;
|
|
708
|
+
end -= 1;
|
|
709
|
+
}
|
|
710
|
+
return end === value.length ? value : value.slice(0, end);
|
|
711
|
+
}
|
|
712
|
+
function extractShortcutCommand(logs) {
|
|
713
|
+
for (let i = logs.length - 1; i >= 0; i--) {
|
|
714
|
+
const line = stripAnsi(logs[i]).trim();
|
|
715
|
+
if (!line) continue;
|
|
716
|
+
const shortcutMatch = line.match(/(?:shortcut command|快捷指令)\s*:\s*(prun\b.*)$/i);
|
|
717
|
+
if (shortcutMatch) return shortcutMatch[1].trim();
|
|
718
|
+
const runningMatch = line.match(/(?:is running for you\.\.\.|正在为您执行\.{3})\s+(\S.*)$/i);
|
|
719
|
+
if (runningMatch) {
|
|
720
|
+
const command = runningMatch[1].trim();
|
|
721
|
+
if (command) return command.startsWith("prun ") ? command : `prun ${command}`;
|
|
722
|
+
}
|
|
723
|
+
const unquoted = stripTrailingNonAscii(line).trim().replace(/^['"]|['"]$/g, "");
|
|
724
|
+
if (unquoted.startsWith("prun ")) return unquoted;
|
|
725
|
+
if (unquoted === "prun") return unquoted;
|
|
726
|
+
const idx = unquoted.lastIndexOf("prun ");
|
|
727
|
+
if (idx !== -1) return unquoted.slice(idx).trim();
|
|
728
|
+
}
|
|
729
|
+
return "";
|
|
730
|
+
}
|
|
731
|
+
function resolveHistoryTarget() {
|
|
732
|
+
const shellEnv = node_process.default.env.SHELL || "/bin/bash";
|
|
733
|
+
const shellName = node_process.default.env.FISH_VERSION && "fish" || node_process.default.env.ZSH_VERSION && "zsh" || node_process.default.env.BASH_VERSION && "bash" || shellEnv.split("/").pop() || "bash";
|
|
734
|
+
const home = node_process.default.env.HOME || node_os.default.homedir();
|
|
735
|
+
const histFileEnv = node_process.default.env.HISTFILE;
|
|
736
|
+
const xdgDataHome = node_process.default.env.XDG_DATA_HOME;
|
|
737
|
+
let historyFile = "";
|
|
738
|
+
let historyKind = "bash";
|
|
739
|
+
if (shellName === "zsh") {
|
|
740
|
+
historyKind = "zsh";
|
|
741
|
+
historyFile = histFileEnv || node_path.default.join(home, ".zsh_history");
|
|
742
|
+
} else if (shellName === "fish") {
|
|
743
|
+
historyKind = "fish";
|
|
744
|
+
const base = xdgDataHome || node_path.default.join(home, ".local", "share");
|
|
745
|
+
historyFile = node_path.default.join(base, "fish", "fish_history");
|
|
746
|
+
} else {
|
|
747
|
+
historyKind = "bash";
|
|
748
|
+
historyFile = histFileEnv || node_path.default.join(home, ".bash_history");
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
historyFile,
|
|
752
|
+
historyKind
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
function buildHistoryEntry(command, kind) {
|
|
756
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
757
|
+
if (kind === "zsh") return `: ${timestamp}:0;${command}\n`;
|
|
758
|
+
if (kind === "fish") return `- cmd: ${command}\n when: ${timestamp}\n`;
|
|
759
|
+
if (node_process.default.env.HISTTIMEFORMAT) return `#${timestamp}\n${command}\n`;
|
|
760
|
+
return `${command}\n`;
|
|
761
|
+
}
|
|
762
|
+
function writeShellHistory(command) {
|
|
763
|
+
try {
|
|
764
|
+
const { historyFile, historyKind } = resolveHistoryTarget();
|
|
765
|
+
if (!historyFile) return;
|
|
766
|
+
if (!node_fs.default.existsSync(historyFile)) {
|
|
767
|
+
console.log(picocolors.default.yellow(isZh$2 ? `未找到 history 文件: ${historyFile}` : `History file not found: ${historyFile}`));
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
const entry = buildHistoryEntry(command, historyKind);
|
|
771
|
+
node_fs.default.appendFileSync(historyFile, entry, "utf8");
|
|
772
|
+
} catch (error) {
|
|
773
|
+
console.log(picocolors.default.red(`${isZh$2 ? "写入 history 失败" : "Failed to write history"}${error ? `: ${String(error)}` : ""}`));
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
function shellQuote(value) {
|
|
777
|
+
if (value === "") return "''";
|
|
778
|
+
if (safeShellValue.test(value)) return value;
|
|
779
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
780
|
+
}
|
|
781
|
+
function detectShell() {
|
|
782
|
+
const envShell = node_process.default.env.SHELL || "";
|
|
783
|
+
if (node_process.default.env.FISH_VERSION) return "fish";
|
|
784
|
+
if (node_process.default.env.ZSH_VERSION) return "zsh";
|
|
785
|
+
if (node_process.default.env.BASH_VERSION) return "bash";
|
|
786
|
+
return envShell.split("/").pop() || "zsh";
|
|
787
|
+
}
|
|
788
|
+
function ensurePrunAutoInit() {
|
|
789
|
+
if (!shouldAutoInit()) return;
|
|
790
|
+
const shell = detectShell();
|
|
791
|
+
const home = node_process.default.env.HOME || node_os.default.homedir();
|
|
792
|
+
let rcFile = "";
|
|
793
|
+
let initLine = "";
|
|
794
|
+
if (shell === "zsh") {
|
|
795
|
+
const zdotdir = node_process.default.env.ZDOTDIR || home;
|
|
796
|
+
rcFile = node_path.default.join(zdotdir, ".zshrc");
|
|
797
|
+
initLine = "eval \"$(prun --init zsh)\"";
|
|
798
|
+
} else if (shell === "bash") {
|
|
799
|
+
rcFile = node_path.default.join(home, ".bashrc");
|
|
800
|
+
initLine = "eval \"$(prun --init bash)\"";
|
|
801
|
+
} else if (shell === "fish") {
|
|
802
|
+
const configHome = node_process.default.env.XDG_CONFIG_HOME || node_path.default.join(home, ".config");
|
|
803
|
+
rcFile = node_path.default.join(configHome, "fish", "config.fish");
|
|
804
|
+
initLine = "prun --init fish | source";
|
|
805
|
+
} else return;
|
|
806
|
+
try {
|
|
807
|
+
const dir = node_path.default.dirname(rcFile);
|
|
808
|
+
if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
|
|
809
|
+
const content = node_fs.default.existsSync(rcFile) ? node_fs.default.readFileSync(rcFile, "utf8") : "";
|
|
810
|
+
if (!/prun\s+--init/.test(content)) {
|
|
811
|
+
const prefix = content.length && !content.endsWith("\n") ? "\n" : "";
|
|
812
|
+
node_fs.default.appendFileSync(rcFile, `${prefix}${initLine}\n`, "utf8");
|
|
813
|
+
}
|
|
814
|
+
} catch {}
|
|
815
|
+
}
|
|
816
|
+
function shouldAutoInit() {
|
|
817
|
+
const auto = node_process.default.env.PI_AUTO_INIT || node_process.default.env.PRUN_AUTO_INIT;
|
|
818
|
+
if (auto != null) return isNoHistory(auto);
|
|
819
|
+
if (isNoHistory(node_process.default.env.PI_NO_AUTO_INIT || node_process.default.env.PRUN_NO_AUTO_INIT)) return false;
|
|
820
|
+
if (node_process.default.env.CI) return false;
|
|
821
|
+
if (!node_process.default.stdout.isTTY || !node_process.default.stdin.isTTY) return false;
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
function printPrunInit(args = []) {
|
|
825
|
+
const shellArg = args[0];
|
|
826
|
+
const binArg = args[1];
|
|
827
|
+
const bin = shellQuote(binArg || node_process.default.env.PRUN_BIN || "prun");
|
|
828
|
+
const shell = shellArg || detectShell() || "zsh";
|
|
829
|
+
let script = "";
|
|
830
|
+
if (shell === "zsh") script = [
|
|
831
|
+
"prun() {",
|
|
832
|
+
` local bin=${bin}`,
|
|
833
|
+
" local -a cmd",
|
|
834
|
+
" cmd=(${=bin})",
|
|
835
|
+
" command \"${cmd[@]}\" \"$@\"",
|
|
836
|
+
" fc -R",
|
|
837
|
+
"}"
|
|
838
|
+
].join("\n");
|
|
839
|
+
else if (shell === "bash") script = [
|
|
840
|
+
"prun() {",
|
|
841
|
+
` local bin=${bin}`,
|
|
842
|
+
" local -a cmd",
|
|
843
|
+
" read -r -a cmd <<< \"$bin\"",
|
|
844
|
+
" command \"${cmd[@]}\" \"$@\"",
|
|
845
|
+
" history -n",
|
|
846
|
+
"}"
|
|
847
|
+
].join("\n");
|
|
848
|
+
else if (shell === "fish") script = [
|
|
849
|
+
"function prun",
|
|
850
|
+
` set -l bin ${bin}`,
|
|
851
|
+
" set -l cmd (string split -- \" \" $bin)",
|
|
852
|
+
" command $cmd $argv",
|
|
853
|
+
" history --merge",
|
|
854
|
+
"end"
|
|
855
|
+
].join("\n");
|
|
856
|
+
else {
|
|
857
|
+
console.log(picocolors.default.red(isZh$2 ? `不支持的 shell: ${shell}` : `Unsupported shell: ${shell}`));
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
console.log(script);
|
|
621
861
|
}
|
|
622
862
|
|
|
623
863
|
//#endregion
|
|
@@ -700,6 +940,10 @@ async function setup() {
|
|
|
700
940
|
}
|
|
701
941
|
const argv = node_process.default.argv.slice(2);
|
|
702
942
|
await help(argv);
|
|
943
|
+
if ((exec === "prun" || exec === "prun.mjs") && argv[0] === "--init") {
|
|
944
|
+
printPrunInit(argv.slice(1));
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
703
947
|
let params = (0, lazy_js_utils.spaceFormat)(argv.join(" ")).trim();
|
|
704
948
|
if (!await (0, lazy_js_utils_node.hasPkg)(rootPath)) {
|
|
705
949
|
if (await (0, lazy_js_utils_node.isGo)(rootPath)) {
|
package/dist/index.mjs
CHANGED
|
@@ -11,39 +11,39 @@ import os from "node:os";
|
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
12
|
|
|
13
13
|
//#region package.json
|
|
14
|
-
var version = "0.1.
|
|
14
|
+
var version = "0.1.26";
|
|
15
15
|
|
|
16
16
|
//#endregion
|
|
17
17
|
//#region src/installDeps.ts
|
|
18
|
-
const isZh$
|
|
18
|
+
const isZh$7 = process.env.PI_Lang === "zh";
|
|
19
19
|
const gumDocUrl = "https://github.com/charmbracelet/gum";
|
|
20
20
|
const niDocUrl = "https://github.com/antfu/ni";
|
|
21
21
|
async function installDeps(options = {}) {
|
|
22
22
|
const { gum = true, ni = true, strict = true } = options;
|
|
23
23
|
const platform = process.platform;
|
|
24
24
|
if (gum && !await isInstallPkg("gum")) if (platform === "darwin") {
|
|
25
|
-
console.log(color.cyan(isZh$
|
|
25
|
+
console.log(color.cyan(isZh$7 ? "正在为您安装必要的依赖gum..." : "Installing gum..."));
|
|
26
26
|
const { status } = await jsShell("brew install gum", [
|
|
27
27
|
"inherit",
|
|
28
28
|
"pipe",
|
|
29
29
|
"inherit"
|
|
30
30
|
]);
|
|
31
|
-
if (status === 0) console.log(color.cyan(isZh$
|
|
31
|
+
if (status === 0) console.log(color.cyan(isZh$7 ? "gum 安装成功!" : "gum installed successfully!"));
|
|
32
32
|
else {
|
|
33
|
-
console.log(color.red(isZh$
|
|
33
|
+
console.log(color.red(isZh$7 ? `gum 安装失败,请尝试从官网解决安装问题! ${gumDocUrl}` : `gum installation failed, please try manual install: ${gumDocUrl}`));
|
|
34
34
|
if (strict) process.exit(1);
|
|
35
35
|
}
|
|
36
|
-
} else console.log(color.yellow(isZh$
|
|
36
|
+
} else console.log(color.yellow(isZh$7 ? `未检测到 gum,请根据系统手动安装: ${gumDocUrl}` : `gum not found, please install it manually: ${gumDocUrl}`));
|
|
37
37
|
if (ni && !await isInstallPkg("ni")) {
|
|
38
|
-
console.log(color.cyan(isZh$
|
|
38
|
+
console.log(color.cyan(isZh$7 ? "正在为您安装必要的依赖ni..." : "Installing ni..."));
|
|
39
39
|
const { status } = await jsShell("npm i -g @antfu/ni", [
|
|
40
40
|
"inherit",
|
|
41
41
|
"pipe",
|
|
42
42
|
"inherit"
|
|
43
43
|
]);
|
|
44
|
-
if (status === 0) console.log(color.cyan(isZh$
|
|
44
|
+
if (status === 0) console.log(color.cyan(isZh$7 ? "ni 安装成功!" : "ni installed successfully!"));
|
|
45
45
|
else {
|
|
46
|
-
console.log(color.red(isZh$
|
|
46
|
+
console.log(color.red(isZh$7 ? `ni 安装失败,请尝试从官网解决安装问题! ${niDocUrl}` : `ni installation failed, please try manual install: ${niDocUrl}`));
|
|
47
47
|
if (strict) process.exit(1);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -51,7 +51,7 @@ async function installDeps(options = {}) {
|
|
|
51
51
|
|
|
52
52
|
//#endregion
|
|
53
53
|
//#region src/help.ts
|
|
54
|
-
const isZh$
|
|
54
|
+
const isZh$6 = process.env.PI_Lang === "zh";
|
|
55
55
|
async function ensureGum() {
|
|
56
56
|
if (await isInstallPkg("gum")) return true;
|
|
57
57
|
await installDeps({
|
|
@@ -62,9 +62,9 @@ async function ensureGum() {
|
|
|
62
62
|
return await isInstallPkg("gum");
|
|
63
63
|
}
|
|
64
64
|
function printPlainVersion() {
|
|
65
|
-
console.log(isZh$
|
|
66
|
-
console.log(isZh$
|
|
67
|
-
console.log(isZh$
|
|
65
|
+
console.log(isZh$6 ? `pi 版本: ${version}` : `pi version: ${version}`);
|
|
66
|
+
console.log(isZh$6 ? "请为我的努力点一个行 🌟" : "Please give me a 🌟 for my efforts");
|
|
67
|
+
console.log(isZh$6 ? "谢谢 🤟" : "Thank you 🤟");
|
|
68
68
|
}
|
|
69
69
|
function printPlainHelp() {
|
|
70
70
|
console.log([
|
|
@@ -85,7 +85,7 @@ function printPlainHelp() {
|
|
|
85
85
|
async function help(argv) {
|
|
86
86
|
const arg = argv[0];
|
|
87
87
|
if (arg === "-v" || arg === "--version") {
|
|
88
|
-
if (await ensureGum()) await jsShell(isZh$
|
|
88
|
+
if (await ensureGum()) await jsShell(isZh$6 ? `gum style \
|
|
89
89
|
--foreground 212 --border-foreground 212 --border double \
|
|
90
90
|
--align center --width 50 --margin "1 2" --padding "2 4" \
|
|
91
91
|
"pi 版本: ${version}" "请为我的努力点一个行 🌟" "谢谢 🤟"` : `gum style \
|
|
@@ -113,7 +113,7 @@ function pa() {
|
|
|
113
113
|
|
|
114
114
|
//#endregion
|
|
115
115
|
//#region src/detectNode.ts
|
|
116
|
-
const isZh$
|
|
116
|
+
const isZh$5 = process.env.PI_Lang === "zh";
|
|
117
117
|
async function detectNode() {
|
|
118
118
|
let pkg;
|
|
119
119
|
try {
|
|
@@ -132,7 +132,7 @@ async function detectNode() {
|
|
|
132
132
|
const hasFnm = await isInstallPkg("fnm");
|
|
133
133
|
if (!hasGum || !hasFnm) {
|
|
134
134
|
const missing = [!hasGum ? "gum" : "", !hasFnm ? "fnm" : ""].filter(Boolean).join(", ");
|
|
135
|
-
console.log(color.yellow(isZh$
|
|
135
|
+
console.log(color.yellow(isZh$5 ? `当前 node 版本不满足 ${pkg.engines.node},未检测到 ${missing},请手动切换版本。` : `Current Node version does not satisfy ${pkg.engines.node}. Missing ${missing}. Please switch manually.`));
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
138
138
|
const { result, status } = await jsShell(`echo "yes\nno" | gum filter --placeholder=" 当前node版本不满足 ${pkg.engines.node},是否切换node版本"`, [
|
|
@@ -164,7 +164,7 @@ const Dw = /\s-Dw/g;
|
|
|
164
164
|
const w = /\s-w/g;
|
|
165
165
|
const D = /\s-D(?!w)/g;
|
|
166
166
|
const d = /\s-d(?!w)/g;
|
|
167
|
-
const isZh$
|
|
167
|
+
const isZh$4 = process.env.PI_Lang === "zh";
|
|
168
168
|
const log$1 = console.log;
|
|
169
169
|
async function getParams(params) {
|
|
170
170
|
const root = process.cwd();
|
|
@@ -207,7 +207,7 @@ async function getParams(params) {
|
|
|
207
207
|
default: return d.test(params) ? params.replace(d, " -D") : params;
|
|
208
208
|
}
|
|
209
209
|
} catch {
|
|
210
|
-
console.log(color.red(`${isZh$
|
|
210
|
+
console.log(color.red(`${isZh$4 ? "package.json并不存在,在以下目录中:" : "package.json has not been found in"} ${process.cwd()}`));
|
|
211
211
|
process.exit(1);
|
|
212
212
|
}
|
|
213
213
|
}
|
|
@@ -247,7 +247,7 @@ async function getLatestVersion(pkg, isZh = true) {
|
|
|
247
247
|
return `${data.join(" ")}${isZh ? " 安装成功! 😊" : " successfully! 😊"}`;
|
|
248
248
|
}
|
|
249
249
|
async function pushHistory(command) {
|
|
250
|
-
log$1(color.bold(color.blue(`${isZh$
|
|
250
|
+
log$1(color.bold(color.blue(`${isZh$4 ? "快捷指令" : "shortcut command"}: ${command}`)));
|
|
251
251
|
const shellName = (process.env.SHELL || "/bin/bash").split("/").pop() || "bash";
|
|
252
252
|
let historyFile = "";
|
|
253
253
|
let historyFormat = "bash";
|
|
@@ -271,7 +271,7 @@ async function pushHistory(command) {
|
|
|
271
271
|
}
|
|
272
272
|
try {
|
|
273
273
|
if (!fs.existsSync(historyFile)) {
|
|
274
|
-
log$1(color.yellow(`${isZh$
|
|
274
|
+
log$1(color.yellow(`${isZh$4 ? `未找到 ${shellName} 历史文件` : `${shellName} history file not found`}`));
|
|
275
275
|
return;
|
|
276
276
|
}
|
|
277
277
|
const raw = fs.readFileSync(historyFile, "utf8");
|
|
@@ -360,22 +360,22 @@ async function pushHistory(command) {
|
|
|
360
360
|
fs.writeFileSync(tmpPath, finalContent, "utf8");
|
|
361
361
|
fs.renameSync(tmpPath, historyFile);
|
|
362
362
|
} catch (err) {
|
|
363
|
-
log$1(color.red(`${isZh$
|
|
363
|
+
log$1(color.red(`${isZh$4 ? `❌ 添加到 ${shellName} 历史记录失败` : `❌ Failed to add to ${shellName} history`}${err ? `: ${String(err)}` : ""}`));
|
|
364
364
|
}
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
//#endregion
|
|
368
368
|
//#region src/pi.ts
|
|
369
|
-
const isZh$
|
|
369
|
+
const isZh$3 = process.env.PI_Lang === "zh";
|
|
370
370
|
async function pi(params, pkg, executor = "ni") {
|
|
371
371
|
await detectNode();
|
|
372
372
|
const text = pkg ? `Installing ${params} ...` : "Updating dependency ...";
|
|
373
373
|
const isLatest = executor === "pil";
|
|
374
374
|
const start = Date.now();
|
|
375
375
|
let successMsg = "";
|
|
376
|
-
if (isLatest) successMsg = await getLatestVersion(pkg, isZh$
|
|
377
|
-
else successMsg = pkg ? isZh$
|
|
378
|
-
const failMsg = pkg ? isZh$
|
|
376
|
+
if (isLatest) successMsg = await getLatestVersion(pkg, isZh$3);
|
|
377
|
+
else successMsg = pkg ? isZh$3 ? `${pkg} 安装成功! 😊` : `Installed ${pkg} successfully! 😊` : isZh$3 ? "依赖更新成功! 😊" : "Updated dependency successfully! 😊";
|
|
378
|
+
const failMsg = pkg ? isZh$3 ? `${params} 安装失败 😭` : `Failed to install ${params} 😭` : isZh$3 ? "依赖更新失败 😭" : "Failed to update dependency 😭";
|
|
379
379
|
const isSilent = process.env.PI_SILENT === "true";
|
|
380
380
|
let stdio = isSilent ? "inherit" : [
|
|
381
381
|
"inherit",
|
|
@@ -419,7 +419,7 @@ async function pi(params, pkg, executor = "ni") {
|
|
|
419
419
|
let { status, result } = await runCommands(cmdList);
|
|
420
420
|
if (result && result.includes("pnpm versions with respective Node.js version support")) {
|
|
421
421
|
log(result);
|
|
422
|
-
log(color.yellow(isZh$
|
|
422
|
+
log(color.yellow(isZh$3 ? "正在尝试使用 npm 再次执行..." : "Trying to use npm to run again..."));
|
|
423
423
|
const fallbackCommands = isLatest ? latestParams.map((p) => `npm install ${p}`) : [`npm install${newParams ? ` ${newParams}` : runSockets}`];
|
|
424
424
|
const fallbackResults = await Promise.all(fallbackCommands.map((command) => jsShell(command, { stdio })));
|
|
425
425
|
const fallbackFailed = fallbackResults.find((r) => r.status !== 0);
|
|
@@ -435,7 +435,7 @@ async function pi(params, pkg, executor = "ni") {
|
|
|
435
435
|
pushHistory(runCmd);
|
|
436
436
|
} else if (result && result.includes("Not Found - 404")) {
|
|
437
437
|
const _pkg = result.match(/\/[^/:]+:/)?.[0].slice(1, -1);
|
|
438
|
-
const _result = isZh$
|
|
438
|
+
const _result = isZh$3 ? `${_pkg} 包名可能有误或者版本号不存在,并不能在npm中搜索到,请检查` : `${_pkg} the package name may be wrong, and cannot be found in npm, please check`;
|
|
439
439
|
loading_status.fail(color.red(result ? `${failMsg}\n${_result}` : failMsg));
|
|
440
440
|
} else loading_status.fail(color.red(result ? `${failMsg}\n${result}` : failMsg));
|
|
441
441
|
if (result) {
|
|
@@ -465,9 +465,24 @@ function getCcommand() {
|
|
|
465
465
|
|
|
466
466
|
//#endregion
|
|
467
467
|
//#region src/pfind.ts
|
|
468
|
-
function
|
|
468
|
+
function isNoHistory$1(value) {
|
|
469
|
+
if (!value) return false;
|
|
470
|
+
const normalized = value.toLowerCase();
|
|
471
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
472
|
+
}
|
|
473
|
+
async function pfind(params) {
|
|
474
|
+
const hadNoHistoryEnv = process.env.CCOMMAND_NO_HISTORY != null || process.env.NO_HISTORY != null;
|
|
475
|
+
const initialNoHistory = process.env.CCOMMAND_NO_HISTORY ?? process.env.NO_HISTORY;
|
|
476
|
+
const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory$1(initialNoHistory));
|
|
469
477
|
const { ccommand } = getCcommand();
|
|
470
|
-
|
|
478
|
+
const prevNoHistory = process.env.CCOMMAND_NO_HISTORY;
|
|
479
|
+
if (shouldWriteHistory) delete process.env.CCOMMAND_NO_HISTORY;
|
|
480
|
+
try {
|
|
481
|
+
await ccommand(`find ${params}`);
|
|
482
|
+
} finally {
|
|
483
|
+
if (prevNoHistory == null) delete process.env.CCOMMAND_NO_HISTORY;
|
|
484
|
+
else process.env.CCOMMAND_NO_HISTORY = prevNoHistory;
|
|
485
|
+
}
|
|
471
486
|
}
|
|
472
487
|
|
|
473
488
|
//#endregion
|
|
@@ -582,9 +597,234 @@ async function pix(params) {
|
|
|
582
597
|
|
|
583
598
|
//#endregion
|
|
584
599
|
//#region src/prun.ts
|
|
585
|
-
function prun(params) {
|
|
600
|
+
async function prun(params) {
|
|
601
|
+
ensurePrunAutoInit();
|
|
602
|
+
const hadNoHistoryEnv = process.env.CCOMMAND_NO_HISTORY != null || process.env.NO_HISTORY != null;
|
|
603
|
+
const initialNoHistory = process.env.CCOMMAND_NO_HISTORY ?? process.env.NO_HISTORY;
|
|
586
604
|
const { ccommand } = getCcommand();
|
|
587
|
-
|
|
605
|
+
const prevNoHistory = process.env.CCOMMAND_NO_HISTORY;
|
|
606
|
+
const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory(initialNoHistory));
|
|
607
|
+
const { lines, restore } = captureOutput();
|
|
608
|
+
process.env.CCOMMAND_NO_HISTORY = "1";
|
|
609
|
+
try {
|
|
610
|
+
await ccommand(params);
|
|
611
|
+
} finally {
|
|
612
|
+
restore();
|
|
613
|
+
if (prevNoHistory == null) delete process.env.CCOMMAND_NO_HISTORY;
|
|
614
|
+
else process.env.CCOMMAND_NO_HISTORY = prevNoHistory;
|
|
615
|
+
}
|
|
616
|
+
const shortcut = extractShortcutCommand(lines());
|
|
617
|
+
if (shortcut && shouldWriteHistory) writeShellHistory(shortcut);
|
|
618
|
+
}
|
|
619
|
+
const isZh$2 = process.env.PI_Lang === "zh";
|
|
620
|
+
const safeShellValue = /^[\w./:@%+=,-]+$/;
|
|
621
|
+
const ansiEscape = String.fromCharCode(27);
|
|
622
|
+
const ansiRegex = new RegExp(`${ansiEscape}\\[[0-9;]*m`, "g");
|
|
623
|
+
function stripAnsi(value) {
|
|
624
|
+
return value.replace(ansiRegex, "");
|
|
625
|
+
}
|
|
626
|
+
function captureOutput() {
|
|
627
|
+
const stdoutWrite = process.stdout.write.bind(process.stdout);
|
|
628
|
+
const stderrWrite = process.stderr.write.bind(process.stderr);
|
|
629
|
+
let buffer = "";
|
|
630
|
+
const output = [];
|
|
631
|
+
const pushBufferLines = () => {
|
|
632
|
+
let idx = buffer.indexOf("\n");
|
|
633
|
+
while (idx !== -1) {
|
|
634
|
+
output.push(buffer.slice(0, idx));
|
|
635
|
+
buffer = buffer.slice(idx + 1);
|
|
636
|
+
idx = buffer.indexOf("\n");
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
process.stdout.write = ((chunk, encoding, cb) => {
|
|
640
|
+
const text = typeof chunk === "string" ? chunk : chunk?.toString(encoding || "utf8");
|
|
641
|
+
if (text) {
|
|
642
|
+
buffer += text;
|
|
643
|
+
pushBufferLines();
|
|
644
|
+
}
|
|
645
|
+
return stdoutWrite(chunk, encoding, cb);
|
|
646
|
+
});
|
|
647
|
+
process.stderr.write = ((chunk, encoding, cb) => {
|
|
648
|
+
const text = typeof chunk === "string" ? chunk : chunk?.toString(encoding || "utf8");
|
|
649
|
+
if (text) {
|
|
650
|
+
buffer += text;
|
|
651
|
+
pushBufferLines();
|
|
652
|
+
}
|
|
653
|
+
return stderrWrite(chunk, encoding, cb);
|
|
654
|
+
});
|
|
655
|
+
const restore = () => {
|
|
656
|
+
if (buffer.trim()) output.push(buffer);
|
|
657
|
+
buffer = "";
|
|
658
|
+
process.stdout.write = stdoutWrite;
|
|
659
|
+
process.stderr.write = stderrWrite;
|
|
660
|
+
};
|
|
661
|
+
return {
|
|
662
|
+
lines: () => output.slice(),
|
|
663
|
+
restore
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function isNoHistory(value) {
|
|
667
|
+
if (!value) return false;
|
|
668
|
+
const normalized = value.toLowerCase();
|
|
669
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
670
|
+
}
|
|
671
|
+
function stripTrailingNonAscii(value) {
|
|
672
|
+
let end = value.length;
|
|
673
|
+
while (end > 0) {
|
|
674
|
+
if (value.charCodeAt(end - 1) <= 127) break;
|
|
675
|
+
end -= 1;
|
|
676
|
+
}
|
|
677
|
+
return end === value.length ? value : value.slice(0, end);
|
|
678
|
+
}
|
|
679
|
+
function extractShortcutCommand(logs) {
|
|
680
|
+
for (let i = logs.length - 1; i >= 0; i--) {
|
|
681
|
+
const line = stripAnsi(logs[i]).trim();
|
|
682
|
+
if (!line) continue;
|
|
683
|
+
const shortcutMatch = line.match(/(?:shortcut command|快捷指令)\s*:\s*(prun\b.*)$/i);
|
|
684
|
+
if (shortcutMatch) return shortcutMatch[1].trim();
|
|
685
|
+
const runningMatch = line.match(/(?:is running for you\.\.\.|正在为您执行\.{3})\s+(\S.*)$/i);
|
|
686
|
+
if (runningMatch) {
|
|
687
|
+
const command = runningMatch[1].trim();
|
|
688
|
+
if (command) return command.startsWith("prun ") ? command : `prun ${command}`;
|
|
689
|
+
}
|
|
690
|
+
const unquoted = stripTrailingNonAscii(line).trim().replace(/^['"]|['"]$/g, "");
|
|
691
|
+
if (unquoted.startsWith("prun ")) return unquoted;
|
|
692
|
+
if (unquoted === "prun") return unquoted;
|
|
693
|
+
const idx = unquoted.lastIndexOf("prun ");
|
|
694
|
+
if (idx !== -1) return unquoted.slice(idx).trim();
|
|
695
|
+
}
|
|
696
|
+
return "";
|
|
697
|
+
}
|
|
698
|
+
function resolveHistoryTarget() {
|
|
699
|
+
const shellEnv = process.env.SHELL || "/bin/bash";
|
|
700
|
+
const shellName = process.env.FISH_VERSION && "fish" || process.env.ZSH_VERSION && "zsh" || process.env.BASH_VERSION && "bash" || shellEnv.split("/").pop() || "bash";
|
|
701
|
+
const home = process.env.HOME || os.homedir();
|
|
702
|
+
const histFileEnv = process.env.HISTFILE;
|
|
703
|
+
const xdgDataHome = process.env.XDG_DATA_HOME;
|
|
704
|
+
let historyFile = "";
|
|
705
|
+
let historyKind = "bash";
|
|
706
|
+
if (shellName === "zsh") {
|
|
707
|
+
historyKind = "zsh";
|
|
708
|
+
historyFile = histFileEnv || path.join(home, ".zsh_history");
|
|
709
|
+
} else if (shellName === "fish") {
|
|
710
|
+
historyKind = "fish";
|
|
711
|
+
const base = xdgDataHome || path.join(home, ".local", "share");
|
|
712
|
+
historyFile = path.join(base, "fish", "fish_history");
|
|
713
|
+
} else {
|
|
714
|
+
historyKind = "bash";
|
|
715
|
+
historyFile = histFileEnv || path.join(home, ".bash_history");
|
|
716
|
+
}
|
|
717
|
+
return {
|
|
718
|
+
historyFile,
|
|
719
|
+
historyKind
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function buildHistoryEntry(command, kind) {
|
|
723
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
724
|
+
if (kind === "zsh") return `: ${timestamp}:0;${command}\n`;
|
|
725
|
+
if (kind === "fish") return `- cmd: ${command}\n when: ${timestamp}\n`;
|
|
726
|
+
if (process.env.HISTTIMEFORMAT) return `#${timestamp}\n${command}\n`;
|
|
727
|
+
return `${command}\n`;
|
|
728
|
+
}
|
|
729
|
+
function writeShellHistory(command) {
|
|
730
|
+
try {
|
|
731
|
+
const { historyFile, historyKind } = resolveHistoryTarget();
|
|
732
|
+
if (!historyFile) return;
|
|
733
|
+
if (!fs.existsSync(historyFile)) {
|
|
734
|
+
console.log(color.yellow(isZh$2 ? `未找到 history 文件: ${historyFile}` : `History file not found: ${historyFile}`));
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const entry = buildHistoryEntry(command, historyKind);
|
|
738
|
+
fs.appendFileSync(historyFile, entry, "utf8");
|
|
739
|
+
} catch (error) {
|
|
740
|
+
console.log(color.red(`${isZh$2 ? "写入 history 失败" : "Failed to write history"}${error ? `: ${String(error)}` : ""}`));
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
function shellQuote(value) {
|
|
744
|
+
if (value === "") return "''";
|
|
745
|
+
if (safeShellValue.test(value)) return value;
|
|
746
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
747
|
+
}
|
|
748
|
+
function detectShell() {
|
|
749
|
+
const envShell = process.env.SHELL || "";
|
|
750
|
+
if (process.env.FISH_VERSION) return "fish";
|
|
751
|
+
if (process.env.ZSH_VERSION) return "zsh";
|
|
752
|
+
if (process.env.BASH_VERSION) return "bash";
|
|
753
|
+
return envShell.split("/").pop() || "zsh";
|
|
754
|
+
}
|
|
755
|
+
function ensurePrunAutoInit() {
|
|
756
|
+
if (!shouldAutoInit()) return;
|
|
757
|
+
const shell = detectShell();
|
|
758
|
+
const home = process.env.HOME || os.homedir();
|
|
759
|
+
let rcFile = "";
|
|
760
|
+
let initLine = "";
|
|
761
|
+
if (shell === "zsh") {
|
|
762
|
+
const zdotdir = process.env.ZDOTDIR || home;
|
|
763
|
+
rcFile = path.join(zdotdir, ".zshrc");
|
|
764
|
+
initLine = "eval \"$(prun --init zsh)\"";
|
|
765
|
+
} else if (shell === "bash") {
|
|
766
|
+
rcFile = path.join(home, ".bashrc");
|
|
767
|
+
initLine = "eval \"$(prun --init bash)\"";
|
|
768
|
+
} else if (shell === "fish") {
|
|
769
|
+
const configHome = process.env.XDG_CONFIG_HOME || path.join(home, ".config");
|
|
770
|
+
rcFile = path.join(configHome, "fish", "config.fish");
|
|
771
|
+
initLine = "prun --init fish | source";
|
|
772
|
+
} else return;
|
|
773
|
+
try {
|
|
774
|
+
const dir = path.dirname(rcFile);
|
|
775
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
776
|
+
const content = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, "utf8") : "";
|
|
777
|
+
if (!/prun\s+--init/.test(content)) {
|
|
778
|
+
const prefix = content.length && !content.endsWith("\n") ? "\n" : "";
|
|
779
|
+
fs.appendFileSync(rcFile, `${prefix}${initLine}\n`, "utf8");
|
|
780
|
+
}
|
|
781
|
+
} catch {}
|
|
782
|
+
}
|
|
783
|
+
function shouldAutoInit() {
|
|
784
|
+
const auto = process.env.PI_AUTO_INIT || process.env.PRUN_AUTO_INIT;
|
|
785
|
+
if (auto != null) return isNoHistory(auto);
|
|
786
|
+
if (isNoHistory(process.env.PI_NO_AUTO_INIT || process.env.PRUN_NO_AUTO_INIT)) return false;
|
|
787
|
+
if (process.env.CI) return false;
|
|
788
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY) return false;
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
function printPrunInit(args = []) {
|
|
792
|
+
const shellArg = args[0];
|
|
793
|
+
const binArg = args[1];
|
|
794
|
+
const bin = shellQuote(binArg || process.env.PRUN_BIN || "prun");
|
|
795
|
+
const shell = shellArg || detectShell() || "zsh";
|
|
796
|
+
let script = "";
|
|
797
|
+
if (shell === "zsh") script = [
|
|
798
|
+
"prun() {",
|
|
799
|
+
` local bin=${bin}`,
|
|
800
|
+
" local -a cmd",
|
|
801
|
+
" cmd=(${=bin})",
|
|
802
|
+
" command \"${cmd[@]}\" \"$@\"",
|
|
803
|
+
" fc -R",
|
|
804
|
+
"}"
|
|
805
|
+
].join("\n");
|
|
806
|
+
else if (shell === "bash") script = [
|
|
807
|
+
"prun() {",
|
|
808
|
+
` local bin=${bin}`,
|
|
809
|
+
" local -a cmd",
|
|
810
|
+
" read -r -a cmd <<< \"$bin\"",
|
|
811
|
+
" command \"${cmd[@]}\" \"$@\"",
|
|
812
|
+
" history -n",
|
|
813
|
+
"}"
|
|
814
|
+
].join("\n");
|
|
815
|
+
else if (shell === "fish") script = [
|
|
816
|
+
"function prun",
|
|
817
|
+
` set -l bin ${bin}`,
|
|
818
|
+
" set -l cmd (string split -- \" \" $bin)",
|
|
819
|
+
" command $cmd $argv",
|
|
820
|
+
" history --merge",
|
|
821
|
+
"end"
|
|
822
|
+
].join("\n");
|
|
823
|
+
else {
|
|
824
|
+
console.log(color.red(isZh$2 ? `不支持的 shell: ${shell}` : `Unsupported shell: ${shell}`));
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
console.log(script);
|
|
588
828
|
}
|
|
589
829
|
|
|
590
830
|
//#endregion
|
|
@@ -667,6 +907,10 @@ async function setup() {
|
|
|
667
907
|
}
|
|
668
908
|
const argv = process.argv.slice(2);
|
|
669
909
|
await help(argv);
|
|
910
|
+
if ((exec === "prun" || exec === "prun.mjs") && argv[0] === "--init") {
|
|
911
|
+
printPrunInit(argv.slice(1));
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
670
914
|
let params = spaceFormat(argv.join(" ")).trim();
|
|
671
915
|
if (!await hasPkg(rootPath)) {
|
|
672
916
|
if (await isGo(rootPath)) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simon_he/pi",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.26",
|
|
5
5
|
"packageManager": "pnpm@10.28.2",
|
|
6
6
|
"description": "An intelligent cross-platform package manager and CLI tool that autodetects project environments (Node.mjs, Go, Rust, Python) with beautiful loading animations and smart command execution.",
|
|
7
7
|
"author": {
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"test": "vitest"
|
|
91
91
|
},
|
|
92
92
|
"dependencies": {
|
|
93
|
-
"ccommand": "^1.0
|
|
93
|
+
"ccommand": "^1.1.0",
|
|
94
94
|
"fast-glob": "^3.3.3",
|
|
95
95
|
"lazy-js-utils": "^0.1.49",
|
|
96
96
|
"ora": "^8.2.0",
|