UNPKG

zip-lib

Version:

zip and unzip library for node

209 lines (208 loc) 6.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.realpath = realpath; exports.readdirp = readdirp; exports.getFileEntry = getFileEntry; exports.ensureFolder = ensureFolder; exports.pathExists = pathExists; exports.rimraf = rimraf; exports.isRootPath = isRootPath; const path = require("path"); const fs = require("fs/promises"); const util = require("node:util"); const fsSync = require("fs"); async function realpath(target) { // fs.promises.realpath has a bug with long path on Windows. // https://github.com/nodejs/node/issues/51031 return util.promisify(fsSync.realpath)(target); } async function readdirp(folder) { const result = []; const files = await fs.readdir(folder); for (const item of files) { const file = path.join(folder, item); const entry = await getFileEntry(file); if (!entry.isSymbolicLink && entry.type === "dir") { const subFiles = await readdirp(file); if (subFiles.length > 0) { result.push(...subFiles); // If the folder is not empty, don't need to add the folder itself. // continue and skip the code below continue; } } result.push(entry); } return result; } async function getFileEntry(target) { const stat = await fs.lstat(target); let isSymbolicLink = false; let fileType = "file"; if (stat.isDirectory()) { fileType = "dir"; } else { if (stat.isSymbolicLink()) { isSymbolicLink = true; // If the path is a link, we must instead use fs.stat() to find out if the // link is a directory or not because lstat will always return the stat of // the link which is always a file. const actualStat = await fs.stat(target); if (actualStat.isDirectory()) { fileType = "dir"; } } } return { path: target, isSymbolicLink, type: fileType, mtime: stat.mtime, mode: stat.mode }; } async function ensureFolder(folder) { // stop at root if (folder === path.dirname(folder)) { return Promise.resolve({ isDirectory: true, isSymbolicLink: false }); } try { const result = await mkdir(folder); return result; } catch (error) { // ENOENT: a parent folder does not exist yet, continue // to create the parent folder and then try again. if (error.code === "ENOENT") { await ensureFolder(path.dirname(folder)); return mkdir(folder); } // Any other error return Promise.reject(error); } } async function pathExists(target) { try { await fs.access(target); return true; } catch (error) { return false; } } async function rimraf(target) { if (isRootPath(target)) { // refuse to recursively delete root return Promise.reject(new Error(`Refuse to recursively delete root, path: "${target}"`)); } try { const stat = await fs.lstat(target); // Folder delete (recursive) - NOT for symbolic links though! if (stat.isDirectory() && !stat.isSymbolicLink()) { // Children const children = await fs.readdir(target); await Promise.all(children.map(child => rimraf(path.join(target, child)))); // Folder await fs.rmdir(target); } // Single file delete else { // chmod as needed to allow for unlink const mode = stat.mode; if (!(mode & 128)) { // 128 === 0200 await fs.chmod(target, mode | 128); } return fs.unlink(target); } } catch (error) { if (error.code !== "ENOENT") { throw error; } } } async function mkdir(folder) { try { await fs.mkdir(folder, 0o777); return { isDirectory: true, isSymbolicLink: false, }; } catch (error) { // ENOENT: a parent folder does not exist yet or folder name is invalid. if (error.code === "ENOENT") { return Promise.reject(error); } // Any other error: check if folder exists and // return normally in that case if its a folder try { const fileStat = await fs.lstat(folder); if (fileStat.isSymbolicLink()) { const realFilePath = await realpath(folder); const realFileStat = await fs.lstat(realFilePath); if (!realFileStat.isDirectory()) { return Promise.reject(new Error(`"${folder}" exists and is not a directory.`)); } return { isDirectory: false, isSymbolicLink: true, realpath: realFilePath, }; } else { if (!fileStat.isDirectory()) { return Promise.reject(new Error(`"${folder}" exists and is not a directory.`)); } return { isDirectory: true, isSymbolicLink: false, }; } } catch (statError) { throw error; // rethrow original error } } } // "A" const charA = 65; // "Z" const charZ = 90; // "a" const chara = 97; // "z" const charz = 122; // ":" const charColon = 58; // "\" const charWinSep = 92; // "/" const cahrUnixSep = 47; function isDriveLetter(char0) { return char0 >= charA && char0 <= charZ || char0 >= chara && char0 <= charz; } const winSep = "\\"; const unixSep = "/"; function isRootPath(target) { if (!target) { return false; } if (target === winSep || target === unixSep) { return true; } if (process.platform === "win32") { if (target.length > 3) { return false; } return isDriveLetter(target.charCodeAt(0)) && target.charCodeAt(1) === charColon && (target.length === 2 || target.charCodeAt(2) === charWinSep || target.charCodeAt(2) === cahrUnixSep); } return false; }