xshell 1.0.38 → 1.0.40
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/file.d.ts +5 -5
- package/file.js +45 -26
- package/i18n/i18n-scan.js +1 -1
- package/i18n/scanner/index.js +1 -1
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +13 -14
- package/path.d.ts +88 -0
- package/path.js +119 -0
- package/process.d.ts +5 -6
- package/process.js +3 -3
- package/prototype.browser.d.ts +11 -7
- package/prototype.browser.js +30 -11
- package/prototype.d.ts +9 -9
- package/prototype.js +14 -15
- package/repl.js +2 -2
- package/server.d.ts +12 -3
- package/server.js +34 -30
package/file.d.ts
CHANGED
|
@@ -79,12 +79,12 @@ export interface FListOptions {
|
|
|
79
79
|
- fpd: 文件夹完整路径 absolute path of directory
|
|
80
80
|
- options?:
|
|
81
81
|
- deep?: `false` 递归遍历 recursively
|
|
82
|
-
- absolute?: `false`
|
|
82
|
+
- absolute?: `false` 返回、打印完整路径而不是相对路径,为 false 时返回的 FStats.fp 也会被修改 Return, print full path instead of relative path, FStats.fp returned when false will also be modified
|
|
83
83
|
- print?: `true`
|
|
84
84
|
- filter?: `true` RegExp | (fp: string) => any 注意当 deep = true 时被 filter 过滤掉的目录及目录中的文件不会包含在结果中
|
|
85
85
|
Note that when deep = true, directories and files in directories that are filtered out by the filter will not be included in the results
|
|
86
|
-
- stats?: `false` 启用后返回 FStats
|
|
87
|
-
Returns the FStats list when enabled
|
|
86
|
+
- stats?: `false` 启用后返回 FStats 列表
|
|
87
|
+
Returns the FStats list when enabled */
|
|
88
88
|
export declare function flist(fpd: string): Promise<string[]>;
|
|
89
89
|
export declare function flist(fpd: string, options?: FListOptions & {
|
|
90
90
|
stats: true;
|
|
@@ -109,7 +109,7 @@ export declare function fdelete(fp: string, { print }?: {
|
|
|
109
109
|
- overwrite?: `true`
|
|
110
110
|
|
|
111
111
|
@example
|
|
112
|
-
fcopy('
|
|
112
|
+
fcopy('D:/temp/camera/', 'D:/camera/') */
|
|
113
113
|
export declare function fcopy(fp_src: string, fp_dst: string, { print, overwrite, }?: {
|
|
114
114
|
print?: boolean;
|
|
115
115
|
overwrite?: boolean;
|
|
@@ -118,7 +118,7 @@ export declare function fcopy(fp_src: string, fp_dst: string, { print, overwrite
|
|
|
118
118
|
- src: 源 文件/文件夹 完整路径 src file/directory absolute path
|
|
119
119
|
- dst: 目标 文件/文件夹 完整路径 dst file/directory absolute path
|
|
120
120
|
@example
|
|
121
|
-
fmove('
|
|
121
|
+
fmove('D:/temp/camera/', 'D:/camera/') */
|
|
122
122
|
export declare function fmove(src: string, dst: string, { overwrite, print }?: {
|
|
123
123
|
overwrite?: boolean;
|
|
124
124
|
print?: boolean;
|
package/file.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { promises as fsp, default as fs
|
|
1
|
+
import { promises as fsp, default as fs } from 'fs';
|
|
2
2
|
import { isUint8Array } from 'util/types';
|
|
3
|
-
import path from 'upath';
|
|
4
3
|
import fse from 'fs-extra';
|
|
4
|
+
import { path } from './path.js';
|
|
5
5
|
import { t } from './i18n/instance.js';
|
|
6
6
|
import { to_json } from './prototype.js';
|
|
7
7
|
import { assert } from './utils.js';
|
|
@@ -30,10 +30,10 @@ export async function fopen(fp, flags, { mode, print } = {}) {
|
|
|
30
30
|
}
|
|
31
31
|
export async function fread(fp, { dir, encoding = 'utf-8', print = true } = {}) {
|
|
32
32
|
if (dir) {
|
|
33
|
-
assert(dir.
|
|
33
|
+
assert(dir.isdir, t('dir 必须以 / 结尾'));
|
|
34
34
|
fp = dir + fp;
|
|
35
35
|
}
|
|
36
|
-
assert(!fp.
|
|
36
|
+
assert(!fp.isdir, t('fp 必须是文件,不能以 / 结尾'));
|
|
37
37
|
assert(path.isAbsolute(fp), `${t('fp 必须是绝对路径:')} ${fp}`);
|
|
38
38
|
assert(encoding !== 'auto');
|
|
39
39
|
if (print)
|
|
@@ -69,7 +69,7 @@ export async function fwrite(fp, data, { dir, print = true, mkdir = false, } = {
|
|
|
69
69
|
}
|
|
70
70
|
else {
|
|
71
71
|
if (dir) {
|
|
72
|
-
assert(dir.
|
|
72
|
+
assert(dir.isdir, t('dir 必须以 / 结尾'));
|
|
73
73
|
fp = dir + fp;
|
|
74
74
|
}
|
|
75
75
|
assert(path.isAbsolute(fp), `${t('fp 必须是绝对路径,当前为:')} ${fp}`);
|
|
@@ -90,7 +90,7 @@ export async function fwrite(fp, data, { dir, print = true, mkdir = false, } = {
|
|
|
90
90
|
}
|
|
91
91
|
export async function fappend(fp, data, { dir, print = true } = {}) {
|
|
92
92
|
if (dir) {
|
|
93
|
-
assert(dir.
|
|
93
|
+
assert(dir.isdir, t('dir 必须以 / 结尾'));
|
|
94
94
|
fp = dir + fp;
|
|
95
95
|
}
|
|
96
96
|
assert(path.isAbsolute(fp), `${t('fp 必须是绝对路径,当前为:')} ${fp}`);
|
|
@@ -101,9 +101,11 @@ export async function fappend(fp, data, { dir, print = true } = {}) {
|
|
|
101
101
|
}
|
|
102
102
|
export async function flist(fpd, options = {}) {
|
|
103
103
|
const { filter, deep = false, absolute = false, print = true, stats = false } = options;
|
|
104
|
+
assert(path.isAbsolute(fpd), t("flist: 参数 fpd: '{{fpd}}' 必须是绝对路径", { fpd }));
|
|
105
|
+
assert(fpd.isdir, t("flist: 参数 fpd: '{{fpd}}' 必须以 / 结尾", { fpd }));
|
|
104
106
|
if (!path.isAbsolute(fpd))
|
|
105
107
|
throw new Error(t('参数 fpd: ') + fpd + t(' 必须是绝对路径'));
|
|
106
|
-
if (!fpd.
|
|
108
|
+
if (!fpd.isdir)
|
|
107
109
|
throw new Error(t('参数 fpd: ') + fpd + t(' 必须以 / 结尾'));
|
|
108
110
|
// readdir withFileTypes 参数在底层有什么区别,速度上有什么差异
|
|
109
111
|
// 都调用了 uv_fs_scandir, 且调用参数相同,仅仅是 Node.js 侧的回调不同 AfterScanDir / AfterScanDirWithTypes
|
|
@@ -125,22 +127,39 @@ export async function flist(fpd, options = {}) {
|
|
|
125
127
|
console.log(fp);
|
|
126
128
|
fps.push(fp);
|
|
127
129
|
}
|
|
130
|
+
async function _fstat(fp) {
|
|
131
|
+
let _stats = await fstat(absolute ? fp : fpd + fp);
|
|
132
|
+
if (!absolute)
|
|
133
|
+
_stats.fp = fp;
|
|
134
|
+
return _stats;
|
|
135
|
+
}
|
|
128
136
|
if (deep)
|
|
129
|
-
return (await Promise.all(fps.map(async (fp) =>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
return (await Promise.all(fps.map(async (fp) => {
|
|
138
|
+
let fpd_stats;
|
|
139
|
+
return fp.isdir ?
|
|
140
|
+
[
|
|
141
|
+
stats ? (fpd_stats = await _fstat(fp)) : fp,
|
|
142
|
+
...(await flist(absolute ? fp : fpd + fp, options)).map((fp_or_stats) => {
|
|
143
|
+
if (stats) {
|
|
144
|
+
if (!absolute)
|
|
145
|
+
fp_or_stats.fp = fp + fp_or_stats.fp;
|
|
146
|
+
fpd_stats.size += fp_or_stats.size;
|
|
147
|
+
return fp_or_stats;
|
|
148
|
+
}
|
|
149
|
+
else
|
|
150
|
+
return absolute ? fp_or_stats : fp + fp_or_stats;
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
:
|
|
154
|
+
stats ? _fstat(fp) : fp;
|
|
155
|
+
}))).flat();
|
|
136
156
|
else if (stats)
|
|
137
|
-
return Promise.all(fps.map(
|
|
157
|
+
return Promise.all(fps.map(_fstat));
|
|
138
158
|
else
|
|
139
159
|
return fps;
|
|
140
160
|
}
|
|
141
161
|
export async function fstat(fp) {
|
|
142
|
-
|
|
143
|
-
throw new Error('fp: ' + fp + t(' 必须是绝对路径'));
|
|
162
|
+
assert(path.isAbsolute(fp), t("fstat: 参数 fp: '{{fp}}' 必须是绝对路径", { fp }));
|
|
144
163
|
let stat = await fsp.stat(fp, { bigint: true });
|
|
145
164
|
stat.fp = fp;
|
|
146
165
|
return stat;
|
|
@@ -158,7 +177,7 @@ export async function fdelete(fp, { print = true } = {}) {
|
|
|
158
177
|
try {
|
|
159
178
|
await fsp.rm(fp, { recursive: true });
|
|
160
179
|
if (print)
|
|
161
|
-
if (fp.
|
|
180
|
+
if (fp.isdir)
|
|
162
181
|
console.log((t('删除了文件夹: ') + fp).red);
|
|
163
182
|
else
|
|
164
183
|
console.log((t('删除了文件: ') + fp).red);
|
|
@@ -167,7 +186,7 @@ export async function fdelete(fp, { print = true } = {}) {
|
|
|
167
186
|
catch (error) {
|
|
168
187
|
if (error.code === 'ENOENT') {
|
|
169
188
|
if (print)
|
|
170
|
-
if (fp.
|
|
189
|
+
if (fp.isdir)
|
|
171
190
|
console.log(t('文件夹已不存在: ') + fp);
|
|
172
191
|
else
|
|
173
192
|
console.log(t('文件已不存在: ') + fp);
|
|
@@ -184,9 +203,9 @@ export async function fdelete(fp, { print = true } = {}) {
|
|
|
184
203
|
- overwrite?: `true`
|
|
185
204
|
|
|
186
205
|
@example
|
|
187
|
-
fcopy('
|
|
206
|
+
fcopy('D:/temp/camera/', 'D:/camera/') */
|
|
188
207
|
export async function fcopy(fp_src, fp_dst, { print = true, overwrite = true, } = {}) {
|
|
189
|
-
assert(fp_src.
|
|
208
|
+
assert(fp_src.isdir === fp_dst.isdir, t('fp_src 和 fp_dst 必须同为文件路径或文件夹路径'));
|
|
190
209
|
assert(path.isAbsolute(fp_src) && path.isAbsolute(fp_dst), t('fp_src 和 fp_dst 必须为完整路径'));
|
|
191
210
|
if (print)
|
|
192
211
|
console.log(t('复制'), fp_src, '→', fp_dst);
|
|
@@ -196,9 +215,9 @@ export async function fcopy(fp_src, fp_dst, { print = true, overwrite = true, }
|
|
|
196
215
|
- src: 源 文件/文件夹 完整路径 src file/directory absolute path
|
|
197
216
|
- dst: 目标 文件/文件夹 完整路径 dst file/directory absolute path
|
|
198
217
|
@example
|
|
199
|
-
fmove('
|
|
218
|
+
fmove('D:/temp/camera/', 'D:/camera/') */
|
|
200
219
|
export async function fmove(src, dst, { overwrite = false, print = true } = {}) {
|
|
201
|
-
if (src.
|
|
220
|
+
if (src.isdir !== dst.isdir)
|
|
202
221
|
throw new Error(t('src 和 dst 必须同为文件路径或文件夹路径'));
|
|
203
222
|
if (!path.isAbsolute(src) || !path.isAbsolute(dst))
|
|
204
223
|
throw new Error(t('src 和 dst 必须为完整路径'));
|
|
@@ -236,7 +255,7 @@ export async function frename(fp, fp_, { fpd, print = true, overwrite = true } =
|
|
|
236
255
|
- mode?: `'0o777'` */
|
|
237
256
|
export async function fmkdir(fpd, { print = true, mode, } = {}) {
|
|
238
257
|
assert(path.isAbsolute(fpd), t('fpd 必须是绝对路径: ') + fpd);
|
|
239
|
-
assert(fpd.
|
|
258
|
+
assert(fpd.isdir, t('fpd 必须以 / 结尾: ') + fpd);
|
|
240
259
|
// CallingfsPromises.mkdir() when path is a directory that exists results in a rejection only when recursive is false.
|
|
241
260
|
const fpd_ = (await fsp.mkdir(fpd, { recursive: true, mode }))?.replaceAll('\\', '/');
|
|
242
261
|
if (fpd_) {
|
|
@@ -252,8 +271,8 @@ export async function fmkdir(fpd, { print = true, mode, } = {}) {
|
|
|
252
271
|
- fp_link: 目标链接文件/文件夹的路径 target file/directory path */
|
|
253
272
|
export async function flink(fp_real, fp_link, { junction = false, print = true } = {}) {
|
|
254
273
|
assert(path.isAbsolute(fp_real) && path.isAbsolute(fp_link), t('fp 必须是绝对路径'));
|
|
255
|
-
const is_fpd_real = fp_real.
|
|
256
|
-
const is_fpd_link = fp_link.
|
|
274
|
+
const is_fpd_real = fp_real.isdir;
|
|
275
|
+
const is_fpd_link = fp_link.isdir;
|
|
257
276
|
assert(is_fpd_real === is_fpd_link, t('fp_real 和 fp_link 必须同为文件路径或文件夹路径'));
|
|
258
277
|
if (fexists(fp_link))
|
|
259
278
|
throw new Error(t('存在同名') + (is_fpd_link ? t('文件夹') : t('文件')) + ': ' + fp_link + t(',无法创建链接'));
|
package/i18n/i18n-scan.js
CHANGED
package/i18n/scanner/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import i18n_scanner from 'i18next-scanner';
|
|
2
|
-
import path from 'upath';
|
|
3
2
|
import vfs from 'vinyl-fs';
|
|
4
3
|
import sort from 'gulp-sort';
|
|
5
4
|
import ora from 'ora';
|
|
@@ -8,6 +7,7 @@ import Vinyl from 'vinyl';
|
|
|
8
7
|
import through2 from 'through2';
|
|
9
8
|
import CliTable from 'cli-table3';
|
|
10
9
|
import '../../prototype.js';
|
|
10
|
+
import { path } from '../../path.js';
|
|
11
11
|
import { map_stream } from '../../utils.js';
|
|
12
12
|
import { LANGUAGES, } from '../index.js';
|
|
13
13
|
import { RWDict } from '../rwdict.js';
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"interactive programming"
|
|
17
17
|
],
|
|
18
18
|
"engines": {
|
|
19
|
-
"node": ">=20.
|
|
19
|
+
"node": ">=20.5.0",
|
|
20
20
|
"vscode": ">=1.79.0"
|
|
21
21
|
},
|
|
22
22
|
"author": "ShenHongFei <shen.hongfei@outlook.com> (https://github.com/ShenHongFei)",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@babel/core": "^7.22.
|
|
48
|
+
"@babel/core": "^7.22.9",
|
|
49
49
|
"@babel/parser": "^7.22.7",
|
|
50
50
|
"@babel/traverse": "^7.22.8",
|
|
51
51
|
"@koa/cors": "^4.0.0",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"fs-extra": "^11.1.1",
|
|
63
63
|
"gulp-sort": "^2.0.0",
|
|
64
64
|
"hash-string": "^1.0.0",
|
|
65
|
-
"i18next": "^23.2.
|
|
65
|
+
"i18next": "^23.2.11",
|
|
66
66
|
"i18next-scanner": "^4.3.0",
|
|
67
67
|
"js-cookie": "^3.0.5",
|
|
68
68
|
"koa": "^2.14.2",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"koa-useragent": "^4.1.0",
|
|
71
71
|
"lodash": "^4.17.21",
|
|
72
72
|
"map-stream": "0.0.7",
|
|
73
|
+
"mime-types": "^2.1.35",
|
|
73
74
|
"ora": "^6.3.1",
|
|
74
75
|
"qs": "^6.11.2",
|
|
75
76
|
"react": "^18.2.0",
|
|
@@ -81,7 +82,6 @@
|
|
|
81
82
|
"tslib": "^2.6.0",
|
|
82
83
|
"typescript": "^5.1.6",
|
|
83
84
|
"undici": "^5.22.1",
|
|
84
|
-
"upath": "^2.0.1",
|
|
85
85
|
"vinyl": "^3.0.0",
|
|
86
86
|
"vinyl-fs": "^4.0.0",
|
|
87
87
|
"ws": "^8.13.0"
|
|
@@ -97,20 +97,19 @@
|
|
|
97
97
|
"@types/koa": "^2.13.6",
|
|
98
98
|
"@types/koa-compress": "^4.0.3",
|
|
99
99
|
"@types/lodash": "^4.14.195",
|
|
100
|
-
"@types/
|
|
100
|
+
"@types/mime-types": "^2.1.1",
|
|
101
|
+
"@types/node": "^20.4.4",
|
|
101
102
|
"@types/qs": "^6.9.7",
|
|
102
|
-
"@types/react": "^18.2.
|
|
103
|
+
"@types/react": "^18.2.15",
|
|
103
104
|
"@types/through2": "^2.0.38",
|
|
104
105
|
"@types/tough-cookie": "^4.0.2",
|
|
105
106
|
"@types/vinyl-fs": "^3.0.2",
|
|
106
107
|
"@types/vscode": "^1.80.0",
|
|
107
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
108
|
-
"@typescript-eslint/parser": "^6.
|
|
109
|
-
"eslint": "^8.
|
|
110
|
-
"eslint-plugin-react": "^7.
|
|
111
|
-
"eslint-plugin-xlint": "^1.0.6"
|
|
112
|
-
"source-map-loader": "^4.0.1",
|
|
113
|
-
"ts-loader": "^9.4.4"
|
|
108
|
+
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
|
109
|
+
"@typescript-eslint/parser": "^6.1.0",
|
|
110
|
+
"eslint": "^8.45.0",
|
|
111
|
+
"eslint-plugin-react": "^7.33.0",
|
|
112
|
+
"eslint-plugin-xlint": "^1.0.6"
|
|
114
113
|
},
|
|
115
114
|
"scripts": {
|
|
116
115
|
"start": "node --title=xshell --inspect=0.0.0.0:8420 ./xshell.js",
|
package/path.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import { default as npath, type FormatInputPathObject } from 'path';
|
|
3
|
+
export declare function to_fp(str: string): string;
|
|
4
|
+
/** Normalize a string path, reducing '..' and '.' parts.
|
|
5
|
+
When multiple slashes are found, they're replaced by a single one; when the path contains a trailing slash, it is preserved.
|
|
6
|
+
@param path string path to normalize.
|
|
7
|
+
@throws {TypeError} if `path` is not a string. */
|
|
8
|
+
export declare function normalize(path: string): string;
|
|
9
|
+
/** Join all arguments together and normalize the resulting path.
|
|
10
|
+
@param paths paths to join.
|
|
11
|
+
@throws {TypeError} if any of the path segments is not a string. */
|
|
12
|
+
export declare function join(...paths: string[]): string;
|
|
13
|
+
/** The right-most parameter is considered {to}. Other parameters are considered an array of {from}.
|
|
14
|
+
|
|
15
|
+
Starting from leftmost {from} parameter, resolves {to} to an absolute path.
|
|
16
|
+
|
|
17
|
+
If {to} isn't already absolute, {from} arguments are prepended in right to left order,
|
|
18
|
+
until an absolute path is found. If after using all {from} paths still no absolute path is found,
|
|
19
|
+
the current working directory is used as well. The resulting path is normalized,
|
|
20
|
+
and trailing slashes are removed unless the path gets resolved to the root directory.
|
|
21
|
+
|
|
22
|
+
@param paths A sequence of paths or path segments.
|
|
23
|
+
@throws {TypeError} if any of the arguments is not a string. */
|
|
24
|
+
export declare function resolve(...paths: string[]): string;
|
|
25
|
+
/** Determines whether {path} is an absolute path.
|
|
26
|
+
An absolute path will always resolve to the same location, regardless of the working directory.
|
|
27
|
+
|
|
28
|
+
If the given {path} is a zero-length string, `false` will be returned.
|
|
29
|
+
|
|
30
|
+
@param path path to test.
|
|
31
|
+
@throws {TypeError} if `path` is not a string. */
|
|
32
|
+
export declare function isAbsolute(path: string): boolean;
|
|
33
|
+
/** Solve the relative path from {from} to {to} based on the current working directory.
|
|
34
|
+
At times we have two absolute paths, and we need to derive the relative path from one to the other. This is actually the reverse transform of path.resolve.
|
|
35
|
+
|
|
36
|
+
@throws {TypeError} if either `from` or `to` is not a string. */
|
|
37
|
+
export declare function relative(from: string, to: string): string;
|
|
38
|
+
/** Return the directory name of a path. Similar to the Unix dirname command.
|
|
39
|
+
@param path the path to evaluate.
|
|
40
|
+
@throws {TypeError} if `path` is not a string. */
|
|
41
|
+
export declare function dirname(path: string): string;
|
|
42
|
+
/** Return the last portion of a path. Similar to the Unix basename command.
|
|
43
|
+
Often used to extract the file name from a fully qualified path.
|
|
44
|
+
@param path the path to evaluate.
|
|
45
|
+
@param suffix optionally, an extension to remove from the result.
|
|
46
|
+
@throws {TypeError} if `path` is not a string or if `ext` is given and is not a string. */
|
|
47
|
+
export declare function basename(path: string, suffix?: string): string;
|
|
48
|
+
/** Return the extension of the path, from the last '.' to end of string in the last portion of the path.
|
|
49
|
+
If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string.
|
|
50
|
+
|
|
51
|
+
@param path the path to evaluate.
|
|
52
|
+
@throws {TypeError} if `path` is not a string. */
|
|
53
|
+
export declare function extname(path: string): string;
|
|
54
|
+
/** `/` */
|
|
55
|
+
export declare const sep = "/";
|
|
56
|
+
/** The platform-specific file delimiter. ';' or ':'. */
|
|
57
|
+
export declare const delimiter: ":" | ";";
|
|
58
|
+
/** Returns an object from a path string - the opposite of format().
|
|
59
|
+
@param path path to evaluate.
|
|
60
|
+
@throws {TypeError} if `path` is not a string. */
|
|
61
|
+
export declare function parse(path: string): npath.ParsedPath;
|
|
62
|
+
/** Returns a path string from an object - the opposite of parse().
|
|
63
|
+
@param pathObject path to evaluate. */
|
|
64
|
+
export declare function format(pathObject: FormatInputPathObject): string;
|
|
65
|
+
/** On Windows systems only, returns an equivalent namespace-prefixed path for the given path.
|
|
66
|
+
If path is not a string, path will be returned without modifications.
|
|
67
|
+
This method is meaningful only on Windows system.
|
|
68
|
+
On POSIX systems, the method is non-operational and always returns path without modifications. */
|
|
69
|
+
export declare function toNamespacedPath(path: string): any;
|
|
70
|
+
export declare const posix: npath.PlatformPath;
|
|
71
|
+
export declare const win32: npath.PlatformPath;
|
|
72
|
+
/** 统一使用 / 作为分隔符的 path 模块 */
|
|
73
|
+
export declare let path: {
|
|
74
|
+
to_fp: typeof to_fp;
|
|
75
|
+
normalize: typeof normalize;
|
|
76
|
+
join: typeof join;
|
|
77
|
+
resolve: typeof resolve;
|
|
78
|
+
isAbsolute: typeof isAbsolute;
|
|
79
|
+
relative: typeof relative;
|
|
80
|
+
dirname: typeof dirname;
|
|
81
|
+
basename: typeof basename;
|
|
82
|
+
extname: typeof extname;
|
|
83
|
+
sep: string;
|
|
84
|
+
delimiter: ":" | ";";
|
|
85
|
+
parse: typeof parse;
|
|
86
|
+
format: typeof format;
|
|
87
|
+
toNamespacedPath: typeof toNamespacedPath;
|
|
88
|
+
};
|
package/path.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { default as npath } from 'path';
|
|
2
|
+
export function to_fp(str) {
|
|
3
|
+
if (!str)
|
|
4
|
+
return str;
|
|
5
|
+
const fp = str.replaceAll('\\', '/');
|
|
6
|
+
// 转换小写盘符开头的路径
|
|
7
|
+
if (fp[1] === ':' && 'a' <= fp[0] && fp[0] <= 'z')
|
|
8
|
+
return fp[0].toUpperCase() + fp.slice(1);
|
|
9
|
+
else
|
|
10
|
+
return fp;
|
|
11
|
+
}
|
|
12
|
+
/** Normalize a string path, reducing '..' and '.' parts.
|
|
13
|
+
When multiple slashes are found, they're replaced by a single one; when the path contains a trailing slash, it is preserved.
|
|
14
|
+
@param path string path to normalize.
|
|
15
|
+
@throws {TypeError} if `path` is not a string. */
|
|
16
|
+
export function normalize(path) {
|
|
17
|
+
return to_fp(npath.normalize(to_fp(path)));
|
|
18
|
+
}
|
|
19
|
+
/** Join all arguments together and normalize the resulting path.
|
|
20
|
+
@param paths paths to join.
|
|
21
|
+
@throws {TypeError} if any of the path segments is not a string. */
|
|
22
|
+
export function join(...paths) {
|
|
23
|
+
return to_fp(npath.join(...paths.map(p => to_fp(p))));
|
|
24
|
+
}
|
|
25
|
+
/** The right-most parameter is considered {to}. Other parameters are considered an array of {from}.
|
|
26
|
+
|
|
27
|
+
Starting from leftmost {from} parameter, resolves {to} to an absolute path.
|
|
28
|
+
|
|
29
|
+
If {to} isn't already absolute, {from} arguments are prepended in right to left order,
|
|
30
|
+
until an absolute path is found. If after using all {from} paths still no absolute path is found,
|
|
31
|
+
the current working directory is used as well. The resulting path is normalized,
|
|
32
|
+
and trailing slashes are removed unless the path gets resolved to the root directory.
|
|
33
|
+
|
|
34
|
+
@param paths A sequence of paths or path segments.
|
|
35
|
+
@throws {TypeError} if any of the arguments is not a string. */
|
|
36
|
+
export function resolve(...paths) {
|
|
37
|
+
return to_fp(npath.resolve(...paths.map(p => to_fp(p))));
|
|
38
|
+
}
|
|
39
|
+
/** Determines whether {path} is an absolute path.
|
|
40
|
+
An absolute path will always resolve to the same location, regardless of the working directory.
|
|
41
|
+
|
|
42
|
+
If the given {path} is a zero-length string, `false` will be returned.
|
|
43
|
+
|
|
44
|
+
@param path path to test.
|
|
45
|
+
@throws {TypeError} if `path` is not a string. */
|
|
46
|
+
export function isAbsolute(path) {
|
|
47
|
+
return npath.isAbsolute(to_fp(path));
|
|
48
|
+
}
|
|
49
|
+
/** Solve the relative path from {from} to {to} based on the current working directory.
|
|
50
|
+
At times we have two absolute paths, and we need to derive the relative path from one to the other. This is actually the reverse transform of path.resolve.
|
|
51
|
+
|
|
52
|
+
@throws {TypeError} if either `from` or `to` is not a string. */
|
|
53
|
+
export function relative(from, to) {
|
|
54
|
+
return to_fp(npath.relative(to_fp(from), to_fp(to)));
|
|
55
|
+
}
|
|
56
|
+
/** Return the directory name of a path. Similar to the Unix dirname command.
|
|
57
|
+
@param path the path to evaluate.
|
|
58
|
+
@throws {TypeError} if `path` is not a string. */
|
|
59
|
+
export function dirname(path) {
|
|
60
|
+
return to_fp(npath.dirname(path));
|
|
61
|
+
}
|
|
62
|
+
/** Return the last portion of a path. Similar to the Unix basename command.
|
|
63
|
+
Often used to extract the file name from a fully qualified path.
|
|
64
|
+
@param path the path to evaluate.
|
|
65
|
+
@param suffix optionally, an extension to remove from the result.
|
|
66
|
+
@throws {TypeError} if `path` is not a string or if `ext` is given and is not a string. */
|
|
67
|
+
export function basename(path, suffix) {
|
|
68
|
+
return npath.basename(to_fp(path), suffix);
|
|
69
|
+
}
|
|
70
|
+
/** Return the extension of the path, from the last '.' to end of string in the last portion of the path.
|
|
71
|
+
If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string.
|
|
72
|
+
|
|
73
|
+
@param path the path to evaluate.
|
|
74
|
+
@throws {TypeError} if `path` is not a string. */
|
|
75
|
+
export function extname(path) {
|
|
76
|
+
return npath.extname(to_fp(path));
|
|
77
|
+
}
|
|
78
|
+
/** `/` */
|
|
79
|
+
export const sep = '/';
|
|
80
|
+
/** The platform-specific file delimiter. ';' or ':'. */
|
|
81
|
+
export const delimiter = npath.delimiter;
|
|
82
|
+
/** Returns an object from a path string - the opposite of format().
|
|
83
|
+
@param path path to evaluate.
|
|
84
|
+
@throws {TypeError} if `path` is not a string. */
|
|
85
|
+
export function parse(path) {
|
|
86
|
+
return npath.parse(to_fp(path));
|
|
87
|
+
}
|
|
88
|
+
/** Returns a path string from an object - the opposite of parse().
|
|
89
|
+
@param pathObject path to evaluate. */
|
|
90
|
+
export function format(pathObject) {
|
|
91
|
+
return to_fp(npath.format(pathObject));
|
|
92
|
+
}
|
|
93
|
+
/** On Windows systems only, returns an equivalent namespace-prefixed path for the given path.
|
|
94
|
+
If path is not a string, path will be returned without modifications.
|
|
95
|
+
This method is meaningful only on Windows system.
|
|
96
|
+
On POSIX systems, the method is non-operational and always returns path without modifications. */
|
|
97
|
+
export function toNamespacedPath(path) {
|
|
98
|
+
return to_fp(toNamespacedPath(to_fp(path)));
|
|
99
|
+
}
|
|
100
|
+
export const posix = npath.posix;
|
|
101
|
+
export const win32 = npath.win32;
|
|
102
|
+
/** 统一使用 / 作为分隔符的 path 模块 */
|
|
103
|
+
export let path = {
|
|
104
|
+
to_fp,
|
|
105
|
+
normalize,
|
|
106
|
+
join,
|
|
107
|
+
resolve,
|
|
108
|
+
isAbsolute,
|
|
109
|
+
relative,
|
|
110
|
+
dirname,
|
|
111
|
+
basename,
|
|
112
|
+
extname,
|
|
113
|
+
sep,
|
|
114
|
+
delimiter,
|
|
115
|
+
parse,
|
|
116
|
+
format,
|
|
117
|
+
toNamespacedPath
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=path.js.map
|
package/process.d.ts
CHANGED
|
@@ -26,15 +26,14 @@ interface StartOptions {
|
|
|
26
26
|
detached?: boolean;
|
|
27
27
|
/** 为 true 时会设置 UV_PROCESS_WINDOWS_HIDE
|
|
28
28
|
然后设置启动进程的参数 CREATE_NO_WINDOW, 和 SW_HIDE
|
|
29
|
-
|
|
29
|
+
D:/0/libuv/src/win/process.c
|
|
30
30
|
CREATE_NO_WINDOW:
|
|
31
31
|
The process is a console application that is being run without a console window.
|
|
32
32
|
Therefore, the console handle for the application is not set.
|
|
33
33
|
This flag is ignored if the application is not a console application, or
|
|
34
34
|
if it is used with either CREATE_NEW_CONSOLE or DETACHED_PROCESS.
|
|
35
35
|
|
|
36
|
-
具体有什么用还不清楚
|
|
37
|
-
*/
|
|
36
|
+
具体有什么用还不清楚 */
|
|
38
37
|
window?: boolean;
|
|
39
38
|
}
|
|
40
39
|
/** start process
|
|
@@ -66,7 +65,7 @@ export interface CallResult<TOutput extends string | Buffer = string> {
|
|
|
66
65
|
- exe: .exe 路径或文件名 (建议使用路径,跳过 path 搜索,性能更高) path or filename (full path is recommanded to skip path searching for better perf)
|
|
67
66
|
- args: `[]` 参数列表 arguments list
|
|
68
67
|
- options?:
|
|
69
|
-
- cwd?: `
|
|
68
|
+
- cwd?: `process.cwd()`
|
|
70
69
|
- env?: `process.env` 覆盖/添加到 process.env 的环境变量 overwrite/add to process.env
|
|
71
70
|
- encoding?: `'utf-8'` 子进程输出编码, 设置为 binary 时返回 stdout, stderr 类型为 Buffer; 否则是 string
|
|
72
71
|
child output encoding. When set to binary, return stdout, stderr is of type Buffer; otherwise it is string
|
|
@@ -84,7 +83,7 @@ export declare function call(exe: string, args?: string[], options?: CallOptions
|
|
|
84
83
|
- js: .js 路径 (相对路径根据 cwd 解析) path (relative path will resolve based on cwd)
|
|
85
84
|
- args: `[]` 参数列表 arguments list
|
|
86
85
|
- options
|
|
87
|
-
- cwd?: `
|
|
86
|
+
- cwd?: `process.cwd()`
|
|
88
87
|
- env?: `process.env` 覆盖/添加到 process.env 的环境变量 overwrite/add to process.env
|
|
89
88
|
- encoding?: `'utf-8'` 子进程输出编码 child process output encoding
|
|
90
89
|
- print?: `true` print 选项,支持设置细项 print option (with details)
|
|
@@ -93,7 +92,7 @@ export declare function call(exe: string, args?: string[], options?: CallOptions
|
|
|
93
92
|
- throw_code?: `true` code 不为 0 时是否抛出异常 whether to throw Error when code is not 0 */
|
|
94
93
|
export declare function call_nodejs(js: string, args?: string[], options?: CallOptions): Promise<CallResult<string>>;
|
|
95
94
|
export interface TermOptions {
|
|
96
|
-
/** `
|
|
95
|
+
/** `process.cwd()` */
|
|
97
96
|
cwd?: string;
|
|
98
97
|
/** `true` 打印参数 */
|
|
99
98
|
print?: boolean;
|
package/process.js
CHANGED
|
@@ -3,7 +3,7 @@ import { userInfo } from 'os';
|
|
|
3
3
|
import { t } from './i18n/instance.js';
|
|
4
4
|
import './prototype.js';
|
|
5
5
|
import { inspect, DecoderStream } from './utils.js';
|
|
6
|
-
export const exe_nodejs = process.execPath.
|
|
6
|
+
export const exe_nodejs = process.execPath.fp;
|
|
7
7
|
/** start process
|
|
8
8
|
- exe: .exe 路径或文件名 (建议使用完整路径,跳过 path 搜索,性能更高) path or filename (full path is recommanded to skip path searching for better perf)
|
|
9
9
|
- args: `[]` 参数列表 arguments list
|
|
@@ -165,7 +165,7 @@ export async function call(exe, args = [], options = {}) {
|
|
|
165
165
|
- js: .js 路径 (相对路径根据 cwd 解析) path (relative path will resolve based on cwd)
|
|
166
166
|
- args: `[]` 参数列表 arguments list
|
|
167
167
|
- options
|
|
168
|
-
- cwd?: `
|
|
168
|
+
- cwd?: `process.cwd()`
|
|
169
169
|
- env?: `process.env` 覆盖/添加到 process.env 的环境变量 overwrite/add to process.env
|
|
170
170
|
- encoding?: `'utf-8'` 子进程输出编码 child process output encoding
|
|
171
171
|
- print?: `true` print 选项,支持设置细项 print option (with details)
|
|
@@ -181,7 +181,7 @@ export const exe_winterm = `C:/Users/${userInfo().username}/AppData/Local/Micros
|
|
|
181
181
|
- args: 调用参数 call parameter
|
|
182
182
|
- options?: WinTermOptions
|
|
183
183
|
*/
|
|
184
|
-
export async function term(exe, args = [], { cwd =
|
|
184
|
+
export async function term(exe, args = [], { cwd = process.cwd().fp, print = true, title,
|
|
185
185
|
// env
|
|
186
186
|
} = {}) {
|
|
187
187
|
return start(exe_winterm, [
|
package/prototype.browser.d.ts
CHANGED
|
@@ -21,7 +21,6 @@ declare global {
|
|
|
21
21
|
position?: 'left' | 'right';
|
|
22
22
|
}): string;
|
|
23
23
|
to_regexp(this: string, preservations?: string, flags?: string): RegExp;
|
|
24
|
-
to_bool(this: string): boolean;
|
|
25
24
|
/** 字符串模式替换
|
|
26
25
|
- pattern: 匹配部分的格式
|
|
27
26
|
- pattern_: 替换后的格式
|
|
@@ -67,21 +66,26 @@ declare global {
|
|
|
67
66
|
surround(this: string, left: string, right?: string): string;
|
|
68
67
|
surround_tag(this: string, tag_name: string): string;
|
|
69
68
|
to_lf(this: string): string;
|
|
70
|
-
to_crlf(this: string): string;
|
|
71
69
|
/** 'xxx'.replace(/pattern/g, '')
|
|
72
|
-
如果 pattern 是 string 则在创建 RegExp 时自动加上 flags (默认 'g'), 否则忽略 flags
|
|
73
|
-
*/
|
|
70
|
+
如果 pattern 是 string 则在创建 RegExp 时自动加上 flags (默认 'g'), 否则忽略 flags */
|
|
74
71
|
rm(this: string, pattern: string | RegExp, flags?: string): string;
|
|
75
72
|
/** 将 string 划分为行,并去掉最后一个 \n 之后的 '' */
|
|
76
73
|
split_lines(this: string): string[];
|
|
77
|
-
trim_doc_comment(this: string): string;
|
|
78
74
|
split_indent(this: string): {
|
|
79
75
|
indent: number;
|
|
80
76
|
text: string;
|
|
81
77
|
};
|
|
82
78
|
space(this: string): string;
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
/** 等价于 .endsWith('/') */
|
|
80
|
+
isdir: boolean;
|
|
81
|
+
/** 以 `/` 分割的路径 */
|
|
82
|
+
fp: string;
|
|
83
|
+
/** 文件名 (path.basename 的结果), 保留结尾的 /,如:
|
|
84
|
+
- D:/0/aaa.txt -> aaa.txt
|
|
85
|
+
- D:/aaa/ -> aaa/ */
|
|
86
|
+
fname: string;
|
|
87
|
+
/** .txt */
|
|
88
|
+
fext: string;
|
|
85
89
|
}
|
|
86
90
|
interface Date {
|
|
87
91
|
/** - ms?: `false` 显示到 ms */
|
package/prototype.browser.js
CHANGED
|
@@ -109,9 +109,6 @@ Object.defineProperties(String.prototype, {
|
|
|
109
109
|
.map((c) => c === ']' ? '\\]' : c).join('');
|
|
110
110
|
return new RegExp(this.replace(new RegExp(`[${replace_chars}]`, 'g'), '\\$&'), flags);
|
|
111
111
|
},
|
|
112
|
-
to_bool() {
|
|
113
|
-
return this.length && this !== '0' && this.toLowerCase() !== 'false';
|
|
114
|
-
},
|
|
115
112
|
refmt(pattern, pattern_, preservations = '', flags = '', transformer = (name, value) => value || '', pattern_placeholder = /\{.*?\}/g) {
|
|
116
113
|
// --- 转换 pattern 为 pattern_regx
|
|
117
114
|
let last_end = 0;
|
|
@@ -226,9 +223,6 @@ Object.defineProperties(String.prototype, {
|
|
|
226
223
|
to_lf() {
|
|
227
224
|
return this.replace(/\r\n/g, '\n');
|
|
228
225
|
},
|
|
229
|
-
to_crlf() {
|
|
230
|
-
return this.replace(/\n/g, '\r\n');
|
|
231
|
-
},
|
|
232
226
|
rm(pattern, flags = 'g') {
|
|
233
227
|
if (typeof pattern === 'string')
|
|
234
228
|
pattern = new RegExp(pattern, flags);
|
|
@@ -277,13 +271,38 @@ Object.defineProperties(String.prototype, {
|
|
|
277
271
|
.replace(new RegExp(cjk + '([A-Za-z0-9`\\$%\\^&\\*\\-=\\+\\\\\\|\\/@\u00a1-\u00ff\u2022\u2027\u2150-\u218f])', 'g'), '$1 $2')
|
|
278
272
|
.replace(new RegExp('([A-Za-z0-9`\\$%\\^&\\*\\-=\\+\\\\\\|\\/@\u00a1-\u00ff\u2022\u2027\u2150-\u218f])' + cjk, 'g'), '$1 $2');
|
|
279
273
|
},
|
|
280
|
-
|
|
281
|
-
|
|
274
|
+
}),
|
|
275
|
+
// ------------ 文件路径操作
|
|
276
|
+
...to_getter_property_descriptors({
|
|
277
|
+
isdir() {
|
|
278
|
+
return this.endsWith('/');
|
|
279
|
+
},
|
|
280
|
+
fp() {
|
|
281
|
+
if (!this)
|
|
282
|
+
return this;
|
|
283
|
+
const fp = this.replaceAll('\\', '/');
|
|
284
|
+
// 转换小写盘符开头的路径
|
|
285
|
+
if (fp[1] === ':' && 'a' <= fp[0] && fp[0] <= 'z')
|
|
286
|
+
return fp[0].toUpperCase() + fp.slice(1);
|
|
287
|
+
else
|
|
288
|
+
return fp;
|
|
289
|
+
},
|
|
290
|
+
fdir() {
|
|
291
|
+
const fname = (this.endsWith('/') ? this.slice(0, -1) : this).split('/').at(-1);
|
|
292
|
+
return this.slice(0, this.length - (fname.length + (this.endsWith('/') ? 1 : 0)));
|
|
293
|
+
},
|
|
294
|
+
fname() {
|
|
295
|
+
const fname = (this.endsWith('/') ? this.slice(0, -1) : this).split('/').at(-1);
|
|
296
|
+
if (fname)
|
|
297
|
+
return `${fname}${this.endsWith('/') ? '/' : ''}`;
|
|
298
|
+
else
|
|
299
|
+
return '';
|
|
282
300
|
},
|
|
283
|
-
|
|
284
|
-
|
|
301
|
+
fext() {
|
|
302
|
+
const index = this.lastIndexOf('.');
|
|
303
|
+
return index === -1 ? '' : this.slice(index + 1);
|
|
285
304
|
},
|
|
286
|
-
})
|
|
305
|
+
}),
|
|
287
306
|
});
|
|
288
307
|
// ------------------------------------ Date.prototype
|
|
289
308
|
Object.defineProperties(Date.prototype, to_method_property_descriptors({
|
package/prototype.d.ts
CHANGED
|
@@ -11,8 +11,7 @@ declare global {
|
|
|
11
11
|
truncate(this: string, width: number): string;
|
|
12
12
|
/** pad string to `<width>`
|
|
13
13
|
- character?: `' '`
|
|
14
|
-
- position?: `'right'`
|
|
15
|
-
*/
|
|
14
|
+
- position?: `'right'` */
|
|
16
15
|
pad(this: string, width: number, { character, position }?: {
|
|
17
16
|
character?: string;
|
|
18
17
|
position?: 'left' | 'right';
|
|
@@ -22,7 +21,6 @@ declare global {
|
|
|
22
21
|
position?: 'left' | 'right';
|
|
23
22
|
}): string;
|
|
24
23
|
to_regexp(this: string, preservations?: string, flags?: string): RegExp;
|
|
25
|
-
to_bool(this: string): boolean;
|
|
26
24
|
/** 字符串模式替换
|
|
27
25
|
- pattern: 匹配部分的格式
|
|
28
26
|
- pattern_: 替换后的格式
|
|
@@ -68,7 +66,6 @@ declare global {
|
|
|
68
66
|
surround(this: string, left: string, right?: string): string;
|
|
69
67
|
surround_tag(this: string, tag_name: string): string;
|
|
70
68
|
to_lf(this: string): string;
|
|
71
|
-
to_crlf(this: string): string;
|
|
72
69
|
/** 'xxx'.replace(/pattern/g, '')
|
|
73
70
|
如果 pattern 是 string 则在创建 RegExp 时自动加上 flags (默认 'g'), 否则忽略 flags
|
|
74
71
|
*/
|
|
@@ -90,7 +87,6 @@ declare global {
|
|
|
90
87
|
strip_ansi(this: string): string;
|
|
91
88
|
/** 将 string 划分为行,并去掉最后一个 \n 之后的 '' */
|
|
92
89
|
split_lines(this: string): string[];
|
|
93
|
-
trim_doc_comment(this: string): string;
|
|
94
90
|
split_indent(this: string): {
|
|
95
91
|
indent: number;
|
|
96
92
|
text: string;
|
|
@@ -101,14 +97,18 @@ declare global {
|
|
|
101
97
|
decode_base64(this: string, buffer: true): Buffer;
|
|
102
98
|
decode_base64(this: string, buffer?: boolean): string | Buffer;
|
|
103
99
|
space(this: string): string;
|
|
100
|
+
/** 等价于 .endsWith('/') */
|
|
101
|
+
isdir: boolean;
|
|
102
|
+
/** 以 `/` 分割的路径 */
|
|
103
|
+
fp: string;
|
|
104
|
+
/** 父文件夹路径 (path.dirname),并保证以 `/` 结尾 */
|
|
104
105
|
fdir: string;
|
|
105
|
-
/** path.basename,
|
|
106
|
-
-
|
|
107
|
-
-
|
|
106
|
+
/** 文件名 (path.basename 的结果), 保留结尾的 /,如:
|
|
107
|
+
- D:/0/aaa.txt -> aaa.txt
|
|
108
|
+
- D:/aaa/ -> aaa/ */
|
|
108
109
|
fname: string;
|
|
109
110
|
/** .txt */
|
|
110
111
|
fext: string;
|
|
111
|
-
to_slash(this: string): string;
|
|
112
112
|
to_backslash(this: string): string;
|
|
113
113
|
}
|
|
114
114
|
interface Date {
|
package/prototype.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import path from 'upath';
|
|
2
1
|
import byte_size from 'byte-size';
|
|
3
2
|
import EmojiRegex from 'emoji-regex';
|
|
4
3
|
import strip_ansi from 'strip-ansi';
|
|
5
4
|
import chalk from 'chalk';
|
|
6
|
-
|
|
5
|
+
import { to_fp, dirname, basename, extname } from './path.js';
|
|
7
6
|
import { t } from './i18n/instance.js';
|
|
7
|
+
chalk.level = 2;
|
|
8
8
|
export const emoji_regex = EmojiRegex();
|
|
9
9
|
export { chalk };
|
|
10
10
|
export function to_method_property_descriptors(methods) {
|
|
@@ -40,7 +40,7 @@ export const brackets = {
|
|
|
40
40
|
tortoise_shell: ['〔', '〕'],
|
|
41
41
|
};
|
|
42
42
|
const color_map = Object.fromEntries(['red_', 'green_', 'yellow_', 'blue_', 'magenta_', 'cyan_'].map(color => [color, `${color.slice(0, -1)}Bright`]));
|
|
43
|
-
if (!
|
|
43
|
+
if (!globalThis.my_prototype_defined) {
|
|
44
44
|
// ------------------------------------ String.prototype
|
|
45
45
|
Object.defineProperties(String.prototype, {
|
|
46
46
|
...to_getter_property_descriptors({
|
|
@@ -116,9 +116,6 @@ if (!global.my_prototype_defined) {
|
|
|
116
116
|
.map((c) => c === ']' ? '\\]' : c).join('');
|
|
117
117
|
return new RegExp(this.replace(new RegExp(`[${replace_chars}]`, 'g'), '\\$&'), flags);
|
|
118
118
|
},
|
|
119
|
-
to_bool() {
|
|
120
|
-
return this.length && this !== '0' && this.toLowerCase() !== 'false';
|
|
121
|
-
},
|
|
122
119
|
refmt(pattern, pattern_, preservations = '', flags = '', transformer = (name, value) => value || '', pattern_placeholder = /\{.*?\}/g) {
|
|
123
120
|
// --- 转换 pattern 为 pattern_regx
|
|
124
121
|
let last_end = 0;
|
|
@@ -313,23 +310,25 @@ if (!global.my_prototype_defined) {
|
|
|
313
310
|
}]))),
|
|
314
311
|
// ------------ 文件路径操作
|
|
315
312
|
...to_getter_property_descriptors({
|
|
313
|
+
isdir() {
|
|
314
|
+
return this.endsWith('/');
|
|
315
|
+
},
|
|
316
|
+
fp() {
|
|
317
|
+
return to_fp(this);
|
|
318
|
+
},
|
|
316
319
|
fdir() {
|
|
317
|
-
const
|
|
318
|
-
|
|
320
|
+
const fpd = dirname(this);
|
|
321
|
+
// 有可能 fpd 是 '/'
|
|
322
|
+
return fpd.endsWith('/') ? fpd : `${fpd}/`;
|
|
319
323
|
},
|
|
320
324
|
fname() {
|
|
321
|
-
return `${
|
|
325
|
+
return `${basename(this)}${this.endsWith('/') ? '/' : ''}`;
|
|
322
326
|
},
|
|
323
327
|
fext() {
|
|
324
|
-
return
|
|
328
|
+
return extname(this);
|
|
325
329
|
},
|
|
326
330
|
}),
|
|
327
331
|
...to_method_property_descriptors({
|
|
328
|
-
to_slash() {
|
|
329
|
-
if (!this)
|
|
330
|
-
return this;
|
|
331
|
-
return path.normalizeSafe(this);
|
|
332
|
-
},
|
|
333
332
|
to_backslash() {
|
|
334
333
|
return this.replaceAll('/', '\\');
|
|
335
334
|
},
|
package/repl.js
CHANGED
|
@@ -2,9 +2,9 @@ import nvm from 'vm';
|
|
|
2
2
|
import repl from 'repl';
|
|
3
3
|
import process from 'process';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
|
-
import path from 'upath';
|
|
6
5
|
import ts from 'typescript';
|
|
7
6
|
const { factory, createPrinter, SyntaxKind, NodeFlags, ModifierFlags, isIdentifier, isNamedImports, isImportDeclaration, isAwaitExpression, isExpressionStatement, isVariableStatement, isNamespaceImport, isClassDeclaration, isCallExpression, isReturnStatement, isBinaryExpression, isFunctionDeclaration, } = ts;
|
|
7
|
+
import { path } from './path.js';
|
|
8
8
|
import { t } from './i18n/instance.js';
|
|
9
9
|
import './prototype.js';
|
|
10
10
|
import { log_line, delay, inspect, set_inspect_options, Timer, delta2str } from './utils.js';
|
|
@@ -323,11 +323,11 @@ export async function pollute_global() {
|
|
|
323
323
|
exports: global
|
|
324
324
|
});
|
|
325
325
|
await Promise.all([
|
|
326
|
-
pollute_module_default_export('upath', 'path'),
|
|
327
326
|
pollute_module_default_export('lodash/omit.js', 'omit'),
|
|
328
327
|
pollute_module_default_export('lodash/sortBy.js', 'sort_by'),
|
|
329
328
|
pollute_module_default_export('lodash/groupBy.js', 'group_by'),
|
|
330
329
|
pollute_module_default_export('qs', 'qs'),
|
|
330
|
+
pollute_module_exports('./path.js'),
|
|
331
331
|
pollute_module_exports('./prototype.js'),
|
|
332
332
|
pollute_module_exports('./utils.js'),
|
|
333
333
|
pollute_module_exports('./process.js'),
|
package/server.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ declare module 'http' {
|
|
|
29
29
|
export declare class Server {
|
|
30
30
|
/** proxy 时需要丢弃的 resposne headers */
|
|
31
31
|
static drop_response_headers: Set<string>;
|
|
32
|
+
js_exts: Set<string>;
|
|
32
33
|
app: Koa;
|
|
33
34
|
handler: ReturnType<Koa['callback']>;
|
|
34
35
|
port: number;
|
|
@@ -50,6 +51,7 @@ export declare class Server {
|
|
|
50
51
|
*/
|
|
51
52
|
parse(ctx: Context): Promise<void>;
|
|
52
53
|
_router(ctx: Context, next: Next): Promise<void>;
|
|
54
|
+
/** 被子类重写以自定义处理逻辑 */
|
|
53
55
|
router(ctx: Context): Promise<boolean>;
|
|
54
56
|
logger(ctx: Context): void;
|
|
55
57
|
proxy(ctx: Context, url: string | URL, headers_?: Record<string, string>): Promise<void>;
|
|
@@ -58,10 +60,17 @@ export declare class Server {
|
|
|
58
60
|
root: string;
|
|
59
61
|
log_404?: boolean;
|
|
60
62
|
}): Promise<boolean>;
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
+
/** 将 body 设置为 fp 所指文件的 ReadStream
|
|
64
|
+
检查 fp 是否合法,设置对应的 content-type,支持缓存,支持分块下载
|
|
65
|
+
- ctx: Koa.Context
|
|
66
|
+
- fp: 文件路径,有 root 时为相对路径或以 root 开头的绝对路径,无 root 时必须是绝对路径
|
|
67
|
+
- options?:
|
|
68
|
+
- root?: 只能访问 root 下面的文件
|
|
69
|
+
- absolute?: `false` 使用绝对路径指定发送的文件
|
|
70
|
+
- download?: `undefined` 在 response.headers 中加上 content-disposition: attachment 指示浏览器下载文件 */
|
|
71
|
+
fsend(ctx: Context, fp: string, { root, absolute, download, }?: {
|
|
63
72
|
root?: string;
|
|
64
|
-
/** `false` */
|
|
65
73
|
absolute?: boolean;
|
|
74
|
+
download?: boolean;
|
|
66
75
|
}): Promise<string>;
|
|
67
76
|
}
|
package/server.js
CHANGED
|
@@ -4,10 +4,10 @@ import { createReadStream } from 'fs';
|
|
|
4
4
|
import { Readable } from 'stream';
|
|
5
5
|
import util from 'util';
|
|
6
6
|
// --- 3rd party
|
|
7
|
-
import upath from 'upath';
|
|
8
7
|
import qs from 'qs';
|
|
9
8
|
import resolve_safely from 'resolve-path';
|
|
10
9
|
import { WebSocketServer } from 'ws';
|
|
10
|
+
import { contentType } from 'mime-types';
|
|
11
11
|
// --- koa & koa middleware
|
|
12
12
|
import { default as Koa } from 'koa';
|
|
13
13
|
import KoaCors from '@koa/cors';
|
|
@@ -28,6 +28,7 @@ export class Server {
|
|
|
28
28
|
'transfer-encoding',
|
|
29
29
|
'x-powered-by'
|
|
30
30
|
]);
|
|
31
|
+
js_exts = new Set(['.js', '.mjs', '.cjs']);
|
|
31
32
|
app;
|
|
32
33
|
handler;
|
|
33
34
|
port;
|
|
@@ -160,6 +161,7 @@ export class Server {
|
|
|
160
161
|
return;
|
|
161
162
|
await next?.();
|
|
162
163
|
}
|
|
164
|
+
/** 被子类重写以自定义处理逻辑 */
|
|
163
165
|
async router(ctx) {
|
|
164
166
|
return false;
|
|
165
167
|
}
|
|
@@ -295,31 +297,37 @@ export class Server {
|
|
|
295
297
|
return false;
|
|
296
298
|
}
|
|
297
299
|
}
|
|
298
|
-
/**
|
|
299
|
-
|
|
300
|
+
/** 将 body 设置为 fp 所指文件的 ReadStream
|
|
301
|
+
检查 fp 是否合法,设置对应的 content-type,支持缓存,支持分块下载
|
|
302
|
+
- ctx: Koa.Context
|
|
303
|
+
- fp: 文件路径,有 root 时为相对路径或以 root 开头的绝对路径,无 root 时必须是绝对路径
|
|
304
|
+
- options?:
|
|
305
|
+
- root?: 只能访问 root 下面的文件
|
|
306
|
+
- absolute?: `false` 使用绝对路径指定发送的文件
|
|
307
|
+
- download?: `undefined` 在 response.headers 中加上 content-disposition: attachment 指示浏览器下载文件 */
|
|
308
|
+
async fsend(ctx, fp, { root, absolute, download, } = {}) {
|
|
309
|
+
assert(root.isdir);
|
|
300
310
|
const { request } = ctx;
|
|
301
311
|
let { response } = ctx;
|
|
302
312
|
if (!absolute && !root)
|
|
303
313
|
throw new Error('fsend with `!absolute && !root`');
|
|
304
|
-
if (absolute)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (
|
|
308
|
-
|
|
309
|
-
if (path.startsWith('/'))
|
|
310
|
-
path = path.slice(1);
|
|
314
|
+
if (!absolute) {
|
|
315
|
+
if (fp.startsWith(root))
|
|
316
|
+
fp = fp.slice(root.length);
|
|
317
|
+
if (fp.startsWith('/'))
|
|
318
|
+
fp = fp.slice(1);
|
|
311
319
|
try {
|
|
312
|
-
|
|
320
|
+
fp = resolve_safely(root, fp).fp;
|
|
313
321
|
}
|
|
314
322
|
catch (error) {
|
|
315
|
-
error.message += `,
|
|
323
|
+
error.message += `, fp: ${fp}`;
|
|
316
324
|
throw error;
|
|
317
325
|
}
|
|
318
326
|
}
|
|
319
327
|
// stat
|
|
320
328
|
let stats;
|
|
321
329
|
try {
|
|
322
|
-
stats = await fstat(
|
|
330
|
+
stats = await fstat(fp);
|
|
323
331
|
}
|
|
324
332
|
catch (error) {
|
|
325
333
|
if (['ENOENT', 'ENAMETOOLONG', 'ENOTDIR'].includes(error.code)) {
|
|
@@ -330,12 +338,6 @@ export class Server {
|
|
|
330
338
|
error.message = `fs.stat 出错: ${error.message}`;
|
|
331
339
|
throw error;
|
|
332
340
|
}
|
|
333
|
-
// size limit
|
|
334
|
-
// if (stats.size >= 100 * 2**20) {
|
|
335
|
-
// let error = new Error('body.length >= 100 mb')
|
|
336
|
-
// ;(error as any).status = 500
|
|
337
|
-
// throw error
|
|
338
|
-
// }
|
|
339
341
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Ranges
|
|
340
342
|
// advertise server support of partial requests
|
|
341
343
|
response.set('accept-ranges', 'bytes');
|
|
@@ -343,15 +345,17 @@ export class Server {
|
|
|
343
345
|
response.set('cache-control', 'max-age=0, must-revalidate');
|
|
344
346
|
if (!response.get('last-modified'))
|
|
345
347
|
response.set('last-modified', stats.mtime.toUTCString());
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
response.set('content-
|
|
348
|
+
if (download)
|
|
349
|
+
response.set('content-disposition', `attachment; filename="${encodeURIComponent(fp.fname)}"`);
|
|
350
|
+
if (!response.get('content-type')) {
|
|
351
|
+
const fext = fp.fext;
|
|
352
|
+
response.set('content-type', (this.js_exts.has(fext) ? 'text/javascript; chatset=utf-8' : contentType(fp.fext))
|
|
353
|
+
|| 'application/octet-stream');
|
|
354
|
+
}
|
|
351
355
|
if (request.fresh) {
|
|
352
356
|
response.status = 304;
|
|
353
357
|
// 以上会自动设置 response.body = null
|
|
354
|
-
return
|
|
358
|
+
return fp;
|
|
355
359
|
}
|
|
356
360
|
if (request.headers.range)
|
|
357
361
|
try {
|
|
@@ -369,19 +373,19 @@ export class Server {
|
|
|
369
373
|
response.status = 206;
|
|
370
374
|
response.set('content-length', String(chunksize));
|
|
371
375
|
response.set('content-range', `bytes ${start}-${end}/${stats.size}`);
|
|
372
|
-
response.body = createReadStream(
|
|
376
|
+
response.body = createReadStream(fp, { start, end });
|
|
373
377
|
}
|
|
374
|
-
catch
|
|
378
|
+
catch {
|
|
375
379
|
response.status = 416;
|
|
376
380
|
response.set('content-length', String(stats.size));
|
|
377
381
|
response.set('content-range', `bytes */${stats.size}`);
|
|
378
|
-
response.body = createReadStream(
|
|
382
|
+
response.body = createReadStream(fp);
|
|
379
383
|
}
|
|
380
384
|
else {
|
|
381
385
|
response.set('content-length', String(stats.size));
|
|
382
|
-
response.body = createReadStream(
|
|
386
|
+
response.body = createReadStream(fp);
|
|
383
387
|
}
|
|
384
|
-
return
|
|
388
|
+
return fp;
|
|
385
389
|
}
|
|
386
390
|
}
|
|
387
391
|
//# sourceMappingURL=server.js.map
|