blob: 3c1f9431e97f9e209de11be2683f179d58629e3c [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001'use strict';
2const fileType = require('file-type');
3const getStream = require('get-stream');
4const pify = require('pify');
5const yauzl = require('yauzl');
6
7const getType = (entry, mode) => {
8 const IFMT = 61440;
9 const IFDIR = 16384;
10 const IFLNK = 40960;
11 const madeBy = entry.versionMadeBy >> 8;
12
13 if ((mode & IFMT) === IFLNK) {
14 return 'symlink';
15 }
16
17 if ((mode & IFMT) === IFDIR || (madeBy === 0 && entry.externalFileAttributes === 16)) {
18 return 'directory';
19 }
20
21 return 'file';
22};
23
24const extractEntry = (entry, zip) => {
25 const file = {
26 mode: (entry.externalFileAttributes >> 16) & 0xFFFF,
27 mtime: entry.getLastModDate(),
28 path: entry.fileName
29 };
30
31 file.type = getType(entry, file.mode);
32
33 if (file.mode === 0 && file.type === 'directory') {
34 file.mode = 493;
35 }
36
37 if (file.mode === 0) {
38 file.mode = 420;
39 }
40
41 return pify(zip.openReadStream.bind(zip))(entry)
42 .then(getStream.buffer)
43 .then(buf => {
44 file.data = buf;
45
46 if (file.type === 'symlink') {
47 file.linkname = buf.toString();
48 }
49
50 return file;
51 })
52 .catch(err => {
53 zip.close();
54 throw err;
55 });
56};
57
58const extractFile = zip => new Promise((resolve, reject) => {
59 const files = [];
60
61 zip.readEntry();
62
63 zip.on('entry', entry => {
64 extractEntry(entry, zip)
65 .catch(reject)
66 .then(file => {
67 files.push(file);
68 zip.readEntry();
69 });
70 });
71
72 zip.on('error', reject);
73 zip.on('end', () => resolve(files));
74});
75
76module.exports = () => buf => {
77 if (!Buffer.isBuffer(buf)) {
78 return Promise.reject(new TypeError(`Expected a Buffer, got ${typeof buf}`));
79 }
80
81 if (!fileType(buf) || fileType(buf).ext !== 'zip') {
82 return Promise.resolve([]);
83 }
84
85 return pify(yauzl.fromBuffer)(buf, {lazyEntries: true}).then(extractFile);
86};