2023-03-30 22:56:58 +02:00
|
|
|
import fsp from "node:fs/promises";
|
|
|
|
import fs from "fs";
|
|
|
|
import path from "node:path";
|
|
|
|
import yauzl from "yauzl";
|
2023-08-21 04:26:58 +02:00
|
|
|
import yazl from "yazl";
|
2023-03-30 22:56:58 +02:00
|
|
|
import stream from "node:stream";
|
|
|
|
import { promisify } from "node:util";
|
|
|
|
|
|
|
|
const pipeline = promisify(stream.pipeline);
|
|
|
|
|
|
|
|
async function pathExists(path: string) {
|
|
|
|
try {
|
|
|
|
await fsp.stat(path);
|
|
|
|
return true;
|
|
|
|
} catch (error) {
|
|
|
|
if ((error as { code: string }).code === "ENOENT") {
|
|
|
|
return false;
|
2023-03-29 09:54:29 +02:00
|
|
|
}
|
2023-03-30 22:56:58 +02:00
|
|
|
throw error;
|
2023-03-29 09:54:29 +02:00
|
|
|
}
|
2023-03-30 22:56:58 +02:00
|
|
|
}
|
2023-03-29 09:54:29 +02:00
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
// Handlings of non posix path is not implemented correctly
|
|
|
|
// it work by coincidence. Don't have the time to fix but it should be fixed.
|
|
|
|
export async function unzip(file: string, targetFolder: string, specificDirsToExtract?: string[]) {
|
|
|
|
specificDirsToExtract = specificDirsToExtract?.map(dirPath => {
|
|
|
|
if (!dirPath.endsWith("/") || !dirPath.endsWith("\\")) {
|
|
|
|
dirPath += "/";
|
|
|
|
}
|
|
|
|
|
|
|
|
return dirPath;
|
|
|
|
});
|
2023-03-29 09:54:29 +02:00
|
|
|
|
2023-03-30 22:56:58 +02:00
|
|
|
if (!targetFolder.endsWith("/") || !targetFolder.endsWith("\\")) {
|
|
|
|
targetFolder += "/";
|
|
|
|
}
|
2023-03-31 09:36:59 -06:00
|
|
|
if (!fs.existsSync(targetFolder)) {
|
|
|
|
fs.mkdirSync(targetFolder, { recursive: true });
|
|
|
|
}
|
2023-03-29 09:54:29 +02:00
|
|
|
|
2023-03-30 22:56:58 +02:00
|
|
|
return new Promise<void>((resolve, reject) => {
|
|
|
|
yauzl.open(file, { lazyEntries: true }, async (err, zipfile) => {
|
|
|
|
if (err) {
|
|
|
|
reject(err);
|
|
|
|
return;
|
|
|
|
}
|
2023-03-29 09:54:29 +02:00
|
|
|
|
2023-03-30 22:56:58 +02:00
|
|
|
zipfile.readEntry();
|
2023-03-29 09:54:29 +02:00
|
|
|
|
2023-03-30 22:56:58 +02:00
|
|
|
zipfile.on("entry", async entry => {
|
2023-08-21 04:26:58 +02:00
|
|
|
if (specificDirsToExtract !== undefined) {
|
|
|
|
const dirPath = specificDirsToExtract.find(dirPath => entry.fileName.startsWith(dirPath));
|
|
|
|
|
2023-03-30 22:56:58 +02:00
|
|
|
// Skip files outside of the unzipSubPath
|
2023-08-21 04:26:58 +02:00
|
|
|
if (dirPath === undefined) {
|
2023-03-30 22:56:58 +02:00
|
|
|
zipfile.readEntry();
|
|
|
|
return;
|
|
|
|
}
|
2023-03-29 09:54:29 +02:00
|
|
|
|
2023-03-30 22:56:58 +02:00
|
|
|
// Remove the unzipSubPath from the file name
|
2023-08-21 04:26:58 +02:00
|
|
|
entry.fileName = entry.fileName.substring(dirPath.length);
|
2023-03-30 22:56:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const target = path.join(targetFolder, entry.fileName);
|
|
|
|
|
|
|
|
// Directory file names end with '/'.
|
|
|
|
// Note that entries for directories themselves are optional.
|
|
|
|
// An entry's fileName implicitly requires its parent directories to exist.
|
|
|
|
if (/[\/\\]$/.test(target)) {
|
|
|
|
await fsp.mkdir(target, { recursive: true });
|
|
|
|
|
|
|
|
zipfile.readEntry();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip existing files
|
|
|
|
if (await pathExists(target)) {
|
|
|
|
zipfile.readEntry();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
zipfile.openReadStream(entry, async (err, readStream) => {
|
|
|
|
if (err) {
|
|
|
|
reject(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-21 04:26:58 +02:00
|
|
|
await fsp.mkdir(path.dirname(target), { "recursive": true });
|
|
|
|
|
2023-03-30 22:56:58 +02:00
|
|
|
await pipeline(readStream, fs.createWriteStream(target));
|
|
|
|
|
|
|
|
zipfile.readEntry();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
zipfile.once("end", function () {
|
|
|
|
zipfile.close();
|
|
|
|
resolve();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2023-03-29 09:54:29 +02:00
|
|
|
}
|
2023-08-21 04:26:58 +02:00
|
|
|
|
|
|
|
// NOTE: This code was directly copied from ChatGPT and appears to function as expected.
|
|
|
|
// However, confidence in its complete accuracy and robustness is limited.
|
|
|
|
export async function zip(sourceFolder: string, targetZip: string) {
|
|
|
|
return new Promise<void>(async (resolve, reject) => {
|
|
|
|
const zipfile = new yazl.ZipFile();
|
|
|
|
const files: string[] = [];
|
|
|
|
|
|
|
|
// Recursive function to explore directories and their subdirectories
|
|
|
|
async function exploreDir(dir: string) {
|
|
|
|
const dirContent = await fsp.readdir(dir);
|
|
|
|
for (const file of dirContent) {
|
|
|
|
const filePath = path.join(dir, file);
|
|
|
|
const stat = await fsp.stat(filePath);
|
|
|
|
if (stat.isDirectory()) {
|
|
|
|
await exploreDir(filePath);
|
|
|
|
} else if (stat.isFile()) {
|
|
|
|
files.push(filePath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collecting all files to be zipped
|
|
|
|
await exploreDir(sourceFolder);
|
|
|
|
|
|
|
|
// Adding files to zip
|
|
|
|
for (const file of files) {
|
|
|
|
const relativePath = path.relative(sourceFolder, file);
|
|
|
|
zipfile.addFile(file, relativePath);
|
|
|
|
}
|
|
|
|
|
|
|
|
zipfile.outputStream
|
|
|
|
.pipe(fs.createWriteStream(targetZip))
|
|
|
|
.on("close", () => resolve())
|
|
|
|
.on("error", err => reject(err)); // Listen to error events
|
|
|
|
|
|
|
|
zipfile.end();
|
|
|
|
});
|
|
|
|
}
|