zip-lib
Version:
zip and unzip library for node
209 lines (208 loc) • 6.32 kB
JavaScript
;
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;
}