zip-lib 1.2.2 → 1.3.0
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 +295 -203
- package/lib/fs.d.ts +2 -0
- package/lib/fs.js +81 -61
- package/lib/index.d.ts +21 -2
- package/lib/index.js +27 -24
- package/lib/unzip.d.ts +33 -10
- package/lib/unzip.js +195 -128
- package/lib/zip.d.ts +25 -10
- package/lib/zip.js +143 -60
- package/package.json +3 -3
package/lib/unzip.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.Unzip = void 0;
|
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
5
|
const fs = require("node:fs/promises");
|
|
6
6
|
const path = require("node:path");
|
|
7
|
+
const promises_1 = require("node:stream/promises");
|
|
7
8
|
const yauzl = require("yauzl");
|
|
8
9
|
const cancelable_1 = require("./cancelable");
|
|
9
10
|
const exfs = require("./fs");
|
|
@@ -41,6 +42,7 @@ class EntryContext {
|
|
|
41
42
|
this.symlinkAsFileOnWindows = symlinkAsFileOnWindows;
|
|
42
43
|
this._symlinkFileNames = [];
|
|
43
44
|
this._symlinkFolders = [];
|
|
45
|
+
this._inspectedFolders = [];
|
|
44
46
|
this._ensuredFolders = [];
|
|
45
47
|
}
|
|
46
48
|
get decodeEntryFileName() {
|
|
@@ -64,25 +66,38 @@ class EntryContext {
|
|
|
64
66
|
this._symlinkFolders.push({ folder, realpath });
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
|
-
isOutside(baseDir, targetPath) {
|
|
68
|
-
const absoluteBase = path.resolve(baseDir);
|
|
69
|
-
const absoluteTarget = path.resolve(targetPath);
|
|
70
|
-
const relative = path.relative(absoluteBase, absoluteTarget);
|
|
71
|
-
return relative.startsWith("..") || path.isAbsolute(relative);
|
|
72
|
-
}
|
|
73
69
|
getFilePath() {
|
|
74
70
|
return path.resolve(path.join(this.targetFolder, this.decodeEntryFileName));
|
|
75
71
|
}
|
|
72
|
+
async inspectFolder(folder) {
|
|
73
|
+
if (this._inspectedFolders.includes(folder) || folder === path.dirname(folder)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
await this.inspectFolder(path.dirname(folder));
|
|
77
|
+
const folderStat = await exfs.statFolder(folder);
|
|
78
|
+
if (!folderStat) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (folderStat.isSymbolicLink) {
|
|
82
|
+
this.addSymlinkFolder(folder, folderStat.realpath);
|
|
83
|
+
}
|
|
84
|
+
this._inspectedFolders.push(folder);
|
|
85
|
+
}
|
|
76
86
|
async ensureFolder(folder) {
|
|
77
87
|
if (this._ensuredFolders.includes(folder)) {
|
|
78
88
|
return;
|
|
79
89
|
}
|
|
90
|
+
await this.inspectFolder(path.dirname(folder));
|
|
80
91
|
const folderStat = await exfs.ensureFolder(folder);
|
|
81
92
|
if (folderStat.isSymbolicLink) {
|
|
82
93
|
this.addSymlinkFolder(folder, folderStat.realpath);
|
|
83
94
|
}
|
|
84
95
|
this._ensuredFolders.push(folder);
|
|
85
96
|
}
|
|
97
|
+
isSymlinkTargetOutsideTargetFolder(linkTarget, linkFilePath) {
|
|
98
|
+
const fileDir = path.dirname(linkFilePath);
|
|
99
|
+
return exfs.isOutside(this.realTargetFolder, path.resolve(fileDir, linkTarget));
|
|
100
|
+
}
|
|
86
101
|
async isOutsideTargetFolder(tpath) {
|
|
87
102
|
if (this._symlinkFileNames.length === 0 && this._symlinkFolders.length === 0) {
|
|
88
103
|
return false;
|
|
@@ -92,7 +107,7 @@ class EntryContext {
|
|
|
92
107
|
}
|
|
93
108
|
for (const { folder, realpath } of this._symlinkFolders) {
|
|
94
109
|
if (tpath.includes(folder)) {
|
|
95
|
-
if (
|
|
110
|
+
if (exfs.isOutside(this.realTargetFolder, realpath)) {
|
|
96
111
|
return true;
|
|
97
112
|
}
|
|
98
113
|
}
|
|
@@ -100,7 +115,7 @@ class EntryContext {
|
|
|
100
115
|
for (const fileName of this._symlinkFileNames) {
|
|
101
116
|
if (tpath.includes(fileName)) {
|
|
102
117
|
const realFilePath = await exfs.realpath(tpath);
|
|
103
|
-
if (
|
|
118
|
+
if (exfs.isOutside(this.realTargetFolder, realFilePath)) {
|
|
104
119
|
return true;
|
|
105
120
|
}
|
|
106
121
|
}
|
|
@@ -119,98 +134,124 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
119
134
|
super();
|
|
120
135
|
this.options = options;
|
|
121
136
|
}
|
|
122
|
-
|
|
123
|
-
* Extract the zip file to the specified location.
|
|
124
|
-
* @param zipFile
|
|
125
|
-
* @param targetFolder
|
|
126
|
-
* @param options
|
|
127
|
-
*/
|
|
128
|
-
async extract(zipFile, targetFolder) {
|
|
129
|
-
let extractedEntriesCount = 0;
|
|
137
|
+
async extract(zipFileOrBuffer, targetFolder) {
|
|
130
138
|
const token = new cancelable_1.CancellationToken();
|
|
131
139
|
this.token = token;
|
|
140
|
+
const { zfile, realTargetFolder } = await this.prepareExtraction(zipFileOrBuffer, targetFolder, token);
|
|
141
|
+
this.zipFile = zfile;
|
|
142
|
+
await this.processEntries(zfile, targetFolder, realTargetFolder, token);
|
|
143
|
+
}
|
|
144
|
+
async prepareExtraction(zipFileOrBuffer, targetFolder, token) {
|
|
132
145
|
if (this.isOverwrite()) {
|
|
133
146
|
await exfs.rimraf(targetFolder);
|
|
134
147
|
}
|
|
135
148
|
if (token.isCancelled) {
|
|
136
|
-
|
|
149
|
+
throw this.canceledError();
|
|
137
150
|
}
|
|
138
151
|
await exfs.ensureFolder(targetFolder);
|
|
139
152
|
const realTargetFolder = await exfs.realpath(targetFolder);
|
|
140
|
-
const zfile = await this.openZip(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
153
|
+
const zfile = await this.openZip(zipFileOrBuffer, token);
|
|
154
|
+
return { zfile, realTargetFolder };
|
|
155
|
+
}
|
|
156
|
+
async processEntries(zfile, targetFolder, realTargetFolder, token) {
|
|
157
|
+
return await new Promise((resolve, reject) => {
|
|
158
|
+
let extractedEntriesCount = 0;
|
|
144
159
|
let anyError = null;
|
|
145
160
|
const total = zfile.entryCount;
|
|
161
|
+
const entryContext = new EntryContext(targetFolder, realTargetFolder, this.symlinkToFile());
|
|
162
|
+
const entryEvent = new EntryEvent(total);
|
|
163
|
+
const settle = this.createPromiseSettler(resolve, reject);
|
|
164
|
+
const disposeCancel = token.onCancelled(() => {
|
|
165
|
+
this.closeZip();
|
|
166
|
+
settle.reject(this.canceledError());
|
|
167
|
+
});
|
|
146
168
|
zfile.once("error", (err) => {
|
|
169
|
+
disposeCancel();
|
|
170
|
+
anyError = this.wrapError(err, token.isCancelled);
|
|
147
171
|
this.closeZip();
|
|
148
|
-
|
|
172
|
+
settle.reject(anyError);
|
|
149
173
|
});
|
|
150
174
|
zfile.once("close", () => {
|
|
175
|
+
disposeCancel();
|
|
151
176
|
this.zipFile = null;
|
|
152
177
|
if (anyError) {
|
|
153
|
-
|
|
178
|
+
settle.reject(this.wrapError(anyError, token.isCancelled));
|
|
154
179
|
}
|
|
155
|
-
else {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// If the zip content is empty, it will not receive the
|
|
160
|
-
|
|
161
|
-
c(void 0);
|
|
162
|
-
}
|
|
180
|
+
else if (token.isCancelled) {
|
|
181
|
+
settle.reject(this.canceledError());
|
|
182
|
+
}
|
|
183
|
+
else if (extractedEntriesCount >= total) {
|
|
184
|
+
// If the zip content is empty, it will not receive the zfile.on("entry") event.
|
|
185
|
+
settle.resolve();
|
|
163
186
|
}
|
|
164
187
|
});
|
|
165
|
-
// Because openZip is an asynchronous method, openZip may not be completed when calling cancel,
|
|
166
|
-
// so we need to check if it has been canceled after the openZip method returns.
|
|
167
|
-
if (token.isCancelled) {
|
|
168
|
-
this.closeZip();
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const entryContext = new EntryContext(targetFolder, realTargetFolder, this.symlinkToFile());
|
|
172
|
-
const entryEvent = new EntryEvent(total);
|
|
173
188
|
zfile.on("entry", async (entry) => {
|
|
174
|
-
// use UTF-8 in all situations
|
|
175
|
-
// see https://github.com/thejoshwolfe/yauzl/issues/84
|
|
176
|
-
const rawName = entry.fileName.toString("utf8");
|
|
177
|
-
// allow backslash
|
|
178
|
-
const fileName = rawName.replace(/\\/g, "/");
|
|
179
|
-
// Because `decodeStrings` is `false`, we need to manually verify the entryname
|
|
180
|
-
// see https://github.com/thejoshwolfe/yauzl#validatefilenamefilename
|
|
181
|
-
const errorMessage = yauzl.validateFileName(fileName);
|
|
182
|
-
if (errorMessage != null) {
|
|
183
|
-
anyError = new Error(errorMessage);
|
|
184
|
-
this.closeZip();
|
|
185
|
-
e(anyError);
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
entryEvent.entryName = fileName;
|
|
189
|
-
this.onEntryCallback(entryEvent);
|
|
190
|
-
entryContext.decodeEntryFileName = fileName;
|
|
191
189
|
try {
|
|
192
|
-
|
|
193
|
-
entryEvent.reset();
|
|
194
|
-
zfile.readEntry();
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
await this.handleEntry(zfile, entry, entryContext, token);
|
|
198
|
-
}
|
|
190
|
+
await this.handleZipEntry(zfile, entry, entryContext, entryEvent, token);
|
|
199
191
|
extractedEntriesCount++;
|
|
200
|
-
if (extractedEntriesCount
|
|
201
|
-
|
|
192
|
+
if (extractedEntriesCount >= total) {
|
|
193
|
+
settle.resolve();
|
|
202
194
|
}
|
|
203
195
|
}
|
|
204
196
|
catch (error) {
|
|
205
197
|
anyError = this.wrapError(error, token.isCancelled);
|
|
206
198
|
this.closeZip();
|
|
207
|
-
|
|
199
|
+
settle.reject(anyError);
|
|
208
200
|
}
|
|
209
201
|
});
|
|
202
|
+
this.readNextEntry(zfile, token);
|
|
210
203
|
});
|
|
211
204
|
}
|
|
205
|
+
readNextEntry(zfile, token) {
|
|
206
|
+
if (token.isCancelled) {
|
|
207
|
+
this.closeZip();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
zfile.readEntry();
|
|
211
|
+
}
|
|
212
|
+
createPromiseSettler(resolve, reject) {
|
|
213
|
+
let settled = false;
|
|
214
|
+
return {
|
|
215
|
+
resolve: () => {
|
|
216
|
+
if (settled) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
settled = true;
|
|
220
|
+
resolve();
|
|
221
|
+
},
|
|
222
|
+
reject: (error) => {
|
|
223
|
+
if (settled) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
settled = true;
|
|
227
|
+
reject(error);
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
async handleZipEntry(zfile, entry, entryContext, entryEvent, token) {
|
|
232
|
+
// use UTF-8 in all situations
|
|
233
|
+
// see https://github.com/thejoshwolfe/yauzl/issues/84
|
|
234
|
+
const rawName = entry.fileName.toString("utf8");
|
|
235
|
+
// allow backslash
|
|
236
|
+
const fileName = rawName.replace(/\\/g, "/");
|
|
237
|
+
// Because decodeStrings is false, we need to manually verify the entry name
|
|
238
|
+
// see https://github.com/thejoshwolfe/yauzl#validatefilenamefilename
|
|
239
|
+
const errorMessage = yauzl.validateFileName(fileName);
|
|
240
|
+
if (errorMessage != null) {
|
|
241
|
+
throw new Error(errorMessage);
|
|
242
|
+
}
|
|
243
|
+
entryEvent.entryName = fileName;
|
|
244
|
+
this.onEntryCallback(entryEvent);
|
|
245
|
+
entryContext.decodeEntryFileName = fileName;
|
|
246
|
+
if (entryEvent.isPrevented) {
|
|
247
|
+
entryEvent.reset();
|
|
248
|
+
this.readNextEntry(zfile, token);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
await this.handleEntry(zfile, entry, entryContext, token);
|
|
252
|
+
}
|
|
212
253
|
/**
|
|
213
|
-
* Cancel
|
|
254
|
+
* Cancel extraction.
|
|
214
255
|
* If the cancel method is called after the extract is complete, nothing will happen.
|
|
215
256
|
*/
|
|
216
257
|
cancel() {
|
|
@@ -226,29 +267,41 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
226
267
|
this.zipFile = null;
|
|
227
268
|
}
|
|
228
269
|
}
|
|
229
|
-
openZip(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
270
|
+
async openZip(zipFileOrBuffer, token) {
|
|
271
|
+
const options = {
|
|
272
|
+
lazyEntries: true,
|
|
273
|
+
// see https://github.com/thejoshwolfe/yauzl/issues/84
|
|
274
|
+
decodeStrings: false,
|
|
275
|
+
};
|
|
276
|
+
try {
|
|
277
|
+
return await new Promise((resolve, reject) => {
|
|
278
|
+
const callback = (err, zfile) => {
|
|
279
|
+
if (err) {
|
|
280
|
+
reject(this.wrapError(err, token.isCancelled));
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
resolve(zfile);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
if (typeof zipFileOrBuffer === "string") {
|
|
287
|
+
yauzl.open(zipFileOrBuffer, options, callback);
|
|
238
288
|
}
|
|
239
289
|
else {
|
|
240
|
-
|
|
290
|
+
yauzl.fromBuffer(zipFileOrBuffer, options, callback);
|
|
241
291
|
}
|
|
242
292
|
});
|
|
243
|
-
}
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
throw this.wrapError(error, token.isCancelled);
|
|
296
|
+
}
|
|
244
297
|
}
|
|
245
298
|
async handleEntry(zfile, entry, entryContext, token) {
|
|
246
299
|
if (/\/$/.test(entryContext.decodeEntryFileName)) {
|
|
247
300
|
// Directory file names end with '/'.
|
|
248
|
-
// Note that
|
|
301
|
+
// Note that entries for directories themselves are optional.
|
|
249
302
|
// An entry's fileName implicitly requires its parent directories to exist.
|
|
250
303
|
await exfs.ensureFolder(entryContext.getFilePath());
|
|
251
|
-
|
|
304
|
+
this.readNextEntry(zfile, token);
|
|
252
305
|
}
|
|
253
306
|
else {
|
|
254
307
|
// file entry
|
|
@@ -256,13 +309,13 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
256
309
|
}
|
|
257
310
|
}
|
|
258
311
|
openZipFileStream(zfile, entry, token) {
|
|
259
|
-
return new Promise((
|
|
312
|
+
return new Promise((resolve, reject) => {
|
|
260
313
|
zfile.openReadStream(entry, (err, readStream) => {
|
|
261
314
|
if (err) {
|
|
262
|
-
|
|
315
|
+
reject(this.wrapError(err, token.isCancelled));
|
|
263
316
|
}
|
|
264
317
|
else {
|
|
265
|
-
|
|
318
|
+
resolve(readStream);
|
|
266
319
|
}
|
|
267
320
|
});
|
|
268
321
|
});
|
|
@@ -270,7 +323,7 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
270
323
|
async extractEntry(zfile, entry, entryContext, token) {
|
|
271
324
|
const filePath = entryContext.getFilePath();
|
|
272
325
|
const fileDir = path.dirname(filePath);
|
|
273
|
-
await entryContext.
|
|
326
|
+
await entryContext.inspectFolder(fileDir);
|
|
274
327
|
const outside = await entryContext.isOutsideTargetFolder(fileDir);
|
|
275
328
|
if (outside) {
|
|
276
329
|
const error = new Error(`Refuse to write file outside "${entryContext.targetFolder}", file: "${filePath}"`);
|
|
@@ -279,55 +332,69 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
279
332
|
}
|
|
280
333
|
const readStream = await this.openZipFileStream(zfile, entry, token);
|
|
281
334
|
await this.writeEntryToFile(readStream, entry, entryContext, token);
|
|
282
|
-
|
|
335
|
+
this.readNextEntry(zfile, token);
|
|
283
336
|
}
|
|
284
337
|
async writeEntryToFile(readStream, entry, entryContext, token) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
338
|
+
var _a;
|
|
339
|
+
try {
|
|
340
|
+
const filePath = entryContext.getFilePath();
|
|
341
|
+
const mode = this.modeFromEntry(entry);
|
|
342
|
+
// see https://unix.stackexchange.com/questions/193465/what-file-mode-is-a-symlink
|
|
343
|
+
const isSymlink = (mode & 0o170000) === 0o120000;
|
|
344
|
+
if (isSymlink) {
|
|
345
|
+
entryContext.symlinkFileNames.push(path.resolve(path.join(entryContext.targetFolder, entryContext.decodeEntryFileName)));
|
|
290
346
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const mode = this.modeFromEntry(entry);
|
|
299
|
-
// see https://unix.stackexchange.com/questions/193465/what-file-mode-is-a-symlink
|
|
300
|
-
const isSymlink = (mode & 0o170000) === 0o120000;
|
|
301
|
-
if (isSymlink) {
|
|
302
|
-
entryContext.symlinkFileNames.push(path.resolve(path.join(entryContext.targetFolder, entryContext.decodeEntryFileName)));
|
|
347
|
+
if (isSymlink && !this.symlinkToFile()) {
|
|
348
|
+
const linkContent = await this.readStreamContent(readStream, token);
|
|
349
|
+
if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.safeSymlinksOnly) &&
|
|
350
|
+
entryContext.isSymlinkTargetOutsideTargetFolder(linkContent, filePath)) {
|
|
351
|
+
const error = new Error(`Dangerous link path was refused : "${entryContext.targetFolder}", file: "${filePath}", target: "${linkContent}". Set safeSymlinksOnly to false to allow writing through this symlink.`);
|
|
352
|
+
error.name = "AF_ILLEGAL_TARGET";
|
|
353
|
+
throw error;
|
|
303
354
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
});
|
|
355
|
+
await entryContext.ensureFolder(path.dirname(filePath));
|
|
356
|
+
await this.createSymlink(linkContent, filePath);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
await entryContext.ensureFolder(path.dirname(filePath));
|
|
360
|
+
const fileStream = (0, node_fs_1.createWriteStream)(filePath, { mode });
|
|
361
|
+
const pipelinePromise = (0, promises_1.pipeline)(readStream, fileStream);
|
|
362
|
+
const disposeCancel = token.onCancelled(() => {
|
|
363
|
+
fileStream.destroy(this.canceledError());
|
|
364
|
+
});
|
|
365
|
+
try {
|
|
366
|
+
await pipelinePromise;
|
|
317
367
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
fileStream.once("close", () => c());
|
|
321
|
-
fileStream.once("error", (err) => {
|
|
322
|
-
e(this.wrapError(err, token.isCancelled));
|
|
323
|
-
});
|
|
324
|
-
readStream.pipe(fileStream);
|
|
368
|
+
finally {
|
|
369
|
+
disposeCancel();
|
|
325
370
|
}
|
|
326
371
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
throw this.wrapError(error, token.isCancelled);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async readStreamContent(readStream, token) {
|
|
378
|
+
let linkContent = "";
|
|
379
|
+
const readPromise = new Promise((resolve, reject) => {
|
|
380
|
+
readStream.on("data", (chunk) => {
|
|
381
|
+
linkContent += typeof chunk === "string" ? chunk : chunk.toString();
|
|
382
|
+
});
|
|
383
|
+
readStream.once("end", resolve);
|
|
384
|
+
readStream.once("error", (err) => {
|
|
385
|
+
reject(this.wrapError(err, token.isCancelled));
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
const disposeCancel = token.onCancelled(() => {
|
|
389
|
+
readStream.destroy(this.canceledError());
|
|
330
390
|
});
|
|
391
|
+
try {
|
|
392
|
+
await readPromise;
|
|
393
|
+
return linkContent;
|
|
394
|
+
}
|
|
395
|
+
finally {
|
|
396
|
+
disposeCancel();
|
|
397
|
+
}
|
|
331
398
|
}
|
|
332
399
|
modeFromEntry(entry) {
|
|
333
400
|
const attr = entry.externalFileAttributes >> 16 || 33188;
|
|
@@ -335,7 +402,7 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
335
402
|
.map((mask) => attr & mask)
|
|
336
403
|
.reduce((a, b) => a + b, attr & 61440 /* S_IFMT */);
|
|
337
404
|
}
|
|
338
|
-
async createSymlink(linkContent,
|
|
405
|
+
async createSymlink(linkContent, filePath) {
|
|
339
406
|
let linkType = "file";
|
|
340
407
|
if (process.platform === "win32") {
|
|
341
408
|
if (/\/$/.test(linkContent)) {
|
|
@@ -344,7 +411,7 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
344
411
|
else {
|
|
345
412
|
let targetPath = linkContent;
|
|
346
413
|
if (!path.isAbsolute(linkContent)) {
|
|
347
|
-
targetPath = path.join(path.dirname(
|
|
414
|
+
targetPath = path.join(path.dirname(filePath), linkContent);
|
|
348
415
|
}
|
|
349
416
|
try {
|
|
350
417
|
const stat = await fs.stat(targetPath);
|
|
@@ -357,7 +424,7 @@ class Unzip extends cancelable_1.Cancelable {
|
|
|
357
424
|
}
|
|
358
425
|
}
|
|
359
426
|
}
|
|
360
|
-
await fs.symlink(linkContent,
|
|
427
|
+
await fs.symlink(linkContent, filePath, linkType);
|
|
361
428
|
}
|
|
362
429
|
isOverwrite() {
|
|
363
430
|
var _a;
|
package/lib/zip.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Cancelable } from "./cancelable";
|
|
2
2
|
export interface IZipOptions {
|
|
3
3
|
/**
|
|
4
|
-
* Indicates how to handle
|
|
4
|
+
* Indicates how to handle the given path when it is a symbolic link.
|
|
5
5
|
*
|
|
6
6
|
* `true`: add the target of the symbolic link to the zip.
|
|
7
7
|
*
|
|
8
|
-
* `false`: add symbolic link itself to the zip.
|
|
8
|
+
* `false`: add the symbolic link itself to the zip.
|
|
9
9
|
*
|
|
10
10
|
* The default value is `false`.
|
|
11
11
|
*/
|
|
@@ -13,7 +13,7 @@ export interface IZipOptions {
|
|
|
13
13
|
/**
|
|
14
14
|
* Sets the compression level.
|
|
15
15
|
*
|
|
16
|
-
* 0
|
|
16
|
+
* `0`: the file data will be stored; otherwise, the file data will be deflated.
|
|
17
17
|
*
|
|
18
18
|
* The default value is `6`.
|
|
19
19
|
*/
|
|
@@ -30,29 +30,44 @@ export declare class Zip extends Cancelable {
|
|
|
30
30
|
constructor(options?: IZipOptions | undefined);
|
|
31
31
|
private yazlFile;
|
|
32
32
|
private isPipe;
|
|
33
|
+
private isChunk;
|
|
33
34
|
private zipStream;
|
|
34
35
|
private zipFiles;
|
|
35
36
|
private zipFolders;
|
|
36
37
|
private token;
|
|
37
38
|
/**
|
|
38
|
-
* Adds a file from the file system at realPath
|
|
39
|
+
* Adds a file from the file system at `realPath` to the zip file as `metadataPath`.
|
|
39
40
|
* @param file
|
|
40
|
-
* @param metadataPath Typically metadataPath would be calculated as path.relative(root, realPath)
|
|
41
|
+
* @param metadataPath Typically, `metadataPath` would be calculated as `path.relative(root, realPath)`.
|
|
41
42
|
* A valid metadataPath must not start with "/" or /[A-Za-z]:\//, and must not contain "..".
|
|
42
43
|
*/
|
|
43
44
|
addFile(file: string, metadataPath?: string): void;
|
|
44
45
|
/**
|
|
45
|
-
* Adds a folder from the file system at realPath
|
|
46
|
+
* Adds a folder from the file system at `realPath` to the zip file as `metadataPath`.
|
|
46
47
|
* @param folder
|
|
47
|
-
* @param metadataPath Typically metadataPath would be calculated as path.relative(root, realPath)
|
|
48
|
+
* @param metadataPath Typically, `metadataPath` would be calculated as `path.relative(root, realPath)`.
|
|
48
49
|
* A valid metadataPath must not start with "/" or /[A-Za-z]:\//, and must not contain "..".
|
|
49
50
|
*/
|
|
50
51
|
addFolder(folder: string, metadataPath?: string): void;
|
|
51
52
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
53
|
+
* Zips the content and returns it as a single Buffer.
|
|
54
|
+
*
|
|
55
|
+
* @returns A promise that resolves to the zipped Buffer.
|
|
56
|
+
*/
|
|
57
|
+
archive(): Promise<Buffer>;
|
|
58
|
+
/**
|
|
59
|
+
* Zips the content and saves it directly to the specified file path.
|
|
60
|
+
*
|
|
61
|
+
* @param zipFile The absolute or relative path where the .zip file will be created.
|
|
62
|
+
* @returns A promise that resolves when the file has been fully written.
|
|
54
63
|
*/
|
|
55
64
|
archive(zipFile: string): Promise<void>;
|
|
65
|
+
private prepareArchive;
|
|
66
|
+
private bindArchiveOutput;
|
|
67
|
+
private archiveToFile;
|
|
68
|
+
private archiveToBuffer;
|
|
69
|
+
private createPromiseSettler;
|
|
70
|
+
private addQueuedEntries;
|
|
56
71
|
/**
|
|
57
72
|
* Cancel compression.
|
|
58
73
|
* If the cancel method is called after the archive is complete, nothing will happen.
|
|
@@ -62,7 +77,7 @@ export declare class Zip extends Cancelable {
|
|
|
62
77
|
private addFileStream;
|
|
63
78
|
private addSymlink;
|
|
64
79
|
private walkDir;
|
|
65
|
-
private
|
|
80
|
+
private stop;
|
|
66
81
|
private followSymlink;
|
|
67
82
|
/**
|
|
68
83
|
* Retrieves the yazl options based on the current settings.
|