xshell 1.2.29 → 1.2.31
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/fflate.d.ts +359 -0
- package/fflate.js +1175 -0
- package/file.d.ts +42 -1
- package/file.js +222 -4
- package/package.json +7 -7
package/file.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { promises as fsp, default as fs } from 'fs';
|
|
2
|
+
import type { UnzipFileFilter } from './fflate.ts';
|
|
2
3
|
export { fsp };
|
|
3
4
|
export type Encoding = 'utf-8' | 'gb18030' | 'shift-jis' | 'utf-16le';
|
|
4
5
|
export type FileHandle = fsp.FileHandle & {
|
|
@@ -71,7 +72,7 @@ export interface FWriteOptions {
|
|
|
71
72
|
- any: 通过 JSON.stringify 转为文本后写入文件
|
|
72
73
|
- options?:
|
|
73
74
|
- print?: `true`
|
|
74
|
-
- mtime?: 在写入后设置修改时间 */
|
|
75
|
+
- mtime?: 在写入后设置修改时间 (utc 毫秒数) */
|
|
75
76
|
export declare function fwrite(fp: string, data: string | Uint8Array | any, options?: FWriteOptions): Promise<string>;
|
|
76
77
|
export declare function fwrite(fp: FileHandle, data: string | Uint8Array | any, options?: FWriteOptions): Promise<FileHandle>;
|
|
77
78
|
export declare function fwrite(fp: string | FileHandle, data: string | Uint8Array | any, options?: FWriteOptions): Promise<string | FileHandle>;
|
|
@@ -220,6 +221,46 @@ export declare function fzip(data: string | Record<string, string | Uint8Array>,
|
|
|
220
221
|
- info?: `true` 开始压缩、压缩完成
|
|
221
222
|
- files?: `true` 打印压缩文件列表 */
|
|
222
223
|
export declare function zip(data: string | Record<string, string | Uint8Array>, options?: ZipOptions): Promise<Buffer>;
|
|
224
|
+
export interface UnzipOptions {
|
|
225
|
+
encoding?: Encoding;
|
|
226
|
+
dryrun?: boolean;
|
|
227
|
+
print?: {
|
|
228
|
+
info: boolean;
|
|
229
|
+
files: boolean;
|
|
230
|
+
};
|
|
231
|
+
strip?: boolean;
|
|
232
|
+
clean?: boolean;
|
|
233
|
+
filter?: UnzipFileFilter;
|
|
234
|
+
}
|
|
235
|
+
/** 解压 zip 包至文件夹,返回 fpd_out
|
|
236
|
+
- zip:
|
|
237
|
+
- 包路径 (string) 或
|
|
238
|
+
- 包数据 (Uint8Array | ArrayBuffer)
|
|
239
|
+
- fpd_out: 解压到文件夹的完整路径 (string)
|
|
240
|
+
- options?:
|
|
241
|
+
- encoding?: `utf-8` 压缩包文件名编码
|
|
242
|
+
- dryrun?: 只是打印一下文件名列表,看看编码是否正确,不实际解压
|
|
243
|
+
- print?:
|
|
244
|
+
- info?: `true` 开始解压、解压完成
|
|
245
|
+
- files?: `true` 打印解压缩的文件列表
|
|
246
|
+
- strip?: `true` 压缩包内只有一个顶层文件夹时自动去除该层级
|
|
247
|
+
- clean?: `true` 忽略解压 ftrashes 这些垃圾文件
|
|
248
|
+
- filter?: 用于过滤要解压的文件, 返回 falsy 值时忽略这个文件,要注意 file.name 为完整的相对路径(没有 strip) */
|
|
249
|
+
export declare function funzip(zip: string | Uint8Array | ArrayBuffer, fpd_out: string, options?: UnzipOptions): Promise<string>;
|
|
250
|
+
/** 解压 zip 包至内存,返回解压后的数据 (Record<文件或文件夹 (以 / 结尾) 的相对路径, Uint8Array>)
|
|
251
|
+
- zip:
|
|
252
|
+
- 压缩文件路径 (string) 或
|
|
253
|
+
- 压缩文件内容 (buffer)
|
|
254
|
+
- options?:
|
|
255
|
+
- encoding?: `utf-8` 压缩包文件名编码
|
|
256
|
+
- dryrun?: 只是打印一下文件名列表,看看编码是否正确,不实际解压
|
|
257
|
+
- print?:
|
|
258
|
+
- info?: `true` 开始解压、解压完成
|
|
259
|
+
- files?: `true` 打印解压缩的文件列表
|
|
260
|
+
- strip?: `true` 压缩包内只有一个顶层文件夹时自动去除该层级
|
|
261
|
+
- clean?: `true` 忽略解压 ftrashes 这些垃圾文件
|
|
262
|
+
- filter?: 用于过滤要解压的文件, 返回 falsy 值时忽略这个文件,要注意 file.name 为完整的相对路径(没有 strip) */
|
|
263
|
+
export declare function unzip(zip: string | Uint8Array | ArrayBuffer, options?: UnzipOptions): Promise<Record<string, Uint8Array>>;
|
|
223
264
|
export declare let fwatchers: Record<string, fs.FSWatcher>;
|
|
224
265
|
/** 跟踪文本文件追加的内容,类似 tail -f */
|
|
225
266
|
export declare function ftail(fp: string, handler: (lines: string[]) => void | Promise<void>, { print }?: {
|
package/file.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { promises as fsp, default as fs } from 'fs';
|
|
2
|
-
import { isUint8Array } from 'util/types';
|
|
2
|
+
import { isArrayBuffer, isUint8Array } from 'util/types';
|
|
3
3
|
import { t } from "./i18n/instance.js";
|
|
4
|
-
import { to_json } from "./prototype.js";
|
|
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 } from "./utils.js";
|
|
7
|
+
import { check, Lock, WritableMemoryStream, url_width, throttle, decode } 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'];
|
|
@@ -100,7 +100,8 @@ export async function fwrite(fp, data, { print = true, mtime } = {}) {
|
|
|
100
100
|
let handle = await fopen(fp, 'w', noprint);
|
|
101
101
|
try {
|
|
102
102
|
await handle.write(data, 0);
|
|
103
|
-
|
|
103
|
+
const mseconds = mtime / 1000;
|
|
104
|
+
await handle.utimes(mseconds, mseconds);
|
|
104
105
|
}
|
|
105
106
|
finally {
|
|
106
107
|
await handle.close();
|
|
@@ -544,6 +545,223 @@ async function _zip(data, fp_zip, { dirname, print = { files: true, info: true }
|
|
|
544
545
|
console.log(`压缩完成,总大小 ${size.to_fsize_str()}`);
|
|
545
546
|
return fp_zip || ostream.pbuffer;
|
|
546
547
|
}
|
|
548
|
+
/** 解压 zip 包至文件夹,返回 fpd_out
|
|
549
|
+
- zip:
|
|
550
|
+
- 包路径 (string) 或
|
|
551
|
+
- 包数据 (Uint8Array | ArrayBuffer)
|
|
552
|
+
- fpd_out: 解压到文件夹的完整路径 (string)
|
|
553
|
+
- options?:
|
|
554
|
+
- encoding?: `utf-8` 压缩包文件名编码
|
|
555
|
+
- dryrun?: 只是打印一下文件名列表,看看编码是否正确,不实际解压
|
|
556
|
+
- print?:
|
|
557
|
+
- info?: `true` 开始解压、解压完成
|
|
558
|
+
- files?: `true` 打印解压缩的文件列表
|
|
559
|
+
- strip?: `true` 压缩包内只有一个顶层文件夹时自动去除该层级
|
|
560
|
+
- clean?: `true` 忽略解压 ftrashes 这些垃圾文件
|
|
561
|
+
- filter?: 用于过滤要解压的文件, 返回 falsy 值时忽略这个文件,要注意 file.name 为完整的相对路径(没有 strip) */
|
|
562
|
+
export async function funzip(zip, fpd_out, options) {
|
|
563
|
+
await _unzip(zip, fpd_out, options);
|
|
564
|
+
return fpd_out;
|
|
565
|
+
}
|
|
566
|
+
/** 解压 zip 包至内存,返回解压后的数据 (Record<文件或文件夹 (以 / 结尾) 的相对路径, Uint8Array>)
|
|
567
|
+
- zip:
|
|
568
|
+
- 压缩文件路径 (string) 或
|
|
569
|
+
- 压缩文件内容 (buffer)
|
|
570
|
+
- options?:
|
|
571
|
+
- encoding?: `utf-8` 压缩包文件名编码
|
|
572
|
+
- dryrun?: 只是打印一下文件名列表,看看编码是否正确,不实际解压
|
|
573
|
+
- print?:
|
|
574
|
+
- info?: `true` 开始解压、解压完成
|
|
575
|
+
- files?: `true` 打印解压缩的文件列表
|
|
576
|
+
- strip?: `true` 压缩包内只有一个顶层文件夹时自动去除该层级
|
|
577
|
+
- clean?: `true` 忽略解压 ftrashes 这些垃圾文件
|
|
578
|
+
- filter?: 用于过滤要解压的文件, 返回 falsy 值时忽略这个文件,要注意 file.name 为完整的相对路径(没有 strip) */
|
|
579
|
+
export async function unzip(zip, options) {
|
|
580
|
+
return _unzip(zip, undefined, options);
|
|
581
|
+
}
|
|
582
|
+
async function _unzip(zip, fpd_out, { encoding = 'utf-8', dryrun = false, print = {
|
|
583
|
+
files: true,
|
|
584
|
+
info: true
|
|
585
|
+
}, strip = true, clean = true, filter: _filter } = {}) {
|
|
586
|
+
const str_out = typeof fpd_out === 'string' ? fpd_out : '内存';
|
|
587
|
+
let fp_zip = 'buffer';
|
|
588
|
+
let nfiles = 0;
|
|
589
|
+
let size_uncompressed = 0;
|
|
590
|
+
if (isArrayBuffer(zip))
|
|
591
|
+
zip = new Uint8Array(zip);
|
|
592
|
+
else if (typeof zip === 'string') {
|
|
593
|
+
check(['zip', 'apk', 'crx', 'vsix'].includes(zip.fext), 'fp_src 应该以 zip 等后缀结尾');
|
|
594
|
+
fp_zip = zip;
|
|
595
|
+
zip = await fread(zip, { encoding: 'binary', print: false });
|
|
596
|
+
}
|
|
597
|
+
if (print.info)
|
|
598
|
+
log_action(`开始${dryrun ? '测试' : ''}解压`, `${fp_zip} (${zip.length.to_fsize_str()})`, str_out);
|
|
599
|
+
if (!dryrun && fpd_out)
|
|
600
|
+
await fmkdir(fpd_out, { print: print.info });
|
|
601
|
+
let dryrun_unzipped = {};
|
|
602
|
+
const { unzipSync } = await import("./fflate.js");
|
|
603
|
+
let unzipped = unzipSync(zip, {
|
|
604
|
+
decoder: encoding === 'utf-8'
|
|
605
|
+
? decode
|
|
606
|
+
: (() => {
|
|
607
|
+
let text_decoder = new TextDecoder(encoding);
|
|
608
|
+
return buf => text_decoder.decode(buf);
|
|
609
|
+
})(),
|
|
610
|
+
filter(file) {
|
|
611
|
+
const { name, originalSize } = file;
|
|
612
|
+
// 需要递归的清理 LICENSE, .husky/, .husky/pre-commit 等文件
|
|
613
|
+
if (clean) {
|
|
614
|
+
const lname = name.toLowerCase();
|
|
615
|
+
let to_clean = false;
|
|
616
|
+
// xxx/xxx/xxx/a.txt
|
|
617
|
+
// xxx/xxx/
|
|
618
|
+
// a.txt
|
|
619
|
+
// xxx/
|
|
620
|
+
// 处理每个文件夹部分
|
|
621
|
+
let ileft = 0, iright = 0;
|
|
622
|
+
for (; (iright = lname.indexOf('/', ileft) + 1) !== 0; ileft = iright)
|
|
623
|
+
if (ftrashes.has(lname.slice(ileft, iright))) {
|
|
624
|
+
to_clean = true;
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
// 处理最后剩下的文件名
|
|
628
|
+
if (!to_clean && ileft < lname.length && ftrashes.has(lname.slice(ileft)))
|
|
629
|
+
to_clean = true;
|
|
630
|
+
if (to_clean) {
|
|
631
|
+
if (print.files)
|
|
632
|
+
console.log(`清理了 ${name}`);
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (!clean && name.startsWith('__MACOSX/') ||
|
|
637
|
+
_filter && !_filter(file))
|
|
638
|
+
return false;
|
|
639
|
+
else {
|
|
640
|
+
nfiles++;
|
|
641
|
+
size_uncompressed += originalSize;
|
|
642
|
+
if (dryrun) {
|
|
643
|
+
dryrun_unzipped[name] = null;
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
else
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
if (dryrun)
|
|
652
|
+
unzipped = dryrun_unzipped;
|
|
653
|
+
if (strip) {
|
|
654
|
+
let top_common_dir = '';
|
|
655
|
+
for (const key in unzipped) {
|
|
656
|
+
const islash = key.indexOf('/');
|
|
657
|
+
if (islash === -1) { // 顶层有文件
|
|
658
|
+
top_common_dir = '';
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
const top_dir = key.slice(0, islash + 1);
|
|
663
|
+
if (top_common_dir) {
|
|
664
|
+
if (top_common_dir !== top_dir) {
|
|
665
|
+
top_common_dir = '';
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
else
|
|
670
|
+
top_common_dir = top_dir;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (top_common_dir) {
|
|
674
|
+
let unzipped_ = {};
|
|
675
|
+
for (const key in unzipped) {
|
|
676
|
+
const key_ = key.slice(top_common_dir.length);
|
|
677
|
+
if (key_)
|
|
678
|
+
unzipped_[key_] = unzipped[key];
|
|
679
|
+
}
|
|
680
|
+
unzipped = unzipped_;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
let pwrites = [];
|
|
684
|
+
for (const key in unzipped) {
|
|
685
|
+
const value = unzipped[key];
|
|
686
|
+
if (fpd_out && !dryrun) {
|
|
687
|
+
const fp = fpd_out + key;
|
|
688
|
+
if (fp.isdir)
|
|
689
|
+
await fmkdir(fp, noprint);
|
|
690
|
+
else {
|
|
691
|
+
let pwrite = fwrite(fp, value, noprint);
|
|
692
|
+
pwrite.catch(noop); // suppress PromiseRejectionHandledWarning: Promise rejection was handled asynchronously
|
|
693
|
+
pwrites.push(pwrite);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (print.files)
|
|
697
|
+
console.log(`解压出 ${key}`);
|
|
698
|
+
}
|
|
699
|
+
if (fpd_out && !dryrun)
|
|
700
|
+
await Promise.all(pwrites);
|
|
701
|
+
if (print.info)
|
|
702
|
+
console.log(`解压了 ${nfiles} 个文件,解压后文件总大小为 ${size_uncompressed.to_fsize_str()}`);
|
|
703
|
+
if (!fpd_out || dryrun)
|
|
704
|
+
return unzipped;
|
|
705
|
+
}
|
|
706
|
+
const ftrashes = new Set([
|
|
707
|
+
'__MACOSX/',
|
|
708
|
+
'.babelrc.js',
|
|
709
|
+
'.babelrc',
|
|
710
|
+
'.ci',
|
|
711
|
+
'.circleci',
|
|
712
|
+
'.dockerignore',
|
|
713
|
+
'.DS_Store',
|
|
714
|
+
'.editorconfig',
|
|
715
|
+
'.eslintignore',
|
|
716
|
+
'.eslintrc.js',
|
|
717
|
+
'.eslintrc.json',
|
|
718
|
+
'.eslintrc.yaml',
|
|
719
|
+
'.eslintrc.yml',
|
|
720
|
+
'.eslintrc',
|
|
721
|
+
'.gitattributes',
|
|
722
|
+
'.github',
|
|
723
|
+
'.gitignore',
|
|
724
|
+
'.husky/',
|
|
725
|
+
'.jsbeautifyrc',
|
|
726
|
+
'.jshintrc',
|
|
727
|
+
'.mailmap',
|
|
728
|
+
'.markdownlint.js',
|
|
729
|
+
'.node-version',
|
|
730
|
+
'.npmignore',
|
|
731
|
+
'.prettierignore',
|
|
732
|
+
'.prettierrc.js',
|
|
733
|
+
'.prettierrc.json',
|
|
734
|
+
'.prettierrc.yaml',
|
|
735
|
+
'.prettierrc',
|
|
736
|
+
'.project',
|
|
737
|
+
'.releaserc.json',
|
|
738
|
+
'.stylelintrc.json',
|
|
739
|
+
'.travis.yml',
|
|
740
|
+
'.vscode/',
|
|
741
|
+
'AUTHORS.txt',
|
|
742
|
+
'AUTHORS',
|
|
743
|
+
'bower',
|
|
744
|
+
'CODE_OF_CONDUCT.md',
|
|
745
|
+
'codecov.yml',
|
|
746
|
+
'commitlint.config.js',
|
|
747
|
+
'CONTRIBUTING.md',
|
|
748
|
+
'COPING.md',
|
|
749
|
+
'husky.config.js',
|
|
750
|
+
'LICENCE.md',
|
|
751
|
+
'LICENCE.txt',
|
|
752
|
+
'LICENCE',
|
|
753
|
+
'LICENSE.md',
|
|
754
|
+
'LICENSE.txt',
|
|
755
|
+
'LICENSE',
|
|
756
|
+
'lint-staged.config.js',
|
|
757
|
+
'open-bot.yaml',
|
|
758
|
+
'package-lock.json',
|
|
759
|
+
'pnpm-lock.yaml',
|
|
760
|
+
'prettier.config.js',
|
|
761
|
+
'stylelint.config.js',
|
|
762
|
+
'tslint.json',
|
|
763
|
+
'yarn.lock',
|
|
764
|
+
].map(f => f.toLowerCase()));
|
|
547
765
|
export let fwatchers = {};
|
|
548
766
|
/** 跟踪文本文件追加的内容,类似 tail -f */
|
|
549
767
|
export async function ftail(fp, handler, { print = true } = {}) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -67,14 +67,14 @@
|
|
|
67
67
|
"commander": "^13.1.0",
|
|
68
68
|
"css-loader": "^7.1.2",
|
|
69
69
|
"emoji-regex": "^10.4.0",
|
|
70
|
-
"eslint": "^9.
|
|
70
|
+
"eslint": "^9.24.0",
|
|
71
71
|
"eslint-plugin-import": "^2.31.0",
|
|
72
|
-
"eslint-plugin-react": "^7.37.
|
|
72
|
+
"eslint-plugin-react": "^7.37.5",
|
|
73
73
|
"gulp-sort": "^2.0.0",
|
|
74
74
|
"https-proxy-agent": "^7.0.6",
|
|
75
75
|
"i18next": "^24.2.3",
|
|
76
76
|
"i18next-scanner": "^4.6.0",
|
|
77
|
-
"koa": "^2.16.
|
|
77
|
+
"koa": "^2.16.1",
|
|
78
78
|
"koa-compress": "^5.1.1",
|
|
79
79
|
"license-webpack-plugin": "^4.0.2",
|
|
80
80
|
"map-stream": "^0.0.7",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"react-i18next": "^15.4.1",
|
|
85
85
|
"react-object-model": "^1.2.23",
|
|
86
86
|
"resolve-path": "^1.4.0",
|
|
87
|
-
"sass": "^1.86.
|
|
87
|
+
"sass": "^1.86.3",
|
|
88
88
|
"sass-loader": "^16.0.5",
|
|
89
89
|
"source-map-loader": "^5.0.0",
|
|
90
90
|
"strip-ansi": "^7.1.0",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"tough-cookie": "^5.1.2",
|
|
94
94
|
"ts-loader": "^9.5.2",
|
|
95
95
|
"tslib": "^2.8.1",
|
|
96
|
-
"typescript": "^5.8.
|
|
96
|
+
"typescript": "^5.8.3",
|
|
97
97
|
"ua-parser-js": "^2.0.3",
|
|
98
98
|
"undici": "^7.7.0",
|
|
99
99
|
"vinyl": "^3.0.0",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"@types/tough-cookie": "^4.0.5",
|
|
119
119
|
"@types/ua-parser-js": "^0.7.39",
|
|
120
120
|
"@types/vinyl-fs": "^3.0.5",
|
|
121
|
-
"@types/vscode": "^1.
|
|
121
|
+
"@types/vscode": "^1.99.0",
|
|
122
122
|
"@types/webpack-bundle-analyzer": "^4.7.0"
|
|
123
123
|
},
|
|
124
124
|
"pnpm": {
|