@ainc/fs 0.1.20 → 0.1.22
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 +5 -19
- package/bin/download.mjs +76 -0
- package/dist/concurrency.d.ts +43 -0
- package/dist/concurrency.js +99 -0
- package/dist/dirname.js +3 -2
- package/dist/download.d.ts +30 -0
- package/dist/download.js +205 -0
- package/dist/downloadFile.d.ts +6 -0
- package/dist/downloadFile.js +83 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +12 -6
- package/dist/jsonc.js +1 -1
- package/dist/md5.d.ts +6 -0
- package/dist/md5.js +23 -0
- package/dist/relative.d.ts +7 -1
- package/dist/resolveFile.js +6 -8
- package/dist/retry.d.ts +6 -0
- package/dist/retry.js +50 -0
- package/dist/stat.d.ts +1 -1
- package/esm/concurrency.mjs +96 -0
- package/esm/copy.mjs +49 -0
- package/esm/dirname.mjs +26 -0
- package/esm/download.mjs +203 -0
- package/esm/downloadFile.mjs +81 -0
- package/esm/findUp.mjs +33 -0
- package/esm/index.mjs +26 -0
- package/esm/jsonc.mjs +29 -0
- package/esm/lookup.mjs +48 -0
- package/esm/md5.mjs +21 -0
- package/esm/mkdir.mjs +37 -0
- package/esm/readFile.mjs +21 -0
- package/esm/relative.mjs +66 -0
- package/esm/resolveFile.mjs +36 -0
- package/esm/retry.mjs +48 -0
- package/esm/rm.mjs +32 -0
- package/esm/stat.mjs +45 -0
- package/esm/stripComments.mjs +118 -0
- package/esm/writeFile.mjs +39 -0
- package/package.json +30 -10
- /package/dist/{utils/stripComments.d.ts → stripComments.d.ts} +0 -0
- /package/dist/{utils/stripComments.js → stripComments.js} +0 -0
package/README.md
CHANGED
|
@@ -1,26 +1,12 @@
|
|
|
1
|
-
# @ainc/
|
|
2
|
-
Let's do something nice with @ainc/
|
|
1
|
+
# @ainc/fs
|
|
2
|
+
Let's do something nice with @ainc/fs!
|
|
3
3
|
|
|
4
4
|
## Install
|
|
5
5
|
``` shell
|
|
6
|
-
$
|
|
6
|
+
$ pnpm add @ainc/fs
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## Usage
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## VS Code
|
|
15
|
-
``` json
|
|
16
|
-
{
|
|
17
|
-
"name": "Launch Script",
|
|
18
|
-
"type": "node",
|
|
19
|
-
"request": "launch",
|
|
20
|
-
"autoAttachChildProcesses": true,
|
|
21
|
-
"runtimeExecutable": "esb",
|
|
22
|
-
"args": [
|
|
23
|
-
"${file}"
|
|
24
|
-
]
|
|
25
|
-
}
|
|
10
|
+
``` javascript
|
|
11
|
+
import * as fs from '@ainc/fs';
|
|
26
12
|
```
|
package/bin/download.mjs
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*****************************************
|
|
3
|
+
* Created by edonet@163.com
|
|
4
|
+
* Created on 2026-03-01 12:25:56
|
|
5
|
+
*****************************************
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
*****************************************
|
|
12
|
+
* 加载依赖
|
|
13
|
+
*****************************************
|
|
14
|
+
*/
|
|
15
|
+
import { downloadFile } from '../esm/downloadFile.mjs';
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
*****************************************
|
|
20
|
+
* 执行脚本
|
|
21
|
+
*****************************************
|
|
22
|
+
*/
|
|
23
|
+
main();
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
*****************************************
|
|
28
|
+
* 主函数
|
|
29
|
+
*****************************************
|
|
30
|
+
*/
|
|
31
|
+
async function main() {
|
|
32
|
+
const args = process.argv.slice(2);
|
|
33
|
+
if (!args.length) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 下载多个文件
|
|
38
|
+
if (isURL(args[0])) {
|
|
39
|
+
return Promise.all(args.map(url => downloadFile(resolvePath(url), url)));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 下载文件
|
|
43
|
+
if (isURL(args[1])) {
|
|
44
|
+
await downloadFile(args[0], args[1]);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
*****************************************
|
|
51
|
+
* 判断是否为 URL
|
|
52
|
+
*****************************************
|
|
53
|
+
* @param {string} url
|
|
54
|
+
*/
|
|
55
|
+
function isURL(url) {
|
|
56
|
+
if (url) {
|
|
57
|
+
return url.startsWith('http://') || url.startsWith('https://');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
*****************************************
|
|
64
|
+
* 解析下载路径
|
|
65
|
+
*****************************************
|
|
66
|
+
* @param {string} url
|
|
67
|
+
*/
|
|
68
|
+
function resolvePath(url) {
|
|
69
|
+
const idx = url.lastIndexOf('/');
|
|
70
|
+
if (idx !== -1) {
|
|
71
|
+
return '.' + url.slice(idx);
|
|
72
|
+
} else {
|
|
73
|
+
return Math.random().toString(16).slice(2);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*****************************************
|
|
3
|
+
* 任务类型
|
|
4
|
+
*****************************************
|
|
5
|
+
*/
|
|
6
|
+
export type Handler<T, P> = (data: T) => P | Promise<P>;
|
|
7
|
+
export type Result<T> = {
|
|
8
|
+
value: T | Promise<T>;
|
|
9
|
+
err?: unknown;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
*****************************************
|
|
13
|
+
* 并发控制
|
|
14
|
+
*****************************************
|
|
15
|
+
*/
|
|
16
|
+
export declare class Concurrency<T, P> {
|
|
17
|
+
/** 并发容量 */
|
|
18
|
+
private _capacity;
|
|
19
|
+
/** 处理函数 */
|
|
20
|
+
private _handler;
|
|
21
|
+
/** 待处理任务 */
|
|
22
|
+
private _pending;
|
|
23
|
+
/** 正在执行的任务数 */
|
|
24
|
+
private _running;
|
|
25
|
+
/** 结果缓存队列 */
|
|
26
|
+
private _results;
|
|
27
|
+
/** 当前值 */
|
|
28
|
+
private _value?;
|
|
29
|
+
/** 初始化 */
|
|
30
|
+
constructor(capacity: number, handler: Handler<T, P>);
|
|
31
|
+
/** 添加任务 */
|
|
32
|
+
add(data: T): this;
|
|
33
|
+
/** 执行处理 */
|
|
34
|
+
execute<R = P>(handler?: Handler<P, R>): Promise<R[]>;
|
|
35
|
+
/** 读取值 */
|
|
36
|
+
value(): P;
|
|
37
|
+
/** 执行处理 */
|
|
38
|
+
next(): Promise<boolean>;
|
|
39
|
+
/** 执行任务 */
|
|
40
|
+
private _execute;
|
|
41
|
+
/** 处理任务 */
|
|
42
|
+
private _process;
|
|
43
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*****************************************
|
|
3
|
+
* Created by edonet@163.com
|
|
4
|
+
* Created on 2026-03-14 13:42:34
|
|
5
|
+
*****************************************
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.Concurrency = void 0;
|
|
10
|
+
/**
|
|
11
|
+
*****************************************
|
|
12
|
+
* 并发控制
|
|
13
|
+
*****************************************
|
|
14
|
+
*/
|
|
15
|
+
class Concurrency {
|
|
16
|
+
/** 初始化 */
|
|
17
|
+
constructor(capacity, handler) {
|
|
18
|
+
/** 待处理任务 */
|
|
19
|
+
this._pending = [];
|
|
20
|
+
/** 正在执行的任务数 */
|
|
21
|
+
this._running = 0;
|
|
22
|
+
/** 结果缓存队列 */
|
|
23
|
+
this._results = [];
|
|
24
|
+
this._capacity = Math.max(1, capacity);
|
|
25
|
+
this._handler = handler;
|
|
26
|
+
}
|
|
27
|
+
/** 添加任务 */
|
|
28
|
+
add(data) {
|
|
29
|
+
this._pending.push(data);
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
/** 执行处理 */
|
|
33
|
+
async execute(handler) {
|
|
34
|
+
const results = [];
|
|
35
|
+
const fn = handler || (v => v);
|
|
36
|
+
// 遍历结果
|
|
37
|
+
while (await this.next()) {
|
|
38
|
+
results.push(await fn(this._value));
|
|
39
|
+
}
|
|
40
|
+
// 返回结果
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
/** 读取值 */
|
|
44
|
+
value() {
|
|
45
|
+
return this._value;
|
|
46
|
+
}
|
|
47
|
+
/** 执行处理 */
|
|
48
|
+
async next() {
|
|
49
|
+
if (this._pending.length) {
|
|
50
|
+
this._execute();
|
|
51
|
+
}
|
|
52
|
+
// 不存在结果,已经处理完
|
|
53
|
+
if (!this._results.length) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
// 等待结果
|
|
57
|
+
const res = this._results.shift();
|
|
58
|
+
await res.value;
|
|
59
|
+
if (res.err) {
|
|
60
|
+
throw res.err;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this._value = res.value;
|
|
64
|
+
}
|
|
65
|
+
// 返回结果
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
/** 执行任务 */
|
|
69
|
+
async _execute() {
|
|
70
|
+
while (this._running < this._capacity) {
|
|
71
|
+
if (this._pending.length) {
|
|
72
|
+
this._process(this._pending.shift());
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** 处理任务 */
|
|
80
|
+
_process(data) {
|
|
81
|
+
const res = {};
|
|
82
|
+
// 添加到结果集
|
|
83
|
+
this._results.push(res);
|
|
84
|
+
// 执行任务
|
|
85
|
+
try {
|
|
86
|
+
const value = res.value = this._handler(data);
|
|
87
|
+
if (value instanceof Promise) {
|
|
88
|
+
this._running++;
|
|
89
|
+
value
|
|
90
|
+
.then(val => res.value = val, err => res.err = err)
|
|
91
|
+
.finally(() => (this._running--, this._execute()));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
res.err = err;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.Concurrency = Concurrency;
|
package/dist/dirname.js
CHANGED
|
@@ -19,9 +19,10 @@ const node_path_1 = require("node:path");
|
|
|
19
19
|
*****************************************
|
|
20
20
|
*/
|
|
21
21
|
function dirname(path) {
|
|
22
|
-
const
|
|
22
|
+
const dest = (0, node_path_1.isAbsolute)(path) ? path : (0, node_path_1.resolve)(path);
|
|
23
|
+
const parent = (0, node_path_1.dirname)(dest);
|
|
23
24
|
// 校验目录
|
|
24
|
-
if (parent && parent !==
|
|
25
|
+
if (parent && parent !== dest) {
|
|
25
26
|
return parent;
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*****************************************
|
|
3
|
+
* 下载选项
|
|
4
|
+
*****************************************
|
|
5
|
+
*/
|
|
6
|
+
export interface DownloadOptions {
|
|
7
|
+
/** 并发下载数(默认 8) */
|
|
8
|
+
concurrency: number;
|
|
9
|
+
/** 每个分片大小(默认 1MB) */
|
|
10
|
+
chunkSize: number;
|
|
11
|
+
/** 重试次数(默认 3) */
|
|
12
|
+
retryTimes: number;
|
|
13
|
+
/** 是否强制下载 */
|
|
14
|
+
force?: boolean;
|
|
15
|
+
/** 下载进度 */
|
|
16
|
+
onProgress?(size: number, total: number): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
*****************************************
|
|
20
|
+
* 下载选项
|
|
21
|
+
*****************************************
|
|
22
|
+
*/
|
|
23
|
+
export interface Options extends RequestInit, Partial<DownloadOptions> {
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
*****************************************
|
|
27
|
+
* 下载文件
|
|
28
|
+
*****************************************
|
|
29
|
+
*/
|
|
30
|
+
export declare function download(dest: string, url: string | URL | Request, options?: Options): Promise<string>;
|
package/dist/download.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*****************************************
|
|
3
|
+
* Created by edonet@163.com
|
|
4
|
+
* Created on 2026-02-25 16:26:38
|
|
5
|
+
*****************************************
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.download = download;
|
|
10
|
+
/**
|
|
11
|
+
*****************************************
|
|
12
|
+
* 加载依赖
|
|
13
|
+
*****************************************
|
|
14
|
+
*/
|
|
15
|
+
const node_os_1 = require("node:os");
|
|
16
|
+
const node_path_1 = require("node:path");
|
|
17
|
+
const promises_1 = require("node:fs/promises");
|
|
18
|
+
const node_fs_1 = require("node:fs");
|
|
19
|
+
const node_stream_1 = require("node:stream");
|
|
20
|
+
const promises_2 = require("node:stream/promises");
|
|
21
|
+
const retry_1 = require("./retry");
|
|
22
|
+
const md5_1 = require("./md5");
|
|
23
|
+
const concurrency_1 = require("./concurrency");
|
|
24
|
+
/**
|
|
25
|
+
*****************************************
|
|
26
|
+
* 下载文件
|
|
27
|
+
*****************************************
|
|
28
|
+
*/
|
|
29
|
+
async function download(dest, url, options = {}) {
|
|
30
|
+
const { concurrency, chunkSize, retryTimes, force, onProgress, ...init } = options;
|
|
31
|
+
const path = (0, node_path_1.resolve)(dest);
|
|
32
|
+
const opts = {
|
|
33
|
+
concurrency: concurrency && concurrency >= 1 ? concurrency : 8,
|
|
34
|
+
chunkSize: chunkSize && chunkSize >= 1 ? chunkSize : 1 * 1024 * 1024,
|
|
35
|
+
retryTimes: retryTimes && retryTimes >= 1 ? retryTimes : 3,
|
|
36
|
+
force,
|
|
37
|
+
onProgress,
|
|
38
|
+
};
|
|
39
|
+
// 创建下载目录
|
|
40
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(path), { recursive: true });
|
|
41
|
+
// 尝试分片下载
|
|
42
|
+
if (await downloadFileByChunk(path, url, init, opts)) {
|
|
43
|
+
return path;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
return (0, retry_1.retry)(opts.retryTimes, () => downloadFile(path, url, init, opts));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
*****************************************
|
|
51
|
+
* 获取文件大小
|
|
52
|
+
*****************************************
|
|
53
|
+
*/
|
|
54
|
+
async function downloadFileByChunk(dest, url, init, options) {
|
|
55
|
+
const stats = await (0, promises_1.stat)(dest).catch(err => err.code === 'ENOENT' ? null : Promise.reject(err));
|
|
56
|
+
// 已经存在文件,不再下载
|
|
57
|
+
if (stats && stats.isFile() && !options.force) {
|
|
58
|
+
options.onProgress && options.onProgress(stats.size, stats.size);
|
|
59
|
+
return dest;
|
|
60
|
+
}
|
|
61
|
+
// 发起获取文件信息请求
|
|
62
|
+
const resp = await fetch(url, { method: 'HEAD' });
|
|
63
|
+
if (!resp.ok || resp.headers.get('accept-ranges') !== 'bytes') {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// 获取文件总大小
|
|
67
|
+
const totalSize = parseInt(resp.headers.get('content-length') || '0', 10);
|
|
68
|
+
if (!totalSize) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// 获取分片配置
|
|
72
|
+
const chunkConcurrency = new concurrency_1.Concurrency(options.concurrency, downloadChunk);
|
|
73
|
+
const chunkSize = options.chunkSize;
|
|
74
|
+
const chunkCount = Math.ceil(totalSize / chunkSize);
|
|
75
|
+
const downloadedSize = [0];
|
|
76
|
+
const downloadedTempFile = dest + '.download';
|
|
77
|
+
const dir = getDownloadDir(url, options);
|
|
78
|
+
const stream = (0, node_fs_1.createWriteStream)(downloadedTempFile);
|
|
79
|
+
// 生成任务
|
|
80
|
+
for (let idx = 0; idx < chunkCount; idx++) {
|
|
81
|
+
chunkConcurrency.add({
|
|
82
|
+
start: idx * chunkSize,
|
|
83
|
+
end: Math.min((idx + 1) * chunkSize - 1, totalSize - 1),
|
|
84
|
+
idx,
|
|
85
|
+
url,
|
|
86
|
+
dir,
|
|
87
|
+
init,
|
|
88
|
+
options,
|
|
89
|
+
totalSize,
|
|
90
|
+
downloadedSize,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// 创建下载目录
|
|
94
|
+
await (0, promises_1.mkdir)(dir, { recursive: true });
|
|
95
|
+
// 合并文件
|
|
96
|
+
await chunkConcurrency.execute(path => pipe(path, stream));
|
|
97
|
+
// 结束传输
|
|
98
|
+
stream.end();
|
|
99
|
+
// 重命名临时文件
|
|
100
|
+
await (0, promises_1.rename)(downloadedTempFile, dest);
|
|
101
|
+
await (0, promises_1.rm)(dir, { recursive: true });
|
|
102
|
+
// 返回文件路径
|
|
103
|
+
return dest;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
*****************************************
|
|
107
|
+
* 下载分片
|
|
108
|
+
*****************************************
|
|
109
|
+
*/
|
|
110
|
+
async function downloadChunk(downloadChunkOptions) {
|
|
111
|
+
const { idx, start, end, url, dir, init, options, downloadedSize, totalSize } = downloadChunkOptions;
|
|
112
|
+
const path = (0, node_path_1.join)(dir, idx + '');
|
|
113
|
+
const opts = { ...options };
|
|
114
|
+
const requestInit = {
|
|
115
|
+
...init,
|
|
116
|
+
headers: { ...init.headers, Range: `bytes=${start}-${end}` },
|
|
117
|
+
};
|
|
118
|
+
// 添加进度监听
|
|
119
|
+
if (options.onProgress) {
|
|
120
|
+
opts.onProgress = size => {
|
|
121
|
+
downloadedSize[idx] = size;
|
|
122
|
+
options.onProgress(downloadedSize.reduce((a, b) => a + b, 0), totalSize);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// 执行下载
|
|
126
|
+
return (0, retry_1.retry)(options.retryTimes, () => downloadFile(path, url, requestInit, opts));
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
*****************************************
|
|
130
|
+
* 生成下载 ID
|
|
131
|
+
*****************************************
|
|
132
|
+
*/
|
|
133
|
+
function generateDownloadID(url, options) {
|
|
134
|
+
const now = new Date();
|
|
135
|
+
now.setHours(0, 0, 0, 0);
|
|
136
|
+
return (0, md5_1.md5)(`${url}?chunkSize=${options.chunkSize || 0}&t=${now.getTime()}`);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
*****************************************
|
|
140
|
+
* 获取下载 URL
|
|
141
|
+
*****************************************
|
|
142
|
+
*/
|
|
143
|
+
function getDownloadURL(url) {
|
|
144
|
+
if (typeof url === 'string') {
|
|
145
|
+
return url;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
return url instanceof URL ? url.href : url.url;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
*****************************************
|
|
153
|
+
* 获取下载目录
|
|
154
|
+
*****************************************
|
|
155
|
+
*/
|
|
156
|
+
function getDownloadDir(url, options) {
|
|
157
|
+
return (0, node_path_1.join)((0, node_os_1.tmpdir)(), 'downloads', generateDownloadID(getDownloadURL(url), options));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
*****************************************
|
|
161
|
+
* 写入文件流
|
|
162
|
+
*****************************************
|
|
163
|
+
*/
|
|
164
|
+
function pipe(src, dest) {
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
const stream = (0, node_fs_1.createReadStream)(src);
|
|
167
|
+
stream.once('close', resolve);
|
|
168
|
+
stream.once('error', reject);
|
|
169
|
+
stream.pipe(dest, { end: false });
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
*****************************************
|
|
174
|
+
* 下载文件
|
|
175
|
+
*****************************************
|
|
176
|
+
*/
|
|
177
|
+
async function downloadFile(dest, url, init, options) {
|
|
178
|
+
const stats = await (0, promises_1.stat)(dest).catch(err => err.code === 'ENOENT' ? null : Promise.reject(err));
|
|
179
|
+
// 已经存在文件,不再下载
|
|
180
|
+
if (stats && stats.isFile() && !options.force) {
|
|
181
|
+
options.onProgress && options.onProgress(stats.size, stats.size);
|
|
182
|
+
return dest;
|
|
183
|
+
}
|
|
184
|
+
// 发送请求
|
|
185
|
+
const resp = await fetch(url, init);
|
|
186
|
+
if (!resp.ok) {
|
|
187
|
+
throw new Error(`${resp.status} ${resp.statusText}`);
|
|
188
|
+
}
|
|
189
|
+
// 设置流的大小(如果 Content-Length 头存在)
|
|
190
|
+
const totalSize = parseInt(resp.headers.get('Content-Length') || '0', 10);
|
|
191
|
+
const stream = node_stream_1.Readable.from(resp.body || '');
|
|
192
|
+
const downloadedTempFile = dest + '.download';
|
|
193
|
+
const progress = options.onProgress;
|
|
194
|
+
// 添加进度监听
|
|
195
|
+
if (progress) {
|
|
196
|
+
let size = 0;
|
|
197
|
+
stream.on('data', chunk => progress(size += chunk.length, totalSize));
|
|
198
|
+
stream.on('end', () => size <= totalSize && progress(totalSize, totalSize));
|
|
199
|
+
}
|
|
200
|
+
// 下载文件
|
|
201
|
+
await (0, promises_2.pipeline)(stream, (0, node_fs_1.createWriteStream)(downloadedTempFile));
|
|
202
|
+
await (0, promises_1.rename)(downloadedTempFile, dest);
|
|
203
|
+
// 返回文件路径
|
|
204
|
+
return dest;
|
|
205
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*****************************************
|
|
3
|
+
* Created by edonet@163.com
|
|
4
|
+
* Created on 2026-03-14 10:51:52
|
|
5
|
+
*****************************************
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.downloadFile = downloadFile;
|
|
10
|
+
/**
|
|
11
|
+
*****************************************
|
|
12
|
+
* 加载依赖
|
|
13
|
+
*****************************************
|
|
14
|
+
*/
|
|
15
|
+
const download_1 = require("./download");
|
|
16
|
+
/**
|
|
17
|
+
*****************************************
|
|
18
|
+
* 下载文件
|
|
19
|
+
*****************************************
|
|
20
|
+
*/
|
|
21
|
+
async function downloadFile(dest, url) {
|
|
22
|
+
const print = stdout();
|
|
23
|
+
// 打印日志
|
|
24
|
+
console.log('[download]:', url);
|
|
25
|
+
// 下载文件
|
|
26
|
+
await (0, download_1.download)(dest, url, {
|
|
27
|
+
onProgress(size, total) {
|
|
28
|
+
if (size < total) {
|
|
29
|
+
print(` -> ${((size / total) * 100).toFixed(2)}% (${format(size)}/${format(total)})`);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
print('[download]: done', true);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
*****************************************
|
|
39
|
+
* 格式化字节大小为易读格式(B/KB/MB/GB)
|
|
40
|
+
*****************************************
|
|
41
|
+
*/
|
|
42
|
+
function format(bytes) {
|
|
43
|
+
let limit = 1;
|
|
44
|
+
// 遍历单位
|
|
45
|
+
for (let unit of ['B', 'KB', 'MB', 'GB']) {
|
|
46
|
+
const next = 1024 * limit;
|
|
47
|
+
if (bytes < next) {
|
|
48
|
+
return `${(bytes / limit).toFixed(2)} ${unit}`;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
limit = next;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// 返回默认
|
|
55
|
+
return `${(bytes / limit).toFixed(2)} TB`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
*****************************************
|
|
59
|
+
* 进度条
|
|
60
|
+
*****************************************
|
|
61
|
+
*/
|
|
62
|
+
function stdout() {
|
|
63
|
+
let progressContent = '';
|
|
64
|
+
let progressSize = 0;
|
|
65
|
+
let progressTime = 0;
|
|
66
|
+
return function print(content, flush) {
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
if ((content === progressContent || now - progressTime < 600) && !flush) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const size = content.length;
|
|
72
|
+
const diff = progressSize - size;
|
|
73
|
+
progressContent = content;
|
|
74
|
+
progressTime = now;
|
|
75
|
+
progressSize = size;
|
|
76
|
+
if (diff > 0) {
|
|
77
|
+
process.stdout.write(`\r${content}${' '.repeat(diff)}`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
process.stdout.write(`\r${content}`);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
* 导出接口
|
|
4
4
|
*****************************************
|
|
5
5
|
*/
|
|
6
|
+
export { resolve, join, isAbsolute } from 'node:path';
|
|
7
|
+
export { relative, isRelative } from './relative';
|
|
6
8
|
export { dirname } from './dirname';
|
|
7
|
-
export { mkdir } from './mkdir';
|
|
8
9
|
export { stat, isDir, isFile } from './stat';
|
|
9
|
-
export { relative, isRelative } from './relative';
|
|
10
10
|
export { resolveFile } from './resolveFile';
|
|
11
11
|
export { readFile } from './readFile';
|
|
12
12
|
export { writeFile } from './writeFile';
|
|
13
13
|
export { copy } from './copy';
|
|
14
14
|
export { rm } from './rm';
|
|
15
15
|
export { lookup } from './lookup';
|
|
16
|
+
export { md5 } from './md5';
|
|
16
17
|
export { json, jsonc } from './jsonc';
|
|
17
18
|
export { findUp } from './findUp';
|
|
19
|
+
export { download } from './download';
|
package/dist/index.js
CHANGED
|
@@ -6,23 +6,25 @@
|
|
|
6
6
|
*/
|
|
7
7
|
'use strict';
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.findUp = exports.jsonc = exports.json = exports.lookup = exports.rm = exports.copy = exports.writeFile = exports.readFile = exports.resolveFile = exports.
|
|
9
|
+
exports.download = exports.findUp = exports.jsonc = exports.json = exports.md5 = exports.lookup = exports.rm = exports.copy = exports.writeFile = exports.readFile = exports.resolveFile = exports.isFile = exports.isDir = exports.stat = exports.dirname = exports.isRelative = exports.relative = exports.isAbsolute = exports.join = exports.resolve = void 0;
|
|
10
10
|
/**
|
|
11
11
|
*****************************************
|
|
12
12
|
* 导出接口
|
|
13
13
|
*****************************************
|
|
14
14
|
*/
|
|
15
|
+
var node_path_1 = require("node:path");
|
|
16
|
+
Object.defineProperty(exports, "resolve", { enumerable: true, get: function () { return node_path_1.resolve; } });
|
|
17
|
+
Object.defineProperty(exports, "join", { enumerable: true, get: function () { return node_path_1.join; } });
|
|
18
|
+
Object.defineProperty(exports, "isAbsolute", { enumerable: true, get: function () { return node_path_1.isAbsolute; } });
|
|
19
|
+
var relative_1 = require("./relative");
|
|
20
|
+
Object.defineProperty(exports, "relative", { enumerable: true, get: function () { return relative_1.relative; } });
|
|
21
|
+
Object.defineProperty(exports, "isRelative", { enumerable: true, get: function () { return relative_1.isRelative; } });
|
|
15
22
|
var dirname_1 = require("./dirname");
|
|
16
23
|
Object.defineProperty(exports, "dirname", { enumerable: true, get: function () { return dirname_1.dirname; } });
|
|
17
|
-
var mkdir_1 = require("./mkdir");
|
|
18
|
-
Object.defineProperty(exports, "mkdir", { enumerable: true, get: function () { return mkdir_1.mkdir; } });
|
|
19
24
|
var stat_1 = require("./stat");
|
|
20
25
|
Object.defineProperty(exports, "stat", { enumerable: true, get: function () { return stat_1.stat; } });
|
|
21
26
|
Object.defineProperty(exports, "isDir", { enumerable: true, get: function () { return stat_1.isDir; } });
|
|
22
27
|
Object.defineProperty(exports, "isFile", { enumerable: true, get: function () { return stat_1.isFile; } });
|
|
23
|
-
var relative_1 = require("./relative");
|
|
24
|
-
Object.defineProperty(exports, "relative", { enumerable: true, get: function () { return relative_1.relative; } });
|
|
25
|
-
Object.defineProperty(exports, "isRelative", { enumerable: true, get: function () { return relative_1.isRelative; } });
|
|
26
28
|
var resolveFile_1 = require("./resolveFile");
|
|
27
29
|
Object.defineProperty(exports, "resolveFile", { enumerable: true, get: function () { return resolveFile_1.resolveFile; } });
|
|
28
30
|
var readFile_1 = require("./readFile");
|
|
@@ -35,8 +37,12 @@ var rm_1 = require("./rm");
|
|
|
35
37
|
Object.defineProperty(exports, "rm", { enumerable: true, get: function () { return rm_1.rm; } });
|
|
36
38
|
var lookup_1 = require("./lookup");
|
|
37
39
|
Object.defineProperty(exports, "lookup", { enumerable: true, get: function () { return lookup_1.lookup; } });
|
|
40
|
+
var md5_1 = require("./md5");
|
|
41
|
+
Object.defineProperty(exports, "md5", { enumerable: true, get: function () { return md5_1.md5; } });
|
|
38
42
|
var jsonc_1 = require("./jsonc");
|
|
39
43
|
Object.defineProperty(exports, "json", { enumerable: true, get: function () { return jsonc_1.json; } });
|
|
40
44
|
Object.defineProperty(exports, "jsonc", { enumerable: true, get: function () { return jsonc_1.jsonc; } });
|
|
41
45
|
var findUp_1 = require("./findUp");
|
|
42
46
|
Object.defineProperty(exports, "findUp", { enumerable: true, get: function () { return findUp_1.findUp; } });
|
|
47
|
+
var download_1 = require("./download");
|
|
48
|
+
Object.defineProperty(exports, "download", { enumerable: true, get: function () { return download_1.download; } });
|
package/dist/jsonc.js
CHANGED
|
@@ -13,7 +13,7 @@ exports.jsonc = jsonc;
|
|
|
13
13
|
* 加载依赖
|
|
14
14
|
*****************************************
|
|
15
15
|
*/
|
|
16
|
-
const stripComments_1 = require("./
|
|
16
|
+
const stripComments_1 = require("./stripComments");
|
|
17
17
|
/**
|
|
18
18
|
*****************************************
|
|
19
19
|
* 解析 JSON
|