@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/README.md +0 -1
- package/dist/index.cjs +450 -145
- package/dist/index.mjs +449 -146
- package/package.json +11 -12
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,
|
|
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.
|
|
15
|
+
var version = "0.2.4";
|
|
15
16
|
|
|
16
17
|
//#endregion
|
|
17
|
-
//#region src/
|
|
18
|
-
const isZh$
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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$
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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 = "
|
|
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
|
|
699
|
+
const { detected, tool } = await resolvePkgTool();
|
|
388
700
|
const maxSockets = sockets || 4;
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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 =
|
|
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 (!
|
|
495
|
-
console.log(color.yellow(isZh ? "
|
|
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
|
|
499
|
-
|
|
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
|
-
}
|
|
507
|
-
params = choose.
|
|
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
|
|
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: `${
|
|
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
|
|
859
|
-
|
|
860
|
-
"
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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
|
-
}
|
|
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 {
|
|
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) => {
|