1 | import fs from "node:fs";
|
2 | import path from "node:path";
|
3 | import stream from "node:stream";
|
4 |
|
5 | import tar from "tar";
|
6 | import yauzl from "yauzl-promise";
|
7 |
|
8 | import util from "./util.js";
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | export default async function decompress(filePath, cacheDir) {
|
17 | if (filePath.endsWith(".zip")) {
|
18 | await unzip(filePath, cacheDir);
|
19 | } else {
|
20 | await tar.extract({
|
21 | file: filePath,
|
22 | C: cacheDir
|
23 | });
|
24 | }
|
25 | }
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | function modeFromEntry(entry) {
|
34 | const attr = entry.externalFileAttributes >> 16 || 33188;
|
35 |
|
36 | return [448 , 56 , 7 ]
|
37 | .map(mask => attr & mask)
|
38 | .reduce((a, b) => a + b, attr & 61440 );
|
39 | }
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | async function unzip(zippedFile, cacheDir) {
|
51 | const zip = await yauzl.open(zippedFile);
|
52 | let entry = await zip.readEntry();
|
53 | const symlinks = [];
|
54 |
|
55 | while (entry !== null) {
|
56 | let entryPathAbs = path.join(cacheDir, entry.filename);
|
57 |
|
58 | const isSymlink = ((modeFromEntry(entry) & 0o170000) === 0o120000);
|
59 |
|
60 | if (isSymlink) {
|
61 |
|
62 | symlinks.push(entry);
|
63 | } else {
|
64 |
|
65 | await fs.promises.mkdir(path.dirname(entryPathAbs), {recursive: true});
|
66 | if (!entry.filename.endsWith('/')) {
|
67 | const readStream = await entry.openReadStream();
|
68 | const writeStream = fs.createWriteStream(entryPathAbs);
|
69 | await stream.promises.pipeline(readStream, writeStream);
|
70 |
|
71 |
|
72 | const mode = modeFromEntry(entry);
|
73 | await fs.promises.chmod(entryPathAbs, mode);
|
74 | }
|
75 | }
|
76 |
|
77 |
|
78 | entry = await zip.readEntry();
|
79 | }
|
80 |
|
81 |
|
82 | for (const symlinkEntry of symlinks) {
|
83 | let entryPathAbs = path.join(cacheDir, symlinkEntry.filename);
|
84 | const readStream = await symlinkEntry.openReadStream();
|
85 | const chunks = [];
|
86 | readStream.on("data", (chunk) => chunks.push(chunk));
|
87 | await new Promise(resolve => readStream.on("end", resolve));
|
88 | const linkTarget = Buffer.concat(chunks).toString('utf8').trim();
|
89 |
|
90 | const fileExists = await util.fileExists(entryPathAbs);
|
91 | if (!fileExists) {
|
92 | await fs.promises.symlink(linkTarget, entryPathAbs);
|
93 | }
|
94 | }
|
95 | } |
\ | No newline at end of file |