@simon_he/pi 0.2.2 → 0.2.4

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 CHANGED
@@ -3,104 +3,419 @@ import path from "node:path";
3
3
  import process from "node:process";
4
4
  import fg from "fast-glob";
5
5
  import { isFile, isWin, spaceFormat } from "lazy-js-utils";
6
- import { getPkg, getPkgTool, hasPkg, isGo, isInstallPkg, isRust, jsShell, useNodeWorker } from "lazy-js-utils/node";
6
+ import { getPkg, getPkgTool, hasPkg, isGo, isRust, jsShell, useNodeWorker } from "lazy-js-utils/node";
7
7
  import color from "picocolors";
8
+ import readline from "node:readline";
8
9
  import { log } from "node:console";
9
10
  import fs from "node:fs";
10
11
  import os from "node:os";
11
12
  import { fileURLToPath } from "node:url";
12
13
 
13
14
  //#region package.json
14
- var version = "0.2.2";
15
+ var version = "0.2.4";
15
16
 
16
17
  //#endregion
17
- //#region src/installDeps.ts
18
- const isZh$7 = process.env.PI_Lang === "zh";
19
- const gumDocUrl = "https://github.com/charmbracelet/gum";
20
- const niDocUrl = "https://github.com/antfu/ni";
21
- async function installDeps(options = {}) {
22
- const { gum = true, ni = true, strict = true } = options;
23
- const platform = process.platform;
24
- if (gum && !await isInstallPkg("gum")) if (platform === "darwin") {
25
- console.log(color.cyan(isZh$7 ? "正在为您安装必要的依赖gum..." : "Installing gum..."));
26
- const { status } = await jsShell("brew install gum", [
27
- "inherit",
28
- "pipe",
29
- "inherit"
30
- ]);
31
- if (status === 0) console.log(color.cyan(isZh$7 ? "gum 安装成功!" : "gum installed successfully!"));
32
- else {
33
- console.log(color.red(isZh$7 ? `gum 安装失败,请尝试从官网解决安装问题! ${gumDocUrl}` : `gum installation failed, please try manual install: ${gumDocUrl}`));
34
- if (strict) process.exit(1);
18
+ //#region src/tty.ts
19
+ const isZh$6 = process.env.PI_Lang === "zh";
20
+ function isInteractive() {
21
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
22
+ }
23
+ function stripAnsi$1(input) {
24
+ let output = "";
25
+ for (let i = 0; i < input.length; i++) {
26
+ const ch = input[i];
27
+ if (ch === "\x1B" && input[i + 1] === "[") {
28
+ let j = i + 2;
29
+ while (j < input.length && /[0-9;]/.test(input[j])) j++;
30
+ if (input[j] === "m") {
31
+ i = j;
32
+ continue;
33
+ }
35
34
  }
36
- } else console.log(color.yellow(isZh$7 ? `未检测到 gum,请根据系统手动安装: ${gumDocUrl}` : `gum not found, please install it manually: ${gumDocUrl}`));
37
- if (ni && !await isInstallPkg("ni")) {
38
- console.log(color.cyan(isZh$7 ? "正在为您安装必要的依赖ni..." : "Installing ni..."));
39
- const { status } = await jsShell("npm i -g @antfu/ni", [
40
- "inherit",
41
- "pipe",
42
- "inherit"
43
- ]);
44
- if (status === 0) console.log(color.cyan(isZh$7 ? "ni 安装成功!" : "ni installed successfully!"));
35
+ output += ch;
36
+ }
37
+ return output;
38
+ }
39
+ function charWidth(ch) {
40
+ return (ch.codePointAt(0) ?? 0) >= 4352 ? 2 : 1;
41
+ }
42
+ function stringWidth(input) {
43
+ const clean = stripAnsi$1(input);
44
+ let width = 0;
45
+ for (const ch of clean) width += charWidth(ch);
46
+ return width;
47
+ }
48
+ function rowCount(input, columns) {
49
+ const width = stringWidth(input);
50
+ return Math.max(1, Math.ceil(width / columns));
51
+ }
52
+ function fuzzyScore(option, query) {
53
+ const optionLower = option.toLowerCase();
54
+ const queryLower = query.toLowerCase();
55
+ let score = 0;
56
+ let lastIndex = -1;
57
+ for (const ch of queryLower) {
58
+ const idx = optionLower.indexOf(ch, lastIndex + 1);
59
+ if (idx === -1) return null;
60
+ score += idx === lastIndex + 1 ? 5 : 1;
61
+ score -= idx;
62
+ lastIndex = idx;
63
+ }
64
+ return score;
65
+ }
66
+ function getMatchIndices(option, query) {
67
+ if (!query) return [];
68
+ const optionLower = option.toLowerCase();
69
+ const queryLower = query.toLowerCase();
70
+ const indices = [];
71
+ let lastIndex = -1;
72
+ for (const ch of queryLower) {
73
+ const idx = optionLower.indexOf(ch, lastIndex + 1);
74
+ if (idx === -1) return [];
75
+ indices.push(idx);
76
+ lastIndex = idx;
77
+ }
78
+ return indices;
79
+ }
80
+ function highlightMatch(option, query, active) {
81
+ if (!query) return active ? color.cyan(option) : option;
82
+ const matchSet = new Set(getMatchIndices(option, query));
83
+ let output = "";
84
+ for (let i = 0; i < option.length; i++) {
85
+ const ch = option[i];
86
+ if (matchSet.has(i)) output += color.bold(color.yellow(ch));
87
+ else output += active ? color.cyan(ch) : ch;
88
+ }
89
+ return output;
90
+ }
91
+ function filterOptions(options, query) {
92
+ if (!query) return options;
93
+ const ranked = options.map((option, index) => {
94
+ const score = fuzzyScore(option, query);
95
+ return score === null ? null : {
96
+ option,
97
+ score,
98
+ index
99
+ };
100
+ }).filter(Boolean);
101
+ ranked.sort((a, b) => {
102
+ if (b.score !== a.score) return b.score - a.score;
103
+ return a.index - b.index;
104
+ });
105
+ return ranked.map((item) => item.option);
106
+ }
107
+ function getVisibleWindow(total, cursor, limit) {
108
+ if (total <= limit) return {
109
+ start: 0,
110
+ end: total
111
+ };
112
+ let start = cursor - Math.floor(limit / 2);
113
+ let end = start + limit;
114
+ if (start < 0) {
115
+ start = 0;
116
+ end = limit;
117
+ }
118
+ if (end > total) {
119
+ end = total;
120
+ start = Math.max(0, end - limit);
121
+ }
122
+ return {
123
+ start,
124
+ end
125
+ };
126
+ }
127
+ async function runSelect(options, config) {
128
+ if (!isInteractive()) return null;
129
+ if (options.length === 0) return null;
130
+ const stdin = process.stdin;
131
+ const stdout = process.stdout;
132
+ readline.emitKeypressEvents(stdin, { escapeCodeTimeout: 50 });
133
+ if (stdin.isTTY) stdin.setRawMode(true);
134
+ let query = "";
135
+ let inputCursor = 0;
136
+ let filtered = options;
137
+ let cursor = 0;
138
+ const selected = /* @__PURE__ */ new Set();
139
+ let searchMode = false;
140
+ let anchorSet = false;
141
+ let columns = stdout.columns || 80;
142
+ let rows = stdout.rows || 24;
143
+ let maxVisible = Math.max(5, rows - 4);
144
+ const updateDimensions = () => {
145
+ columns = stdout.columns || 80;
146
+ rows = stdout.rows || 24;
147
+ maxVisible = Math.max(5, rows - 4);
148
+ };
149
+ const updateFiltered = () => {
150
+ filtered = filterOptions(options, query);
151
+ if (filtered.length === 0) cursor = 0;
152
+ else if (cursor >= filtered.length) cursor = filtered.length - 1;
153
+ };
154
+ const render = () => {
155
+ updateFiltered();
156
+ if (query.length > 0) searchMode = true;
157
+ const prompt = searchMode ? "/ " : "> ";
158
+ const header = `? ${config.placeholder}`;
159
+ const inputLine = `${prompt}${query}`;
160
+ const hint = config.mode === "multiple" ? isZh$6 ? "↑/↓ 选择,空格标记,Tab 补全,/ 搜索,回车确认,Esc 取消" : "Use ↑/↓ to move, Space to toggle, Tab to complete, / to search, Enter to confirm, Esc to cancel" : isZh$6 ? "↑/↓ 选择,Tab 补全,/ 搜索,回车确认,Esc 取消" : "Use ↑/↓ to move, Tab to complete, / to search, Enter to confirm, Esc to cancel";
161
+ const lines = [header, inputLine];
162
+ if (filtered.length === 0) lines.push(color.dim(isZh$6 ? "没有匹配项" : "No matches"));
45
163
  else {
46
- console.log(color.red(isZh$7 ? `ni 安装失败,请尝试从官网解决安装问题! ${niDocUrl}` : `ni installation failed, please try manual install: ${niDocUrl}`));
47
- if (strict) process.exit(1);
164
+ const window = getVisibleWindow(filtered.length, cursor, maxVisible);
165
+ const visible = filtered.slice(window.start, window.end);
166
+ if (window.start > 0) lines.push(color.dim("…"));
167
+ visible.forEach((option, index) => {
168
+ const active = window.start + index === cursor;
169
+ const picked = selected.has(option);
170
+ const prefix = config.mode === "multiple" ? picked ? "[x] " : "[ ] " : "";
171
+ const indicator = active ? ">" : " ";
172
+ const renderedOption = highlightMatch(option, query, active);
173
+ const renderedPrefix = active ? color.cyan(prefix) : prefix;
174
+ const content = `${active ? color.cyan(indicator) : indicator} ${renderedPrefix}${renderedOption}`;
175
+ lines.push(content);
176
+ });
177
+ if (window.end < filtered.length) lines.push(color.dim("…"));
48
178
  }
49
- }
179
+ lines.push(color.dim(`${hint} (${Math.min(cursor + 1, filtered.length)}/${filtered.length})`));
180
+ if (!anchorSet) {
181
+ readline.cursorTo(stdout, 0);
182
+ stdout.write("\x1B[s");
183
+ anchorSet = true;
184
+ } else {
185
+ stdout.write("\x1B[u");
186
+ readline.cursorTo(stdout, 0);
187
+ readline.clearScreenDown(stdout);
188
+ }
189
+ stdout.write(lines.join("\n"));
190
+ const headerRows = rowCount(header, columns);
191
+ const inputRowOffset = Math.floor((prompt.length + inputCursor) / columns);
192
+ const inputCol = (prompt.length + inputCursor) % columns;
193
+ const targetRowOffset = headerRows + inputRowOffset;
194
+ stdout.write("\x1B[u");
195
+ readline.moveCursor(stdout, 0, targetRowOffset);
196
+ readline.cursorTo(stdout, inputCol);
197
+ };
198
+ return new Promise((resolve) => {
199
+ let onKeypress;
200
+ let onResize;
201
+ const done = (value) => {
202
+ if (stdin.isTTY) stdin.setRawMode(false);
203
+ stdin.off("keypress", onKeypress);
204
+ process.off("SIGWINCH", onResize);
205
+ stdout.write("\x1B[?25h");
206
+ if (anchorSet) {
207
+ stdout.write("\x1B[u");
208
+ readline.cursorTo(stdout, 0);
209
+ readline.clearScreenDown(stdout);
210
+ anchorSet = false;
211
+ }
212
+ stdout.write("\n");
213
+ resolve(value);
214
+ };
215
+ const confirmSelection = () => {
216
+ if (filtered.length === 0) return done(null);
217
+ if (config.mode === "multiple") {
218
+ const picked = options.filter((option) => selected.has(option));
219
+ if (picked.length > 0) return done(picked);
220
+ return done([filtered[cursor]]);
221
+ }
222
+ return done(filtered[cursor]);
223
+ };
224
+ const cancelSelection = () => done(null);
225
+ onKeypress = (input, key) => {
226
+ if (key?.ctrl && key.name === "c") return cancelSelection();
227
+ if (key?.name === "escape") {
228
+ if (query.length > 0) {
229
+ query = "";
230
+ inputCursor = 0;
231
+ return render();
232
+ }
233
+ return cancelSelection();
234
+ }
235
+ if (key?.name === "return") return confirmSelection();
236
+ if (key?.name === "up") {
237
+ if (filtered.length > 0) cursor = (cursor - 1 + filtered.length) % filtered.length;
238
+ return render();
239
+ }
240
+ if (key?.name === "down") {
241
+ if (filtered.length > 0) cursor = (cursor + 1) % filtered.length;
242
+ return render();
243
+ }
244
+ if (config.mode === "multiple" && key?.name === "space") {
245
+ const option = filtered[cursor];
246
+ if (option) if (selected.has(option)) selected.delete(option);
247
+ else selected.add(option);
248
+ return render();
249
+ }
250
+ if (key?.name === "tab") {
251
+ if (filtered.length > 0) {
252
+ query = filtered[cursor] || filtered[0];
253
+ inputCursor = query.length;
254
+ searchMode = true;
255
+ return render();
256
+ }
257
+ return;
258
+ }
259
+ if (input === "/" && !key?.ctrl && !key?.meta) {
260
+ if (!searchMode) {
261
+ searchMode = true;
262
+ query = "";
263
+ inputCursor = 0;
264
+ return render();
265
+ }
266
+ if (searchMode && query.length === 0) {
267
+ searchMode = false;
268
+ return render();
269
+ }
270
+ }
271
+ if (key?.name === "left") {
272
+ if (inputCursor > 0) inputCursor -= 1;
273
+ return render();
274
+ }
275
+ if (key?.name === "right") {
276
+ if (inputCursor < query.length) inputCursor += 1;
277
+ return render();
278
+ }
279
+ if (key?.name === "home" || key?.ctrl && key?.name === "a") {
280
+ inputCursor = 0;
281
+ return render();
282
+ }
283
+ if (key?.name === "end" || key?.ctrl && key?.name === "e") {
284
+ inputCursor = query.length;
285
+ return render();
286
+ }
287
+ if (key?.name === "backspace") {
288
+ if (query) {
289
+ query = query.slice(0, Math.max(0, inputCursor - 1)) + query.slice(inputCursor);
290
+ inputCursor = Math.max(0, inputCursor - 1);
291
+ return render();
292
+ }
293
+ return;
294
+ }
295
+ if (key?.name === "delete") {
296
+ if (query && inputCursor < query.length) {
297
+ query = query.slice(0, inputCursor) + query.slice(inputCursor + 1);
298
+ return render();
299
+ }
300
+ return;
301
+ }
302
+ if (key?.ctrl && key?.name === "u") {
303
+ query = "";
304
+ inputCursor = 0;
305
+ return render();
306
+ }
307
+ if (input && !key?.ctrl && !key?.meta && input.length === 1) {
308
+ if (config.mode !== "multiple" || input !== " ") {
309
+ query = query.slice(0, inputCursor) + input + query.slice(inputCursor);
310
+ inputCursor += input.length;
311
+ searchMode = true;
312
+ }
313
+ return render();
314
+ }
315
+ };
316
+ onResize = () => {
317
+ updateDimensions();
318
+ if (anchorSet) {
319
+ stdout.write("\x1B[u");
320
+ readline.cursorTo(stdout, 0);
321
+ stdout.write("\x1B[s");
322
+ }
323
+ render();
324
+ };
325
+ process.on("SIGWINCH", onResize);
326
+ stdin.on("keypress", onKeypress);
327
+ render();
328
+ });
329
+ }
330
+ async function ttySelect(options, placeholder) {
331
+ const result = await runSelect(options, {
332
+ placeholder,
333
+ mode: "single"
334
+ });
335
+ return typeof result === "string" ? result : null;
336
+ }
337
+ async function ttyMultiSelect(options, placeholder) {
338
+ const result = await runSelect(options, {
339
+ placeholder,
340
+ mode: "multiple"
341
+ });
342
+ return Array.isArray(result) ? result : null;
343
+ }
344
+ function renderBox(lines, options = {}) {
345
+ const width = options.width ?? Math.max(...lines.map((line) => line.length), 0);
346
+ const paddingX = options.paddingX ?? 2;
347
+ const paddingY = options.paddingY ?? 1;
348
+ const marginX = options.marginX ?? 0;
349
+ const marginY = options.marginY ?? 0;
350
+ const align = options.align ?? "left";
351
+ const innerWidth = width + paddingX * 2;
352
+ const margin = " ".repeat(marginX);
353
+ const top = `${margin}+${"-".repeat(innerWidth)}+`;
354
+ const bottom = top;
355
+ const emptyLine = `${margin}|${" ".repeat(innerWidth)}|`;
356
+ const alignedLines = lines.map((line) => {
357
+ const space = Math.max(0, width - line.length);
358
+ const leftPad = align === "center" ? Math.floor(space / 2) : 0;
359
+ const rightPad = space - leftPad;
360
+ return `${margin}|${" ".repeat(paddingX + leftPad)}${line}${" ".repeat(paddingX + rightPad)}|`;
361
+ });
362
+ const output = [];
363
+ for (let i = 0; i < marginY; i++) output.push("");
364
+ output.push(top);
365
+ for (let i = 0; i < paddingY; i++) output.push(emptyLine);
366
+ output.push(...alignedLines);
367
+ for (let i = 0; i < paddingY; i++) output.push(emptyLine);
368
+ output.push(bottom);
369
+ for (let i = 0; i < marginY; i++) output.push("");
370
+ return output.join("\n");
50
371
  }
51
372
 
52
373
  //#endregion
53
374
  //#region src/help.ts
54
- const isZh$6 = process.env.PI_Lang === "zh";
55
- async function ensureGum() {
56
- if (await isInstallPkg("gum")) return true;
57
- await installDeps({
58
- gum: true,
59
- ni: false,
60
- strict: false
61
- });
62
- return await isInstallPkg("gum");
63
- }
64
- function printPlainVersion() {
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
- }
69
- function printPlainHelp() {
70
- console.log([
71
- "PI Commands:",
72
- "~ pi: install package",
73
- "~ pix: npx package",
74
- "~ pui: uninstall package",
75
- "~ prun: run package script",
76
- "~ pinit: package init",
77
- "~ pbuild: go build | cargo build",
78
- "~ pfind: find monorepo of yarn or pnpm",
79
- "~ pa: agent alias",
80
- "~ pu: package upgrade",
81
- "~ pci: package clean install",
82
- "~ pil: package latest install"
83
- ].join("\n"));
84
- }
375
+ const isZh$5 = process.env.PI_Lang === "zh";
85
376
  async function help(argv) {
86
377
  const arg = argv[0];
87
378
  if (arg === "-v" || arg === "--version") {
88
- if (await ensureGum()) await jsShell(isZh$6 ? `gum style \
89
- --foreground 212 --border-foreground 212 --border double \
90
- --align center --width 50 --margin "1 2" --padding "2 4" \
91
- "pi 版本: ${version}" "请为我的努力点一个行 🌟" "谢谢 🤟"` : `gum style \
92
- --foreground 212 --border-foreground 212 --border double \
93
- --align center --width 50 --margin "1 2" --padding "2 4" \
94
- "pi version: ${version}" "Please give me a 🌟 for my efforts" "Thank you 🤟"`, "inherit");
95
- else printPlainVersion();
379
+ const message = isZh$5 ? [
380
+ `pi 版本: ${version}`,
381
+ "请为我的努力点一个行 🌟",
382
+ "谢谢 🤟"
383
+ ] : [
384
+ `pi version: ${version}`,
385
+ "Please give me a 🌟 for my efforts",
386
+ "Thank you 🤟"
387
+ ];
388
+ console.log(renderBox(message, {
389
+ align: "center",
390
+ width: 50,
391
+ marginX: 2,
392
+ marginY: 1,
393
+ paddingX: 4,
394
+ paddingY: 2
395
+ }));
96
396
  process.exit(0);
97
397
  } else if (arg === "-h" || arg === "--help") {
98
- if (await ensureGum()) await jsShell(`gum style \
99
- --foreground 212 --border-foreground 212 --border double \
100
- --align left --width 50 --margin "1 2" --padding "1 1" \
101
- "PI Commands:" "~ pi: install package" "~ pix: npx package" "~ pui: uninstall package" "~ prun: run package script" "~ pinit: package init" "~ pbuild: go build | cargo build" "~ pfind: find monorepo of yarn or pnpm" "~ pa: agent alias" "~ pu: package upgrade" "~ pci: package clean install" "~ pil: package latest install"
102
- `, "inherit");
103
- else printPlainHelp();
398
+ console.log(renderBox([
399
+ "PI Commands:",
400
+ "~ pi: install package",
401
+ "~ pix: npx package",
402
+ "~ pui: uninstall package",
403
+ "~ prun: run package script",
404
+ "~ pinit: package init",
405
+ "~ pbuild: go build | cargo build",
406
+ "~ pfind: find monorepo of yarn or pnpm",
407
+ "~ pa: agent alias",
408
+ "~ pu: package upgrade",
409
+ "~ pci: package clean install",
410
+ "~ pil: package latest install"
411
+ ], {
412
+ align: "left",
413
+ width: 50,
414
+ marginX: 2,
415
+ marginY: 1,
416
+ paddingX: 1,
417
+ paddingY: 1
418
+ }));
104
419
  process.exit(0);
105
420
  }
106
421
  }
@@ -113,46 +428,43 @@ function pa() {
113
428
 
114
429
  //#endregion
115
430
  //#region src/detectNode.ts
116
- const isZh$5 = process.env.PI_Lang === "zh";
117
431
  async function detectNode() {
118
- let pkg;
119
432
  try {
120
- pkg = await getPkg();
433
+ await getPkg();
121
434
  } catch {
122
435
  const cwd = process.cwd();
123
436
  console.log(color.red(`当前目录: ${cwd} 没有package.json文件`));
124
437
  process.exit(1);
125
438
  }
126
- if (pkg.engines?.node) {
127
- const semver = await import("semver");
128
- const satisfies = semver.satisfies || semver.default?.satisfies;
129
- if (!satisfies) return;
130
- if (!satisfies(process.version, pkg.engines.node)) {
131
- const hasGum = await isInstallPkg("gum");
132
- const hasFnm = await isInstallPkg("fnm");
133
- if (!hasGum || !hasFnm) {
134
- const missing = [!hasGum ? "gum" : "", !hasFnm ? "fnm" : ""].filter(Boolean).join(", ");
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
- return;
137
- }
138
- const { result, status } = await jsShell(`echo "yes\nno" | gum filter --placeholder=" 当前node版本不满足 ${pkg.engines.node},是否切换node版本"`, [
139
- "inherit",
140
- "pipe",
141
- "inherit"
142
- ]);
143
- if (status === 0 && result === "yes") await jsShell(`
144
- current=$(echo $(fnm current))
145
- registery=$(echo "$(fnm ls)" | sed 's/system//g' | sed 's/default//g' | sed 's/\* //g' | sed "s/$current/\* $current/g" | gum filter --placeholder=" 请选择一个node版本")
146
- registery=$(echo $\{registery// /} | sed 's/\*//g')
147
- if [ $registery ]; then
148
- fnm use $\{registery% -*}
149
- fi
150
- `, [
151
- "inherit",
152
- "pipe",
153
- "inherit"
154
- ]);
155
- }
439
+ }
440
+
441
+ //#endregion
442
+ //#region src/pkgManager.ts
443
+ async function resolvePkgTool() {
444
+ const detected = await getPkgTool() || "npm";
445
+ const fallback = process.env.PI_DEFAULT;
446
+ return {
447
+ detected,
448
+ tool: detected === "npm" && fallback ? fallback : detected
449
+ };
450
+ }
451
+ function getInstallCommand(tool, hasParams) {
452
+ const action = hasParams ? "add" : "install";
453
+ switch (tool) {
454
+ case "pnpm": return `pnpm ${action}`;
455
+ case "yarn": return `yarn ${action}`;
456
+ case "bun": return `bun ${action}`;
457
+ case "npm": return "npm install";
458
+ default: return `${tool} ${action}`;
459
+ }
460
+ }
461
+ function getRemoveCommand(tool) {
462
+ switch (tool) {
463
+ case "pnpm": return "pnpm remove";
464
+ case "yarn": return "yarn remove";
465
+ case "bun": return "bun remove";
466
+ case "npm": return "npm uninstall";
467
+ default: return `${tool} remove`;
156
468
  }
157
469
  }
158
470
 
@@ -367,7 +679,7 @@ async function pushHistory(command) {
367
679
  //#endregion
368
680
  //#region src/pi.ts
369
681
  const isZh$3 = process.env.PI_Lang === "zh";
370
- async function pi(params, pkg, executor = "ni") {
682
+ async function pi(params, pkg, executor = "pi") {
371
683
  await detectNode();
372
684
  const text = pkg ? `Installing ${params} ...` : "Updating dependency ...";
373
685
  const isLatest = executor === "pil";
@@ -384,22 +696,13 @@ async function pi(params, pkg, executor = "ni") {
384
696
  ];
385
697
  let loading_status;
386
698
  const { PI_DEFAULT, PI_MaxSockets: sockets } = process.env;
387
- const pkgTool = await getPkgTool();
699
+ const { detected, tool } = await resolvePkgTool();
388
700
  const maxSockets = sockets || 4;
389
- const install = !params ? "install" : "add";
390
- if (pkgTool === "npm") if (PI_DEFAULT) {
391
- executor = `${PI_DEFAULT} ${install}`;
392
- loading_status = await loading(text, isSilent);
393
- } else {
394
- stdio = "inherit";
395
- executor = "ni";
396
- }
397
- else {
398
- executor = `${pkgTool} ${install}`;
399
- loading_status = await loading(text, isSilent);
400
- }
701
+ if (detected === "npm" && !PI_DEFAULT) stdio = "inherit";
702
+ else loading_status = await loading(text, isSilent);
703
+ executor = getInstallCommand(tool, Boolean(params));
401
704
  const newParams = isLatest ? "" : await getParams(params);
402
- const runSockets = executor.split(" ")[0] === "npm" ? ` --max-sockets=${maxSockets}` : "";
705
+ const runSockets = tool === "npm" ? ` --max-sockets=${maxSockets}` : "";
403
706
  const latestParams = Array.isArray(params) ? params : params ? [params] : [];
404
707
  const cmdList = isLatest ? latestParams.map((p) => `${executor} ${p}`) : [`${executor}${newParams ? ` ${newParams}` : runSockets}`];
405
708
  const runCmd = isLatest ? cmdList.join(" & ") : cmdList[0];
@@ -491,20 +794,16 @@ async function pil(params) {
491
794
  const isZh = process.env.PI_Lang === "zh";
492
795
  const { dependencies = {}, devDependencies = {} } = await getPkg();
493
796
  if (!params) {
494
- if (!await isInstallPkg("gum")) {
495
- console.log(color.yellow(isZh ? "未检测到 gum,请先安装 gum 后再选择依赖。" : "gum not found. Please install gum before selecting dependencies."));
797
+ if (!isInteractive()) {
798
+ console.log(color.yellow(isZh ? "当前不是交互式终端,请直接传入要升级的依赖。" : "No interactive TTY detected, please pass the dependency names directly."));
496
799
  process.exit(1);
497
800
  }
498
- const { result: choose, status } = await jsShell(`echo ${[...Object.keys(dependencies).map((key) => `${key}: ${dependencies[key].replace(/([><~])/g, "\\$1")}`), ...Object.keys(devDependencies).map((key) => `${key}: ${devDependencies[key].replace(/([><~])/g, "\\$1")}`)].join(",")} | sed "s/,/\\n/g" | gum filter --no-limit --placeholder=" 🤔${process.env.PI_Lang === "zh" ? "请选择一个需要获取最新版本的依赖" : "Please select a dependency that needs to obtain the latest version."}"`, { stdio: [
499
- "inherit",
500
- "pipe",
501
- "inherit"
502
- ] });
503
- if (status === 130) {
801
+ const choose = await ttyMultiSelect([...Object.keys(dependencies).map((key) => `${key}: ${dependencies[key]}`), ...Object.keys(devDependencies).map((key) => `${key}: ${devDependencies[key]}`)], ` 🤔${process.env.PI_Lang === "zh" ? "请选择一个需要获取最新版本的依赖" : "Please select a dependency that needs to obtain the latest version."}`);
802
+ if (!choose || choose.length === 0) {
504
803
  console.log(color.dim("已取消"));
505
804
  process.exit(0);
506
- } else if (status !== 0) throw new Error(choose);
507
- params = choose.trim().split("\n").map((i) => {
805
+ }
806
+ params = choose.map((i) => {
508
807
  const name = i.split(": ")[0];
509
808
  if (name in devDependencies) return `${name}@latest -D`;
510
809
  return `${name}@latest -S`;
@@ -573,11 +872,14 @@ async function pinit() {
573
872
 
574
873
  //#endregion
575
874
  //#region src/pio.ts
576
- async function pio(params, pkg, executor = "ni") {
875
+ async function pio(params, pkg) {
577
876
  const successMsg = pkg ? `Installed ${pkg} successfully! 😊` : "Updated dependency successfully! 😊";
578
877
  const failMsg = pkg ? `Failed to install ${pkg} 😭` : "Failed to update dependency! 😭";
878
+ const offline = "--prefer-offline";
879
+ const newParams = await getParams(params);
880
+ const { tool } = await resolvePkgTool();
579
881
  const { status, result } = await useNodeWorker({
580
- params: `${executor} ${await getParams(params)} --prefer-offline`,
882
+ params: `${getInstallCommand(tool, Boolean(params))}${newParams ? ` ${newParams}` : ""} ${offline}`.trim(),
581
883
  stdio: "inherit"
582
884
  });
583
885
  const loading_status = await loading("");
@@ -855,15 +1157,16 @@ async function pui(params, pkg) {
855
1157
  const text = `${isZh$1 ? "正在为您卸载" : "Uninstalling"} ${pkg} ...`;
856
1158
  if (!params) {
857
1159
  const { dependencies = {}, devDependencies = {} } = await getPkg();
858
- const { result: choose, status } = await jsShell(`echo ${[...Object.keys(dependencies).map((key) => `${key}: ${dependencies[key]}`), ...Object.keys(devDependencies).map((key) => `${key}: ${devDependencies[key]}`)].join(",")} | sed "s/,/\\n/g" | gum filter --placeholder=" 🤔${process.env.PI_Lang === "zh" ? "请选择一个需要删除依赖" : "Please select a dependency to get the latest version."}"`, [
859
- "inherit",
860
- "pipe",
861
- "inherit"
862
- ]);
863
- if (status === 130) {
1160
+ const deps = [...Object.keys(dependencies).map((key) => `${key}: ${dependencies[key]}`), ...Object.keys(devDependencies).map((key) => `${key}: ${devDependencies[key]}`)];
1161
+ if (!isInteractive()) {
1162
+ console.log(color.yellow(isZh$1 ? "当前不是交互式终端,请直接传入要卸载的依赖。" : "No interactive TTY detected, please pass the dependency name directly."));
1163
+ process.exit(1);
1164
+ }
1165
+ const choose = await ttySelect(deps, ` 🤔${process.env.PI_Lang === "zh" ? "请选择一个需要删除依赖" : "Please select a dependency to uninstall."}`);
1166
+ if (!choose) {
864
1167
  console.log(color.dim("已取消"));
865
1168
  process.exit(0);
866
- } else if (status !== 0) throw new Error(choose);
1169
+ }
867
1170
  pkg = params = choose.split(": ")[0];
868
1171
  }
869
1172
  const start = Date.now();
@@ -874,7 +1177,8 @@ async function pui(params, pkg) {
874
1177
  process.exit(1);
875
1178
  }
876
1179
  const loading_status = await loading(text);
877
- const { status, result } = await useNodeWorker(`nun ${params}`);
1180
+ const { tool } = await resolvePkgTool();
1181
+ const { status, result } = await useNodeWorker(`${getRemoveCommand(tool)} ${params}`);
878
1182
  const costTime = (Date.now() - start) / 1e3;
879
1183
  successMsg += color.blue(` ---- ⏰:${costTime}s`);
880
1184
  if (status === 0) loading_status.succeed(color.green(successMsg));
@@ -980,7 +1284,6 @@ async function setup() {
980
1284
  return;
981
1285
  }
982
1286
  const pkg = argv.filter((v) => !v.startsWith("-")).join(" ");
983
- await installDeps();
984
1287
  await handler(params, pkg);
985
1288
  }
986
1289
  if (!process.env.PI_TEST) setup().catch((error) => {