import type SevenZip from "7z-wasm"; import type { AsyncZipOptions, AsyncZippable, AsyncZippableFile, Unzipped, } from "fflate"; import { basename, extname, join } from "path"; import { BASE_ZIP_CONFIG } from "utils/constants"; import { loadFiles } from "utils/functions"; export const createZippable = (path: string, file: Buffer): AsyncZippable => path .split("/") .reduceRight((value, key) => ({ [key]: value }), [ file, BASE_ZIP_CONFIG, ] as AsyncZippableFile as AsyncZippable); export const addEntryToZippable = ( oldZippable: AsyncZippable, newZippable: AsyncZippable ): AsyncZippable => { const [[key, value]] = Object.entries(newZippable); // eslint-disable-next-line no-param-reassign oldZippable[key] = key in oldZippable ? addEntryToZippable( oldZippable[key] as AsyncZippable, newZippable[key] as AsyncZippable ) : value; return oldZippable; }; const unzipAsync = (zipFile: Buffer): Promise => new Promise((resolve, reject) => { import("fflate").then(({ unzip }) => unzip(zipFile, (error, data) => (error ? reject(error) : resolve(data))) ); }); export const zipAsync = ( data: AsyncZippable, opts: AsyncZipOptions = BASE_ZIP_CONFIG ): Promise => new Promise((resolve, reject) => { import("fflate").then(({ zip }) => zip(data, opts, (error, zipData) => error ? reject(error) : resolve(zipData) ) ); }); export const addFileToZip = async ( buffer: Buffer, filePath: string, zipFilePath: string, readFile: (path: string) => Promise ): Promise => Buffer.from( await zipAsync( addEntryToZippable( (buffer.length > 0 && (await unzipAsync(buffer))) || {}, createZippable(zipFilePath, await readFile(filePath)) ) ) ); export const isFileInZip = ( buffer: Buffer, zipFilePath: string ): Promise => new Promise((resolve, reject) => { import("fflate").then(({ unzip }) => unzip(buffer, (unzipError, zipData) => unzipError ? reject(unzipError) : resolve(Object.keys(zipData).includes(zipFilePath)) ) ); }); export { unzipAsync as unzip }; declare global { interface Window { SevenZip: typeof SevenZip; } } export const unarchive = async ( path: string, data: Buffer ): Promise => { if (!window.SevenZip) { await loadFiles(["System/7zip/7zz.es6.js"]); } if (!window.SevenZip) return {}; const sevenZip = await window.SevenZip(); const fileName = basename(path); const extractFolder = join("/", basename(path, extname(path))); sevenZip.FS.mkdir(extractFolder); sevenZip.FS.chdir(extractFolder); const stream = sevenZip.FS.open(fileName, "w+"); sevenZip.FS.write(stream, data, 0, data.length); sevenZip.FS.close(stream); sevenZip.callMain(["-y", "x", fileName]); const extractedFiles = sevenZip.FS.readdir(extractFolder); const reduceFiles = (currentPath: string) => (accFiles: Unzipped, file: string): Unzipped => { if ([".", "..", fileName].includes(file)) return accFiles; const filePath = join(currentPath, file); const extractPath = filePath.replace(extractFolder, ""); try { sevenZip.FS.chmod(filePath, 0o777); } catch { // Ignore failure to change permissions } Object.assign( accFiles, sevenZip.FS.isDir(sevenZip.FS.stat(filePath).mode) ? { [join(extractPath, "/")]: Buffer.from(""), ...sevenZip.FS.readdir(filePath).reduce( reduceFiles(filePath), {} ), } : { [extractPath]: sevenZip.FS.readFile(filePath, { flags: "r" }), } ); return accFiles; }; return extractedFiles.reduce(reduceFiles(extractFolder), {}); };