@mindbase/node-tools 1.3.11 → 1.3.12
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/package.json +1 -1
- package/src/clear/cleaner.js +13 -32
- package/src/common/ui/pagination.js +9 -0
- package/src/common/ui/single-select.js +50 -5
- package/src/git-log/ui.js +48 -11
- package/src/publish/ui.js +12 -0
package/package.json
CHANGED
package/src/clear/cleaner.js
CHANGED
|
@@ -1,41 +1,22 @@
|
|
|
1
|
-
const
|
|
2
|
-
const { platform } = require("process");
|
|
1
|
+
const fs = require("fs");
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
let rmAvailable = null;
|
|
6
|
-
|
|
7
|
-
// 检测 rm 命令是否可用
|
|
8
|
-
function checkRmAvailable() {
|
|
9
|
-
if (rmAvailable !== null) return rmAvailable;
|
|
10
|
-
const result = spawnSync("rm", ["--version"], { shell: true });
|
|
11
|
-
rmAvailable = result.status === 0;
|
|
12
|
-
return rmAvailable;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// 执行删除命令
|
|
3
|
+
// 删除目录
|
|
16
4
|
function removeDir(path) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return result.status === 0;
|
|
23
|
-
} else {
|
|
24
|
-
const result = spawnSync("rm", ["-rf", path], { shell: true, stdio: "inherit" });
|
|
25
|
-
return result.status === 0;
|
|
5
|
+
try {
|
|
6
|
+
fs.rmSync(path, { recursive: true, force: true });
|
|
7
|
+
return true;
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
26
10
|
}
|
|
27
11
|
}
|
|
28
12
|
|
|
13
|
+
// 删除文件
|
|
29
14
|
function removeFile(path) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return result.status === 0;
|
|
36
|
-
} else {
|
|
37
|
-
const result = spawnSync("rm", [path], { shell: true, stdio: "inherit" });
|
|
38
|
-
return result.status === 0;
|
|
15
|
+
try {
|
|
16
|
+
fs.rmSync(path, { force: true });
|
|
17
|
+
return true;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
39
20
|
}
|
|
40
21
|
}
|
|
41
22
|
|
|
@@ -58,6 +58,15 @@ class PagedDisplay {
|
|
|
58
58
|
this.currentPage = page;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 跳转到指定索引位置的记录
|
|
64
|
+
* @param {number} index - 记录的绝对索引(从0开始)
|
|
65
|
+
*/
|
|
66
|
+
goToIndex(index) {
|
|
67
|
+
const page = Math.floor(index / this.pageSize);
|
|
68
|
+
this.jumpToPage(page);
|
|
69
|
+
}
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
module.exports = { PagedDisplay };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 通用单选菜单组件
|
|
3
|
-
* 使用 readline
|
|
3
|
+
* 使用 readline 原生键盘处理,支持翻页
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const readline = require('readline');
|
|
@@ -23,19 +23,43 @@ function singleSelect(items, options = {}) {
|
|
|
23
23
|
|
|
24
24
|
let currentIndex = 0;
|
|
25
25
|
|
|
26
|
-
//
|
|
26
|
+
// 计算可见条目数(不超过终端高度)
|
|
27
|
+
function getVisibleCount() {
|
|
28
|
+
const terminalHeight = process.stdout.rows || 24;
|
|
29
|
+
const titleLines = options.title ? 2 : 0; // 标题 + 空行
|
|
30
|
+
const hintLines = options.hint ? 2 : 0; // 空行 + 提示
|
|
31
|
+
const reservedLines = titleLines + hintLines + 1; // 预留 1 行余量
|
|
32
|
+
return Math.max(3, terminalHeight - reservedLines);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 显示选项(支持翻页窗口)
|
|
27
36
|
function display() {
|
|
28
37
|
console.clear();
|
|
29
38
|
if (options.title) {
|
|
30
39
|
console.log(options.title);
|
|
31
40
|
console.log('');
|
|
32
41
|
}
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
|
|
43
|
+
const visibleCount = getVisibleCount();
|
|
44
|
+
// 计算窗口位置:确保当前项在可见区域内
|
|
45
|
+
let windowStart = 0;
|
|
46
|
+
if (items.length > visibleCount) {
|
|
47
|
+
// 当前项接近窗口底部时,向上滚动
|
|
48
|
+
windowStart = Math.max(0, currentIndex - Math.floor(visibleCount / 2));
|
|
49
|
+
// 不超过最大窗口起始位置
|
|
50
|
+
windowStart = Math.min(windowStart, items.length - visibleCount);
|
|
51
|
+
}
|
|
52
|
+
const windowEnd = Math.min(windowStart + visibleCount, items.length);
|
|
53
|
+
|
|
54
|
+
// 显示可见窗口内的选项
|
|
55
|
+
for (let i = windowStart; i < windowEnd; i++) {
|
|
56
|
+
const item = items[i];
|
|
57
|
+
const isCurrent = i === currentIndex;
|
|
35
58
|
const prefix = isCurrent ? '> ' : ' ';
|
|
36
59
|
const color = isCurrent ? chalk.green : chalk.gray;
|
|
37
60
|
console.log(prefix + color(item.title));
|
|
38
|
-
}
|
|
61
|
+
}
|
|
62
|
+
|
|
39
63
|
if (options.hint) {
|
|
40
64
|
console.log('');
|
|
41
65
|
console.log(chalk.dim(options.hint));
|
|
@@ -55,6 +79,20 @@ function singleSelect(items, options = {}) {
|
|
|
55
79
|
} else if (key.name === 'down') {
|
|
56
80
|
currentIndex = Math.min(items.length - 1, currentIndex + 1);
|
|
57
81
|
display();
|
|
82
|
+
} else if (key.name === 'pageup' || key.name === 'left') {
|
|
83
|
+
const visibleCount = getVisibleCount();
|
|
84
|
+
currentIndex = Math.max(0, currentIndex - visibleCount);
|
|
85
|
+
display();
|
|
86
|
+
} else if (key.name === 'pagedown' || key.name === 'right') {
|
|
87
|
+
const visibleCount = getVisibleCount();
|
|
88
|
+
currentIndex = Math.min(items.length - 1, currentIndex + visibleCount);
|
|
89
|
+
display();
|
|
90
|
+
} else if (key.name === 'home') {
|
|
91
|
+
currentIndex = 0;
|
|
92
|
+
display();
|
|
93
|
+
} else if (key.name === 'end') {
|
|
94
|
+
currentIndex = items.length - 1;
|
|
95
|
+
display();
|
|
58
96
|
} else if (key.name === 'return') {
|
|
59
97
|
cleanup();
|
|
60
98
|
resolve(items[currentIndex].value);
|
|
@@ -67,13 +105,20 @@ function singleSelect(items, options = {}) {
|
|
|
67
105
|
}
|
|
68
106
|
};
|
|
69
107
|
|
|
108
|
+
// 终端大小变化时重新渲染
|
|
109
|
+
const resizeHandler = () => {
|
|
110
|
+
display();
|
|
111
|
+
};
|
|
112
|
+
|
|
70
113
|
const cleanup = () => {
|
|
71
114
|
process.stdin.removeListener('keypress', handler);
|
|
115
|
+
process.stdout.off('resize', resizeHandler);
|
|
72
116
|
process.stdin.setRawMode(false);
|
|
73
117
|
process.stdin.pause();
|
|
74
118
|
};
|
|
75
119
|
|
|
76
120
|
process.stdin.on('keypress', handler);
|
|
121
|
+
process.stdout.on('resize', resizeHandler);
|
|
77
122
|
});
|
|
78
123
|
}
|
|
79
124
|
|
package/src/git-log/ui.js
CHANGED
|
@@ -172,13 +172,26 @@ async function inputFilters (repos) {
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
// 是否已初始化 keypress 事件
|
|
176
|
+
let keypressInitialized = false;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 确保输入流初始化(只执行一次)
|
|
180
|
+
*/
|
|
181
|
+
function ensureInputStreamInitialized() {
|
|
182
|
+
if (!keypressInitialized) {
|
|
183
|
+
readline.emitKeypressEvents(process.stdin);
|
|
184
|
+
keypressInitialized = true;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
175
188
|
/**
|
|
176
|
-
*
|
|
177
|
-
* @returns {Promise<string>}
|
|
189
|
+
* 等待日志导航按键(支持 resize)
|
|
190
|
+
* @returns {Promise<string>} 导航动作或 'resize'
|
|
178
191
|
*/
|
|
179
192
|
function waitForLogNavigation () {
|
|
180
193
|
return new Promise((resolve) => {
|
|
181
|
-
|
|
194
|
+
ensureInputStreamInitialized();
|
|
182
195
|
process.stdin.resume();
|
|
183
196
|
process.stdin.setRawMode(true);
|
|
184
197
|
|
|
@@ -198,13 +211,19 @@ function waitForLogNavigation () {
|
|
|
198
211
|
}
|
|
199
212
|
};
|
|
200
213
|
|
|
214
|
+
const resizeHandler = () => {
|
|
215
|
+
cleanup();
|
|
216
|
+
resolve('resize');
|
|
217
|
+
};
|
|
218
|
+
|
|
201
219
|
const cleanup = () => {
|
|
202
220
|
process.stdin.removeListener('keypress', handler);
|
|
203
|
-
process.
|
|
204
|
-
|
|
221
|
+
process.stdout.off('resize', resizeHandler);
|
|
222
|
+
// 不暂停 stdin,保持流处于活跃状态以便下次 resume
|
|
205
223
|
};
|
|
206
224
|
|
|
207
225
|
process.stdin.on('keypress', handler);
|
|
226
|
+
process.stdout.on('resize', resizeHandler);
|
|
208
227
|
});
|
|
209
228
|
}
|
|
210
229
|
|
|
@@ -243,18 +262,21 @@ function renderLogPage (page, totalCount) {
|
|
|
243
262
|
|
|
244
263
|
/**
|
|
245
264
|
* 计算动态分页大小
|
|
265
|
+
* 精确计算确保不超过终端高度
|
|
246
266
|
* @returns {number} 每页显示条数
|
|
247
267
|
*/
|
|
248
268
|
function calculateDynamicPageSize () {
|
|
249
269
|
const terminalHeight = process.stdout.rows || 24;
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
const
|
|
270
|
+
// 头部:标题1行 + 空行1行 + 提示1行 = 3行
|
|
271
|
+
const reservedLines = 3;
|
|
272
|
+
const availableLines = Math.max(terminalHeight - reservedLines, 5);
|
|
273
|
+
// 每条日志:仓库1行 + 提交1行 + 作者1行 + 日期1行 + 信息1行 + 分隔空行1行 = 6行
|
|
274
|
+
const linesPerLog = 6;
|
|
253
275
|
return Math.floor(availableLines / linesPerLog);
|
|
254
276
|
}
|
|
255
277
|
|
|
256
278
|
/**
|
|
257
|
-
*
|
|
279
|
+
* 显示日志(支持重新筛选和 resize)
|
|
258
280
|
* @param {array} logs - 日志列表
|
|
259
281
|
* @param {function} onRefilter - 重新筛选的回调
|
|
260
282
|
* @returns {Promise<void>}
|
|
@@ -267,8 +289,8 @@ async function displayLogs (logs, onRefilter) {
|
|
|
267
289
|
|
|
268
290
|
await withScreenSession(async (renderer) => {
|
|
269
291
|
// 创建分页显示(动态计算每页条数)
|
|
270
|
-
|
|
271
|
-
|
|
292
|
+
let pageSize = calculateDynamicPageSize();
|
|
293
|
+
let pagedDisplay = new PagedDisplay(logs, pageSize);
|
|
272
294
|
|
|
273
295
|
// 主循环
|
|
274
296
|
while (true) {
|
|
@@ -289,7 +311,22 @@ async function displayLogs (logs, onRefilter) {
|
|
|
289
311
|
onRefilter();
|
|
290
312
|
return;
|
|
291
313
|
} else if (action === 'exit') {
|
|
314
|
+
// 退出时恢复 stdin 状态
|
|
315
|
+
process.stdin.setRawMode(false);
|
|
316
|
+
process.stdin.pause();
|
|
292
317
|
break;
|
|
318
|
+
} else if (action === 'resize') {
|
|
319
|
+
// 终端大小改变,重新计算分页
|
|
320
|
+
const oldPageSize = pageSize;
|
|
321
|
+
pageSize = calculateDynamicPageSize();
|
|
322
|
+
if (oldPageSize !== pageSize) {
|
|
323
|
+
// 获取当前页第一条记录的绝对索引
|
|
324
|
+
const currentIndex = pagedDisplay.currentPage * pagedDisplay.pageSize;
|
|
325
|
+
// 重新创建分页器
|
|
326
|
+
pagedDisplay = new PagedDisplay(logs, pageSize);
|
|
327
|
+
// 尝试恢复到之前的位置
|
|
328
|
+
pagedDisplay.goToIndex(Math.min(currentIndex, logs.length - 1));
|
|
329
|
+
}
|
|
293
330
|
}
|
|
294
331
|
}
|
|
295
332
|
});
|
package/src/publish/ui.js
CHANGED
|
@@ -423,6 +423,18 @@ async function showPublishResults(renderer, results, registries) {
|
|
|
423
423
|
* 运行发布流程
|
|
424
424
|
*/
|
|
425
425
|
async function runPublishFlow(targetPath, options) {
|
|
426
|
+
const { existsSync } = require('fs');
|
|
427
|
+
const { join } = require('path');
|
|
428
|
+
|
|
429
|
+
// 检查 package.json 是否存在
|
|
430
|
+
const packageJsonPath = join(targetPath, 'package.json');
|
|
431
|
+
if (!existsSync(packageJsonPath)) {
|
|
432
|
+
console.error(chalk.red('\n错误: 当前目录不是 npm 包或 npm 工作空间'));
|
|
433
|
+
console.error(chalk.yellow(`\n未找到 package.json 文件: ${packageJsonPath}`));
|
|
434
|
+
console.error(chalk.gray('\n请确保在正确的 npm 项目目录下执行此命令\n'));
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
|
|
426
438
|
return withScreenSession(async (renderer) => {
|
|
427
439
|
// 1. 扫描预览页
|
|
428
440
|
const scanResult = await showScanPreview(renderer, targetPath);
|