-
Notifications
You must be signed in to change notification settings - Fork 301
/
decompress.js
77 lines (68 loc) · 2.38 KB
/
decompress.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import fs from "node:fs";
import path from "node:path";
import stream from "node:stream";
import tar from "tar";
import yauzl from "yauzl-promise";
/**
* Decompresses a file at `filePath` to `cacheDir` directory.
*
* @param {string} filePath - file path to compressed binary
* @param {string} cacheDir - directory to decompress into
*/
export default async function decompress(filePath, cacheDir) {
if (filePath.endsWith(".zip")) {
await unzip(filePath, cacheDir);
} else {
await tar.extract({
file: filePath,
C: cacheDir
});
}
}
/**
* Get file mode from entry. Reference implementation is [here](https://github.com/fpsqdb/zip-lib/blob/ac447d269218d396e05cd7072d0e9cd82b5ec52c/src/unzip.ts#L380).
*
* @param {yauzl.Entry} entry - Yauzl entry
* @return {number} - entry's file mode
*/
function modeFromEntry(entry) {
const attr = entry.externalFileAttributes >> 16 || 33188;
return [448 /* S_IRWXU */, 56 /* S_IRWXG */, 7 /* S_IRWXO */]
.map(mask => attr & mask)
.reduce((a, b) => a + b, attr & 61440 /* S_IFMT */);
}
/**
* Unzip `zippedFile` to `cacheDir`.
*
* @async
* @function
* @param {string} zippedFile - file path to .zip file
* @param {string} cacheDir - directory to unzip in
* @return {Promise<void>}
*/
async function unzip(zippedFile, cacheDir) {
const zip = await yauzl.open(zippedFile);
let entry = await zip.readEntry();
while (entry !== null) {
let entryPathAbs = path.join(cacheDir, entry.filename);
/* Create the directory beforehand to prevent `ENOENT: no such file or directory` errors. */
await fs.promises.mkdir(path.dirname(entryPathAbs), { recursive: true });
/* Check if entry is a symbolic link */
const isSymlink = ((modeFromEntry(entry) & 0o170000) === 0o120000);
const readStream = await entry.openReadStream();
if (isSymlink) {
const chunks = [];
/* Read stream into Array. */
readStream.on("data", (chunk) => chunks.push(chunk));
await stream.promises.finished(readStream);
const link = Buffer.concat(chunks).toString('utf8').trim();
await fs.promises.symlink(link, entryPathAbs)
} else {
// Pipe read to write stream
const writeStream = fs.createWriteStream(entryPathAbs);
await stream.promises.pipeline(readStream, writeStream);
}
// Read next entry
entry = await zip.readEntry();
}
}