@ybgnb/utils 0.1.9 → 0.2.0
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/core.cjs +1 -503
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +1 -436
- package/dist/core.js.map +1 -1
- package/dist/dom.cjs +1 -262
- package/dist/dom.cjs.map +1 -1
- package/dist/dom.js +1 -224
- package/dist/dom.js.map +1 -1
- package/dist/node.cjs +1 -409
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +1 -349
- package/dist/node.js.map +1 -1
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -1,350 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
function getEnv() {
|
|
3
|
-
const env = {};
|
|
4
|
-
for (const envKey in process.env) {
|
|
5
|
-
env[envKey] = process.env[envKey];
|
|
6
|
-
}
|
|
7
|
-
return env;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// src/node/utils/file/base.ts
|
|
11
|
-
import fs from "fs/promises";
|
|
12
|
-
async function isFile(filePath) {
|
|
13
|
-
try {
|
|
14
|
-
return (await fs.stat(filePath)).isFile();
|
|
15
|
-
} catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
async function existsFile(file, mode) {
|
|
20
|
-
try {
|
|
21
|
-
fs.access(file, mode);
|
|
22
|
-
return true;
|
|
23
|
-
} catch {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
async function ensureDirSync(dirPath) {
|
|
28
|
-
fs.mkdir(dirPath, { recursive: true });
|
|
29
|
-
}
|
|
30
|
-
async function ensureDir(dirPath) {
|
|
31
|
-
await fs.mkdir(dirPath, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
function isFileNotFoundError(error) {
|
|
34
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// src/node/utils/file/delete.ts
|
|
38
|
-
import fs2 from "fs/promises";
|
|
39
|
-
import path from "path";
|
|
40
|
-
async function emptyDirectory(dir) {
|
|
41
|
-
const deletedPaths = [];
|
|
42
|
-
const entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
43
|
-
for (const entry of entries) {
|
|
44
|
-
const fullPath = path.join(dir, entry.name);
|
|
45
|
-
if (entry.isDirectory()) {
|
|
46
|
-
deletedPaths.push(...await emptyDirectory(fullPath));
|
|
47
|
-
await fs2.rmdir(fullPath);
|
|
48
|
-
} else {
|
|
49
|
-
await fs2.unlink(fullPath);
|
|
50
|
-
}
|
|
51
|
-
deletedPaths.push(fullPath);
|
|
52
|
-
}
|
|
53
|
-
return deletedPaths;
|
|
54
|
-
}
|
|
55
|
-
async function deleteFiles(paths) {
|
|
56
|
-
const results = await Promise.allSettled(
|
|
57
|
-
paths.map(async (file) => {
|
|
58
|
-
await fs2.unlink(file);
|
|
59
|
-
return file;
|
|
60
|
-
})
|
|
61
|
-
);
|
|
62
|
-
return results.filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// src/node/utils/file/download.ts
|
|
66
|
-
import { pipeline } from "stream/promises";
|
|
67
|
-
import { createWriteStream } from "fs";
|
|
68
|
-
import path2 from "path";
|
|
69
|
-
async function downloadFile(url, filePath) {
|
|
70
|
-
const file = path2.resolve(filePath);
|
|
71
|
-
const res = await fetch(url);
|
|
72
|
-
if (!res.ok || !res.body) {
|
|
73
|
-
throw new Error(`\u4E0B\u8F7D\u5931\u8D25: ${res.status}`);
|
|
74
|
-
}
|
|
75
|
-
await pipeline(res.body, createWriteStream(file));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// src/node/utils/file/find.ts
|
|
79
|
-
import fs3 from "fs/promises";
|
|
80
|
-
import path3 from "path";
|
|
81
|
-
async function findFilesByPrefixAndSuffix(dir, prefix, suffix) {
|
|
82
|
-
return (await fs3.readdir(dir, { withFileTypes: true })).filter(
|
|
83
|
-
(dirent) => (
|
|
84
|
-
// 仅保留文件(排除子目录)
|
|
85
|
-
dirent.isFile() && // 文件名匹配前缀
|
|
86
|
-
dirent.name.startsWith(prefix || "") && // 文件名匹配后缀
|
|
87
|
-
dirent.name.endsWith(suffix || "")
|
|
88
|
-
)
|
|
89
|
-
).map((dirent) => path3.join(dir, dirent.name));
|
|
90
|
-
}
|
|
91
|
-
async function findFiles(dir, target) {
|
|
92
|
-
const results = [];
|
|
93
|
-
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
94
|
-
for (const entry of entries) {
|
|
95
|
-
const fullPath = path3.join(dir, entry.name);
|
|
96
|
-
if (entry.isDirectory()) {
|
|
97
|
-
results.push(...await findFiles(fullPath, target));
|
|
98
|
-
} else if (typeof target === "string" && entry.name === target) {
|
|
99
|
-
results.push(fullPath);
|
|
100
|
-
} else if (typeof target === "function" && target(entry.name, fullPath)) {
|
|
101
|
-
results.push(fullPath);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return results;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// src/node/utils/file/json.ts
|
|
108
|
-
import fs4 from "fs/promises";
|
|
109
|
-
import path4 from "path";
|
|
110
|
-
async function readJSONFile(filePath) {
|
|
111
|
-
if (!await isFile(filePath)) {
|
|
112
|
-
throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
113
|
-
}
|
|
114
|
-
const jsonRaw = await fs4.readFile(path4.resolve(filePath), "utf-8");
|
|
115
|
-
return JSON.parse(jsonRaw);
|
|
116
|
-
}
|
|
117
|
-
async function writeJSONFile(file, data) {
|
|
118
|
-
const tempFile = `${file}.tmp.${Date.now()}`;
|
|
119
|
-
const jsonContent = JSON.stringify(data, null, 2);
|
|
120
|
-
await fs4.writeFile(tempFile, jsonContent, "utf-8");
|
|
121
|
-
await fs4.rename(tempFile, file);
|
|
122
|
-
}
|
|
123
|
-
async function updateJSON(file, updater) {
|
|
124
|
-
const oldData = await readJSONFile(file);
|
|
125
|
-
const newData = updater(oldData);
|
|
126
|
-
await writeJSONFile(file, newData);
|
|
127
|
-
return newData;
|
|
128
|
-
}
|
|
129
|
-
async function readOrInitJSON(file, initData) {
|
|
130
|
-
try {
|
|
131
|
-
const content = await fs4.readFile(file, "utf-8");
|
|
132
|
-
return JSON.parse(content);
|
|
133
|
-
} catch (error) {
|
|
134
|
-
if (!isFileNotFoundError(error)) {
|
|
135
|
-
throw error;
|
|
136
|
-
}
|
|
137
|
-
const data = await initData();
|
|
138
|
-
await ensureDir(path4.dirname(file));
|
|
139
|
-
await fs4.writeFile(file, JSON.stringify(data, null, 2), "utf-8");
|
|
140
|
-
return data;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// src/core/utils/number/format.ts
|
|
145
|
-
function formatUnitSize(value, base, units, invalidText = "") {
|
|
146
|
-
if (!Number.isFinite(value)) {
|
|
147
|
-
return { size: 0, unit: "", text: invalidText };
|
|
148
|
-
}
|
|
149
|
-
let size = value;
|
|
150
|
-
let unitIndex = 0;
|
|
151
|
-
while (size >= base && unitIndex < units.length - 1) {
|
|
152
|
-
size /= base;
|
|
153
|
-
unitIndex++;
|
|
154
|
-
}
|
|
155
|
-
return {
|
|
156
|
-
size,
|
|
157
|
-
unit: units[unitIndex],
|
|
158
|
-
text: `${size.toFixed(2).replace(/\.?0+$/, "")} ${units[unitIndex]}`
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// src/node/utils/file/size.ts
|
|
163
|
-
import fs5 from "fs/promises";
|
|
164
|
-
import path5 from "path";
|
|
165
|
-
function formatFileSizeFromKB(sizeKB, invalidText = "") {
|
|
166
|
-
const { size, unit, text } = formatUnitSize(sizeKB, 1024, ["KB", "MB", "GB"], invalidText);
|
|
167
|
-
if (text === invalidText) {
|
|
168
|
-
return invalidText;
|
|
169
|
-
}
|
|
170
|
-
const integerPart = Math.floor(size);
|
|
171
|
-
let fractionDigits;
|
|
172
|
-
if (integerPart > 99) fractionDigits = 0;
|
|
173
|
-
else if (integerPart > 9) fractionDigits = 1;
|
|
174
|
-
else fractionDigits = 2;
|
|
175
|
-
const rounded = Math.round(size * 10 ** fractionDigits) / 10 ** fractionDigits;
|
|
176
|
-
return `${rounded} ${unit}`;
|
|
177
|
-
}
|
|
178
|
-
async function getFileSize(filePath) {
|
|
179
|
-
try {
|
|
180
|
-
const stat2 = await fs5.stat(filePath);
|
|
181
|
-
return stat2.size;
|
|
182
|
-
} catch {
|
|
183
|
-
return 0;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
async function getSymbolicLinkSize(linkPath) {
|
|
187
|
-
try {
|
|
188
|
-
const stat2 = await fs5.lstat(linkPath);
|
|
189
|
-
return stat2.size;
|
|
190
|
-
} catch {
|
|
191
|
-
return 0;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
async function getDirSize(dir) {
|
|
195
|
-
let entries;
|
|
196
|
-
try {
|
|
197
|
-
entries = await fs5.readdir(dir, { withFileTypes: true });
|
|
198
|
-
} catch {
|
|
199
|
-
return 0;
|
|
200
|
-
}
|
|
201
|
-
const tasks = [];
|
|
202
|
-
for (const entry of entries) {
|
|
203
|
-
const fullPath = path5.join(dir, entry.name);
|
|
204
|
-
if (entry.isDirectory()) {
|
|
205
|
-
tasks.push(getDirSize(fullPath));
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (entry.isSymbolicLink()) {
|
|
209
|
-
tasks.push(getSymbolicLinkSize(fullPath));
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
tasks.push(getFileSize(fullPath));
|
|
213
|
-
}
|
|
214
|
-
const results = await Promise.allSettled(tasks);
|
|
215
|
-
return results.reduce((total, result) => {
|
|
216
|
-
return total + (result.status === "fulfilled" ? result.value : 0);
|
|
217
|
-
}, 0);
|
|
218
|
-
}
|
|
219
|
-
async function getFileSizeKB(filePath) {
|
|
220
|
-
try {
|
|
221
|
-
const stat2 = await fs5.stat(filePath);
|
|
222
|
-
if (stat2.isDirectory()) {
|
|
223
|
-
return await getDirSize(filePath) / 1024;
|
|
224
|
-
} else {
|
|
225
|
-
return stat2.size / 1024;
|
|
226
|
-
}
|
|
227
|
-
} catch {
|
|
228
|
-
return 0;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// src/core/utils/error.ts
|
|
233
|
-
function createAbortError(msg) {
|
|
234
|
-
const error = new Error(msg ?? "\u64CD\u4F5C\u5DF2\u53D6\u6D88");
|
|
235
|
-
error.name = "AbortError";
|
|
236
|
-
return error;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// src/node/utils/win/cmd.ts
|
|
240
|
-
import { exec } from "child_process";
|
|
241
|
-
import * as iconv from "iconv-lite";
|
|
242
|
-
async function execWinCmd(cmd, options) {
|
|
243
|
-
return new Promise((resolve, reject) => {
|
|
244
|
-
if (options?.signal?.aborted) {
|
|
245
|
-
throw createAbortError();
|
|
246
|
-
}
|
|
247
|
-
let abortHandler = null;
|
|
248
|
-
const child = exec(`${cmd}`, { encoding: "buffer" }, (error, stdout, stderr) => {
|
|
249
|
-
const stdoutStr = iconv.decode(stdout, "cp936");
|
|
250
|
-
const stderrStr = iconv.decode(stderr, "cp936");
|
|
251
|
-
if (abortHandler) {
|
|
252
|
-
options?.signal?.removeEventListener("abort", abortHandler);
|
|
253
|
-
}
|
|
254
|
-
const exitCode = error ? Number(error.code ?? 0) : 0;
|
|
255
|
-
if (options?.codeIsSuccess ? !options.codeIsSuccess(exitCode) : exitCode !== 0) {
|
|
256
|
-
reject(new Error(`\u547D\u4EE4\u884C\u6267\u884C\u51FA\u9519 (${exitCode}): ${stderrStr || stdoutStr}`));
|
|
257
|
-
} else {
|
|
258
|
-
resolve(stdoutStr);
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
let timeout;
|
|
262
|
-
abortHandler = () => {
|
|
263
|
-
child.kill("SIGTERM");
|
|
264
|
-
timeout = setTimeout(() => {
|
|
265
|
-
child.kill("SIGKILL");
|
|
266
|
-
}, 3e3);
|
|
267
|
-
reject(createAbortError());
|
|
268
|
-
};
|
|
269
|
-
if (options?.signal) {
|
|
270
|
-
options.signal.addEventListener("abort", abortHandler);
|
|
271
|
-
}
|
|
272
|
-
child.on("close", () => {
|
|
273
|
-
if (timeout !== void 0) clearTimeout(timeout);
|
|
274
|
-
options?.signal?.removeEventListener("abort", abortHandler);
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// src/node/utils/win/copy.ts
|
|
280
|
-
import path6 from "path";
|
|
281
|
-
async function copyFile(source, destination, signal) {
|
|
282
|
-
const src = path6.resolve(source);
|
|
283
|
-
const dest = path6.resolve(destination);
|
|
284
|
-
let command;
|
|
285
|
-
if (await isFile(source)) {
|
|
286
|
-
command = `copy "${src}" "${dest}"`;
|
|
287
|
-
await execWinCmd(command, { signal });
|
|
288
|
-
} else {
|
|
289
|
-
command = `robocopy "${src}" "${dest}" /E /NFL /NDL /NJH /NJS /NC /NS /NP`;
|
|
290
|
-
await execWinCmd(command, {
|
|
291
|
-
codeIsSuccess: (number) => number < 8,
|
|
292
|
-
signal
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// src/node/utils/win/explorer.ts
|
|
298
|
-
import path7 from "path";
|
|
299
|
-
import * as fs6 from "fs/promises";
|
|
300
|
-
async function showInExplorer(fileOrDir) {
|
|
301
|
-
fileOrDir = path7.resolve(fileOrDir);
|
|
302
|
-
if ((await fs6.stat(fileOrDir)).isDirectory()) {
|
|
303
|
-
await execWinCmd(`start "" "${fileOrDir}"`);
|
|
304
|
-
} else {
|
|
305
|
-
await execWinCmd(`start "" explorer /select,"${fileOrDir}"`);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// src/node/utils/win/regedit.ts
|
|
310
|
-
import path8 from "path";
|
|
311
|
-
async function openRegedit(path9) {
|
|
312
|
-
return execWinCmd(
|
|
313
|
-
`taskkill /f /im regedit.exe & REG ADD "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit" /v "LastKey" /d "${path9}" /f & regedit`
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
async function exportRegedit(regPath, filePath) {
|
|
317
|
-
filePath = path8.resolve(filePath);
|
|
318
|
-
await ensureDir(regPath);
|
|
319
|
-
await execWinCmd(`reg export "${regPath}" "${filePath}" /y`, void 0);
|
|
320
|
-
}
|
|
321
|
-
async function importRegedit(regPath, filePath) {
|
|
322
|
-
await execWinCmd(`reg import "${path8.resolve(filePath)}"`, void 0);
|
|
323
|
-
}
|
|
324
|
-
export {
|
|
325
|
-
copyFile,
|
|
326
|
-
deleteFiles,
|
|
327
|
-
downloadFile,
|
|
328
|
-
emptyDirectory,
|
|
329
|
-
ensureDir,
|
|
330
|
-
ensureDirSync,
|
|
331
|
-
execWinCmd,
|
|
332
|
-
existsFile,
|
|
333
|
-
exportRegedit,
|
|
334
|
-
findFiles,
|
|
335
|
-
findFilesByPrefixAndSuffix,
|
|
336
|
-
formatFileSizeFromKB,
|
|
337
|
-
getDirSize,
|
|
338
|
-
getEnv,
|
|
339
|
-
getFileSizeKB,
|
|
340
|
-
importRegedit,
|
|
341
|
-
isFile,
|
|
342
|
-
isFileNotFoundError,
|
|
343
|
-
openRegedit,
|
|
344
|
-
readJSONFile,
|
|
345
|
-
readOrInitJSON,
|
|
346
|
-
showInExplorer,
|
|
347
|
-
updateJSON,
|
|
348
|
-
writeJSONFile
|
|
349
|
-
};
|
|
1
|
+
function K(){let t={};for(let r in process.env)t[r]=process.env[r];return t}import d from"fs/promises";import{mkdirSync as N}from"fs";async function u(t){try{return(await d.stat(t)).isFile()}catch{return!1}}async function Y(t,r){try{return d.access(t,r),!0}catch{return!1}}function X(t){N(t,{recursive:!0})}async function f(t){await d.mkdir(t,{recursive:!0})}function y(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}import m from"fs/promises";import O from"path";async function A(t){let r=[],e=await m.readdir(t,{withFileTypes:!0});for(let n of e){let o=O.join(t,n.name);n.isDirectory()?(r.push(...await A(o)),await m.rmdir(o)):await m.unlink(o),r.push(o)}return r}async function rt(t){return(await Promise.allSettled(t.map(async e=>(await m.unlink(e),e)))).filter(e=>e.status==="fulfilled").map(e=>e.value)}import{pipeline as M}from"stream/promises";import{createWriteStream as B}from"fs";import C from"path";async function st(t,r){let e=C.resolve(r),n=await fetch(t);if(!n.ok||!n.body)throw new Error(`\u4E0B\u8F7D\u5931\u8D25: ${n.status}`);await M(n.body,B(e))}import b from"fs/promises";import T from"path";async function ft(t,r,e){return(await b.readdir(t,{withFileTypes:!0})).filter(n=>n.isFile()&&n.name.startsWith(r||"")&&n.name.endsWith(e||"")).map(n=>T.join(t,n.name))}async function L(t,r){let e=[],n=await b.readdir(t,{withFileTypes:!0});for(let o of n){let i=T.join(t,o.name);o.isDirectory()?e.push(...await L(i,r)):(typeof r=="string"&&o.name===r||typeof r=="function"&&r(o.name,i))&&e.push(i)}return e}import c from"fs/promises";import E from"path";async function U(t){if(!await u(t))throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");let r=await c.readFile(E.resolve(t),"utf-8");return JSON.parse(r)}async function z(t,r){let e=`${t}.tmp.${Date.now()}`,n=JSON.stringify(r,null,2);await c.writeFile(e,n,"utf-8"),await c.rename(e,t)}async function dt(t,r){let e=await U(t),n=r(e);return await z(t,n),n}async function xt(t,r){try{let e=await c.readFile(t,"utf-8");return JSON.parse(e)}catch(e){if(!y(e))throw e;let n=await r();return await f(E.dirname(t)),await c.writeFile(t,JSON.stringify(n,null,2),"utf-8"),n}}function S(t,r,e,n=""){if(!Number.isFinite(t))return{size:0,unit:"",text:n};let o=t,i=0;for(;o>=r&&i<e.length-1;)o/=r,i++;return{size:o,unit:e[i],text:`${o.toFixed(2).replace(/\.?0+$/,"")} ${e[i]}`}}import p from"fs/promises";import I from"path";function Et(t,r=""){let{size:e,unit:n,text:o}=S(t,1024,["KB","MB","GB"],r);if(o===r)return r;let i=Math.floor(e),s;return i>99?s=0:i>9?s=1:s=2,`${Math.round(e*10**s)/10**s} ${n}`}async function v(t){try{return(await p.stat(t)).size}catch{return 0}}async function G(t){try{return(await p.lstat(t)).size}catch{return 0}}async function P(t){let r;try{r=await p.readdir(t,{withFileTypes:!0})}catch{return 0}let e=[];for(let o of r){let i=I.join(t,o.name);if(o.isDirectory()){e.push(P(i));continue}if(o.isSymbolicLink()){e.push(G(i));continue}e.push(v(i))}return(await Promise.allSettled(e)).reduce((o,i)=>o+(i.status==="fulfilled"?i.value:0),0)}async function St(t){try{let r=await p.stat(t);return r.isDirectory()?await P(t)/1024:r.size/1024}catch{return 0}}function x(t){let r=new Error(t??"\u64CD\u4F5C\u5DF2\u53D6\u6D88");return r.name="AbortError",r}import{exec as J}from"child_process";import*as h from"iconv-lite";async function a(t,r){return new Promise((e,n)=>{if(r?.signal?.aborted)throw x();let o=null,i=J(`${t}`,{encoding:"buffer"},(l,j,D)=>{let w=h.decode(j,"cp936"),k=h.decode(D,"cp936");o&&r?.signal?.removeEventListener("abort",o);let g=l?Number(l.code??0):0;(r?.codeIsSuccess?!r.codeIsSuccess(g):g!==0)?n(new Error(`\u547D\u4EE4\u884C\u6267\u884C\u51FA\u9519 (${g}): ${k||w}`)):e(w)}),s;o=()=>{i.kill("SIGTERM"),s=setTimeout(()=>{i.kill("SIGKILL")},3e3),n(x())},r?.signal&&r.signal.addEventListener("abort",o),i.on("close",()=>{s!==void 0&&clearTimeout(s),r?.signal?.removeEventListener("abort",o)})})}import $ from"path";async function yr(t,r,e){let n=$.resolve(t),o=$.resolve(r),i;await u(t)?(i=`copy "${n}" "${o}"`,await a(i,{signal:e})):(i=`robocopy "${n}" "${o}" /E /NFL /NDL /NJH /NJS /NC /NS /NP`,await a(i,{codeIsSuccess:s=>s<8,signal:e}))}import W from"path";import*as F from"fs/promises";async function Sr(t){t=W.resolve(t),(await F.stat(t)).isDirectory()?await a(`start "" "${t}"`):await a(`start "" explorer /select,"${t}"`)}import R from"path";async function jr(t){return a(`taskkill /f /im regedit.exe & REG ADD "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit" /v "LastKey" /d "${t}" /f & regedit`)}async function Dr(t,r){r=R.resolve(r),await f(t),await a(`reg export "${t}" "${r}" /y`,void 0)}async function kr(t,r){await a(`reg import "${R.resolve(r)}"`,void 0)}export{yr as copyFile,rt as deleteFiles,st as downloadFile,A as emptyDirectory,f as ensureDir,X as ensureDirSync,a as execWinCmd,Y as existsFile,Dr as exportRegedit,L as findFiles,ft as findFilesByPrefixAndSuffix,Et as formatFileSizeFromKB,P as getDirSize,K as getEnv,St as getFileSizeKB,kr as importRegedit,u as isFile,y as isFileNotFoundError,jr as openRegedit,U as readJSONFile,xt as readOrInitJSON,Sr as showInExplorer,dt as updateJSON,z as writeJSONFile};
|
|
350
2
|
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/node/utils/env.ts","../src/node/utils/file/base.ts","../src/node/utils/file/delete.ts","../src/node/utils/file/download.ts","../src/node/utils/file/find.ts","../src/node/utils/file/json.ts","../src/core/utils/number/format.ts","../src/node/utils/file/size.ts","../src/core/utils/error.ts","../src/node/utils/win/cmd.ts","../src/node/utils/win/copy.ts","../src/node/utils/win/explorer.ts","../src/node/utils/win/regedit.ts"],"sourcesContent":["/**\n * 获取环境变量\n */\nexport function getEnv(): { [p: string]: string } {\n const env: { [p: string]: string } = {}\n for (const envKey in process.env) {\n env[envKey] = process.env[envKey] as string\n }\n return env\n}\n","import fs from 'node:fs/promises'\n\n/**\n * 判断路径是否是文件\n */\nexport async function isFile(filePath: string) {\n try {\n return (await fs.stat(filePath)).isFile()\n } catch {\n return false\n }\n}\n\n/**\n * 判断文件是否存在\n */\nexport async function existsFile(file: string, mode?: number) {\n try {\n fs.access(file, mode)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * 确保目录存在\n */\nexport async function ensureDirSync(dirPath: string) {\n fs.mkdir(dirPath, { recursive: true })\n}\n\n/**\n * 确保目录存在\n */\nexport async function ensureDir(dirPath: string) {\n await fs.mkdir(dirPath, { recursive: true })\n}\n\n/**\n * 判断错误是否为文件不存在\n */\nexport function isFileNotFoundError(error: unknown): boolean {\n return (\n typeof error === 'object' && error !== null && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT'\n )\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * 删除指定目录下的所有文件和子目录(保留指定目录)\n * @param dir 目标目录路径\n * @return 成功删除的文件或目录路径数组\n */\nexport async function emptyDirectory(dir: string) {\n const deletedPaths: string[] = []\n // 读取目录中的所有文件和子目录\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n // 如果是目录,递归删除其内容\n deletedPaths.push(...(await emptyDirectory(fullPath)))\n // 删除空目录\n await fs.rmdir(fullPath)\n } else {\n // 如果是文件,删除文件\n await fs.unlink(fullPath)\n }\n deletedPaths.push(fullPath)\n }\n return deletedPaths\n}\n\n/**\n * 删除文件(不删除目录)\n * @param paths 文件路径列表\n * @returns 成功删除的文件路径数组\n */\nexport async function deleteFiles(paths: string[]): Promise<string[]> {\n const results = await Promise.allSettled(\n paths.map(async (file) => {\n await fs.unlink(file)\n return file\n }),\n )\n\n return results.filter((r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled').map((r) => r.value)\n}\n","import { pipeline } from 'node:stream/promises'\nimport { createWriteStream } from 'node:fs'\nimport path from 'node:path'\n\n/**\n * 下载文件\n */\nexport async function downloadFile(url: string, filePath: string) {\n const file = path.resolve(filePath)\n const res = await fetch(url)\n\n if (!res.ok || !res.body) {\n throw new Error(`下载失败: ${res.status}`)\n }\n\n await pipeline(res.body, createWriteStream(file))\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * 查找目录下符合条件的文件路径(不遍历子目录)\n * @param dir 目标目录路径\n * @param prefix 文件名前缀(需全字匹配)\n * @param suffix 文件名后缀(需全字匹配,如 \".txt\")\n * @return 符合条件的文件完整路径的数组\n */\nexport async function findFilesByPrefixAndSuffix(dir: string, prefix?: string, suffix?: string): Promise<string[]> {\n return (await fs.readdir(dir, { withFileTypes: true }))\n .filter(\n (dirent) =>\n // 仅保留文件(排除子目录)\n dirent.isFile() &&\n // 文件名匹配前缀\n dirent.name.startsWith(prefix || '') &&\n // 文件名匹配后缀\n dirent.name.endsWith(suffix || ''),\n )\n .map((dirent) => path.join(dir, dirent.name))\n}\n\n/**\n * 查找文件(包含子目录)\n * @param dir 目录\n * @param target 查找目标名称或者过滤函数\n */\nexport async function findFiles(\n dir: string,\n target: string | ((fileName: string, fullPath?: string) => boolean),\n): Promise<string[]> {\n const results: string[] = []\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...(await findFiles(fullPath, target)))\n } else if (typeof target === 'string' && entry.name === target) {\n results.push(fullPath)\n } else if (typeof target === 'function' && target(entry.name, fullPath)) {\n results.push(fullPath)\n }\n }\n return results\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { isFileNotFoundError, ensureDir, isFile } from './base.js'\n\n/**\n * 读取 JSON 文件\n */\nexport async function readJSONFile<T>(filePath: string): Promise<T> {\n if (!(await isFile(filePath))) {\n throw new Error('文件不存在')\n }\n const jsonRaw = await fs.readFile(path.resolve(filePath), 'utf-8')\n return JSON.parse(jsonRaw) as T\n}\n\n/**\n * 将数据原子写入 JSON 文件(先写临时文件再替换)\n */\nexport async function writeJSONFile<T>(file: string, data: T): Promise<void> {\n const tempFile = `${file}.tmp.${Date.now()}`\n const jsonContent = JSON.stringify(data, null, 2)\n await fs.writeFile(tempFile, jsonContent, 'utf-8')\n // 直接替换(会覆原文件)\n await fs.rename(tempFile, file)\n}\n\n/**\n * 读取 JSON 文件,修改后写回(原子操作)\n */\nexport async function updateJSON<T>(file: string, updater: (oldData: T) => T): Promise<T> {\n const oldData = await readJSONFile<T>(file)\n const newData = updater(oldData)\n await writeJSONFile(file, newData)\n return newData\n}\n\n/**\n * 从文件读取 JSON,若文件不存在则调用 initData 初始化并写入\n *\n * @param file 文件绝对路径或相对路径\n * @param initData 返回要写入的数据的异步函数\n * @returns 解析后的数据\n */\nexport async function readOrInitJSON<T>(file: string, initData: () => Promise<T>): Promise<T> {\n try {\n const content = await fs.readFile(file, 'utf-8')\n // 解析 JSON,若失败则抛出错误(不会误判为“不存在”)\n return JSON.parse(content) as T\n } catch (error) {\n // 文件不存在等其他错误要透传\n if (!isFileNotFoundError(error)) {\n throw error\n }\n // 文件不存在,生成初始数据\n const data = await initData()\n // 确保目录存在\n await ensureDir(path.dirname(file))\n // 写入文件,格式化 JSON\n await fs.writeFile(file, JSON.stringify(data, null, 2), 'utf-8')\n return data\n }\n}\n","/**\n * 格式化后的单位数据(如文件大小)\n */\nexport interface FormattedUnitSize {\n /** 换算后该单位的数值(未四舍五入) */\n size: number\n /** 单位名称,如 'MB' */\n unit: string\n /** 显示文本:数值四舍五入保留两位小数 + 单位,如 '1.50 MB' */\n text: string\n}\n\n/**\n * 按进制自动格式化单位\n * @param value 数值\n * @param base 进制(1024 / 1000)\n * @param units 单位列表\n * @param invalidText 无效值返回文本\n */\nexport function formatUnitSize(value: number, base: number, units: string[], invalidText = ''): FormattedUnitSize {\n if (!Number.isFinite(value)) {\n return { size: 0, unit: '', text: invalidText }\n }\n\n let size = value\n let unitIndex = 0\n\n while (size >= base && unitIndex < units.length - 1) {\n size /= base\n unitIndex++\n }\n\n return {\n size,\n unit: units[unitIndex],\n text: `${size.toFixed(2).replace(/\\.?0+$/, '')} ${units[unitIndex]}`,\n }\n}\n","import { formatUnitSize } from '../../../core/utils/number/format.js'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * 将 KB 文件大小格式化为字符串\n * @param sizeKB 文件大小(单位:KB)\n * @param invalidText 输入无效时返回文本\n */\nexport function formatFileSizeFromKB(sizeKB: number, invalidText: string = '') {\n const { size, unit, text } = formatUnitSize(sizeKB, 1024, ['KB', 'MB', 'GB'], invalidText)\n\n if (text === invalidText) {\n return invalidText\n }\n\n // 整数部分\n const integerPart = Math.floor(size)\n\n // 根据整数位数动态控制小数位\n let fractionDigits: number\n // 整数位 >= 3:不保留小数\n if (integerPart > 99) fractionDigits = 0\n // 整数位 = 2:保留 1 位小数\n else if (integerPart > 9) fractionDigits = 1\n // 整数位 = 1:保留 2 位小数\n else fractionDigits = 2\n\n const rounded = Math.round(size * 10 ** fractionDigits) / 10 ** fractionDigits\n return `${rounded} ${unit}`\n}\n\n/**\n * 获取单个文件的大小(字节)\n * @param filePath 文件路径\n * @returns 文件大小,读取失败返回 0\n */\nasync function getFileSize(filePath: string): Promise<number> {\n try {\n const stat = await fs.stat(filePath)\n return stat.size\n } catch {\n // 权限不足或文件不存在时忽略,当作 0\n return 0\n }\n}\n\n/**\n * 获取符号链接自身的大小(指向路径的字符串长度,不是目标文件大小)\n */\nasync function getSymbolicLinkSize(linkPath: string): Promise<number> {\n try {\n const stat = await fs.lstat(linkPath)\n return stat.size\n } catch {\n return 0\n }\n}\n\n/**\n * 获取目录总大小(字节),递归统计所有子文件\n * 符号链接:只计算链接文件本身的大小,不跟随指向目录\n * 权限错误:跳过该文件/目录,大小计为 0\n * @param dir 目录路径\n * @returns 总字节数\n */\nexport async function getDirSize(dir: string): Promise<number> {\n let entries\n try {\n entries = await fs.readdir(dir, { withFileTypes: true })\n } catch {\n // 无法读取目录时忽略,计为 0\n return 0\n }\n\n const tasks: Promise<number>[] = []\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n\n // 目录:递归\n if (entry.isDirectory()) {\n tasks.push(getDirSize(fullPath))\n continue\n }\n\n // 符号链接:只计算链接自身大小(不跟随)\n if (entry.isSymbolicLink()) {\n tasks.push(getSymbolicLinkSize(fullPath))\n continue\n }\n\n // 普通文件\n tasks.push(getFileSize(fullPath))\n }\n\n const results = await Promise.allSettled(tasks)\n return results.reduce((total, result) => {\n return total + (result.status === 'fulfilled' ? result.value : 0)\n }, 0)\n}\n\n/**\n * 获取文件/文件夹大小\n * @param filePath 文件/文件夹路径\n */\nexport async function getFileSizeKB(filePath: string) {\n try {\n const stat = await fs.stat(filePath)\n if (stat.isDirectory()) {\n return (await getDirSize(filePath)) / 1024\n } else {\n return stat.size / 1024\n }\n } catch {\n return 0\n }\n}\n","import { CommonError } from '../error/common-error.js'\n\n/**\n * 是否为取消操作的错误\n */\nexport function isCanceledError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n\n return err.name === 'AbortError' || err.name === 'CanceledError' || ('code' in err && err.code === 'ERR_CANCELED')\n}\n\n/**\n * 创建取消错误\n * @param msg\n */\nexport function createAbortError(msg?: string): Error {\n const error = new Error(msg ?? '操作已取消')\n error.name = 'AbortError'\n return error\n}\n\n/**\n * 是否为通用错误对象\n */\nexport function isCommonError(error: unknown): error is CommonError {\n return error instanceof CommonError || (error instanceof Error && error.name === 'CommonError')\n}\n\n/**\n * 给错误消息添加前缀(空格隔开)\n */\nfunction prefixError(error: Error, prefix?: string): Error {\n if (!prefix) return error\n\n error.message = `${prefix} ${error.message}`\n return error\n}\n\n/**\n * 转换到通用异常\n */\nexport function convertToCommonError(error: unknown, prefix?: string) {\n if (isCommonError(error)) {\n return prefixError(error, prefix)\n }\n return new CommonError(`${prefix} ${getErrorMessage(error)}`, error)\n}\n\n/**\n * 获取异常信息\n */\nexport function getErrorMessage(error: unknown): string {\n if (typeof error === 'string') {\n return error\n }\n if (error instanceof Error) {\n return error.message\n }\n if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n return error.message\n }\n return String(error) ?? '未知错误'\n}\n","import { createAbortError } from '../../../core/index.js'\nimport { exec } from 'node:child_process'\nimport * as iconv from 'iconv-lite'\n\n/**\n * 命令执行的选项\n */\ninterface ExecOptions {\n /**\n * 判断结果码是否成功\n * @param number 结果码\n */\n codeIsSuccess?: (number: number) => boolean\n /**\n * 终止信号\n */\n signal?: AbortSignal\n}\n\n/**\n * 执行 windows 的命令\n * @param cmd 命令\n * @param options 执行选项\n */\nexport async function execWinCmd(cmd: string, options?: ExecOptions) {\n return new Promise<string>((resolve, reject) => {\n // 前置检查:如果 signal 已终止,直接拒绝\n if (options?.signal?.aborted) {\n throw createAbortError()\n }\n let abortHandler: (() => void) | null = null\n\n const child = exec(`${cmd}`, { encoding: 'buffer' }, (error, stdout, stderr) => {\n const stdoutStr = iconv.decode(stdout, 'cp936')\n const stderrStr = iconv.decode(stderr, 'cp936')\n\n // 清理 abort 监听\n if (abortHandler) {\n options?.signal?.removeEventListener('abort', abortHandler)\n }\n\n const exitCode = error ? Number((error as NodeJS.ErrnoException).code ?? 0) : 0\n if (options?.codeIsSuccess ? !options.codeIsSuccess(exitCode) : exitCode !== 0) {\n reject(new Error(`命令行执行出错 (${exitCode}): ${stderrStr || stdoutStr}`))\n } else {\n resolve(stdoutStr)\n }\n })\n // 进程取消后超时响应的处理定时器\n let timeout: ReturnType<typeof setTimeout>\n // 定义 abort 处理函数\n abortHandler = () => {\n // 请求终止,进程可拒绝或延迟\n child.kill('SIGTERM')\n timeout = setTimeout(() => {\n child.kill('SIGKILL')\n }, 3000)\n reject(createAbortError())\n }\n // 注册 abort 监听\n if (options?.signal) {\n options.signal.addEventListener('abort', abortHandler)\n }\n\n // 进程意外终止处理\n child.on('close', () => {\n if (timeout !== undefined) clearTimeout(timeout)\n options?.signal?.removeEventListener('abort', abortHandler)\n })\n })\n}\n","import path from 'node:path'\nimport { execWinCmd } from './cmd.js'\nimport { isFile } from '../file/base.js'\n\n/**\n * 文件拷贝工具,支持文件或文件夹\n */\nexport async function copyFile(source: string, destination: string, signal?: AbortSignal) {\n const src = path.resolve(source)\n const dest = path.resolve(destination)\n\n let command: string\n\n if (await isFile(source)) {\n // 文件用 copy 命令\n command = `copy \"${src}\" \"${dest}\"`\n await execWinCmd(command, { signal: signal })\n } else {\n // 文件夹用 robocopy\n command = `robocopy \"${src}\" \"${dest}\" /E /NFL /NDL /NJH /NJS /NC /NS /NP`\n await execWinCmd(command, {\n codeIsSuccess: (number) => number < 8,\n signal: signal,\n })\n }\n}\n","import path from 'node:path'\nimport * as fs from 'node:fs/promises'\nimport { execWinCmd } from './cmd.js'\n\n/**\n * 打开资源管理器并定位到目录或者文件\n * @param fileOrDir 定位的目录或文件\n */\nexport async function showInExplorer(fileOrDir: string) {\n fileOrDir = path.resolve(fileOrDir)\n if ((await fs.stat(fileOrDir)).isDirectory()) {\n await execWinCmd(`start \"\" \"${fileOrDir}\"`)\n } else {\n await execWinCmd(`start \"\" explorer /select,\"${fileOrDir}\"`)\n }\n}\n","import { execWinCmd } from './cmd.js'\nimport path from 'node:path'\nimport { ensureDir } from '../file/base.js'\n\n/**\n * 打开注册表\n * @param path 显示的路径\n */\nexport async function openRegedit(path: string) {\n return execWinCmd(\n `taskkill /f /im regedit.exe & REG ADD \"HKCU\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Applets\\\\Regedit\" /v \"LastKey\" /d \"${path}\" /f & regedit`,\n )\n}\n\n/**\n * 导出注册表\n * @param regPath 注册表路径\n * @param filePath 导出的文件路径\n */\nexport async function exportRegedit(regPath: string, filePath: string) {\n filePath = path.resolve(filePath)\n await ensureDir(regPath)\n await execWinCmd(`reg export \"${regPath}\" \"${filePath}\" /y`, undefined)\n}\n\n/**\n * 导入注册表\n * @param regPath 注册表路径\n * @param filePath 导入的文件路径\n */\nexport async function importRegedit(regPath: string, filePath: string) {\n await execWinCmd(`reg import \"${path.resolve(filePath)}\"`, undefined)\n}\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","fs","path","fs","path","stat","path","path","path","fs","path","path","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/node/utils/env.ts","../src/node/utils/file/base.ts","../src/node/utils/file/delete.ts","../src/node/utils/file/download.ts","../src/node/utils/file/find.ts","../src/node/utils/file/json.ts","../src/core/utils/number/format.ts","../src/node/utils/file/size.ts","../src/core/utils/error.ts","../src/node/utils/win/cmd.ts","../src/node/utils/win/copy.ts","../src/node/utils/win/explorer.ts","../src/node/utils/win/regedit.ts"],"sourcesContent":["/**\n * 获取环境变量\n */\nexport function getEnv(): { [p: string]: string } {\n const env: { [p: string]: string } = {}\n for (const envKey in process.env) {\n env[envKey] = process.env[envKey] as string\n }\n return env\n}\n","import fs from 'node:fs/promises'\nimport { mkdirSync } from 'node:fs'\n\n/**\n * 判断路径是否是文件\n */\nexport async function isFile(filePath: string) {\n try {\n return (await fs.stat(filePath)).isFile()\n } catch {\n return false\n }\n}\n\n/**\n * 判断文件是否存在\n */\nexport async function existsFile(file: string, mode?: number) {\n try {\n fs.access(file, mode)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * 确保目录存在\n */\nexport function ensureDirSync(dirPath: string) {\n mkdirSync(dirPath, { recursive: true })\n}\n\n/**\n * 确保目录存在\n */\nexport async function ensureDir(dirPath: string) {\n await fs.mkdir(dirPath, { recursive: true })\n}\n\n/**\n * 判断错误是否为文件不存在\n */\nexport function isFileNotFoundError(error: unknown): boolean {\n return (\n typeof error === 'object' && error !== null && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT'\n )\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * 删除指定目录下的所有文件和子目录(保留指定目录)\n * @param dir 目标目录路径\n * @return 成功删除的文件或目录路径数组\n */\nexport async function emptyDirectory(dir: string) {\n const deletedPaths: string[] = []\n // 读取目录中的所有文件和子目录\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n // 如果是目录,递归删除其内容\n deletedPaths.push(...(await emptyDirectory(fullPath)))\n // 删除空目录\n await fs.rmdir(fullPath)\n } else {\n // 如果是文件,删除文件\n await fs.unlink(fullPath)\n }\n deletedPaths.push(fullPath)\n }\n return deletedPaths\n}\n\n/**\n * 删除文件(不删除目录)\n * @param paths 文件路径列表\n * @returns 成功删除的文件路径数组\n */\nexport async function deleteFiles(paths: string[]): Promise<string[]> {\n const results = await Promise.allSettled(\n paths.map(async (file) => {\n await fs.unlink(file)\n return file\n }),\n )\n\n return results.filter((r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled').map((r) => r.value)\n}\n","import { pipeline } from 'node:stream/promises'\nimport { createWriteStream } from 'node:fs'\nimport path from 'node:path'\n\n/**\n * 下载文件\n */\nexport async function downloadFile(url: string, filePath: string) {\n const file = path.resolve(filePath)\n const res = await fetch(url)\n\n if (!res.ok || !res.body) {\n throw new Error(`下载失败: ${res.status}`)\n }\n\n await pipeline(res.body, createWriteStream(file))\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * 查找目录下符合条件的文件路径(不遍历子目录)\n * @param dir 目标目录路径\n * @param prefix 文件名前缀(需全字匹配)\n * @param suffix 文件名后缀(需全字匹配,如 \".txt\")\n * @return 符合条件的文件完整路径的数组\n */\nexport async function findFilesByPrefixAndSuffix(dir: string, prefix?: string, suffix?: string): Promise<string[]> {\n return (await fs.readdir(dir, { withFileTypes: true }))\n .filter(\n (dirent) =>\n // 仅保留文件(排除子目录)\n dirent.isFile() &&\n // 文件名匹配前缀\n dirent.name.startsWith(prefix || '') &&\n // 文件名匹配后缀\n dirent.name.endsWith(suffix || ''),\n )\n .map((dirent) => path.join(dir, dirent.name))\n}\n\n/**\n * 查找文件(包含子目录)\n * @param dir 目录\n * @param target 查找目标名称或者过滤函数\n */\nexport async function findFiles(\n dir: string,\n target: string | ((fileName: string, fullPath?: string) => boolean),\n): Promise<string[]> {\n const results: string[] = []\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...(await findFiles(fullPath, target)))\n } else if (typeof target === 'string' && entry.name === target) {\n results.push(fullPath)\n } else if (typeof target === 'function' && target(entry.name, fullPath)) {\n results.push(fullPath)\n }\n }\n return results\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { isFileNotFoundError, ensureDir, isFile } from './base.js'\n\n/**\n * 读取 JSON 文件\n */\nexport async function readJSONFile<T>(filePath: string): Promise<T> {\n if (!(await isFile(filePath))) {\n throw new Error('文件不存在')\n }\n const jsonRaw = await fs.readFile(path.resolve(filePath), 'utf-8')\n return JSON.parse(jsonRaw) as T\n}\n\n/**\n * 将数据原子写入 JSON 文件(先写临时文件再替换)\n */\nexport async function writeJSONFile<T>(file: string, data: T): Promise<void> {\n const tempFile = `${file}.tmp.${Date.now()}`\n const jsonContent = JSON.stringify(data, null, 2)\n await fs.writeFile(tempFile, jsonContent, 'utf-8')\n // 直接替换(会覆原文件)\n await fs.rename(tempFile, file)\n}\n\n/**\n * 读取 JSON 文件,修改后写回(原子操作)\n */\nexport async function updateJSON<T>(file: string, updater: (oldData: T) => T): Promise<T> {\n const oldData = await readJSONFile<T>(file)\n const newData = updater(oldData)\n await writeJSONFile(file, newData)\n return newData\n}\n\n/**\n * 从文件读取 JSON,若文件不存在则调用 initData 初始化并写入\n *\n * @param file 文件绝对路径或相对路径\n * @param initData 返回要写入的数据的异步函数\n * @returns 解析后的数据\n */\nexport async function readOrInitJSON<T>(file: string, initData: () => Promise<T>): Promise<T> {\n try {\n const content = await fs.readFile(file, 'utf-8')\n // 解析 JSON,若失败则抛出错误(不会误判为“不存在”)\n return JSON.parse(content) as T\n } catch (error) {\n // 文件不存在等其他错误要透传\n if (!isFileNotFoundError(error)) {\n throw error\n }\n // 文件不存在,生成初始数据\n const data = await initData()\n // 确保目录存在\n await ensureDir(path.dirname(file))\n // 写入文件,格式化 JSON\n await fs.writeFile(file, JSON.stringify(data, null, 2), 'utf-8')\n return data\n }\n}\n","/**\n * 格式化后的单位数据(如文件大小)\n */\nexport interface FormattedUnitSize {\n /** 换算后该单位的数值(未四舍五入) */\n size: number\n /** 单位名称,如 'MB' */\n unit: string\n /** 显示文本:数值四舍五入保留两位小数 + 单位,如 '1.50 MB' */\n text: string\n}\n\n/**\n * 按进制自动格式化单位\n * @param value 数值\n * @param base 进制(1024 / 1000)\n * @param units 单位列表\n * @param invalidText 无效值返回文本\n */\nexport function formatUnitSize(value: number, base: number, units: string[], invalidText = ''): FormattedUnitSize {\n if (!Number.isFinite(value)) {\n return { size: 0, unit: '', text: invalidText }\n }\n\n let size = value\n let unitIndex = 0\n\n while (size >= base && unitIndex < units.length - 1) {\n size /= base\n unitIndex++\n }\n\n return {\n size,\n unit: units[unitIndex],\n text: `${size.toFixed(2).replace(/\\.?0+$/, '')} ${units[unitIndex]}`,\n }\n}\n","import { formatUnitSize } from '../../../core/utils/number/format.js'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * 将 KB 文件大小格式化为字符串\n * @param sizeKB 文件大小(单位:KB)\n * @param invalidText 输入无效时返回文本\n */\nexport function formatFileSizeFromKB(sizeKB: number, invalidText: string = '') {\n const { size, unit, text } = formatUnitSize(sizeKB, 1024, ['KB', 'MB', 'GB'], invalidText)\n\n if (text === invalidText) {\n return invalidText\n }\n\n // 整数部分\n const integerPart = Math.floor(size)\n\n // 根据整数位数动态控制小数位\n let fractionDigits: number\n // 整数位 >= 3:不保留小数\n if (integerPart > 99) fractionDigits = 0\n // 整数位 = 2:保留 1 位小数\n else if (integerPart > 9) fractionDigits = 1\n // 整数位 = 1:保留 2 位小数\n else fractionDigits = 2\n\n const rounded = Math.round(size * 10 ** fractionDigits) / 10 ** fractionDigits\n return `${rounded} ${unit}`\n}\n\n/**\n * 获取单个文件的大小(字节)\n * @param filePath 文件路径\n * @returns 文件大小,读取失败返回 0\n */\nasync function getFileSize(filePath: string): Promise<number> {\n try {\n const stat = await fs.stat(filePath)\n return stat.size\n } catch {\n // 权限不足或文件不存在时忽略,当作 0\n return 0\n }\n}\n\n/**\n * 获取符号链接自身的大小(指向路径的字符串长度,不是目标文件大小)\n */\nasync function getSymbolicLinkSize(linkPath: string): Promise<number> {\n try {\n const stat = await fs.lstat(linkPath)\n return stat.size\n } catch {\n return 0\n }\n}\n\n/**\n * 获取目录总大小(字节),递归统计所有子文件\n * 符号链接:只计算链接文件本身的大小,不跟随指向目录\n * 权限错误:跳过该文件/目录,大小计为 0\n * @param dir 目录路径\n * @returns 总字节数\n */\nexport async function getDirSize(dir: string): Promise<number> {\n let entries\n try {\n entries = await fs.readdir(dir, { withFileTypes: true })\n } catch {\n // 无法读取目录时忽略,计为 0\n return 0\n }\n\n const tasks: Promise<number>[] = []\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n\n // 目录:递归\n if (entry.isDirectory()) {\n tasks.push(getDirSize(fullPath))\n continue\n }\n\n // 符号链接:只计算链接自身大小(不跟随)\n if (entry.isSymbolicLink()) {\n tasks.push(getSymbolicLinkSize(fullPath))\n continue\n }\n\n // 普通文件\n tasks.push(getFileSize(fullPath))\n }\n\n const results = await Promise.allSettled(tasks)\n return results.reduce((total, result) => {\n return total + (result.status === 'fulfilled' ? result.value : 0)\n }, 0)\n}\n\n/**\n * 获取文件/文件夹大小\n * @param filePath 文件/文件夹路径\n */\nexport async function getFileSizeKB(filePath: string) {\n try {\n const stat = await fs.stat(filePath)\n if (stat.isDirectory()) {\n return (await getDirSize(filePath)) / 1024\n } else {\n return stat.size / 1024\n }\n } catch {\n return 0\n }\n}\n","import { CommonError } from '../error/common-error.js'\n\n/**\n * 是否为取消操作的错误\n */\nexport function isCanceledError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n\n return err.name === 'AbortError' || err.name === 'CanceledError' || ('code' in err && err.code === 'ERR_CANCELED')\n}\n\n/**\n * 创建取消错误\n * @param msg\n */\nexport function createAbortError(msg?: string): Error {\n const error = new Error(msg ?? '操作已取消')\n error.name = 'AbortError'\n return error\n}\n\n/**\n * 是否为通用错误对象\n */\nexport function isCommonError(error: unknown): error is CommonError {\n return error instanceof CommonError || (error instanceof Error && error.name === 'CommonError')\n}\n\n/**\n * 给错误消息添加前缀(空格隔开)\n */\nfunction prefixError(error: Error, prefix?: string): Error {\n if (!prefix) return error\n\n error.message = `${prefix} ${error.message}`\n return error\n}\n\n/**\n * 转换到通用异常\n */\nexport function convertToCommonError(error: unknown, prefix?: string) {\n if (isCommonError(error)) {\n return prefixError(error, prefix)\n }\n return new CommonError(`${prefix} ${getErrorMessage(error)}`, error)\n}\n\n/**\n * 获取异常信息\n */\nexport function getErrorMessage(error: unknown): string {\n if (typeof error === 'string') {\n return error\n }\n if (error instanceof Error) {\n return error.message\n }\n if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n return error.message\n }\n return String(error) ?? '未知错误'\n}\n","import { createAbortError } from '../../../core/index.js'\nimport { exec } from 'node:child_process'\nimport * as iconv from 'iconv-lite'\n\n/**\n * 命令执行的选项\n */\ninterface ExecOptions {\n /**\n * 判断结果码是否成功\n * @param number 结果码\n */\n codeIsSuccess?: (number: number) => boolean\n /**\n * 终止信号\n */\n signal?: AbortSignal\n}\n\n/**\n * 执行 windows 的命令\n * @param cmd 命令\n * @param options 执行选项\n */\nexport async function execWinCmd(cmd: string, options?: ExecOptions) {\n return new Promise<string>((resolve, reject) => {\n // 前置检查:如果 signal 已终止,直接拒绝\n if (options?.signal?.aborted) {\n throw createAbortError()\n }\n let abortHandler: (() => void) | null = null\n\n const child = exec(`${cmd}`, { encoding: 'buffer' }, (error, stdout, stderr) => {\n const stdoutStr = iconv.decode(stdout, 'cp936')\n const stderrStr = iconv.decode(stderr, 'cp936')\n\n // 清理 abort 监听\n if (abortHandler) {\n options?.signal?.removeEventListener('abort', abortHandler)\n }\n\n const exitCode = error ? Number((error as NodeJS.ErrnoException).code ?? 0) : 0\n if (options?.codeIsSuccess ? !options.codeIsSuccess(exitCode) : exitCode !== 0) {\n reject(new Error(`命令行执行出错 (${exitCode}): ${stderrStr || stdoutStr}`))\n } else {\n resolve(stdoutStr)\n }\n })\n // 进程取消后超时响应的处理定时器\n let timeout: ReturnType<typeof setTimeout>\n // 定义 abort 处理函数\n abortHandler = () => {\n // 请求终止,进程可拒绝或延迟\n child.kill('SIGTERM')\n timeout = setTimeout(() => {\n child.kill('SIGKILL')\n }, 3000)\n reject(createAbortError())\n }\n // 注册 abort 监听\n if (options?.signal) {\n options.signal.addEventListener('abort', abortHandler)\n }\n\n // 进程意外终止处理\n child.on('close', () => {\n if (timeout !== undefined) clearTimeout(timeout)\n options?.signal?.removeEventListener('abort', abortHandler)\n })\n })\n}\n","import path from 'node:path'\nimport { execWinCmd } from './cmd.js'\nimport { isFile } from '../file/base.js'\n\n/**\n * 文件拷贝工具,支持文件或文件夹\n */\nexport async function copyFile(source: string, destination: string, signal?: AbortSignal) {\n const src = path.resolve(source)\n const dest = path.resolve(destination)\n\n let command: string\n\n if (await isFile(source)) {\n // 文件用 copy 命令\n command = `copy \"${src}\" \"${dest}\"`\n await execWinCmd(command, { signal: signal })\n } else {\n // 文件夹用 robocopy\n command = `robocopy \"${src}\" \"${dest}\" /E /NFL /NDL /NJH /NJS /NC /NS /NP`\n await execWinCmd(command, {\n codeIsSuccess: (number) => number < 8,\n signal: signal,\n })\n }\n}\n","import path from 'node:path'\nimport * as fs from 'node:fs/promises'\nimport { execWinCmd } from './cmd.js'\n\n/**\n * 打开资源管理器并定位到目录或者文件\n * @param fileOrDir 定位的目录或文件\n */\nexport async function showInExplorer(fileOrDir: string) {\n fileOrDir = path.resolve(fileOrDir)\n if ((await fs.stat(fileOrDir)).isDirectory()) {\n await execWinCmd(`start \"\" \"${fileOrDir}\"`)\n } else {\n await execWinCmd(`start \"\" explorer /select,\"${fileOrDir}\"`)\n }\n}\n","import { execWinCmd } from './cmd.js'\nimport path from 'node:path'\nimport { ensureDir } from '../file/base.js'\n\n/**\n * 打开注册表\n * @param path 显示的路径\n */\nexport async function openRegedit(path: string) {\n return execWinCmd(\n `taskkill /f /im regedit.exe & REG ADD \"HKCU\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Applets\\\\Regedit\" /v \"LastKey\" /d \"${path}\" /f & regedit`,\n )\n}\n\n/**\n * 导出注册表\n * @param regPath 注册表路径\n * @param filePath 导出的文件路径\n */\nexport async function exportRegedit(regPath: string, filePath: string) {\n filePath = path.resolve(filePath)\n await ensureDir(regPath)\n await execWinCmd(`reg export \"${regPath}\" \"${filePath}\" /y`, undefined)\n}\n\n/**\n * 导入注册表\n * @param regPath 注册表路径\n * @param filePath 导入的文件路径\n */\nexport async function importRegedit(regPath: string, filePath: string) {\n await execWinCmd(`reg import \"${path.resolve(filePath)}\"`, undefined)\n}\n"],"mappings":"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","names":["getEnv","env","envKey","fs","mkdirSync","isFile","filePath","existsFile","file","mode","ensureDirSync","dirPath","ensureDir","isFileNotFoundError","error","fs","path","emptyDirectory","dir","deletedPaths","entries","entry","fullPath","deleteFiles","paths","file","r","pipeline","createWriteStream","path","downloadFile","url","filePath","file","res","fs","path","findFilesByPrefixAndSuffix","dir","prefix","suffix","dirent","findFiles","target","results","entries","entry","fullPath","fs","path","readJSONFile","filePath","isFile","jsonRaw","fs","path","writeJSONFile","file","data","tempFile","jsonContent","updateJSON","updater","oldData","newData","readOrInitJSON","initData","content","error","isFileNotFoundError","ensureDir","formatUnitSize","value","base","units","invalidText","size","unitIndex","fs","path","formatFileSizeFromKB","sizeKB","invalidText","size","unit","text","formatUnitSize","integerPart","fractionDigits","getFileSize","filePath","getSymbolicLinkSize","linkPath","getDirSize","dir","entries","tasks","entry","fullPath","total","result","getFileSizeKB","stat","createAbortError","msg","error","exec","iconv","execWinCmd","cmd","options","resolve","reject","createAbortError","abortHandler","child","error","stdout","stderr","stdoutStr","stderrStr","exitCode","timeout","path","copyFile","source","destination","signal","src","path","dest","command","isFile","execWinCmd","number","path","fs","showInExplorer","fileOrDir","path","execWinCmd","path","openRegedit","path","execWinCmd","exportRegedit","regPath","filePath","ensureDir","importRegedit"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ybgnb/utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"author": "hzhilong",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "自用工具库",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"lint": "eslint . --fix",
|
|
34
34
|
"format": "prettier --write .",
|
|
35
35
|
"barrelsby": "barrelsby -c barrelsby.json & import-suffixer \"src/**/*.ts\"",
|
|
36
|
-
"build": "
|
|
36
|
+
"build": "tsx ./scripts/build.ts",
|
|
37
37
|
"npm login": "npm login",
|
|
38
38
|
"npm publish": "npm publish --access public"
|
|
39
39
|
},
|