xshell 1.2.50 → 1.2.53
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/builder.js +4 -4
- package/file.d.ts +38 -3
- package/file.js +109 -2
- package/package.json +8 -8
- package/prototype.browser.d.ts +1 -0
- package/prototype.browser.js +7 -4
- package/prototype.d.ts +1 -0
- package/prototype.js +7 -4
- package/utils.browser.d.ts +4 -0
- package/utils.browser.js +11 -1
- package/utils.d.ts +4 -2
- package/utils.js +9 -4
package/builder.js
CHANGED
|
@@ -2,7 +2,7 @@ import { fileURLToPath } from 'url';
|
|
|
2
2
|
import { not_empty } from "./prototype.js";
|
|
3
3
|
import { noprint } from "./process.js";
|
|
4
4
|
import { Lock, filter_values } from "./utils.js";
|
|
5
|
-
import { fcopy, fmkdir, fwrite, fread } from "./file.js";
|
|
5
|
+
import { fcopy, fmkdir, fwrite, fread, print_info } from "./file.js";
|
|
6
6
|
import { path } from "./path.js";
|
|
7
7
|
const monaco_files = [
|
|
8
8
|
'loader.js',
|
|
@@ -563,7 +563,7 @@ export class Bundler {
|
|
|
563
563
|
await this.build(print);
|
|
564
564
|
await this.close();
|
|
565
565
|
}
|
|
566
|
-
async build_htmls(print =
|
|
566
|
+
async build_htmls(print = print_info) {
|
|
567
567
|
await Promise.all(Object.entries(this.htmls).map(async ([fp_html, { entry = 'index.js', device_viewport: device_width = false, icon, manifest, dependencies: _dependencies = [], title, scripts, mscripts, notice = false, heads, }]) => {
|
|
568
568
|
const html_template = '<!doctype html>\n' +
|
|
569
569
|
'<html>\n' +
|
|
@@ -606,7 +606,7 @@ export class Bundler {
|
|
|
606
606
|
if (print.files)
|
|
607
607
|
console.log(`${this.name} 的所有 html 页面构建完成`);
|
|
608
608
|
}
|
|
609
|
-
async build_single_js(print =
|
|
609
|
+
async build_single_js(print = print_info) {
|
|
610
610
|
const { js, entry } = this.single_js;
|
|
611
611
|
await fwrite(`${this.fpd_out}${js}`, [
|
|
612
612
|
...this.resolve_dependency_assets(this.dependencies, false, { production: this.production })
|
|
@@ -643,7 +643,7 @@ export class Bundler {
|
|
|
643
643
|
];
|
|
644
644
|
}).flat();
|
|
645
645
|
}
|
|
646
|
-
async copy_files({ dependencies: _dependencies = this.dependencies, production = this.production, assets = this.assets, fpd_root = this.fpd_root, fpd_out = this.fpd_out, print =
|
|
646
|
+
async copy_files({ dependencies: _dependencies = this.dependencies, production = this.production, assets = this.assets, fpd_root = this.fpd_root, fpd_out = this.fpd_out, print = print_info } = {}) {
|
|
647
647
|
if (print.files)
|
|
648
648
|
console.log(`复制 ${this.name} 的依赖文件到 ${this.fpd_out}`);
|
|
649
649
|
if (_dependencies.length)
|
package/file.d.ts
CHANGED
|
@@ -6,6 +6,20 @@ export type FileHandle = fsp.FileHandle & {
|
|
|
6
6
|
fp: string;
|
|
7
7
|
};
|
|
8
8
|
export declare const encodings: readonly ["utf-8", "gb18030", "shift-jis", "utf-16le"];
|
|
9
|
+
export declare const print_files: {
|
|
10
|
+
readonly info: true;
|
|
11
|
+
readonly files: true;
|
|
12
|
+
};
|
|
13
|
+
export declare const print_info: {
|
|
14
|
+
readonly info: true;
|
|
15
|
+
readonly files: false;
|
|
16
|
+
};
|
|
17
|
+
export declare const print_info_options: {
|
|
18
|
+
readonly print: {
|
|
19
|
+
readonly info: true;
|
|
20
|
+
readonly files: false;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
9
23
|
export declare const ramdisk: boolean;
|
|
10
24
|
/** fp 所指向的 文件/ 文件夹 是否存在
|
|
11
25
|
Does the file/folder pointed to by fp exist? */
|
|
@@ -19,7 +33,8 @@ export declare function fexists(fp: string, { print }?: {
|
|
|
19
33
|
|
|
20
34
|
- flags: `'r'` https://nodejs.org/docs/latest/api/fs.html#file-system-flags
|
|
21
35
|
- options?:
|
|
22
|
-
- mode?: `'0o666'` Sets the file mode (permission and sticky bits) if the file is created.
|
|
36
|
+
- mode?: `'0o666'` Sets the file mode (permission and sticky bits) if the file is created.
|
|
37
|
+
- print?: `false` */
|
|
23
38
|
export declare function fopen(fp: string, flags?: string | number, { mode, print }?: {
|
|
24
39
|
mode?: fs.Mode;
|
|
25
40
|
print?: boolean;
|
|
@@ -61,11 +76,11 @@ export interface FWriteOptions {
|
|
|
61
76
|
print?: boolean;
|
|
62
77
|
mtime?: number;
|
|
63
78
|
}
|
|
64
|
-
/** 写入 data 到 fp
|
|
79
|
+
/** 写入 data 到 fp 路径或句柄所指的文件
|
|
65
80
|
会在因不存在父文件夹导致写入失败时,自动创建父文件夹,并再次尝试写入
|
|
66
81
|
最好预先创建父文件夹,减少文件系统操作,提升性能
|
|
67
82
|
返回写入的文件路径
|
|
68
|
-
- fp:
|
|
83
|
+
- fp: 目标文件完整路径或句柄
|
|
69
84
|
- data: 支持下面几种类型
|
|
70
85
|
- string: 写入文本
|
|
71
86
|
- Uint8Array, Buffer: 写入二进制 buffer
|
|
@@ -268,4 +283,24 @@ export declare function ftail(fp: string, handler: (lines: string[]) => void | P
|
|
|
268
283
|
export declare function freplace(fp: string, pattern: string | RegExp, replacement: string, { print }?: {
|
|
269
284
|
print?: boolean;
|
|
270
285
|
}): Promise<void>;
|
|
286
|
+
interface FSyncOptions {
|
|
287
|
+
print?: {
|
|
288
|
+
info: boolean;
|
|
289
|
+
files: boolean;
|
|
290
|
+
};
|
|
291
|
+
delete?: boolean;
|
|
292
|
+
}
|
|
293
|
+
/** 同步文件或文件夹 fp_src 到 fp_dst ,返回 fp_dst
|
|
294
|
+
- fp_src: 源文件或文件夹
|
|
295
|
+
- fp_dst: 目标文件或文件夹
|
|
296
|
+
- options?:
|
|
297
|
+
- print?: `print_info`
|
|
298
|
+
- delete?: `false` */
|
|
299
|
+
export declare function fsync(fp_src: string, fp_dst: string, { print, delete: _delete }?: FSyncOptions): Promise<string>;
|
|
300
|
+
/** 根据对象的 fp 属性进行字典序排序 */
|
|
301
|
+
export declare function fp_sorter(l: {
|
|
302
|
+
fp: string;
|
|
303
|
+
}, r: {
|
|
304
|
+
fp: string;
|
|
305
|
+
}): 0 | 1 | -1;
|
|
271
306
|
export declare function log_action(action: string, fp_src: string, fp_dst: string, sep?: string): void;
|
package/file.js
CHANGED
|
@@ -4,10 +4,13 @@ import { t } from "./i18n/instance.js";
|
|
|
4
4
|
import { noop, to_json } from "./prototype.js";
|
|
5
5
|
import { pack, parse } from "./io.js";
|
|
6
6
|
import { path } from "./path.js";
|
|
7
|
-
import { check, Lock, WritableMemoryStream, url_width, throttle, decode } from "./utils.js";
|
|
7
|
+
import { check, Lock, WritableMemoryStream, url_width, throttle, decode, strcmp } from "./utils.js";
|
|
8
8
|
import { noprint } from "./process.js";
|
|
9
9
|
export { fsp };
|
|
10
10
|
export const encodings = ['utf-8', 'gb18030', 'shift-jis', 'utf-16le'];
|
|
11
|
+
export const print_files = { info: true, files: true };
|
|
12
|
+
export const print_info = { info: true, files: false };
|
|
13
|
+
export const print_info_options = { print: print_info };
|
|
11
14
|
export const ramdisk = fexists('T:/TEMP/', noprint);
|
|
12
15
|
/** fp 所指向的 文件/ 文件夹 是否存在
|
|
13
16
|
Does the file/folder pointed to by fp exist? */
|
|
@@ -24,7 +27,8 @@ export function fexists(fp, { print = true } = {}) {
|
|
|
24
27
|
|
|
25
28
|
- flags: `'r'` https://nodejs.org/docs/latest/api/fs.html#file-system-flags
|
|
26
29
|
- options?:
|
|
27
|
-
- mode?: `'0o666'` Sets the file mode (permission and sticky bits) if the file is created.
|
|
30
|
+
- mode?: `'0o666'` Sets the file mode (permission and sticky bits) if the file is created.
|
|
31
|
+
- print?: `false` */
|
|
28
32
|
export async function fopen(fp, flags = 'r', { mode, print } = {}) {
|
|
29
33
|
if (print)
|
|
30
34
|
console.log(t('打开文件'), fp);
|
|
@@ -824,6 +828,109 @@ export async function freplace(fp, pattern, replacement, { print = true } = {})
|
|
|
824
828
|
await fwrite(fp, (await fread(fp, { print }))
|
|
825
829
|
.replaceAll(pattern, replacement), { print });
|
|
826
830
|
}
|
|
831
|
+
/** 同步文件或文件夹 fp_src 到 fp_dst ,返回 fp_dst
|
|
832
|
+
- fp_src: 源文件或文件夹
|
|
833
|
+
- fp_dst: 目标文件或文件夹
|
|
834
|
+
- options?:
|
|
835
|
+
- print?: `print_info`
|
|
836
|
+
- delete?: `false` */
|
|
837
|
+
export async function fsync(fp_src, fp_dst, { print = print_files, delete: _delete = false } = {}) {
|
|
838
|
+
if (print.info)
|
|
839
|
+
log_action('同步', fp_src, fp_dst);
|
|
840
|
+
if (fp_src.isdir) {
|
|
841
|
+
let dst_stats;
|
|
842
|
+
try {
|
|
843
|
+
dst_stats = (await flist(fp_dst, { stats: true, deep: true, print: false })).sort(fp_sorter);
|
|
844
|
+
}
|
|
845
|
+
catch (error) {
|
|
846
|
+
if (error.code === 'ENOENT')
|
|
847
|
+
return fcopy(fp_src, fp_dst, { print: print.info });
|
|
848
|
+
else
|
|
849
|
+
throw error;
|
|
850
|
+
}
|
|
851
|
+
const src_stats = (await flist(fp_src, { deep: true, print: false, stats: true }))
|
|
852
|
+
.sort(fp_sorter);
|
|
853
|
+
let p = 0, q = 0;
|
|
854
|
+
let tasks = [];
|
|
855
|
+
let fpd_changeds = [];
|
|
856
|
+
let fpd_deleteds = [];
|
|
857
|
+
const sync_change = ({ fp }, modify) => {
|
|
858
|
+
if (fpd_changeds.some(fpd_created => fp.startsWith(fpd_created)))
|
|
859
|
+
return;
|
|
860
|
+
if (fp.isdir)
|
|
861
|
+
fpd_changeds.push(fp);
|
|
862
|
+
if (print.files)
|
|
863
|
+
log_action(`同步${modify ? '修改' : '新增'}`, `${fp_src}${fp}`, `${fp_dst}${fp}`);
|
|
864
|
+
tasks.push(fcopy(`${fp_src}${fp}`, `${fp_dst}${fp}`, noprint));
|
|
865
|
+
};
|
|
866
|
+
const sync_delete = ({ fp }) => {
|
|
867
|
+
if (fpd_deleteds.some(fpd_deleted => fp.startsWith(fpd_deleted)))
|
|
868
|
+
return;
|
|
869
|
+
if (fp.isdir)
|
|
870
|
+
fpd_deleteds.push(fp);
|
|
871
|
+
if (print.files)
|
|
872
|
+
console.log('同步删除', `${fp_dst}${fp}`);
|
|
873
|
+
tasks.push(fdelete(`${fp_dst}${fp}`, noprint));
|
|
874
|
+
};
|
|
875
|
+
for (; p < src_stats.length && q < dst_stats.length;) {
|
|
876
|
+
const src = src_stats[p];
|
|
877
|
+
const dst = dst_stats[q];
|
|
878
|
+
if (src.fp === dst.fp) {
|
|
879
|
+
if (!src.fp.isdir && !compare_stat(src, dst))
|
|
880
|
+
sync_change(src, true);
|
|
881
|
+
++p;
|
|
882
|
+
++q;
|
|
883
|
+
}
|
|
884
|
+
else if (src.fp < dst.fp) { // dst 缺少了文件 / 文件夹
|
|
885
|
+
sync_change(src, false);
|
|
886
|
+
++p;
|
|
887
|
+
}
|
|
888
|
+
else { // dst 多了文件 / 文件夹
|
|
889
|
+
if (_delete)
|
|
890
|
+
sync_delete(dst);
|
|
891
|
+
++q;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
for (; p < src_stats.length; ++p)
|
|
895
|
+
sync_change(src_stats[p], false);
|
|
896
|
+
if (_delete)
|
|
897
|
+
for (; q < dst_stats.length; ++q)
|
|
898
|
+
sync_delete(dst_stats[q]);
|
|
899
|
+
await Promise.all(tasks);
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
let same = true;
|
|
903
|
+
const [src_stat, dst_stat] = await Promise.all([
|
|
904
|
+
fstat(fp_src),
|
|
905
|
+
(async () => {
|
|
906
|
+
try {
|
|
907
|
+
return await fstat(fp_dst);
|
|
908
|
+
}
|
|
909
|
+
catch (error) {
|
|
910
|
+
if (error.code === 'ENOENT')
|
|
911
|
+
same = false;
|
|
912
|
+
else
|
|
913
|
+
throw error;
|
|
914
|
+
}
|
|
915
|
+
})()
|
|
916
|
+
]);
|
|
917
|
+
if (same)
|
|
918
|
+
same = compare_stat(src_stat, dst_stat);
|
|
919
|
+
if (same)
|
|
920
|
+
log_action('跳过相同', fp_src, fp_dst);
|
|
921
|
+
else
|
|
922
|
+
await fcopy(fp_src, fp_dst, { print: print.info });
|
|
923
|
+
}
|
|
924
|
+
return fp_dst;
|
|
925
|
+
}
|
|
926
|
+
/** 根据对象的 fp 属性进行字典序排序 */
|
|
927
|
+
export function fp_sorter(l, r) {
|
|
928
|
+
return strcmp(l.fp, r.fp);
|
|
929
|
+
}
|
|
930
|
+
/** 返回修改时间与大小是否相同 */
|
|
931
|
+
function compare_stat(src, dst) {
|
|
932
|
+
return src.size === dst.size && Math.abs(Number(src.mtimeMs) - Number(dst.mtimeMs)) <= 1;
|
|
933
|
+
}
|
|
827
934
|
export function log_action(action, fp_src, fp_dst, sep = '->') {
|
|
828
935
|
console.log(`${`${action} ${fp_src}`.pad(url_width)} ${sep} ${fp_dst}`);
|
|
829
936
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.53",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
"@babel/parser": "^7.27.7",
|
|
54
54
|
"@babel/traverse": "^7.27.7",
|
|
55
55
|
"@koa/cors": "^5.0.0",
|
|
56
|
-
"@stylistic/eslint-plugin": "^5.
|
|
56
|
+
"@stylistic/eslint-plugin": "^5.1.0",
|
|
57
57
|
"@svgr/webpack": "^8.1.0",
|
|
58
58
|
"@types/sass-loader": "^8.0.9",
|
|
59
59
|
"@types/ws": "^8.18.1",
|
|
60
|
-
"@typescript-eslint/eslint-plugin": "^8.35.
|
|
61
|
-
"@typescript-eslint/parser": "^8.35.
|
|
62
|
-
"@typescript-eslint/utils": "^8.35.
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^8.35.1",
|
|
61
|
+
"@typescript-eslint/parser": "^8.35.1",
|
|
62
|
+
"@typescript-eslint/utils": "^8.35.1",
|
|
63
63
|
"archiver": "^7.0.1",
|
|
64
64
|
"chalk": "^5.4.1",
|
|
65
65
|
"commander": "^14.0.0",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"eslint-plugin-import": "^2.32.0",
|
|
70
70
|
"eslint-plugin-react": "^7.37.5",
|
|
71
71
|
"https-proxy-agent": "^7.0.6",
|
|
72
|
-
"i18next": "^25.
|
|
72
|
+
"i18next": "^25.3.0",
|
|
73
73
|
"i18next-scanner": "^4.6.0",
|
|
74
74
|
"koa": "^3.0.0",
|
|
75
75
|
"koa-compress": "^5.1.1",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"undici": "^7.11.0",
|
|
93
93
|
"webpack": "^5.99.9",
|
|
94
94
|
"webpack-bundle-analyzer": "^4.10.2",
|
|
95
|
-
"ws": "^8.18.
|
|
95
|
+
"ws": "^8.18.3"
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@babel/types": "^7.27.7",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"@types/koa": "^2.15.0",
|
|
104
104
|
"@types/koa-compress": "^4.0.6",
|
|
105
105
|
"@types/mime-types": "^3.0.1",
|
|
106
|
-
"@types/node": "^24.0.
|
|
106
|
+
"@types/node": "^24.0.8",
|
|
107
107
|
"@types/react": "^19.1.8",
|
|
108
108
|
"@types/tough-cookie": "^4.0.5",
|
|
109
109
|
"@types/ua-parser-js": "^0.7.39",
|
package/prototype.browser.d.ts
CHANGED
|
@@ -224,6 +224,7 @@ export declare const empty: (value: any) => boolean;
|
|
|
224
224
|
export declare const is_key_type: IsKeyType;
|
|
225
225
|
type IsKeyType = (key: any) => key is string | number | symbol;
|
|
226
226
|
export declare function rethrow(error: Error): void;
|
|
227
|
+
export declare function to_snake_case(str: string): string;
|
|
227
228
|
export declare function to_method_property_descriptors(methods: {
|
|
228
229
|
[name: string]: Function;
|
|
229
230
|
}): PropertyDescriptorMap;
|
package/prototype.browser.js
CHANGED
|
@@ -13,6 +13,12 @@ export const is_key_type = ((key) => key_types.includes(typeof key));
|
|
|
13
13
|
export function rethrow(error) {
|
|
14
14
|
throw error;
|
|
15
15
|
}
|
|
16
|
+
export function to_snake_case(str) {
|
|
17
|
+
return str.replace(/([A-Z])/g, '_$1')
|
|
18
|
+
.toLowerCase()
|
|
19
|
+
.replace('-', '_')
|
|
20
|
+
.strip_if_start('_');
|
|
21
|
+
}
|
|
16
22
|
export function to_method_property_descriptors(methods) {
|
|
17
23
|
return Object.fromEntries(Object.entries(methods)
|
|
18
24
|
.map(([name, value]) => ([name, {
|
|
@@ -120,10 +126,7 @@ Object.defineProperties(String.prototype, {
|
|
|
120
126
|
return new RegExp(this.replace(new RegExp(`[${replace_chars}]`, 'g'), '\\$&'), flags);
|
|
121
127
|
},
|
|
122
128
|
to_snake_case() {
|
|
123
|
-
return this
|
|
124
|
-
.toLowerCase()
|
|
125
|
-
.replace('-', '_')
|
|
126
|
-
.replace(/^_+/, '');
|
|
129
|
+
return to_snake_case(this);
|
|
127
130
|
},
|
|
128
131
|
refmt(pattern, pattern_, preservations = '', flags = '', transformer = (name, value) => value || '', pattern_placeholder = /\{.*?\}/g) {
|
|
129
132
|
// --- 转换 pattern 为 pattern_regx
|
package/prototype.d.ts
CHANGED
|
@@ -254,6 +254,7 @@ export declare const empty: (value: any) => boolean;
|
|
|
254
254
|
export declare const is_key_type: IsKeyType;
|
|
255
255
|
type IsKeyType = (key: any) => key is string | number | symbol;
|
|
256
256
|
export declare function rethrow(error: Error): void;
|
|
257
|
+
export declare function to_snake_case(str: string): string;
|
|
257
258
|
export declare function to_method_property_descriptors(methods: {
|
|
258
259
|
[name: string]: Function;
|
|
259
260
|
}): PropertyDescriptorMap;
|
package/prototype.js
CHANGED
|
@@ -14,6 +14,12 @@ export const is_key_type = ((key) => key_types.includes(typeof key));
|
|
|
14
14
|
export function rethrow(error) {
|
|
15
15
|
throw error;
|
|
16
16
|
}
|
|
17
|
+
export function to_snake_case(str) {
|
|
18
|
+
return str.replace(/([A-Z])/g, '_$1')
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
.replace('-', '_')
|
|
21
|
+
.strip_if_start('_');
|
|
22
|
+
}
|
|
17
23
|
export function to_method_property_descriptors(methods) {
|
|
18
24
|
return Object.fromEntries(Object.entries(methods)
|
|
19
25
|
.map(([name, value]) => ([name, {
|
|
@@ -124,10 +130,7 @@ if (!globalThis.my_prototype_defined) {
|
|
|
124
130
|
return new RegExp(this.replace(new RegExp(`[${replace_chars}]`, 'g'), '\\$&'), flags);
|
|
125
131
|
},
|
|
126
132
|
to_snake_case() {
|
|
127
|
-
return this
|
|
128
|
-
.toLowerCase()
|
|
129
|
-
.replace('-', '_')
|
|
130
|
-
.replace(/^_+/, '');
|
|
133
|
+
return to_snake_case(this);
|
|
131
134
|
},
|
|
132
135
|
refmt(pattern, pattern_, preservations = '', flags = '', transformer = (name, value) => value || '', pattern_placeholder = /\{.*?\}/g) {
|
|
133
136
|
// --- 转换 pattern 为 pattern_regx
|
package/utils.browser.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export declare function unique<TObj>(iterable: TObj[] | Iterable<TObj>, mapper?:
|
|
|
16
16
|
export declare function seq<T = number>(n: number, generator?: (index: number) => T): T[];
|
|
17
17
|
/** 将 keys, values 数组按对应的顺序组合成一个对象 */
|
|
18
18
|
export declare function zip_object<TValue>(keys: (string | number)[], values: TValue[]): Record<string, TValue>;
|
|
19
|
+
/** 映射对象中的 keys, 返回新对象
|
|
20
|
+
- obj: 对象
|
|
21
|
+
- mapper?: `to_snake_case` (key: string) => string 或者 Record<string, string> */
|
|
22
|
+
export declare function map_keys<TReturn>(obj: any, mapper?: ((key: string) => string) | Record<string, string>): TReturn;
|
|
19
23
|
/** 过滤对象中的 values, 返回新对象
|
|
20
24
|
- obj
|
|
21
25
|
- filter?: `not_empty` */
|
package/utils.browser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { is_key_type, select, not_empty, ident } from "./prototype.browser.js";
|
|
1
|
+
import { is_key_type, select, not_empty, ident, to_snake_case } from "./prototype.browser.js";
|
|
2
2
|
import { t } from "./i18n/instance.js";
|
|
3
3
|
export function assert(assertion, message) {
|
|
4
4
|
if (!assertion) {
|
|
@@ -54,6 +54,16 @@ export function zip_object(keys, values) {
|
|
|
54
54
|
return obj;
|
|
55
55
|
}, {});
|
|
56
56
|
}
|
|
57
|
+
/** 映射对象中的 keys, 返回新对象
|
|
58
|
+
- obj: 对象
|
|
59
|
+
- mapper?: `to_snake_case` (key: string) => string 或者 Record<string, string> */
|
|
60
|
+
export function map_keys(obj, mapper = to_snake_case) {
|
|
61
|
+
return Object.fromEntries(Object.entries(obj)
|
|
62
|
+
.map(typeof mapper === 'function' ?
|
|
63
|
+
([key, value]) => [mapper(key), value]
|
|
64
|
+
:
|
|
65
|
+
([key, value]) => [mapper[key] || key, value]));
|
|
66
|
+
}
|
|
57
67
|
/** 过滤对象中的 values, 返回新对象
|
|
58
68
|
- obj
|
|
59
69
|
- filter?: `not_empty` */
|
package/utils.d.ts
CHANGED
|
@@ -25,8 +25,10 @@ export declare function seq<T = number>(n: number, generator?: (index: number) =
|
|
|
25
25
|
export declare function sort_keys<TObj>(obj: TObj): TObj;
|
|
26
26
|
/** 将 keys, values 数组按对应的顺序组合成一个对象 */
|
|
27
27
|
export declare function zip_object<TValue>(keys: (string | number)[], values: TValue[]): Record<string, TValue>;
|
|
28
|
-
/** 映射对象中的 keys, 返回新对象
|
|
29
|
-
|
|
28
|
+
/** 映射对象中的 keys, 返回新对象
|
|
29
|
+
- obj: 对象
|
|
30
|
+
- mapper?: `to_snake_case` (key: string) => string 或者 Record<string, string> */
|
|
31
|
+
export declare function map_keys<TReturn>(obj: any, mapper?: ((key: string) => string) | Record<string, string>): TReturn;
|
|
30
32
|
/** 映射对象中的 values, 返回新对象 */
|
|
31
33
|
export declare function map_values<TValue, TNewValue>(obj: {
|
|
32
34
|
[key: string]: TValue;
|
package/utils.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Stream, Writable, Transform } from 'stream';
|
|
|
2
2
|
import util from 'util';
|
|
3
3
|
import timers from 'timers/promises';
|
|
4
4
|
import { t } from "./i18n/instance.js";
|
|
5
|
-
import { select, not_empty, is_key_type, noop, ident } from "./prototype.js";
|
|
5
|
+
import { select, not_empty, is_key_type, noop, ident, to_snake_case } from "./prototype.js";
|
|
6
6
|
/** `180` 输出字符宽度 */
|
|
7
7
|
export const output_width = 180;
|
|
8
8
|
export const url_width = 52;
|
|
@@ -81,10 +81,15 @@ export function zip_object(keys, values) {
|
|
|
81
81
|
return obj;
|
|
82
82
|
}, {});
|
|
83
83
|
}
|
|
84
|
-
/** 映射对象中的 keys, 返回新对象
|
|
85
|
-
|
|
84
|
+
/** 映射对象中的 keys, 返回新对象
|
|
85
|
+
- obj: 对象
|
|
86
|
+
- mapper?: `to_snake_case` (key: string) => string 或者 Record<string, string> */
|
|
87
|
+
export function map_keys(obj, mapper = to_snake_case) {
|
|
86
88
|
return Object.fromEntries(Object.entries(obj)
|
|
87
|
-
.map(
|
|
89
|
+
.map(typeof mapper === 'function' ?
|
|
90
|
+
([key, value]) => [mapper(key), value]
|
|
91
|
+
:
|
|
92
|
+
([key, value]) => [mapper[key] || key, value]));
|
|
88
93
|
}
|
|
89
94
|
/** 映射对象中的 values, 返回新对象 */
|
|
90
95
|
export function map_values(obj, mapper) {
|