zip-lib 0.5.0 → 0.7.2
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 +3 -4
- package/lib/cancelable.d.ts +16 -5
- package/lib/cancelable.js +37 -9
- package/lib/fs.js +47 -31
- package/lib/index.js +13 -6
- package/lib/unzip.d.ts +5 -3
- package/lib/unzip.js +129 -42
- package/lib/util.js +19 -59
- package/lib/zip.d.ts +3 -3
- package/lib/zip.js +77 -69
- package/package.json +9 -7
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# zip-lib
|
|
2
2
|
zip and unzip library for node.
|
|
3
3
|
|
|
4
|
-
[](https://www.npmjs.org/package/zip-lib) [](https://github.com/fpsqdb/zip-lib/blob/master/LICENSE) 
|
|
4
|
+
[](https://www.npmjs.org/package/zip-lib) [](https://github.com/fpsqdb/zip-lib/blob/master/LICENSE)  
|
|
5
5
|
|
|
6
6
|
## Install
|
|
7
7
|
|
|
@@ -350,11 +350,10 @@ Object
|
|
|
350
350
|
|
|
351
351
|
Object
|
|
352
352
|
- `overwrite?`: String (optional) - If it is true, the target directory will be deleted before extract. The default value is `false`.
|
|
353
|
-
- `symlinkAsFileOnWindows?`: Boolean (optional) - Extract symbolic links as files on Windows. This value is only available on Windows and ignored on other platforms. The default value is `true
|
|
353
|
+
- `symlinkAsFileOnWindows?`: Boolean (optional) - Extract symbolic links as files on Windows. This value is only available on Windows and ignored on other platforms. The default value is `true`.<br>If `true`, the symlink in the zip will be extracted as a normal file on Windows.<br>If `false`, the symlink in the zip will be extracted as a symlink correctly on Windows, but an `EPERM` error will be thrown under non-administrators.
|
|
354
354
|
|
|
355
|
-
> On Windows, the default security policy allows only administrators to create symbolic links.
|
|
355
|
+
> ⚠**WARNING:** On Windows, the default security policy allows only administrators to create symbolic links. If you set `symlinkAsFileOnWindows` to `false` and the zip contains symlink, be sure to run the code under the administrator, otherwise an `EPERM` error will be thrown.
|
|
356
356
|
|
|
357
|
-
If `true`, the symlink in the zip will be extracted as a normal file on Windows.<br>If `false`, the symlink in the zip will be extracted as a symlink correctly on Windows, but an `EPERM` error will be thrown under non-administrators.
|
|
358
357
|
- `onEntry?`: Function (optional) - Called before an item is extracted.<br>Arguments:
|
|
359
358
|
- `event`: Object - Represents an event that an entry is about to be extracted.
|
|
360
359
|
- `entryName`: String (readonly) - Entry name.
|
package/lib/cancelable.d.ts
CHANGED
|
@@ -4,13 +4,24 @@ export interface ICancelable {
|
|
|
4
4
|
*/
|
|
5
5
|
cancel(): void;
|
|
6
6
|
}
|
|
7
|
-
export declare
|
|
7
|
+
export declare class CancellationToken {
|
|
8
|
+
private _isCancelled;
|
|
9
|
+
private _callbacks;
|
|
8
10
|
/**
|
|
9
|
-
*
|
|
11
|
+
* A flag signalling is cancellation has been requested.
|
|
12
|
+
*/
|
|
13
|
+
get isCancelled(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Subscribe a callback when cancellation is requested. The callback
|
|
16
|
+
* only ever fires `once` as cancellation can only happen once.
|
|
17
|
+
* @param cb A function will be called whent cancellation is requested.
|
|
18
|
+
* @returns A function that Unsubscribe the cancellation callback.
|
|
10
19
|
*/
|
|
11
|
-
|
|
12
|
-
protected isCanceled: boolean;
|
|
20
|
+
onCancelled(cb: () => void): () => void;
|
|
13
21
|
cancel(): void;
|
|
22
|
+
}
|
|
23
|
+
export declare abstract class Cancelable implements ICancelable {
|
|
24
|
+
abstract cancel(): void;
|
|
14
25
|
/**
|
|
15
26
|
* Ignore any other error if the `cancel` method has been called
|
|
16
27
|
*
|
|
@@ -19,7 +30,7 @@ export declare abstract class Cancelable implements ICancelable {
|
|
|
19
30
|
* see https://travis-ci.org/fpsqdb/zip-lib/jobs/606040627#L124
|
|
20
31
|
* @param error
|
|
21
32
|
*/
|
|
22
|
-
protected wrapError(error: Error): Error;
|
|
33
|
+
protected wrapError(error: Error, isCanceled: boolean): Error;
|
|
23
34
|
/**
|
|
24
35
|
* Returns an error that signals cancellation.
|
|
25
36
|
*/
|
package/lib/cancelable.js
CHANGED
|
@@ -1,15 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
3
|
+
exports.Cancelable = exports.CancellationToken = void 0;
|
|
4
|
+
class CancellationToken {
|
|
5
|
+
constructor() {
|
|
6
|
+
this._isCancelled = false;
|
|
7
|
+
this._callbacks = new Set();
|
|
8
|
+
}
|
|
4
9
|
/**
|
|
5
|
-
*
|
|
10
|
+
* A flag signalling is cancellation has been requested.
|
|
6
11
|
*/
|
|
7
|
-
|
|
8
|
-
this.
|
|
12
|
+
get isCancelled() {
|
|
13
|
+
return this._isCancelled;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Subscribe a callback when cancellation is requested. The callback
|
|
17
|
+
* only ever fires `once` as cancellation can only happen once.
|
|
18
|
+
* @param cb A function will be called whent cancellation is requested.
|
|
19
|
+
* @returns A function that Unsubscribe the cancellation callback.
|
|
20
|
+
*/
|
|
21
|
+
onCancelled(cb) {
|
|
22
|
+
if (this.isCancelled) {
|
|
23
|
+
cb();
|
|
24
|
+
return () => {
|
|
25
|
+
// noop
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
this._callbacks.add(cb);
|
|
29
|
+
return () => this._callbacks.delete(cb);
|
|
9
30
|
}
|
|
10
31
|
cancel() {
|
|
11
|
-
this.
|
|
32
|
+
if (this._isCancelled) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this._isCancelled = true;
|
|
36
|
+
this._callbacks.forEach((cb) => cb());
|
|
37
|
+
this._callbacks.clear();
|
|
12
38
|
}
|
|
39
|
+
}
|
|
40
|
+
exports.CancellationToken = CancellationToken;
|
|
41
|
+
class Cancelable {
|
|
13
42
|
/**
|
|
14
43
|
* Ignore any other error if the `cancel` method has been called
|
|
15
44
|
*
|
|
@@ -18,8 +47,8 @@ class Cancelable {
|
|
|
18
47
|
* see https://travis-ci.org/fpsqdb/zip-lib/jobs/606040627#L124
|
|
19
48
|
* @param error
|
|
20
49
|
*/
|
|
21
|
-
wrapError(error) {
|
|
22
|
-
if (
|
|
50
|
+
wrapError(error, isCanceled) {
|
|
51
|
+
if (isCanceled) {
|
|
23
52
|
return this.canceledError();
|
|
24
53
|
}
|
|
25
54
|
return error;
|
|
@@ -28,10 +57,9 @@ class Cancelable {
|
|
|
28
57
|
* Returns an error that signals cancellation.
|
|
29
58
|
*/
|
|
30
59
|
canceledError() {
|
|
31
|
-
|
|
60
|
+
const error = new Error("Canceled");
|
|
32
61
|
error.name = error.message;
|
|
33
62
|
return error;
|
|
34
63
|
}
|
|
35
64
|
}
|
|
36
65
|
exports.Cancelable = Cancelable;
|
|
37
|
-
//# sourceMappingURL=cancelable.js.map
|
package/lib/fs.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isRootPath = exports.rimraf = exports.pathExists = exports.ensureFolder = exports.getFileEntry = exports.readdirp = void 0;
|
|
3
4
|
const path = require("path");
|
|
4
5
|
const util = require("./util");
|
|
5
6
|
async function readdirp(folder) {
|
|
6
|
-
|
|
7
|
+
const result = [];
|
|
7
8
|
const files = await util.readdir(folder);
|
|
8
|
-
for (
|
|
9
|
-
const file = path.join(folder,
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
if (stat.isDirectory()) {
|
|
13
|
-
fileType = "dir";
|
|
9
|
+
for (const item of files) {
|
|
10
|
+
const file = path.join(folder, item);
|
|
11
|
+
const entry = await getFileEntry(file);
|
|
12
|
+
if (!entry.isSymbolicLink && entry.type === "dir") {
|
|
14
13
|
const subFiles = await readdirp(file);
|
|
15
14
|
if (subFiles.length > 0) {
|
|
16
15
|
result.push(...subFiles);
|
|
@@ -19,21 +18,39 @@ async function readdirp(folder) {
|
|
|
19
18
|
continue;
|
|
20
19
|
}
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
if (stat.isSymbolicLink()) {
|
|
24
|
-
fileType = "symlink";
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
result.push({
|
|
28
|
-
path: file,
|
|
29
|
-
type: fileType,
|
|
30
|
-
mtime: stat.mtime,
|
|
31
|
-
mode: stat.mode
|
|
32
|
-
});
|
|
21
|
+
result.push(entry);
|
|
33
22
|
}
|
|
34
23
|
return result;
|
|
35
24
|
}
|
|
36
25
|
exports.readdirp = readdirp;
|
|
26
|
+
async function getFileEntry(target) {
|
|
27
|
+
const stat = await util.lstat(target);
|
|
28
|
+
let isSymbolicLink = false;
|
|
29
|
+
let fileType = "file";
|
|
30
|
+
if (stat.isDirectory()) {
|
|
31
|
+
fileType = "dir";
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
if (stat.isSymbolicLink()) {
|
|
35
|
+
isSymbolicLink = true;
|
|
36
|
+
// If the path is a link, we must instead use fs.stat() to find out if the
|
|
37
|
+
// link is a directory or not because lstat will always return the stat of
|
|
38
|
+
// the link which is always a file.
|
|
39
|
+
const actualStat = await util.stat(target);
|
|
40
|
+
if (actualStat.isDirectory()) {
|
|
41
|
+
fileType = "dir";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
path: target,
|
|
47
|
+
isSymbolicLink,
|
|
48
|
+
type: fileType,
|
|
49
|
+
mtime: stat.mtime,
|
|
50
|
+
mode: stat.mode
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
exports.getFileEntry = getFileEntry;
|
|
37
54
|
async function ensureFolder(folder) {
|
|
38
55
|
// stop at root
|
|
39
56
|
if (folder === path.dirname(folder)) {
|
|
@@ -54,9 +71,9 @@ async function ensureFolder(folder) {
|
|
|
54
71
|
}
|
|
55
72
|
}
|
|
56
73
|
exports.ensureFolder = ensureFolder;
|
|
57
|
-
async function pathExists(
|
|
74
|
+
async function pathExists(target) {
|
|
58
75
|
try {
|
|
59
|
-
await util.access(
|
|
76
|
+
await util.access(target);
|
|
60
77
|
return true;
|
|
61
78
|
}
|
|
62
79
|
catch (error) {
|
|
@@ -101,14 +118,14 @@ async function mkdir(folder) {
|
|
|
101
118
|
await util.mkdir(folder, 0o777);
|
|
102
119
|
}
|
|
103
120
|
catch (error) {
|
|
104
|
-
// ENOENT: a parent folder does not exist yet
|
|
121
|
+
// ENOENT: a parent folder does not exist yet or folder name is invalid.
|
|
105
122
|
if (error.code === "ENOENT") {
|
|
106
123
|
return Promise.reject(error);
|
|
107
124
|
}
|
|
108
125
|
// Any other error: check if folder exists and
|
|
109
126
|
// return normally in that case if its a folder
|
|
110
127
|
try {
|
|
111
|
-
const fileStat = await util.
|
|
128
|
+
const fileStat = await util.stat(folder);
|
|
112
129
|
if (!fileStat.isDirectory()) {
|
|
113
130
|
return Promise.reject(new Error(`"${folder}" exists and is not a directory.`));
|
|
114
131
|
}
|
|
@@ -137,23 +154,22 @@ function isDriveLetter(char0) {
|
|
|
137
154
|
}
|
|
138
155
|
const winSep = "\\";
|
|
139
156
|
const unixSep = "/";
|
|
140
|
-
function isRootPath(
|
|
141
|
-
if (!
|
|
157
|
+
function isRootPath(target) {
|
|
158
|
+
if (!target) {
|
|
142
159
|
return false;
|
|
143
160
|
}
|
|
144
|
-
if (
|
|
145
|
-
|
|
161
|
+
if (target === winSep ||
|
|
162
|
+
target === unixSep) {
|
|
146
163
|
return true;
|
|
147
164
|
}
|
|
148
165
|
if (process.platform === "win32") {
|
|
149
|
-
if (
|
|
166
|
+
if (target.length > 3) {
|
|
150
167
|
return false;
|
|
151
168
|
}
|
|
152
|
-
return isDriveLetter(
|
|
153
|
-
&&
|
|
154
|
-
&& (
|
|
169
|
+
return isDriveLetter(target.charCodeAt(0))
|
|
170
|
+
&& target.charCodeAt(1) === charColon
|
|
171
|
+
&& (target.length === 2 || target.charCodeAt(2) === charWinSep || target.charCodeAt(2) === cahrUnixSep);
|
|
155
172
|
}
|
|
156
173
|
return false;
|
|
157
174
|
}
|
|
158
175
|
exports.isRootPath = isRootPath;
|
|
159
|
-
//# sourceMappingURL=fs.js.map
|
package/lib/index.js
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
function
|
|
3
|
-
|
|
4
|
-
}
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
5
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.extract = exports.archiveFolder = exports.archiveFile = void 0;
|
|
6
14
|
const zip_1 = require("./zip");
|
|
7
15
|
const unzip_1 = require("./unzip");
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
__exportStar(require("./zip"), exports);
|
|
17
|
+
__exportStar(require("./unzip"), exports);
|
|
10
18
|
/**
|
|
11
19
|
* Compress a single file to zip.
|
|
12
20
|
* @param file
|
|
@@ -42,4 +50,3 @@ function extract(zipFile, targetFolder, options) {
|
|
|
42
50
|
return unzip.extract(zipFile, targetFolder);
|
|
43
51
|
}
|
|
44
52
|
exports.extract = extract;
|
|
45
|
-
//# sourceMappingURL=index.js.map
|
package/lib/unzip.d.ts
CHANGED
|
@@ -9,11 +9,13 @@ export interface IExtractOptions {
|
|
|
9
9
|
* Extract symbolic links as files on Windows. This value is only available on Windows and ignored on other platforms.
|
|
10
10
|
* The default value is `true`.
|
|
11
11
|
*
|
|
12
|
-
* > On Windows, the default security policy allows only administrators to create symbolic links.
|
|
13
|
-
*
|
|
14
12
|
* If `true`, the symlink in the zip will be extracted as a normal file on Windows.
|
|
15
13
|
*
|
|
16
14
|
* If `false`, the symlink in the zip will be extracted as a symlink correctly on Windows, but an `EPERM` error will be thrown under non-administrators.
|
|
15
|
+
*
|
|
16
|
+
* > ⚠**WARNING:** On Windows, the default security policy allows only administrators to create symbolic links.
|
|
17
|
+
* If you set `symlinkAsFileOnWindows` to `false` and the zip contains symlink,
|
|
18
|
+
* be sure to run the code under the administrator, otherwise an `EPERM` error will be thrown.
|
|
17
19
|
*/
|
|
18
20
|
symlinkAsFileOnWindows?: boolean;
|
|
19
21
|
/**
|
|
@@ -49,7 +51,7 @@ export declare class Unzip extends Cancelable {
|
|
|
49
51
|
*/
|
|
50
52
|
constructor(options?: IExtractOptions | undefined);
|
|
51
53
|
private zipFile;
|
|
52
|
-
private
|
|
54
|
+
private token;
|
|
53
55
|
/**
|
|
54
56
|
* Extract the zip file to the specified location.
|
|
55
57
|
* @param zipFile
|
package/lib/unzip.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Unzip = void 0;
|
|
3
4
|
const yauzl = require("yauzl");
|
|
4
5
|
const exfs = require("./fs");
|
|
5
6
|
const fs_1 = require("fs");
|
|
@@ -33,6 +34,50 @@ class EntryEvent {
|
|
|
33
34
|
this._isPrevented = false;
|
|
34
35
|
}
|
|
35
36
|
}
|
|
37
|
+
class EntryContext {
|
|
38
|
+
constructor(_targetFolder, _realTargetFolder, symlinkAsFileOnWindows) {
|
|
39
|
+
this._targetFolder = _targetFolder;
|
|
40
|
+
this._realTargetFolder = _realTargetFolder;
|
|
41
|
+
this.symlinkAsFileOnWindows = symlinkAsFileOnWindows;
|
|
42
|
+
this._symlinkFileNames = [];
|
|
43
|
+
}
|
|
44
|
+
get decodeEntryFileName() {
|
|
45
|
+
return this._decodeEntryFileName;
|
|
46
|
+
}
|
|
47
|
+
set decodeEntryFileName(name) {
|
|
48
|
+
this._decodeEntryFileName = name;
|
|
49
|
+
}
|
|
50
|
+
get targetFolder() {
|
|
51
|
+
return this._targetFolder;
|
|
52
|
+
}
|
|
53
|
+
get realTargetFolder() {
|
|
54
|
+
return this._realTargetFolder;
|
|
55
|
+
}
|
|
56
|
+
get symlinkFileNames() {
|
|
57
|
+
return this._symlinkFileNames;
|
|
58
|
+
}
|
|
59
|
+
getFilePath() {
|
|
60
|
+
return path.join(this.targetFolder, this.decodeEntryFileName);
|
|
61
|
+
}
|
|
62
|
+
async isOutsideTargetFolder(tpath) {
|
|
63
|
+
if (this.symlinkFileNames.length === 0) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (process.platform === "win32" &&
|
|
67
|
+
this.symlinkAsFileOnWindows) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
for (const fileName of this.symlinkFileNames) {
|
|
71
|
+
if (tpath.includes(fileName)) {
|
|
72
|
+
const realFilePath = await util.realpath(tpath);
|
|
73
|
+
if (realFilePath.indexOf(this.realTargetFolder) !== 0) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
36
81
|
/**
|
|
37
82
|
* Extract the zip file.
|
|
38
83
|
*/
|
|
@@ -52,37 +97,48 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
52
97
|
*/
|
|
53
98
|
async extract(zipFile, targetFolder) {
|
|
54
99
|
let extractedEntriesCount = 0;
|
|
55
|
-
|
|
100
|
+
const token = new cancelable_1.CancellationToken();
|
|
101
|
+
this.token = token;
|
|
56
102
|
if (this.isOverwrite()) {
|
|
57
103
|
await exfs.rimraf(targetFolder);
|
|
58
104
|
}
|
|
59
|
-
if (
|
|
105
|
+
if (token.isCancelled) {
|
|
60
106
|
return Promise.reject(this.canceledError());
|
|
61
107
|
}
|
|
62
108
|
await exfs.ensureFolder(targetFolder);
|
|
63
|
-
const
|
|
109
|
+
const realTargetFolder = await util.realpath(targetFolder);
|
|
110
|
+
const zfile = await this.openZip(zipFile, token);
|
|
64
111
|
this.zipFile = zfile;
|
|
65
112
|
zfile.readEntry();
|
|
66
113
|
return new Promise((c, e) => {
|
|
114
|
+
let anyError = null;
|
|
67
115
|
const total = zfile.entryCount;
|
|
68
116
|
zfile.once("error", (err) => {
|
|
69
|
-
|
|
117
|
+
this.closeZip();
|
|
118
|
+
e(this.wrapError(err, token.isCancelled));
|
|
70
119
|
});
|
|
71
120
|
zfile.once("close", () => {
|
|
72
|
-
|
|
73
|
-
|
|
121
|
+
this.zipFile = null;
|
|
122
|
+
if (anyError) {
|
|
123
|
+
e(this.wrapError(anyError, token.isCancelled));
|
|
74
124
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
125
|
+
else {
|
|
126
|
+
if (token.isCancelled) {
|
|
127
|
+
e(this.canceledError());
|
|
128
|
+
}
|
|
129
|
+
// If the zip content is empty, it will not receive the `zfile.on("entry")` event.
|
|
130
|
+
else if (total === 0) {
|
|
131
|
+
c(void 0);
|
|
132
|
+
}
|
|
78
133
|
}
|
|
79
134
|
});
|
|
80
135
|
// Because openZip is an asynchronous method, openZip may not be completed when calling cancel,
|
|
81
136
|
// so we need to check if it has been canceled after the openZip method returns.
|
|
82
|
-
if (
|
|
137
|
+
if (token.isCancelled) {
|
|
83
138
|
this.closeZip();
|
|
84
139
|
return;
|
|
85
140
|
}
|
|
141
|
+
const entryContext = new EntryContext(targetFolder, realTargetFolder, this.symlinkToFile());
|
|
86
142
|
const entryEvent = new EntryEvent(total);
|
|
87
143
|
zfile.on("entry", async (entry) => {
|
|
88
144
|
// use UTF-8 in all situations
|
|
@@ -94,19 +150,21 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
94
150
|
// see https://github.com/thejoshwolfe/yauzl#validatefilenamefilename
|
|
95
151
|
const errorMessage = yauzl.validateFileName(fileName);
|
|
96
152
|
if (errorMessage != null) {
|
|
97
|
-
|
|
153
|
+
anyError = new Error(errorMessage);
|
|
98
154
|
this.closeZip();
|
|
155
|
+
e(anyError);
|
|
99
156
|
return;
|
|
100
157
|
}
|
|
101
158
|
entryEvent.entryName = fileName;
|
|
102
159
|
this.onEntryCallback(entryEvent);
|
|
160
|
+
entryContext.decodeEntryFileName = fileName;
|
|
103
161
|
try {
|
|
104
162
|
if (entryEvent.isPrevented) {
|
|
105
163
|
entryEvent.reset();
|
|
106
164
|
zfile.readEntry();
|
|
107
165
|
}
|
|
108
166
|
else {
|
|
109
|
-
await this.handleEntry(zfile, entry,
|
|
167
|
+
await this.handleEntry(zfile, entry, entryContext, token);
|
|
110
168
|
}
|
|
111
169
|
extractedEntriesCount++;
|
|
112
170
|
if (extractedEntriesCount === total) {
|
|
@@ -114,8 +172,9 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
114
172
|
}
|
|
115
173
|
}
|
|
116
174
|
catch (error) {
|
|
117
|
-
|
|
175
|
+
anyError = this.wrapError(error, token.isCancelled);
|
|
118
176
|
this.closeZip();
|
|
177
|
+
e(anyError);
|
|
119
178
|
}
|
|
120
179
|
});
|
|
121
180
|
});
|
|
@@ -125,9 +184,9 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
125
184
|
* If the cancel method is called after the extract is complete, nothing will happen.
|
|
126
185
|
*/
|
|
127
186
|
cancel() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.
|
|
187
|
+
if (this.token) {
|
|
188
|
+
this.token.cancel();
|
|
189
|
+
this.token = null;
|
|
131
190
|
}
|
|
132
191
|
this.closeZip();
|
|
133
192
|
}
|
|
@@ -137,7 +196,7 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
137
196
|
this.zipFile = null;
|
|
138
197
|
}
|
|
139
198
|
}
|
|
140
|
-
openZip(zipFile) {
|
|
199
|
+
openZip(zipFile, token) {
|
|
141
200
|
return new Promise((c, e) => {
|
|
142
201
|
yauzl.open(zipFile, {
|
|
143
202
|
lazyEntries: true,
|
|
@@ -145,7 +204,7 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
145
204
|
decodeStrings: false
|
|
146
205
|
}, (err, zfile) => {
|
|
147
206
|
if (err) {
|
|
148
|
-
e(this.wrapError(err));
|
|
207
|
+
e(this.wrapError(err, token.isCancelled));
|
|
149
208
|
}
|
|
150
209
|
else {
|
|
151
210
|
c(zfile);
|
|
@@ -153,24 +212,24 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
153
212
|
});
|
|
154
213
|
});
|
|
155
214
|
}
|
|
156
|
-
async handleEntry(zfile, entry,
|
|
157
|
-
if (/\/$/.test(decodeEntryFileName)) {
|
|
215
|
+
async handleEntry(zfile, entry, entryContext, token) {
|
|
216
|
+
if (/\/$/.test(entryContext.decodeEntryFileName)) {
|
|
158
217
|
// Directory file names end with '/'.
|
|
159
218
|
// Note that entires for directories themselves are optional.
|
|
160
219
|
// An entry's fileName implicitly requires its parent directories to exist.
|
|
161
|
-
await exfs.ensureFolder(
|
|
220
|
+
await exfs.ensureFolder(entryContext.getFilePath());
|
|
162
221
|
zfile.readEntry();
|
|
163
222
|
}
|
|
164
223
|
else {
|
|
165
224
|
// file entry
|
|
166
|
-
await this.extractEntry(zfile, entry,
|
|
225
|
+
await this.extractEntry(zfile, entry, entryContext, token);
|
|
167
226
|
}
|
|
168
227
|
}
|
|
169
|
-
openZipFileStream(zfile, entry) {
|
|
228
|
+
openZipFileStream(zfile, entry, token) {
|
|
170
229
|
return new Promise((c, e) => {
|
|
171
230
|
zfile.openReadStream(entry, (err, readStream) => {
|
|
172
231
|
if (err) {
|
|
173
|
-
e(this.wrapError(err));
|
|
232
|
+
e(this.wrapError(err, token.isCancelled));
|
|
174
233
|
}
|
|
175
234
|
else {
|
|
176
235
|
c(readStream);
|
|
@@ -178,32 +237,40 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
178
237
|
});
|
|
179
238
|
});
|
|
180
239
|
}
|
|
181
|
-
async extractEntry(zfile, entry,
|
|
182
|
-
const filePath =
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
240
|
+
async extractEntry(zfile, entry, entryContext, token) {
|
|
241
|
+
const filePath = entryContext.getFilePath();
|
|
242
|
+
const fileDir = path.dirname(filePath);
|
|
243
|
+
await exfs.ensureFolder(fileDir);
|
|
244
|
+
const outside = await entryContext.isOutsideTargetFolder(fileDir);
|
|
245
|
+
if (outside) {
|
|
246
|
+
const error = new Error(`Refuse to write file outside "${entryContext.targetFolder}", file: "${filePath}"`);
|
|
247
|
+
error.name = "AFWRITE";
|
|
248
|
+
return Promise.reject(error);
|
|
249
|
+
}
|
|
250
|
+
const readStream = await this.openZipFileStream(zfile, entry, token);
|
|
251
|
+
await this.writeEntryToFile(readStream, entry, entryContext, token);
|
|
252
|
+
zfile.readEntry();
|
|
189
253
|
}
|
|
190
|
-
async writeEntryToFile(readStream, entry,
|
|
254
|
+
async writeEntryToFile(readStream, entry, entryContext, token) {
|
|
191
255
|
let fileStream;
|
|
192
|
-
|
|
193
|
-
this.cancelCallback = undefined;
|
|
256
|
+
token.onCancelled(() => {
|
|
194
257
|
if (fileStream) {
|
|
195
258
|
readStream.unpipe(fileStream);
|
|
196
|
-
fileStream.destroy(
|
|
259
|
+
fileStream.destroy(this.canceledError());
|
|
197
260
|
}
|
|
198
|
-
};
|
|
261
|
+
});
|
|
199
262
|
return new Promise(async (c, e) => {
|
|
200
263
|
try {
|
|
264
|
+
const filePath = entryContext.getFilePath();
|
|
201
265
|
const mode = this.modeFromEntry(entry);
|
|
202
266
|
// see https://unix.stackexchange.com/questions/193465/what-file-mode-is-a-symlink
|
|
203
267
|
const isSymlink = ((mode & 0o170000) === 0o120000);
|
|
204
268
|
readStream.once("error", (err) => {
|
|
205
|
-
e(this.wrapError(err));
|
|
269
|
+
e(this.wrapError(err, token.isCancelled));
|
|
206
270
|
});
|
|
271
|
+
if (isSymlink) {
|
|
272
|
+
entryContext.symlinkFileNames.push(entryContext.decodeEntryFileName);
|
|
273
|
+
}
|
|
207
274
|
if (isSymlink && !this.symlinkToFile()) {
|
|
208
275
|
let linkContent = "";
|
|
209
276
|
readStream.on("data", (chunk) => {
|
|
@@ -222,13 +289,13 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
222
289
|
fileStream = fs_1.createWriteStream(filePath, { mode });
|
|
223
290
|
fileStream.once("close", () => c());
|
|
224
291
|
fileStream.once("error", (err) => {
|
|
225
|
-
e(this.wrapError(err));
|
|
292
|
+
e(this.wrapError(err, token.isCancelled));
|
|
226
293
|
});
|
|
227
294
|
readStream.pipe(fileStream);
|
|
228
295
|
}
|
|
229
296
|
}
|
|
230
297
|
catch (error) {
|
|
231
|
-
e(this.wrapError(error));
|
|
298
|
+
e(this.wrapError(error, token.isCancelled));
|
|
232
299
|
}
|
|
233
300
|
});
|
|
234
301
|
}
|
|
@@ -239,7 +306,28 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
239
306
|
.reduce((a, b) => a + b, attr & 61440 /* S_IFMT */);
|
|
240
307
|
}
|
|
241
308
|
async createSymlink(linkContent, des) {
|
|
242
|
-
|
|
309
|
+
let linkType = "file";
|
|
310
|
+
if (process.platform === 'win32') {
|
|
311
|
+
if (/\/$/.test(linkContent)) {
|
|
312
|
+
linkType = "dir";
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
let targetPath = linkContent;
|
|
316
|
+
if (!path.isAbsolute(linkContent)) {
|
|
317
|
+
targetPath = path.join(path.dirname(des), linkContent);
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
const stat = await util.stat(targetPath);
|
|
321
|
+
if (stat.isDirectory()) {
|
|
322
|
+
linkType = "dir";
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
// ignore
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
await util.symlink(linkContent, des, linkType);
|
|
243
331
|
}
|
|
244
332
|
isOverwrite() {
|
|
245
333
|
if (this.options &&
|
|
@@ -268,4 +356,3 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
268
356
|
}
|
|
269
357
|
}
|
|
270
358
|
exports.Unzip = Unzip;
|
|
271
|
-
//# sourceMappingURL=unzip.js.map
|
package/lib/util.js
CHANGED
|
@@ -1,89 +1,49 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readlink = exports.symlink = exports.rmdir = exports.access = exports.readdir = exports.chmod = exports.lstat = exports.stat = exports.realpath = exports.mkdir = exports.unlink = void 0;
|
|
3
4
|
const fs = require("fs");
|
|
4
5
|
const util = require("util");
|
|
5
|
-
function asyncCall(fn, ...args) {
|
|
6
|
-
return new Promise((c, e) => fn(...args, (err, result) => err ? e(err) : c(result)));
|
|
7
|
-
}
|
|
8
6
|
function unlink(path) {
|
|
9
|
-
|
|
10
|
-
return util.promisify(fs.unlink)(path);
|
|
11
|
-
}
|
|
12
|
-
else {
|
|
13
|
-
return asyncCall(fs.unlink, path);
|
|
14
|
-
}
|
|
7
|
+
return util.promisify(fs.unlink)(path);
|
|
15
8
|
}
|
|
16
9
|
exports.unlink = unlink;
|
|
17
10
|
function mkdir(path, mode) {
|
|
18
|
-
|
|
19
|
-
return util.promisify(fs.mkdir)(path, mode);
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
return asyncCall(fs.mkdir, path, mode);
|
|
23
|
-
}
|
|
11
|
+
return util.promisify(fs.mkdir)(path, mode);
|
|
24
12
|
}
|
|
25
13
|
exports.mkdir = mkdir;
|
|
14
|
+
function realpath(path) {
|
|
15
|
+
return util.promisify(fs.realpath)(path);
|
|
16
|
+
}
|
|
17
|
+
exports.realpath = realpath;
|
|
18
|
+
function stat(path) {
|
|
19
|
+
return util.promisify(fs.stat)(path);
|
|
20
|
+
}
|
|
21
|
+
exports.stat = stat;
|
|
26
22
|
function lstat(path) {
|
|
27
|
-
|
|
28
|
-
return util.promisify(fs.lstat)(path);
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
return asyncCall(fs.lstat, path);
|
|
32
|
-
}
|
|
23
|
+
return util.promisify(fs.lstat)(path);
|
|
33
24
|
}
|
|
34
25
|
exports.lstat = lstat;
|
|
35
26
|
function chmod(path, mode) {
|
|
36
|
-
|
|
37
|
-
return util.promisify(fs.chmod)(path, mode);
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
return asyncCall(fs.chmod, path, mode);
|
|
41
|
-
}
|
|
27
|
+
return util.promisify(fs.chmod)(path, mode);
|
|
42
28
|
}
|
|
43
29
|
exports.chmod = chmod;
|
|
44
30
|
function readdir(path) {
|
|
45
|
-
|
|
46
|
-
return util.promisify(fs.readdir)(path);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
return asyncCall(fs.readdir, path);
|
|
50
|
-
}
|
|
31
|
+
return util.promisify(fs.readdir)(path);
|
|
51
32
|
}
|
|
52
33
|
exports.readdir = readdir;
|
|
53
34
|
function access(path, mode) {
|
|
54
|
-
|
|
55
|
-
return util.promisify(fs.access)(path, mode);
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
return asyncCall(fs.access, path, mode);
|
|
59
|
-
}
|
|
35
|
+
return util.promisify(fs.access)(path, mode);
|
|
60
36
|
}
|
|
61
37
|
exports.access = access;
|
|
62
38
|
function rmdir(path) {
|
|
63
|
-
|
|
64
|
-
return util.promisify(fs.rmdir)(path);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
return asyncCall(fs.rmdir, path);
|
|
68
|
-
}
|
|
39
|
+
return util.promisify(fs.rmdir)(path);
|
|
69
40
|
}
|
|
70
41
|
exports.rmdir = rmdir;
|
|
71
|
-
function symlink(target, path) {
|
|
72
|
-
|
|
73
|
-
return util.promisify(fs.symlink)(target, path);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
return asyncCall(fs.symlink, target, path);
|
|
77
|
-
}
|
|
42
|
+
function symlink(target, path, type) {
|
|
43
|
+
return util.promisify(fs.symlink)(target, path, type);
|
|
78
44
|
}
|
|
79
45
|
exports.symlink = symlink;
|
|
80
46
|
function readlink(path) {
|
|
81
|
-
|
|
82
|
-
return util.promisify(fs.readlink)(path);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
return asyncCall(fs.readlink, path);
|
|
86
|
-
}
|
|
47
|
+
return util.promisify(fs.readlink)(path);
|
|
87
48
|
}
|
|
88
49
|
exports.readlink = readlink;
|
|
89
|
-
//# sourceMappingURL=util.js.map
|
package/lib/zip.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare class Zip extends Cancelable {
|
|
|
25
25
|
private zipStream;
|
|
26
26
|
private zipFiles;
|
|
27
27
|
private zipFolders;
|
|
28
|
-
private
|
|
28
|
+
private token;
|
|
29
29
|
/**
|
|
30
30
|
* Adds a file from the file system at realPath into the zipfile as metadataPath.
|
|
31
31
|
* @param file
|
|
@@ -50,8 +50,8 @@ export declare class Zip extends Cancelable {
|
|
|
50
50
|
* If the cancel method is called after the archive is complete, nothing will happen.
|
|
51
51
|
*/
|
|
52
52
|
cancel(): void;
|
|
53
|
-
private
|
|
54
|
-
private
|
|
53
|
+
private addEntry;
|
|
54
|
+
private addFileStream;
|
|
55
55
|
private addSymlink;
|
|
56
56
|
private walkDir;
|
|
57
57
|
private stopPipe;
|
package/lib/zip.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Zip = void 0;
|
|
3
4
|
const yazl = require("yazl");
|
|
4
5
|
const fs_1 = require("fs");
|
|
5
6
|
const exfs = require("./fs");
|
|
@@ -45,7 +46,7 @@ class Zip extends cancelable_1.Cancelable {
|
|
|
45
46
|
addFolder(folder, metadataPath) {
|
|
46
47
|
this.zipFolders.push({
|
|
47
48
|
path: folder,
|
|
48
|
-
metadataPath
|
|
49
|
+
metadataPath
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
/**
|
|
@@ -56,40 +57,25 @@ class Zip extends cancelable_1.Cancelable {
|
|
|
56
57
|
if (!zipFile) {
|
|
57
58
|
return Promise.reject(new Error("zipPath must not be empty"));
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
const token = new cancelable_1.CancellationToken();
|
|
61
|
+
this.token = token;
|
|
60
62
|
this.isPipe = false;
|
|
63
|
+
await exfs.ensureFolder(path.dirname(zipFile));
|
|
61
64
|
// Re-instantiate yazl every time the archive method is called to ensure that files are not added repeatedly.
|
|
62
65
|
// This will also make the Zip class reusable.
|
|
63
|
-
this.
|
|
66
|
+
this.yazlFile = new yazl.ZipFile();
|
|
64
67
|
return new Promise(async (c, e) => {
|
|
65
|
-
this.
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
};
|
|
68
|
+
this.yazlFile.once("error", (err) => {
|
|
69
|
+
e(this.wrapError(err, token.isCancelled));
|
|
70
|
+
});
|
|
69
71
|
const zip = this.yazlFile;
|
|
70
|
-
|
|
71
|
-
const files = this.zipFiles;
|
|
72
|
-
for (let fi = 0; fi < files.length; fi++) {
|
|
73
|
-
const file = files[fi];
|
|
74
|
-
await this.addFileOrSymlink(zip, file.path, file.metadataPath);
|
|
75
|
-
}
|
|
76
|
-
if (this.zipFolders.length > 0) {
|
|
77
|
-
await this.walkDir(this.zipFolders);
|
|
78
|
-
}
|
|
79
|
-
await exfs.ensureFolder(path.dirname(zipFile));
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
e(this.wrapError(error));
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
zip.end();
|
|
86
|
-
if (!this.isCanceled) {
|
|
72
|
+
if (!token.isCancelled) {
|
|
87
73
|
this.zipStream = fs_1.createWriteStream(zipFile);
|
|
88
74
|
this.zipStream.once("error", (err) => {
|
|
89
|
-
e(this.wrapError(err));
|
|
75
|
+
e(this.wrapError(err, token.isCancelled));
|
|
90
76
|
});
|
|
91
77
|
this.zipStream.once("close", () => {
|
|
92
|
-
if (
|
|
78
|
+
if (token.isCancelled) {
|
|
93
79
|
e(this.canceledError());
|
|
94
80
|
}
|
|
95
81
|
else {
|
|
@@ -97,11 +83,26 @@ class Zip extends cancelable_1.Cancelable {
|
|
|
97
83
|
}
|
|
98
84
|
});
|
|
99
85
|
zip.outputStream.once("error", (err) => {
|
|
100
|
-
e(this.wrapError(err));
|
|
86
|
+
e(this.wrapError(err, token.isCancelled));
|
|
101
87
|
});
|
|
102
88
|
zip.outputStream.pipe(this.zipStream);
|
|
103
89
|
this.isPipe = true;
|
|
104
90
|
}
|
|
91
|
+
try {
|
|
92
|
+
const files = this.zipFiles;
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
const entry = await exfs.getFileEntry(file.path);
|
|
95
|
+
await this.addEntry(zip, entry, file, token);
|
|
96
|
+
}
|
|
97
|
+
if (this.zipFolders.length > 0) {
|
|
98
|
+
await this.walkDir(this.zipFolders, token);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
e(this.wrapError(error, token.isCancelled));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
zip.end();
|
|
105
106
|
});
|
|
106
107
|
}
|
|
107
108
|
/**
|
|
@@ -109,34 +110,58 @@ class Zip extends cancelable_1.Cancelable {
|
|
|
109
110
|
* If the cancel method is called after the archive is complete, nothing will happen.
|
|
110
111
|
*/
|
|
111
112
|
cancel() {
|
|
112
|
-
|
|
113
|
+
if (this.token) {
|
|
114
|
+
this.token.cancel();
|
|
115
|
+
this.token = null;
|
|
116
|
+
}
|
|
113
117
|
this.stopPipe(this.canceledError());
|
|
114
118
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
async addEntry(zip, entry, file, token) {
|
|
120
|
+
if (entry.isSymbolicLink) {
|
|
121
|
+
if (this.followSymlink()) {
|
|
122
|
+
if (entry.type === "dir") {
|
|
123
|
+
const realPath = await util.realpath(file.path);
|
|
124
|
+
await this.walkDir([{ path: realPath, metadataPath: file.metadataPath }], token);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
zip.addFile(file.path, file.metadataPath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
await this.addSymlink(zip, entry, file.metadataPath);
|
|
132
|
+
}
|
|
124
133
|
}
|
|
125
134
|
else {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
mtime: stat.mtime,
|
|
132
|
-
mode: stat.mode
|
|
133
|
-
}, metadataPath);
|
|
135
|
+
if (entry.type === "dir") {
|
|
136
|
+
zip.addEmptyDirectory(file.metadataPath, {
|
|
137
|
+
mtime: entry.mtime,
|
|
138
|
+
mode: entry.mode
|
|
139
|
+
});
|
|
134
140
|
}
|
|
135
141
|
else {
|
|
136
|
-
|
|
142
|
+
await this.addFileStream(zip, entry, file.metadataPath, token);
|
|
137
143
|
}
|
|
138
144
|
}
|
|
139
145
|
}
|
|
146
|
+
addFileStream(zip, file, metadataPath, token) {
|
|
147
|
+
return new Promise((c, e) => {
|
|
148
|
+
const fileStream = fs_1.createReadStream(file.path);
|
|
149
|
+
fileStream.once("error", (err) => {
|
|
150
|
+
const wrappedError = this.wrapError(err, token.isCancelled);
|
|
151
|
+
this.stopPipe(wrappedError);
|
|
152
|
+
e(wrappedError);
|
|
153
|
+
});
|
|
154
|
+
fileStream.once("close", () => {
|
|
155
|
+
c();
|
|
156
|
+
});
|
|
157
|
+
// If the file attribute is known, add the entry using `addReadStream`,
|
|
158
|
+
// this can reduce the number of calls to the `fs.stat` method.
|
|
159
|
+
zip.addReadStream(fileStream, metadataPath, {
|
|
160
|
+
mode: file.mode,
|
|
161
|
+
mtime: file.mtime
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}
|
|
140
165
|
async addSymlink(zip, file, metadataPath) {
|
|
141
166
|
const linkTarget = await util.readlink(file.path);
|
|
142
167
|
zip.addBuffer(Buffer.from(linkTarget), metadataPath, {
|
|
@@ -144,33 +169,20 @@ class Zip extends cancelable_1.Cancelable {
|
|
|
144
169
|
mode: file.mode
|
|
145
170
|
});
|
|
146
171
|
}
|
|
147
|
-
async walkDir(folders) {
|
|
148
|
-
for (
|
|
149
|
-
if (
|
|
172
|
+
async walkDir(folders, token) {
|
|
173
|
+
for (const folder of folders) {
|
|
174
|
+
if (token.isCancelled) {
|
|
150
175
|
return;
|
|
151
176
|
}
|
|
152
|
-
const folder = folders[fi];
|
|
153
177
|
const entries = await exfs.readdirp(folder.path);
|
|
154
178
|
if (entries.length > 0) {
|
|
155
|
-
for (
|
|
156
|
-
|
|
157
|
-
if (this.isCanceled) {
|
|
179
|
+
for (const entry of entries) {
|
|
180
|
+
if (token.isCancelled) {
|
|
158
181
|
return;
|
|
159
182
|
}
|
|
160
183
|
const relativePath = path.relative(folder.path, entry.path);
|
|
161
184
|
const metadataPath = folder.metadataPath ? path.join(folder.metadataPath, relativePath) : relativePath;
|
|
162
|
-
|
|
163
|
-
this.yazlFile.addEmptyDirectory(metadataPath, {
|
|
164
|
-
mtime: entry.mtime,
|
|
165
|
-
mode: entry.mode
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
else if (entry.type === "symlink" && !this.followSymlink()) {
|
|
169
|
-
await this.addSymlink(this.yazlFile, entry, metadataPath);
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
this.yazlFile.addFile(entry.path, metadataPath);
|
|
173
|
-
}
|
|
185
|
+
await this.addEntry(this.yazlFile, entry, { path: entry.path, metadataPath }, token);
|
|
174
186
|
}
|
|
175
187
|
}
|
|
176
188
|
else {
|
|
@@ -183,9 +195,6 @@ class Zip extends cancelable_1.Cancelable {
|
|
|
183
195
|
}
|
|
184
196
|
}
|
|
185
197
|
stopPipe(err) {
|
|
186
|
-
if (this.yazlErrorCallback) {
|
|
187
|
-
this.yazlErrorCallback(err);
|
|
188
|
-
}
|
|
189
198
|
if (this.isPipe) {
|
|
190
199
|
this.yazlFile.outputStream.unpipe(this.zipStream);
|
|
191
200
|
this.zipStream.destroy(err);
|
|
@@ -202,4 +211,3 @@ class Zip extends cancelable_1.Cancelable {
|
|
|
202
211
|
}
|
|
203
212
|
}
|
|
204
213
|
exports.Zip = Zip;
|
|
205
|
-
//# sourceMappingURL=zip.js.map
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zip-lib",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "zip and unzip library for node",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"compile": "rimraf ./lib && tsc -p ./src/tsconfig.json",
|
|
8
|
+
"release": "rimraf ./lib && tsc -p ./src/tsconfig.release.json",
|
|
8
9
|
"compile-test": "rimraf ./test/out && tsc -p ./test/src/tsconfig.json",
|
|
9
|
-
"test": "node ./test/src/before.js && mocha ./test/out --timeout
|
|
10
|
+
"test": "node ./test/src/before.js && mocha ./test/out --timeout 10000",
|
|
11
|
+
"pack": "npm run release && npm pack"
|
|
10
12
|
},
|
|
11
13
|
"repository": {
|
|
12
14
|
"type": "git",
|
|
@@ -28,12 +30,12 @@
|
|
|
28
30
|
"yazl": "^2.5.1"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|
|
31
|
-
"@types/mocha": "^
|
|
32
|
-
"@types/node": "^8.10.
|
|
33
|
+
"@types/mocha": "^8.2.0",
|
|
34
|
+
"@types/node": "^8.10.66",
|
|
33
35
|
"@types/yauzl": "^2.9.1",
|
|
34
36
|
"@types/yazl": "^2.4.2",
|
|
35
|
-
"mocha": "^
|
|
36
|
-
"rimraf": "^3.0.
|
|
37
|
-
"typescript": "^
|
|
37
|
+
"mocha": "^8.2.1",
|
|
38
|
+
"rimraf": "^3.0.2",
|
|
39
|
+
"typescript": "^4.1.3"
|
|
38
40
|
}
|
|
39
41
|
}
|