7472 lines
222 KiB
JavaScript
7472 lines
222 KiB
JavaScript
"use strict";
|
|
var __create = Object.create;
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
// If the importer is in node compatibility mode or this is not an ESM
|
|
// file that has been converted to a CommonJS file using a Babel-
|
|
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
mod
|
|
));
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
var chunk_SVP4SRAT_exports = {};
|
|
__export(chunk_SVP4SRAT_exports, {
|
|
require_balanced_match: () => require_balanced_match,
|
|
require_p_map: () => require_p_map,
|
|
rimraf: () => rimraf
|
|
});
|
|
module.exports = __toCommonJS(chunk_SVP4SRAT_exports);
|
|
var import_chunk_QGM4M3NI = require("./chunk-QGM4M3NI.js");
|
|
var import_node_url = require("node:url");
|
|
var import_node_path = require("node:path");
|
|
var import_node_url2 = require("node:url");
|
|
var import_fs = require("fs");
|
|
var actualFS = __toESM(require("node:fs"));
|
|
var import_promises = require("node:fs/promises");
|
|
var import_node_events = require("node:events");
|
|
var import_node_stream = __toESM(require("node:stream"));
|
|
var import_node_string_decoder = require("node:string_decoder");
|
|
var import_path = require("path");
|
|
var import_util = require("util");
|
|
var import_fs2 = __toESM(require("fs"));
|
|
var import_fs3 = require("fs");
|
|
var import_fs4 = require("fs");
|
|
var import_path2 = require("path");
|
|
var import_path3 = require("path");
|
|
var import_path4 = require("path");
|
|
var import_os = require("os");
|
|
var import_path5 = require("path");
|
|
var require_indent_string = (0, import_chunk_QGM4M3NI.__commonJS)({
|
|
"../../node_modules/.pnpm/indent-string@4.0.0/node_modules/indent-string/index.js"(exports, module2) {
|
|
"use strict";
|
|
module2.exports = (string, count = 1, options) => {
|
|
options = {
|
|
indent: " ",
|
|
includeEmptyLines: false,
|
|
...options
|
|
};
|
|
if (typeof string !== "string") {
|
|
throw new TypeError(
|
|
`Expected \`input\` to be a \`string\`, got \`${typeof string}\``
|
|
);
|
|
}
|
|
if (typeof count !== "number") {
|
|
throw new TypeError(
|
|
`Expected \`count\` to be a \`number\`, got \`${typeof count}\``
|
|
);
|
|
}
|
|
if (typeof options.indent !== "string") {
|
|
throw new TypeError(
|
|
`Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\``
|
|
);
|
|
}
|
|
if (count === 0) {
|
|
return string;
|
|
}
|
|
const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm;
|
|
return string.replace(regex, options.indent.repeat(count));
|
|
};
|
|
}
|
|
});
|
|
var require_clean_stack = (0, import_chunk_QGM4M3NI.__commonJS)({
|
|
"../../node_modules/.pnpm/clean-stack@2.2.0/node_modules/clean-stack/index.js"(exports, module2) {
|
|
"use strict";
|
|
var os = (0, import_chunk_QGM4M3NI.__require)("os");
|
|
var extractPathRegex = /\s+at.*(?:\(|\s)(.*)\)?/;
|
|
var pathRegex = /^(?:(?:(?:node|(?:internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)\.js:\d+:\d+)|native)/;
|
|
var homeDir = typeof os.homedir === "undefined" ? "" : os.homedir();
|
|
module2.exports = (stack, options) => {
|
|
options = Object.assign({ pretty: false }, options);
|
|
return stack.replace(/\\/g, "/").split("\n").filter((line) => {
|
|
const pathMatches = line.match(extractPathRegex);
|
|
if (pathMatches === null || !pathMatches[1]) {
|
|
return true;
|
|
}
|
|
const match2 = pathMatches[1];
|
|
if (match2.includes(".app/Contents/Resources/electron.asar") || match2.includes(".app/Contents/Resources/default_app.asar")) {
|
|
return false;
|
|
}
|
|
return !pathRegex.test(match2);
|
|
}).filter((line) => line.trim() !== "").map((line) => {
|
|
if (options.pretty) {
|
|
return line.replace(extractPathRegex, (m, p1) => m.replace(p1, p1.replace(homeDir, "~")));
|
|
}
|
|
return line;
|
|
}).join("\n");
|
|
};
|
|
}
|
|
});
|
|
var require_aggregate_error = (0, import_chunk_QGM4M3NI.__commonJS)({
|
|
"../../node_modules/.pnpm/aggregate-error@3.1.0/node_modules/aggregate-error/index.js"(exports, module2) {
|
|
"use strict";
|
|
var indentString = require_indent_string();
|
|
var cleanStack = require_clean_stack();
|
|
var cleanInternalStack = (stack) => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, "");
|
|
var AggregateError = class extends Error {
|
|
constructor(errors) {
|
|
if (!Array.isArray(errors)) {
|
|
throw new TypeError(`Expected input to be an Array, got ${typeof errors}`);
|
|
}
|
|
errors = [...errors].map((error) => {
|
|
if (error instanceof Error) {
|
|
return error;
|
|
}
|
|
if (error !== null && typeof error === "object") {
|
|
return Object.assign(new Error(error.message), error);
|
|
}
|
|
return new Error(error);
|
|
});
|
|
let message = errors.map((error) => {
|
|
return typeof error.stack === "string" ? cleanInternalStack(cleanStack(error.stack)) : String(error);
|
|
}).join("\n");
|
|
message = "\n" + indentString(message, 4);
|
|
super(message);
|
|
this.name = "AggregateError";
|
|
Object.defineProperty(this, "_errors", { value: errors });
|
|
}
|
|
*[Symbol.iterator]() {
|
|
for (const error of this._errors) {
|
|
yield error;
|
|
}
|
|
}
|
|
};
|
|
module2.exports = AggregateError;
|
|
}
|
|
});
|
|
var require_p_map = (0, import_chunk_QGM4M3NI.__commonJS)({
|
|
"../../node_modules/.pnpm/p-map@4.0.0/node_modules/p-map/index.js"(exports, module2) {
|
|
"use strict";
|
|
var AggregateError = require_aggregate_error();
|
|
module2.exports = async (iterable, mapper, {
|
|
concurrency = Infinity,
|
|
stopOnError = true
|
|
} = {}) => {
|
|
return new Promise((resolve6, reject) => {
|
|
if (typeof mapper !== "function") {
|
|
throw new TypeError("Mapper function is required");
|
|
}
|
|
if (!((Number.isSafeInteger(concurrency) || concurrency === Infinity) && concurrency >= 1)) {
|
|
throw new TypeError(`Expected \`concurrency\` to be an integer from 1 and up or \`Infinity\`, got \`${concurrency}\` (${typeof concurrency})`);
|
|
}
|
|
const result = [];
|
|
const errors = [];
|
|
const iterator = iterable[Symbol.iterator]();
|
|
let isRejected = false;
|
|
let isIterableDone = false;
|
|
let resolvingCount = 0;
|
|
let currentIndex = 0;
|
|
const next = () => {
|
|
if (isRejected) {
|
|
return;
|
|
}
|
|
const nextItem = iterator.next();
|
|
const index = currentIndex;
|
|
currentIndex++;
|
|
if (nextItem.done) {
|
|
isIterableDone = true;
|
|
if (resolvingCount === 0) {
|
|
if (!stopOnError && errors.length !== 0) {
|
|
reject(new AggregateError(errors));
|
|
} else {
|
|
resolve6(result);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
resolvingCount++;
|
|
(async () => {
|
|
try {
|
|
const element = await nextItem.value;
|
|
result[index] = await mapper(element, index);
|
|
resolvingCount--;
|
|
next();
|
|
} catch (error) {
|
|
if (stopOnError) {
|
|
isRejected = true;
|
|
reject(error);
|
|
} else {
|
|
errors.push(error);
|
|
resolvingCount--;
|
|
next();
|
|
}
|
|
}
|
|
})();
|
|
};
|
|
for (let i = 0; i < concurrency; i++) {
|
|
next();
|
|
if (isIterableDone) {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var require_balanced_match = (0, import_chunk_QGM4M3NI.__commonJS)({
|
|
"../../node_modules/.pnpm/balanced-match@1.0.2/node_modules/balanced-match/index.js"(exports, module2) {
|
|
"use strict";
|
|
module2.exports = balanced;
|
|
function balanced(a, b, str) {
|
|
if (a instanceof RegExp) a = maybeMatch(a, str);
|
|
if (b instanceof RegExp) b = maybeMatch(b, str);
|
|
var r = range(a, b, str);
|
|
return r && {
|
|
start: r[0],
|
|
end: r[1],
|
|
pre: str.slice(0, r[0]),
|
|
body: str.slice(r[0] + a.length, r[1]),
|
|
post: str.slice(r[1] + b.length)
|
|
};
|
|
}
|
|
function maybeMatch(reg, str) {
|
|
var m = str.match(reg);
|
|
return m ? m[0] : null;
|
|
}
|
|
balanced.range = range;
|
|
function range(a, b, str) {
|
|
var begs, beg, left, right, result;
|
|
var ai = str.indexOf(a);
|
|
var bi = str.indexOf(b, ai + 1);
|
|
var i = ai;
|
|
if (ai >= 0 && bi > 0) {
|
|
if (a === b) {
|
|
return [ai, bi];
|
|
}
|
|
begs = [];
|
|
left = str.length;
|
|
while (i >= 0 && !result) {
|
|
if (i == ai) {
|
|
begs.push(i);
|
|
ai = str.indexOf(a, i + 1);
|
|
} else if (begs.length == 1) {
|
|
result = [begs.pop(), bi];
|
|
} else {
|
|
beg = begs.pop();
|
|
if (beg < left) {
|
|
left = beg;
|
|
right = bi;
|
|
}
|
|
bi = str.indexOf(b, i + 1);
|
|
}
|
|
i = ai < bi && ai >= 0 ? ai : bi;
|
|
}
|
|
if (begs.length) {
|
|
result = [left, right];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
});
|
|
var require_brace_expansion = (0, import_chunk_QGM4M3NI.__commonJS)({
|
|
"../../node_modules/.pnpm/brace-expansion@2.0.1/node_modules/brace-expansion/index.js"(exports, module2) {
|
|
"use strict";
|
|
var balanced = require_balanced_match();
|
|
module2.exports = expandTop;
|
|
var escSlash = "\0SLASH" + Math.random() + "\0";
|
|
var escOpen = "\0OPEN" + Math.random() + "\0";
|
|
var escClose = "\0CLOSE" + Math.random() + "\0";
|
|
var escComma = "\0COMMA" + Math.random() + "\0";
|
|
var escPeriod = "\0PERIOD" + Math.random() + "\0";
|
|
function numeric(str) {
|
|
return parseInt(str, 10) == str ? parseInt(str, 10) : str.charCodeAt(0);
|
|
}
|
|
function escapeBraces(str) {
|
|
return str.split("\\\\").join(escSlash).split("\\{").join(escOpen).split("\\}").join(escClose).split("\\,").join(escComma).split("\\.").join(escPeriod);
|
|
}
|
|
function unescapeBraces(str) {
|
|
return str.split(escSlash).join("\\").split(escOpen).join("{").split(escClose).join("}").split(escComma).join(",").split(escPeriod).join(".");
|
|
}
|
|
function parseCommaParts(str) {
|
|
if (!str)
|
|
return [""];
|
|
var parts = [];
|
|
var m = balanced("{", "}", str);
|
|
if (!m)
|
|
return str.split(",");
|
|
var pre = m.pre;
|
|
var body = m.body;
|
|
var post = m.post;
|
|
var p = pre.split(",");
|
|
p[p.length - 1] += "{" + body + "}";
|
|
var postParts = parseCommaParts(post);
|
|
if (post.length) {
|
|
p[p.length - 1] += postParts.shift();
|
|
p.push.apply(p, postParts);
|
|
}
|
|
parts.push.apply(parts, p);
|
|
return parts;
|
|
}
|
|
function expandTop(str) {
|
|
if (!str)
|
|
return [];
|
|
if (str.substr(0, 2) === "{}") {
|
|
str = "\\{\\}" + str.substr(2);
|
|
}
|
|
return expand2(escapeBraces(str), true).map(unescapeBraces);
|
|
}
|
|
function embrace(str) {
|
|
return "{" + str + "}";
|
|
}
|
|
function isPadded(el) {
|
|
return /^-?0\d/.test(el);
|
|
}
|
|
function lte(i, y) {
|
|
return i <= y;
|
|
}
|
|
function gte(i, y) {
|
|
return i >= y;
|
|
}
|
|
function expand2(str, isTop) {
|
|
var expansions = [];
|
|
var m = balanced("{", "}", str);
|
|
if (!m) return [str];
|
|
var pre = m.pre;
|
|
var post = m.post.length ? expand2(m.post, false) : [""];
|
|
if (/\$$/.test(m.pre)) {
|
|
for (var k = 0; k < post.length; k++) {
|
|
var expansion = pre + "{" + m.body + "}" + post[k];
|
|
expansions.push(expansion);
|
|
}
|
|
} else {
|
|
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
|
|
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
|
|
var isSequence = isNumericSequence || isAlphaSequence;
|
|
var isOptions = m.body.indexOf(",") >= 0;
|
|
if (!isSequence && !isOptions) {
|
|
if (m.post.match(/,.*\}/)) {
|
|
str = m.pre + "{" + m.body + escClose + m.post;
|
|
return expand2(str);
|
|
}
|
|
return [str];
|
|
}
|
|
var n;
|
|
if (isSequence) {
|
|
n = m.body.split(/\.\./);
|
|
} else {
|
|
n = parseCommaParts(m.body);
|
|
if (n.length === 1) {
|
|
n = expand2(n[0], false).map(embrace);
|
|
if (n.length === 1) {
|
|
return post.map(function(p) {
|
|
return m.pre + n[0] + p;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
var N;
|
|
if (isSequence) {
|
|
var x = numeric(n[0]);
|
|
var y = numeric(n[1]);
|
|
var width = Math.max(n[0].length, n[1].length);
|
|
var incr = n.length == 3 ? Math.abs(numeric(n[2])) : 1;
|
|
var test = lte;
|
|
var reverse = y < x;
|
|
if (reverse) {
|
|
incr *= -1;
|
|
test = gte;
|
|
}
|
|
var pad = n.some(isPadded);
|
|
N = [];
|
|
for (var i = x; test(i, y); i += incr) {
|
|
var c;
|
|
if (isAlphaSequence) {
|
|
c = String.fromCharCode(i);
|
|
if (c === "\\")
|
|
c = "";
|
|
} else {
|
|
c = String(i);
|
|
if (pad) {
|
|
var need = width - c.length;
|
|
if (need > 0) {
|
|
var z = new Array(need + 1).join("0");
|
|
if (i < 0)
|
|
c = "-" + z + c.slice(1);
|
|
else
|
|
c = z + c;
|
|
}
|
|
}
|
|
}
|
|
N.push(c);
|
|
}
|
|
} else {
|
|
N = [];
|
|
for (var j = 0; j < n.length; j++) {
|
|
N.push.apply(N, expand2(n[j], false));
|
|
}
|
|
}
|
|
for (var j = 0; j < N.length; j++) {
|
|
for (var k = 0; k < post.length; k++) {
|
|
var expansion = pre + N[j] + post[k];
|
|
if (!isTop || isSequence || expansion)
|
|
expansions.push(expansion);
|
|
}
|
|
}
|
|
}
|
|
return expansions;
|
|
}
|
|
}
|
|
});
|
|
var import_brace_expansion = (0, import_chunk_QGM4M3NI.__toESM)(require_brace_expansion(), 1);
|
|
var MAX_PATTERN_LENGTH = 1024 * 64;
|
|
var assertValidPattern = (pattern) => {
|
|
if (typeof pattern !== "string") {
|
|
throw new TypeError("invalid pattern");
|
|
}
|
|
if (pattern.length > MAX_PATTERN_LENGTH) {
|
|
throw new TypeError("pattern is too long");
|
|
}
|
|
};
|
|
var posixClasses = {
|
|
"[:alnum:]": ["\\p{L}\\p{Nl}\\p{Nd}", true],
|
|
"[:alpha:]": ["\\p{L}\\p{Nl}", true],
|
|
"[:ascii:]": ["\\x00-\\x7f", false],
|
|
"[:blank:]": ["\\p{Zs}\\t", true],
|
|
"[:cntrl:]": ["\\p{Cc}", true],
|
|
"[:digit:]": ["\\p{Nd}", true],
|
|
"[:graph:]": ["\\p{Z}\\p{C}", true, true],
|
|
"[:lower:]": ["\\p{Ll}", true],
|
|
"[:print:]": ["\\p{C}", true],
|
|
"[:punct:]": ["\\p{P}", true],
|
|
"[:space:]": ["\\p{Z}\\t\\r\\n\\v\\f", true],
|
|
"[:upper:]": ["\\p{Lu}", true],
|
|
"[:word:]": ["\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}", true],
|
|
"[:xdigit:]": ["A-Fa-f0-9", false]
|
|
};
|
|
var braceEscape = (s) => s.replace(/[[\]\\-]/g, "\\$&");
|
|
var regexpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
var rangesToString = (ranges) => ranges.join("");
|
|
var parseClass = (glob2, position) => {
|
|
const pos = position;
|
|
if (glob2.charAt(pos) !== "[") {
|
|
throw new Error("not in a brace expression");
|
|
}
|
|
const ranges = [];
|
|
const negs = [];
|
|
let i = pos + 1;
|
|
let sawStart = false;
|
|
let uflag = false;
|
|
let escaping = false;
|
|
let negate = false;
|
|
let endPos = pos;
|
|
let rangeStart = "";
|
|
WHILE: while (i < glob2.length) {
|
|
const c = glob2.charAt(i);
|
|
if ((c === "!" || c === "^") && i === pos + 1) {
|
|
negate = true;
|
|
i++;
|
|
continue;
|
|
}
|
|
if (c === "]" && sawStart && !escaping) {
|
|
endPos = i + 1;
|
|
break;
|
|
}
|
|
sawStart = true;
|
|
if (c === "\\") {
|
|
if (!escaping) {
|
|
escaping = true;
|
|
i++;
|
|
continue;
|
|
}
|
|
}
|
|
if (c === "[" && !escaping) {
|
|
for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) {
|
|
if (glob2.startsWith(cls, i)) {
|
|
if (rangeStart) {
|
|
return ["$.", false, glob2.length - pos, true];
|
|
}
|
|
i += cls.length;
|
|
if (neg)
|
|
negs.push(unip);
|
|
else
|
|
ranges.push(unip);
|
|
uflag = uflag || u;
|
|
continue WHILE;
|
|
}
|
|
}
|
|
}
|
|
escaping = false;
|
|
if (rangeStart) {
|
|
if (c > rangeStart) {
|
|
ranges.push(braceEscape(rangeStart) + "-" + braceEscape(c));
|
|
} else if (c === rangeStart) {
|
|
ranges.push(braceEscape(c));
|
|
}
|
|
rangeStart = "";
|
|
i++;
|
|
continue;
|
|
}
|
|
if (glob2.startsWith("-]", i + 1)) {
|
|
ranges.push(braceEscape(c + "-"));
|
|
i += 2;
|
|
continue;
|
|
}
|
|
if (glob2.startsWith("-", i + 1)) {
|
|
rangeStart = c;
|
|
i += 2;
|
|
continue;
|
|
}
|
|
ranges.push(braceEscape(c));
|
|
i++;
|
|
}
|
|
if (endPos < i) {
|
|
return ["", false, 0, false];
|
|
}
|
|
if (!ranges.length && !negs.length) {
|
|
return ["$.", false, glob2.length - pos, true];
|
|
}
|
|
if (negs.length === 0 && ranges.length === 1 && /^\\?.$/.test(ranges[0]) && !negate) {
|
|
const r = ranges[0].length === 2 ? ranges[0].slice(-1) : ranges[0];
|
|
return [regexpEscape(r), false, endPos - pos, false];
|
|
}
|
|
const sranges = "[" + (negate ? "^" : "") + rangesToString(ranges) + "]";
|
|
const snegs = "[" + (negate ? "" : "^") + rangesToString(negs) + "]";
|
|
const comb = ranges.length && negs.length ? "(" + sranges + "|" + snegs + ")" : ranges.length ? sranges : snegs;
|
|
return [comb, uflag, endPos - pos, true];
|
|
};
|
|
var unescape = (s, { windowsPathsNoEscape = false } = {}) => {
|
|
return windowsPathsNoEscape ? s.replace(/\[([^\/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\])\]/g, "$1$2").replace(/\\([^\/])/g, "$1");
|
|
};
|
|
var types = /* @__PURE__ */ new Set(["!", "?", "+", "*", "@"]);
|
|
var isExtglobType = (c) => types.has(c);
|
|
var startNoTraversal = "(?!(?:^|/)\\.\\.?(?:$|/))";
|
|
var startNoDot = "(?!\\.)";
|
|
var addPatternStart = /* @__PURE__ */ new Set(["[", "."]);
|
|
var justDots = /* @__PURE__ */ new Set(["..", "."]);
|
|
var reSpecials = new Set("().*{}+?[]^$\\!");
|
|
var regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
var qmark = "[^/]";
|
|
var star = qmark + "*?";
|
|
var starNoEmpty = qmark + "+?";
|
|
var AST = class _AST {
|
|
type;
|
|
#root;
|
|
#hasMagic;
|
|
#uflag = false;
|
|
#parts = [];
|
|
#parent;
|
|
#parentIndex;
|
|
#negs;
|
|
#filledNegs = false;
|
|
#options;
|
|
#toString;
|
|
// set to true if it's an extglob with no children
|
|
// (which really means one child of '')
|
|
#emptyExt = false;
|
|
constructor(type, parent, options = {}) {
|
|
this.type = type;
|
|
if (type)
|
|
this.#hasMagic = true;
|
|
this.#parent = parent;
|
|
this.#root = this.#parent ? this.#parent.#root : this;
|
|
this.#options = this.#root === this ? options : this.#root.#options;
|
|
this.#negs = this.#root === this ? [] : this.#root.#negs;
|
|
if (type === "!" && !this.#root.#filledNegs)
|
|
this.#negs.push(this);
|
|
this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;
|
|
}
|
|
get hasMagic() {
|
|
if (this.#hasMagic !== void 0)
|
|
return this.#hasMagic;
|
|
for (const p of this.#parts) {
|
|
if (typeof p === "string")
|
|
continue;
|
|
if (p.type || p.hasMagic)
|
|
return this.#hasMagic = true;
|
|
}
|
|
return this.#hasMagic;
|
|
}
|
|
// reconstructs the pattern
|
|
toString() {
|
|
if (this.#toString !== void 0)
|
|
return this.#toString;
|
|
if (!this.type) {
|
|
return this.#toString = this.#parts.map((p) => String(p)).join("");
|
|
} else {
|
|
return this.#toString = this.type + "(" + this.#parts.map((p) => String(p)).join("|") + ")";
|
|
}
|
|
}
|
|
#fillNegs() {
|
|
if (this !== this.#root)
|
|
throw new Error("should only call on root");
|
|
if (this.#filledNegs)
|
|
return this;
|
|
this.toString();
|
|
this.#filledNegs = true;
|
|
let n;
|
|
while (n = this.#negs.pop()) {
|
|
if (n.type !== "!")
|
|
continue;
|
|
let p = n;
|
|
let pp = p.#parent;
|
|
while (pp) {
|
|
for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) {
|
|
for (const part of n.#parts) {
|
|
if (typeof part === "string") {
|
|
throw new Error("string part in extglob AST??");
|
|
}
|
|
part.copyIn(pp.#parts[i]);
|
|
}
|
|
}
|
|
p = pp;
|
|
pp = p.#parent;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
push(...parts) {
|
|
for (const p of parts) {
|
|
if (p === "")
|
|
continue;
|
|
if (typeof p !== "string" && !(p instanceof _AST && p.#parent === this)) {
|
|
throw new Error("invalid part: " + p);
|
|
}
|
|
this.#parts.push(p);
|
|
}
|
|
}
|
|
toJSON() {
|
|
const ret = this.type === null ? this.#parts.slice().map((p) => typeof p === "string" ? p : p.toJSON()) : [this.type, ...this.#parts.map((p) => p.toJSON())];
|
|
if (this.isStart() && !this.type)
|
|
ret.unshift([]);
|
|
if (this.isEnd() && (this === this.#root || this.#root.#filledNegs && this.#parent?.type === "!")) {
|
|
ret.push({});
|
|
}
|
|
return ret;
|
|
}
|
|
isStart() {
|
|
if (this.#root === this)
|
|
return true;
|
|
if (!this.#parent?.isStart())
|
|
return false;
|
|
if (this.#parentIndex === 0)
|
|
return true;
|
|
const p = this.#parent;
|
|
for (let i = 0; i < this.#parentIndex; i++) {
|
|
const pp = p.#parts[i];
|
|
if (!(pp instanceof _AST && pp.type === "!")) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
isEnd() {
|
|
if (this.#root === this)
|
|
return true;
|
|
if (this.#parent?.type === "!")
|
|
return true;
|
|
if (!this.#parent?.isEnd())
|
|
return false;
|
|
if (!this.type)
|
|
return this.#parent?.isEnd();
|
|
const pl = this.#parent ? this.#parent.#parts.length : 0;
|
|
return this.#parentIndex === pl - 1;
|
|
}
|
|
copyIn(part) {
|
|
if (typeof part === "string")
|
|
this.push(part);
|
|
else
|
|
this.push(part.clone(this));
|
|
}
|
|
clone(parent) {
|
|
const c = new _AST(this.type, parent);
|
|
for (const p of this.#parts) {
|
|
c.copyIn(p);
|
|
}
|
|
return c;
|
|
}
|
|
static #parseAST(str, ast, pos, opt) {
|
|
let escaping = false;
|
|
let inBrace = false;
|
|
let braceStart = -1;
|
|
let braceNeg = false;
|
|
if (ast.type === null) {
|
|
let i2 = pos;
|
|
let acc2 = "";
|
|
while (i2 < str.length) {
|
|
const c = str.charAt(i2++);
|
|
if (escaping || c === "\\") {
|
|
escaping = !escaping;
|
|
acc2 += c;
|
|
continue;
|
|
}
|
|
if (inBrace) {
|
|
if (i2 === braceStart + 1) {
|
|
if (c === "^" || c === "!") {
|
|
braceNeg = true;
|
|
}
|
|
} else if (c === "]" && !(i2 === braceStart + 2 && braceNeg)) {
|
|
inBrace = false;
|
|
}
|
|
acc2 += c;
|
|
continue;
|
|
} else if (c === "[") {
|
|
inBrace = true;
|
|
braceStart = i2;
|
|
braceNeg = false;
|
|
acc2 += c;
|
|
continue;
|
|
}
|
|
if (!opt.noext && isExtglobType(c) && str.charAt(i2) === "(") {
|
|
ast.push(acc2);
|
|
acc2 = "";
|
|
const ext2 = new _AST(c, ast);
|
|
i2 = _AST.#parseAST(str, ext2, i2, opt);
|
|
ast.push(ext2);
|
|
continue;
|
|
}
|
|
acc2 += c;
|
|
}
|
|
ast.push(acc2);
|
|
return i2;
|
|
}
|
|
let i = pos + 1;
|
|
let part = new _AST(null, ast);
|
|
const parts = [];
|
|
let acc = "";
|
|
while (i < str.length) {
|
|
const c = str.charAt(i++);
|
|
if (escaping || c === "\\") {
|
|
escaping = !escaping;
|
|
acc += c;
|
|
continue;
|
|
}
|
|
if (inBrace) {
|
|
if (i === braceStart + 1) {
|
|
if (c === "^" || c === "!") {
|
|
braceNeg = true;
|
|
}
|
|
} else if (c === "]" && !(i === braceStart + 2 && braceNeg)) {
|
|
inBrace = false;
|
|
}
|
|
acc += c;
|
|
continue;
|
|
} else if (c === "[") {
|
|
inBrace = true;
|
|
braceStart = i;
|
|
braceNeg = false;
|
|
acc += c;
|
|
continue;
|
|
}
|
|
if (isExtglobType(c) && str.charAt(i) === "(") {
|
|
part.push(acc);
|
|
acc = "";
|
|
const ext2 = new _AST(c, part);
|
|
part.push(ext2);
|
|
i = _AST.#parseAST(str, ext2, i, opt);
|
|
continue;
|
|
}
|
|
if (c === "|") {
|
|
part.push(acc);
|
|
acc = "";
|
|
parts.push(part);
|
|
part = new _AST(null, ast);
|
|
continue;
|
|
}
|
|
if (c === ")") {
|
|
if (acc === "" && ast.#parts.length === 0) {
|
|
ast.#emptyExt = true;
|
|
}
|
|
part.push(acc);
|
|
acc = "";
|
|
ast.push(...parts, part);
|
|
return i;
|
|
}
|
|
acc += c;
|
|
}
|
|
ast.type = null;
|
|
ast.#hasMagic = void 0;
|
|
ast.#parts = [str.substring(pos - 1)];
|
|
return i;
|
|
}
|
|
static fromGlob(pattern, options = {}) {
|
|
const ast = new _AST(null, void 0, options);
|
|
_AST.#parseAST(pattern, ast, 0, options);
|
|
return ast;
|
|
}
|
|
// returns the regular expression if there's magic, or the unescaped
|
|
// string if not.
|
|
toMMPattern() {
|
|
if (this !== this.#root)
|
|
return this.#root.toMMPattern();
|
|
const glob2 = this.toString();
|
|
const [re, body, hasMagic2, uflag] = this.toRegExpSource();
|
|
const anyMagic = hasMagic2 || this.#hasMagic || this.#options.nocase && !this.#options.nocaseMagicOnly && glob2.toUpperCase() !== glob2.toLowerCase();
|
|
if (!anyMagic) {
|
|
return body;
|
|
}
|
|
const flags = (this.#options.nocase ? "i" : "") + (uflag ? "u" : "");
|
|
return Object.assign(new RegExp(`^${re}$`, flags), {
|
|
_src: re,
|
|
_glob: glob2
|
|
});
|
|
}
|
|
get options() {
|
|
return this.#options;
|
|
}
|
|
// returns the string match, the regexp source, whether there's magic
|
|
// in the regexp (so a regular expression is required) and whether or
|
|
// not the uflag is needed for the regular expression (for posix classes)
|
|
// TODO: instead of injecting the start/end at this point, just return
|
|
// the BODY of the regexp, along with the start/end portions suitable
|
|
// for binding the start/end in either a joined full-path makeRe context
|
|
// (where we bind to (^|/), or a standalone matchPart context (where
|
|
// we bind to ^, and not /). Otherwise slashes get duped!
|
|
//
|
|
// In part-matching mode, the start is:
|
|
// - if not isStart: nothing
|
|
// - if traversal possible, but not allowed: ^(?!\.\.?$)
|
|
// - if dots allowed or not possible: ^
|
|
// - if dots possible and not allowed: ^(?!\.)
|
|
// end is:
|
|
// - if not isEnd(): nothing
|
|
// - else: $
|
|
//
|
|
// In full-path matching mode, we put the slash at the START of the
|
|
// pattern, so start is:
|
|
// - if first pattern: same as part-matching mode
|
|
// - if not isStart(): nothing
|
|
// - if traversal possible, but not allowed: /(?!\.\.?(?:$|/))
|
|
// - if dots allowed or not possible: /
|
|
// - if dots possible and not allowed: /(?!\.)
|
|
// end is:
|
|
// - if last pattern, same as part-matching mode
|
|
// - else nothing
|
|
//
|
|
// Always put the (?:$|/) on negated tails, though, because that has to be
|
|
// there to bind the end of the negated pattern portion, and it's easier to
|
|
// just stick it in now rather than try to inject it later in the middle of
|
|
// the pattern.
|
|
//
|
|
// We can just always return the same end, and leave it up to the caller
|
|
// to know whether it's going to be used joined or in parts.
|
|
// And, if the start is adjusted slightly, can do the same there:
|
|
// - if not isStart: nothing
|
|
// - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$)
|
|
// - if dots allowed or not possible: (?:/|^)
|
|
// - if dots possible and not allowed: (?:/|^)(?!\.)
|
|
//
|
|
// But it's better to have a simpler binding without a conditional, for
|
|
// performance, so probably better to return both start options.
|
|
//
|
|
// Then the caller just ignores the end if it's not the first pattern,
|
|
// and the start always gets applied.
|
|
//
|
|
// But that's always going to be $ if it's the ending pattern, or nothing,
|
|
// so the caller can just attach $ at the end of the pattern when building.
|
|
//
|
|
// So the todo is:
|
|
// - better detect what kind of start is needed
|
|
// - return both flavors of starting pattern
|
|
// - attach $ at the end of the pattern when creating the actual RegExp
|
|
//
|
|
// Ah, but wait, no, that all only applies to the root when the first pattern
|
|
// is not an extglob. If the first pattern IS an extglob, then we need all
|
|
// that dot prevention biz to live in the extglob portions, because eg
|
|
// +(*|.x*) can match .xy but not .yx.
|
|
//
|
|
// So, return the two flavors if it's #root and the first child is not an
|
|
// AST, otherwise leave it to the child AST to handle it, and there,
|
|
// use the (?:^|/) style of start binding.
|
|
//
|
|
// Even simplified further:
|
|
// - Since the start for a join is eg /(?!\.) and the start for a part
|
|
// is ^(?!\.), we can just prepend (?!\.) to the pattern (either root
|
|
// or start or whatever) and prepend ^ or / at the Regexp construction.
|
|
toRegExpSource(allowDot) {
|
|
const dot = allowDot ?? !!this.#options.dot;
|
|
if (this.#root === this)
|
|
this.#fillNegs();
|
|
if (!this.type) {
|
|
const noEmpty = this.isStart() && this.isEnd();
|
|
const src = this.#parts.map((p) => {
|
|
const [re, _, hasMagic2, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
|
|
this.#hasMagic = this.#hasMagic || hasMagic2;
|
|
this.#uflag = this.#uflag || uflag;
|
|
return re;
|
|
}).join("");
|
|
let start2 = "";
|
|
if (this.isStart()) {
|
|
if (typeof this.#parts[0] === "string") {
|
|
const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);
|
|
if (!dotTravAllowed) {
|
|
const aps = addPatternStart;
|
|
const needNoTrav = (
|
|
// dots are allowed, and the pattern starts with [ or .
|
|
dot && aps.has(src.charAt(0)) || // the pattern starts with \., and then [ or .
|
|
src.startsWith("\\.") && aps.has(src.charAt(2)) || // the pattern starts with \.\., and then [ or .
|
|
src.startsWith("\\.\\.") && aps.has(src.charAt(4))
|
|
);
|
|
const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
|
|
start2 = needNoTrav ? startNoTraversal : needNoDot ? startNoDot : "";
|
|
}
|
|
}
|
|
}
|
|
let end = "";
|
|
if (this.isEnd() && this.#root.#filledNegs && this.#parent?.type === "!") {
|
|
end = "(?:$|\\/)";
|
|
}
|
|
const final2 = start2 + src + end;
|
|
return [
|
|
final2,
|
|
unescape(src),
|
|
this.#hasMagic = !!this.#hasMagic,
|
|
this.#uflag
|
|
];
|
|
}
|
|
const repeated = this.type === "*" || this.type === "+";
|
|
const start = this.type === "!" ? "(?:(?!(?:" : "(?:";
|
|
let body = this.#partsToRegExp(dot);
|
|
if (this.isStart() && this.isEnd() && !body && this.type !== "!") {
|
|
const s = this.toString();
|
|
this.#parts = [s];
|
|
this.type = null;
|
|
this.#hasMagic = void 0;
|
|
return [s, unescape(this.toString()), false, false];
|
|
}
|
|
let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ? "" : this.#partsToRegExp(true);
|
|
if (bodyDotAllowed === body) {
|
|
bodyDotAllowed = "";
|
|
}
|
|
if (bodyDotAllowed) {
|
|
body = `(?:${body})(?:${bodyDotAllowed})*?`;
|
|
}
|
|
let final = "";
|
|
if (this.type === "!" && this.#emptyExt) {
|
|
final = (this.isStart() && !dot ? startNoDot : "") + starNoEmpty;
|
|
} else {
|
|
const close = this.type === "!" ? (
|
|
// !() must match something,but !(x) can match ''
|
|
"))" + (this.isStart() && !dot && !allowDot ? startNoDot : "") + star + ")"
|
|
) : this.type === "@" ? ")" : this.type === "?" ? ")?" : this.type === "+" && bodyDotAllowed ? ")" : this.type === "*" && bodyDotAllowed ? `)?` : `)${this.type}`;
|
|
final = start + body + close;
|
|
}
|
|
return [
|
|
final,
|
|
unescape(body),
|
|
this.#hasMagic = !!this.#hasMagic,
|
|
this.#uflag
|
|
];
|
|
}
|
|
#partsToRegExp(dot) {
|
|
return this.#parts.map((p) => {
|
|
if (typeof p === "string") {
|
|
throw new Error("string type in extglob ast??");
|
|
}
|
|
const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);
|
|
this.#uflag = this.#uflag || uflag;
|
|
return re;
|
|
}).filter((p) => !(this.isStart() && this.isEnd()) || !!p).join("|");
|
|
}
|
|
static #parseGlob(glob2, hasMagic2, noEmpty = false) {
|
|
let escaping = false;
|
|
let re = "";
|
|
let uflag = false;
|
|
for (let i = 0; i < glob2.length; i++) {
|
|
const c = glob2.charAt(i);
|
|
if (escaping) {
|
|
escaping = false;
|
|
re += (reSpecials.has(c) ? "\\" : "") + c;
|
|
continue;
|
|
}
|
|
if (c === "\\") {
|
|
if (i === glob2.length - 1) {
|
|
re += "\\\\";
|
|
} else {
|
|
escaping = true;
|
|
}
|
|
continue;
|
|
}
|
|
if (c === "[") {
|
|
const [src, needUflag, consumed, magic] = parseClass(glob2, i);
|
|
if (consumed) {
|
|
re += src;
|
|
uflag = uflag || needUflag;
|
|
i += consumed - 1;
|
|
hasMagic2 = hasMagic2 || magic;
|
|
continue;
|
|
}
|
|
}
|
|
if (c === "*") {
|
|
if (noEmpty && glob2 === "*")
|
|
re += starNoEmpty;
|
|
else
|
|
re += star;
|
|
hasMagic2 = true;
|
|
continue;
|
|
}
|
|
if (c === "?") {
|
|
re += qmark;
|
|
hasMagic2 = true;
|
|
continue;
|
|
}
|
|
re += regExpEscape(c);
|
|
}
|
|
return [re, unescape(glob2), !!hasMagic2, uflag];
|
|
}
|
|
};
|
|
var escape = (s, { windowsPathsNoEscape = false } = {}) => {
|
|
return windowsPathsNoEscape ? s.replace(/[?*()[\]]/g, "[$&]") : s.replace(/[?*()[\]\\]/g, "\\$&");
|
|
};
|
|
var minimatch = (p, pattern, options = {}) => {
|
|
assertValidPattern(pattern);
|
|
if (!options.nocomment && pattern.charAt(0) === "#") {
|
|
return false;
|
|
}
|
|
return new Minimatch(pattern, options).match(p);
|
|
};
|
|
var starDotExtRE = /^\*+([^+@!?\*\[\(]*)$/;
|
|
var starDotExtTest = (ext2) => (f) => !f.startsWith(".") && f.endsWith(ext2);
|
|
var starDotExtTestDot = (ext2) => (f) => f.endsWith(ext2);
|
|
var starDotExtTestNocase = (ext2) => {
|
|
ext2 = ext2.toLowerCase();
|
|
return (f) => !f.startsWith(".") && f.toLowerCase().endsWith(ext2);
|
|
};
|
|
var starDotExtTestNocaseDot = (ext2) => {
|
|
ext2 = ext2.toLowerCase();
|
|
return (f) => f.toLowerCase().endsWith(ext2);
|
|
};
|
|
var starDotStarRE = /^\*+\.\*+$/;
|
|
var starDotStarTest = (f) => !f.startsWith(".") && f.includes(".");
|
|
var starDotStarTestDot = (f) => f !== "." && f !== ".." && f.includes(".");
|
|
var dotStarRE = /^\.\*+$/;
|
|
var dotStarTest = (f) => f !== "." && f !== ".." && f.startsWith(".");
|
|
var starRE = /^\*+$/;
|
|
var starTest = (f) => f.length !== 0 && !f.startsWith(".");
|
|
var starTestDot = (f) => f.length !== 0 && f !== "." && f !== "..";
|
|
var qmarksRE = /^\?+([^+@!?\*\[\(]*)?$/;
|
|
var qmarksTestNocase = ([$0, ext2 = ""]) => {
|
|
const noext = qmarksTestNoExt([$0]);
|
|
if (!ext2)
|
|
return noext;
|
|
ext2 = ext2.toLowerCase();
|
|
return (f) => noext(f) && f.toLowerCase().endsWith(ext2);
|
|
};
|
|
var qmarksTestNocaseDot = ([$0, ext2 = ""]) => {
|
|
const noext = qmarksTestNoExtDot([$0]);
|
|
if (!ext2)
|
|
return noext;
|
|
ext2 = ext2.toLowerCase();
|
|
return (f) => noext(f) && f.toLowerCase().endsWith(ext2);
|
|
};
|
|
var qmarksTestDot = ([$0, ext2 = ""]) => {
|
|
const noext = qmarksTestNoExtDot([$0]);
|
|
return !ext2 ? noext : (f) => noext(f) && f.endsWith(ext2);
|
|
};
|
|
var qmarksTest = ([$0, ext2 = ""]) => {
|
|
const noext = qmarksTestNoExt([$0]);
|
|
return !ext2 ? noext : (f) => noext(f) && f.endsWith(ext2);
|
|
};
|
|
var qmarksTestNoExt = ([$0]) => {
|
|
const len = $0.length;
|
|
return (f) => f.length === len && !f.startsWith(".");
|
|
};
|
|
var qmarksTestNoExtDot = ([$0]) => {
|
|
const len = $0.length;
|
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
};
|
|
var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
var path = {
|
|
win32: { sep: "\\" },
|
|
posix: { sep: "/" }
|
|
};
|
|
var sep = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
|
|
minimatch.sep = sep;
|
|
var GLOBSTAR = Symbol("globstar **");
|
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
var qmark2 = "[^/]";
|
|
var star2 = qmark2 + "*?";
|
|
var twoStarDot = "(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?";
|
|
var twoStarNoDot = "(?:(?!(?:\\/|^)\\.).)*?";
|
|
var filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options);
|
|
minimatch.filter = filter;
|
|
var ext = (a, b = {}) => Object.assign({}, a, b);
|
|
var defaults = (def) => {
|
|
if (!def || typeof def !== "object" || !Object.keys(def).length) {
|
|
return minimatch;
|
|
}
|
|
const orig = minimatch;
|
|
const m = (p, pattern, options = {}) => orig(p, pattern, ext(def, options));
|
|
return Object.assign(m, {
|
|
Minimatch: class Minimatch extends orig.Minimatch {
|
|
constructor(pattern, options = {}) {
|
|
super(pattern, ext(def, options));
|
|
}
|
|
static defaults(options) {
|
|
return orig.defaults(ext(def, options)).Minimatch;
|
|
}
|
|
},
|
|
AST: class AST extends orig.AST {
|
|
/* c8 ignore start */
|
|
constructor(type, parent, options = {}) {
|
|
super(type, parent, ext(def, options));
|
|
}
|
|
/* c8 ignore stop */
|
|
static fromGlob(pattern, options = {}) {
|
|
return orig.AST.fromGlob(pattern, ext(def, options));
|
|
}
|
|
},
|
|
unescape: (s, options = {}) => orig.unescape(s, ext(def, options)),
|
|
escape: (s, options = {}) => orig.escape(s, ext(def, options)),
|
|
filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)),
|
|
defaults: (options) => orig.defaults(ext(def, options)),
|
|
makeRe: (pattern, options = {}) => orig.makeRe(pattern, ext(def, options)),
|
|
braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)),
|
|
match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)),
|
|
sep: orig.sep,
|
|
GLOBSTAR
|
|
});
|
|
};
|
|
minimatch.defaults = defaults;
|
|
var braceExpand = (pattern, options = {}) => {
|
|
assertValidPattern(pattern);
|
|
if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
|
|
return [pattern];
|
|
}
|
|
return (0, import_brace_expansion.default)(pattern);
|
|
};
|
|
minimatch.braceExpand = braceExpand;
|
|
var makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe();
|
|
minimatch.makeRe = makeRe;
|
|
var match = (list, pattern, options = {}) => {
|
|
const mm = new Minimatch(pattern, options);
|
|
list = list.filter((f) => mm.match(f));
|
|
if (mm.options.nonull && !list.length) {
|
|
list.push(pattern);
|
|
}
|
|
return list;
|
|
};
|
|
minimatch.match = match;
|
|
var globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/;
|
|
var regExpEscape2 = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
|
var Minimatch = class {
|
|
options;
|
|
set;
|
|
pattern;
|
|
windowsPathsNoEscape;
|
|
nonegate;
|
|
negate;
|
|
comment;
|
|
empty;
|
|
preserveMultipleSlashes;
|
|
partial;
|
|
globSet;
|
|
globParts;
|
|
nocase;
|
|
isWindows;
|
|
platform;
|
|
windowsNoMagicRoot;
|
|
regexp;
|
|
constructor(pattern, options = {}) {
|
|
assertValidPattern(pattern);
|
|
options = options || {};
|
|
this.options = options;
|
|
this.pattern = pattern;
|
|
this.platform = options.platform || defaultPlatform;
|
|
this.isWindows = this.platform === "win32";
|
|
this.windowsPathsNoEscape = !!options.windowsPathsNoEscape || options.allowWindowsEscape === false;
|
|
if (this.windowsPathsNoEscape) {
|
|
this.pattern = this.pattern.replace(/\\/g, "/");
|
|
}
|
|
this.preserveMultipleSlashes = !!options.preserveMultipleSlashes;
|
|
this.regexp = null;
|
|
this.negate = false;
|
|
this.nonegate = !!options.nonegate;
|
|
this.comment = false;
|
|
this.empty = false;
|
|
this.partial = !!options.partial;
|
|
this.nocase = !!this.options.nocase;
|
|
this.windowsNoMagicRoot = options.windowsNoMagicRoot !== void 0 ? options.windowsNoMagicRoot : !!(this.isWindows && this.nocase);
|
|
this.globSet = [];
|
|
this.globParts = [];
|
|
this.set = [];
|
|
this.make();
|
|
}
|
|
hasMagic() {
|
|
if (this.options.magicalBraces && this.set.length > 1) {
|
|
return true;
|
|
}
|
|
for (const pattern of this.set) {
|
|
for (const part of pattern) {
|
|
if (typeof part !== "string")
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
debug(..._) {
|
|
}
|
|
make() {
|
|
const pattern = this.pattern;
|
|
const options = this.options;
|
|
if (!options.nocomment && pattern.charAt(0) === "#") {
|
|
this.comment = true;
|
|
return;
|
|
}
|
|
if (!pattern) {
|
|
this.empty = true;
|
|
return;
|
|
}
|
|
this.parseNegate();
|
|
this.globSet = [...new Set(this.braceExpand())];
|
|
if (options.debug) {
|
|
this.debug = (...args) => console.error(...args);
|
|
}
|
|
this.debug(this.pattern, this.globSet);
|
|
const rawGlobParts = this.globSet.map((s) => this.slashSplit(s));
|
|
this.globParts = this.preprocess(rawGlobParts);
|
|
this.debug(this.pattern, this.globParts);
|
|
let set = this.globParts.map((s, _, __) => {
|
|
if (this.isWindows && this.windowsNoMagicRoot) {
|
|
const isUNC = s[0] === "" && s[1] === "" && (s[2] === "?" || !globMagic.test(s[2])) && !globMagic.test(s[3]);
|
|
const isDrive = /^[a-z]:/i.test(s[0]);
|
|
if (isUNC) {
|
|
return [...s.slice(0, 4), ...s.slice(4).map((ss) => this.parse(ss))];
|
|
} else if (isDrive) {
|
|
return [s[0], ...s.slice(1).map((ss) => this.parse(ss))];
|
|
}
|
|
}
|
|
return s.map((ss) => this.parse(ss));
|
|
});
|
|
this.debug(this.pattern, set);
|
|
this.set = set.filter((s) => s.indexOf(false) === -1);
|
|
if (this.isWindows) {
|
|
for (let i = 0; i < this.set.length; i++) {
|
|
const p = this.set[i];
|
|
if (p[0] === "" && p[1] === "" && this.globParts[i][2] === "?" && typeof p[3] === "string" && /^[a-z]:$/i.test(p[3])) {
|
|
p[2] = "?";
|
|
}
|
|
}
|
|
}
|
|
this.debug(this.pattern, this.set);
|
|
}
|
|
// various transforms to equivalent pattern sets that are
|
|
// faster to process in a filesystem walk. The goal is to
|
|
// eliminate what we can, and push all ** patterns as far
|
|
// to the right as possible, even if it increases the number
|
|
// of patterns that we have to process.
|
|
preprocess(globParts) {
|
|
if (this.options.noglobstar) {
|
|
for (let i = 0; i < globParts.length; i++) {
|
|
for (let j = 0; j < globParts[i].length; j++) {
|
|
if (globParts[i][j] === "**") {
|
|
globParts[i][j] = "*";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const { optimizationLevel = 1 } = this.options;
|
|
if (optimizationLevel >= 2) {
|
|
globParts = this.firstPhasePreProcess(globParts);
|
|
globParts = this.secondPhasePreProcess(globParts);
|
|
} else if (optimizationLevel >= 1) {
|
|
globParts = this.levelOneOptimize(globParts);
|
|
} else {
|
|
globParts = this.adjascentGlobstarOptimize(globParts);
|
|
}
|
|
return globParts;
|
|
}
|
|
// just get rid of adjascent ** portions
|
|
adjascentGlobstarOptimize(globParts) {
|
|
return globParts.map((parts) => {
|
|
let gs = -1;
|
|
while (-1 !== (gs = parts.indexOf("**", gs + 1))) {
|
|
let i = gs;
|
|
while (parts[i + 1] === "**") {
|
|
i++;
|
|
}
|
|
if (i !== gs) {
|
|
parts.splice(gs, i - gs);
|
|
}
|
|
}
|
|
return parts;
|
|
});
|
|
}
|
|
// get rid of adjascent ** and resolve .. portions
|
|
levelOneOptimize(globParts) {
|
|
return globParts.map((parts) => {
|
|
parts = parts.reduce((set, part) => {
|
|
const prev = set[set.length - 1];
|
|
if (part === "**" && prev === "**") {
|
|
return set;
|
|
}
|
|
if (part === "..") {
|
|
if (prev && prev !== ".." && prev !== "." && prev !== "**") {
|
|
set.pop();
|
|
return set;
|
|
}
|
|
}
|
|
set.push(part);
|
|
return set;
|
|
}, []);
|
|
return parts.length === 0 ? [""] : parts;
|
|
});
|
|
}
|
|
levelTwoFileOptimize(parts) {
|
|
if (!Array.isArray(parts)) {
|
|
parts = this.slashSplit(parts);
|
|
}
|
|
let didSomething = false;
|
|
do {
|
|
didSomething = false;
|
|
if (!this.preserveMultipleSlashes) {
|
|
for (let i = 1; i < parts.length - 1; i++) {
|
|
const p = parts[i];
|
|
if (i === 1 && p === "" && parts[0] === "")
|
|
continue;
|
|
if (p === "." || p === "") {
|
|
didSomething = true;
|
|
parts.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
if (parts[0] === "." && parts.length === 2 && (parts[1] === "." || parts[1] === "")) {
|
|
didSomething = true;
|
|
parts.pop();
|
|
}
|
|
}
|
|
let dd = 0;
|
|
while (-1 !== (dd = parts.indexOf("..", dd + 1))) {
|
|
const p = parts[dd - 1];
|
|
if (p && p !== "." && p !== ".." && p !== "**") {
|
|
didSomething = true;
|
|
parts.splice(dd - 1, 2);
|
|
dd -= 2;
|
|
}
|
|
}
|
|
} while (didSomething);
|
|
return parts.length === 0 ? [""] : parts;
|
|
}
|
|
// First phase: single-pattern processing
|
|
// <pre> is 1 or more portions
|
|
// <rest> is 1 or more portions
|
|
// <p> is any portion other than ., .., '', or **
|
|
// <e> is . or ''
|
|
//
|
|
// **/.. is *brutal* for filesystem walking performance, because
|
|
// it effectively resets the recursive walk each time it occurs,
|
|
// and ** cannot be reduced out by a .. pattern part like a regexp
|
|
// or most strings (other than .., ., and '') can be.
|
|
//
|
|
// <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
|
|
// <pre>/<e>/<rest> -> <pre>/<rest>
|
|
// <pre>/<p>/../<rest> -> <pre>/<rest>
|
|
// **/**/<rest> -> **/<rest>
|
|
//
|
|
// **/*/<rest> -> */**/<rest> <== not valid because ** doesn't follow
|
|
// this WOULD be allowed if ** did follow symlinks, or * didn't
|
|
firstPhasePreProcess(globParts) {
|
|
let didSomething = false;
|
|
do {
|
|
didSomething = false;
|
|
for (let parts of globParts) {
|
|
let gs = -1;
|
|
while (-1 !== (gs = parts.indexOf("**", gs + 1))) {
|
|
let gss = gs;
|
|
while (parts[gss + 1] === "**") {
|
|
gss++;
|
|
}
|
|
if (gss > gs) {
|
|
parts.splice(gs + 1, gss - gs);
|
|
}
|
|
let next = parts[gs + 1];
|
|
const p = parts[gs + 2];
|
|
const p2 = parts[gs + 3];
|
|
if (next !== "..")
|
|
continue;
|
|
if (!p || p === "." || p === ".." || !p2 || p2 === "." || p2 === "..") {
|
|
continue;
|
|
}
|
|
didSomething = true;
|
|
parts.splice(gs, 1);
|
|
const other = parts.slice(0);
|
|
other[gs] = "**";
|
|
globParts.push(other);
|
|
gs--;
|
|
}
|
|
if (!this.preserveMultipleSlashes) {
|
|
for (let i = 1; i < parts.length - 1; i++) {
|
|
const p = parts[i];
|
|
if (i === 1 && p === "" && parts[0] === "")
|
|
continue;
|
|
if (p === "." || p === "") {
|
|
didSomething = true;
|
|
parts.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
if (parts[0] === "." && parts.length === 2 && (parts[1] === "." || parts[1] === "")) {
|
|
didSomething = true;
|
|
parts.pop();
|
|
}
|
|
}
|
|
let dd = 0;
|
|
while (-1 !== (dd = parts.indexOf("..", dd + 1))) {
|
|
const p = parts[dd - 1];
|
|
if (p && p !== "." && p !== ".." && p !== "**") {
|
|
didSomething = true;
|
|
const needDot = dd === 1 && parts[dd + 1] === "**";
|
|
const splin = needDot ? ["."] : [];
|
|
parts.splice(dd - 1, 2, ...splin);
|
|
if (parts.length === 0)
|
|
parts.push("");
|
|
dd -= 2;
|
|
}
|
|
}
|
|
}
|
|
} while (didSomething);
|
|
return globParts;
|
|
}
|
|
// second phase: multi-pattern dedupes
|
|
// {<pre>/*/<rest>,<pre>/<p>/<rest>} -> <pre>/*/<rest>
|
|
// {<pre>/<rest>,<pre>/<rest>} -> <pre>/<rest>
|
|
// {<pre>/**/<rest>,<pre>/<rest>} -> <pre>/**/<rest>
|
|
//
|
|
// {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest>
|
|
// ^-- not valid because ** doens't follow symlinks
|
|
secondPhasePreProcess(globParts) {
|
|
for (let i = 0; i < globParts.length - 1; i++) {
|
|
for (let j = i + 1; j < globParts.length; j++) {
|
|
const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
|
|
if (matched) {
|
|
globParts[i] = [];
|
|
globParts[j] = matched;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return globParts.filter((gs) => gs.length);
|
|
}
|
|
partsMatch(a, b, emptyGSMatch = false) {
|
|
let ai = 0;
|
|
let bi = 0;
|
|
let result = [];
|
|
let which = "";
|
|
while (ai < a.length && bi < b.length) {
|
|
if (a[ai] === b[bi]) {
|
|
result.push(which === "b" ? b[bi] : a[ai]);
|
|
ai++;
|
|
bi++;
|
|
} else if (emptyGSMatch && a[ai] === "**" && b[bi] === a[ai + 1]) {
|
|
result.push(a[ai]);
|
|
ai++;
|
|
} else if (emptyGSMatch && b[bi] === "**" && a[ai] === b[bi + 1]) {
|
|
result.push(b[bi]);
|
|
bi++;
|
|
} else if (a[ai] === "*" && b[bi] && (this.options.dot || !b[bi].startsWith(".")) && b[bi] !== "**") {
|
|
if (which === "b")
|
|
return false;
|
|
which = "a";
|
|
result.push(a[ai]);
|
|
ai++;
|
|
bi++;
|
|
} else if (b[bi] === "*" && a[ai] && (this.options.dot || !a[ai].startsWith(".")) && a[ai] !== "**") {
|
|
if (which === "a")
|
|
return false;
|
|
which = "b";
|
|
result.push(b[bi]);
|
|
ai++;
|
|
bi++;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return a.length === b.length && result;
|
|
}
|
|
parseNegate() {
|
|
if (this.nonegate)
|
|
return;
|
|
const pattern = this.pattern;
|
|
let negate = false;
|
|
let negateOffset = 0;
|
|
for (let i = 0; i < pattern.length && pattern.charAt(i) === "!"; i++) {
|
|
negate = !negate;
|
|
negateOffset++;
|
|
}
|
|
if (negateOffset)
|
|
this.pattern = pattern.slice(negateOffset);
|
|
this.negate = negate;
|
|
}
|
|
// set partial to true to test if, for example,
|
|
// "/a/b" matches the start of "/*/b/*/d"
|
|
// Partial means, if you run out of file before you run
|
|
// out of pattern, then that's fine, as long as all
|
|
// the parts match.
|
|
matchOne(file, pattern, partial = false) {
|
|
const options = this.options;
|
|
if (this.isWindows) {
|
|
const fileDrive = typeof file[0] === "string" && /^[a-z]:$/i.test(file[0]);
|
|
const fileUNC = !fileDrive && file[0] === "" && file[1] === "" && file[2] === "?" && /^[a-z]:$/i.test(file[3]);
|
|
const patternDrive = typeof pattern[0] === "string" && /^[a-z]:$/i.test(pattern[0]);
|
|
const patternUNC = !patternDrive && pattern[0] === "" && pattern[1] === "" && pattern[2] === "?" && typeof pattern[3] === "string" && /^[a-z]:$/i.test(pattern[3]);
|
|
const fdi = fileUNC ? 3 : fileDrive ? 0 : void 0;
|
|
const pdi = patternUNC ? 3 : patternDrive ? 0 : void 0;
|
|
if (typeof fdi === "number" && typeof pdi === "number") {
|
|
const [fd, pd] = [file[fdi], pattern[pdi]];
|
|
if (fd.toLowerCase() === pd.toLowerCase()) {
|
|
pattern[pdi] = fd;
|
|
if (pdi > fdi) {
|
|
pattern = pattern.slice(pdi);
|
|
} else if (fdi > pdi) {
|
|
file = file.slice(fdi);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const { optimizationLevel = 1 } = this.options;
|
|
if (optimizationLevel >= 2) {
|
|
file = this.levelTwoFileOptimize(file);
|
|
}
|
|
this.debug("matchOne", this, { file, pattern });
|
|
this.debug("matchOne", file.length, pattern.length);
|
|
for (var fi = 0, pi = 0, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
|
|
this.debug("matchOne loop");
|
|
var p = pattern[pi];
|
|
var f = file[fi];
|
|
this.debug(pattern, p, f);
|
|
if (p === false) {
|
|
return false;
|
|
}
|
|
if (p === GLOBSTAR) {
|
|
this.debug("GLOBSTAR", [pattern, p, f]);
|
|
var fr = fi;
|
|
var pr = pi + 1;
|
|
if (pr === pl) {
|
|
this.debug("** at the end");
|
|
for (; fi < fl; fi++) {
|
|
if (file[fi] === "." || file[fi] === ".." || !options.dot && file[fi].charAt(0) === ".")
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
while (fr < fl) {
|
|
var swallowee = file[fr];
|
|
this.debug("\nglobstar while", file, fr, pattern, pr, swallowee);
|
|
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
|
|
this.debug("globstar found match!", fr, fl, swallowee);
|
|
return true;
|
|
} else {
|
|
if (swallowee === "." || swallowee === ".." || !options.dot && swallowee.charAt(0) === ".") {
|
|
this.debug("dot detected!", file, fr, pattern, pr);
|
|
break;
|
|
}
|
|
this.debug("globstar swallow a segment, and continue");
|
|
fr++;
|
|
}
|
|
}
|
|
if (partial) {
|
|
this.debug("\n>>> no match, partial?", file, fr, pattern, pr);
|
|
if (fr === fl) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
let hit;
|
|
if (typeof p === "string") {
|
|
hit = f === p;
|
|
this.debug("string match", p, f, hit);
|
|
} else {
|
|
hit = p.test(f);
|
|
this.debug("pattern match", p, f, hit);
|
|
}
|
|
if (!hit)
|
|
return false;
|
|
}
|
|
if (fi === fl && pi === pl) {
|
|
return true;
|
|
} else if (fi === fl) {
|
|
return partial;
|
|
} else if (pi === pl) {
|
|
return fi === fl - 1 && file[fi] === "";
|
|
} else {
|
|
throw new Error("wtf?");
|
|
}
|
|
}
|
|
braceExpand() {
|
|
return braceExpand(this.pattern, this.options);
|
|
}
|
|
parse(pattern) {
|
|
assertValidPattern(pattern);
|
|
const options = this.options;
|
|
if (pattern === "**")
|
|
return GLOBSTAR;
|
|
if (pattern === "")
|
|
return "";
|
|
let m;
|
|
let fastTest = null;
|
|
if (m = pattern.match(starRE)) {
|
|
fastTest = options.dot ? starTestDot : starTest;
|
|
} else if (m = pattern.match(starDotExtRE)) {
|
|
fastTest = (options.nocase ? options.dot ? starDotExtTestNocaseDot : starDotExtTestNocase : options.dot ? starDotExtTestDot : starDotExtTest)(m[1]);
|
|
} else if (m = pattern.match(qmarksRE)) {
|
|
fastTest = (options.nocase ? options.dot ? qmarksTestNocaseDot : qmarksTestNocase : options.dot ? qmarksTestDot : qmarksTest)(m);
|
|
} else if (m = pattern.match(starDotStarRE)) {
|
|
fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
|
|
} else if (m = pattern.match(dotStarRE)) {
|
|
fastTest = dotStarTest;
|
|
}
|
|
const re = AST.fromGlob(pattern, this.options).toMMPattern();
|
|
if (fastTest && typeof re === "object") {
|
|
Reflect.defineProperty(re, "test", { value: fastTest });
|
|
}
|
|
return re;
|
|
}
|
|
makeRe() {
|
|
if (this.regexp || this.regexp === false)
|
|
return this.regexp;
|
|
const set = this.set;
|
|
if (!set.length) {
|
|
this.regexp = false;
|
|
return this.regexp;
|
|
}
|
|
const options = this.options;
|
|
const twoStar = options.noglobstar ? star2 : options.dot ? twoStarDot : twoStarNoDot;
|
|
const flags = new Set(options.nocase ? ["i"] : []);
|
|
let re = set.map((pattern) => {
|
|
const pp = pattern.map((p) => {
|
|
if (p instanceof RegExp) {
|
|
for (const f of p.flags.split(""))
|
|
flags.add(f);
|
|
}
|
|
return typeof p === "string" ? regExpEscape2(p) : p === GLOBSTAR ? GLOBSTAR : p._src;
|
|
});
|
|
pp.forEach((p, i) => {
|
|
const next = pp[i + 1];
|
|
const prev = pp[i - 1];
|
|
if (p !== GLOBSTAR || prev === GLOBSTAR) {
|
|
return;
|
|
}
|
|
if (prev === void 0) {
|
|
if (next !== void 0 && next !== GLOBSTAR) {
|
|
pp[i + 1] = "(?:\\/|" + twoStar + "\\/)?" + next;
|
|
} else {
|
|
pp[i] = twoStar;
|
|
}
|
|
} else if (next === void 0) {
|
|
pp[i - 1] = prev + "(?:\\/|" + twoStar + ")?";
|
|
} else if (next !== GLOBSTAR) {
|
|
pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + "\\/)" + next;
|
|
pp[i + 1] = GLOBSTAR;
|
|
}
|
|
});
|
|
return pp.filter((p) => p !== GLOBSTAR).join("/");
|
|
}).join("|");
|
|
const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
|
|
re = "^" + open + re + close + "$";
|
|
if (this.negate)
|
|
re = "^(?!" + re + ").+$";
|
|
try {
|
|
this.regexp = new RegExp(re, [...flags].join(""));
|
|
} catch (ex) {
|
|
this.regexp = false;
|
|
}
|
|
return this.regexp;
|
|
}
|
|
slashSplit(p) {
|
|
if (this.preserveMultipleSlashes) {
|
|
return p.split("/");
|
|
} else if (this.isWindows && /^\/\/[^\/]+/.test(p)) {
|
|
return ["", ...p.split(/\/+/)];
|
|
} else {
|
|
return p.split(/\/+/);
|
|
}
|
|
}
|
|
match(f, partial = this.partial) {
|
|
this.debug("match", f, this.pattern);
|
|
if (this.comment) {
|
|
return false;
|
|
}
|
|
if (this.empty) {
|
|
return f === "";
|
|
}
|
|
if (f === "/" && partial) {
|
|
return true;
|
|
}
|
|
const options = this.options;
|
|
if (this.isWindows) {
|
|
f = f.split("\\").join("/");
|
|
}
|
|
const ff = this.slashSplit(f);
|
|
this.debug(this.pattern, "split", ff);
|
|
const set = this.set;
|
|
this.debug(this.pattern, "set", set);
|
|
let filename = ff[ff.length - 1];
|
|
if (!filename) {
|
|
for (let i = ff.length - 2; !filename && i >= 0; i--) {
|
|
filename = ff[i];
|
|
}
|
|
}
|
|
for (let i = 0; i < set.length; i++) {
|
|
const pattern = set[i];
|
|
let file = ff;
|
|
if (options.matchBase && pattern.length === 1) {
|
|
file = [filename];
|
|
}
|
|
const hit = this.matchOne(file, pattern, partial);
|
|
if (hit) {
|
|
if (options.flipNegate) {
|
|
return true;
|
|
}
|
|
return !this.negate;
|
|
}
|
|
}
|
|
if (options.flipNegate) {
|
|
return false;
|
|
}
|
|
return this.negate;
|
|
}
|
|
static defaults(def) {
|
|
return minimatch.defaults(def).Minimatch;
|
|
}
|
|
};
|
|
minimatch.AST = AST;
|
|
minimatch.Minimatch = Minimatch;
|
|
minimatch.escape = escape;
|
|
minimatch.unescape = unescape;
|
|
var perf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
|
|
var warned = /* @__PURE__ */ new Set();
|
|
var PROCESS = typeof process === "object" && !!process ? process : {};
|
|
var emitWarning = (msg, type, code, fn) => {
|
|
typeof PROCESS.emitWarning === "function" ? PROCESS.emitWarning(msg, type, code, fn) : console.error(`[${code}] ${type}: ${msg}`);
|
|
};
|
|
var AC = globalThis.AbortController;
|
|
var AS = globalThis.AbortSignal;
|
|
if (typeof AC === "undefined") {
|
|
AS = class AbortSignal {
|
|
onabort;
|
|
_onabort = [];
|
|
reason;
|
|
aborted = false;
|
|
addEventListener(_, fn) {
|
|
this._onabort.push(fn);
|
|
}
|
|
};
|
|
AC = class AbortController {
|
|
constructor() {
|
|
warnACPolyfill();
|
|
}
|
|
signal = new AS();
|
|
abort(reason) {
|
|
if (this.signal.aborted)
|
|
return;
|
|
this.signal.reason = reason;
|
|
this.signal.aborted = true;
|
|
for (const fn of this.signal._onabort) {
|
|
fn(reason);
|
|
}
|
|
this.signal.onabort?.(reason);
|
|
}
|
|
};
|
|
let printACPolyfillWarning = PROCESS.env?.LRU_CACHE_IGNORE_AC_WARNING !== "1";
|
|
const warnACPolyfill = () => {
|
|
if (!printACPolyfillWarning)
|
|
return;
|
|
printACPolyfillWarning = false;
|
|
emitWarning("AbortController is not defined. If using lru-cache in node 14, load an AbortController polyfill from the `node-abort-controller` package. A minimal polyfill is provided for use by LRUCache.fetch(), but it should not be relied upon in other contexts (eg, passing it to other APIs that use AbortController/AbortSignal might have undesirable effects). You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.", "NO_ABORT_CONTROLLER", "ENOTSUP", warnACPolyfill);
|
|
};
|
|
}
|
|
var shouldWarn = (code) => !warned.has(code);
|
|
var TYPE = Symbol("type");
|
|
var isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
|
|
var getUintArray = (max) => !isPosInt(max) ? null : max <= Math.pow(2, 8) ? Uint8Array : max <= Math.pow(2, 16) ? Uint16Array : max <= Math.pow(2, 32) ? Uint32Array : max <= Number.MAX_SAFE_INTEGER ? ZeroArray : null;
|
|
var ZeroArray = class extends Array {
|
|
constructor(size) {
|
|
super(size);
|
|
this.fill(0);
|
|
}
|
|
};
|
|
var Stack = class _Stack {
|
|
heap;
|
|
length;
|
|
// private constructor
|
|
static #constructing = false;
|
|
static create(max) {
|
|
const HeapCls = getUintArray(max);
|
|
if (!HeapCls)
|
|
return [];
|
|
_Stack.#constructing = true;
|
|
const s = new _Stack(max, HeapCls);
|
|
_Stack.#constructing = false;
|
|
return s;
|
|
}
|
|
constructor(max, HeapCls) {
|
|
if (!_Stack.#constructing) {
|
|
throw new TypeError("instantiate Stack using Stack.create(n)");
|
|
}
|
|
this.heap = new HeapCls(max);
|
|
this.length = 0;
|
|
}
|
|
push(n) {
|
|
this.heap[this.length++] = n;
|
|
}
|
|
pop() {
|
|
return this.heap[--this.length];
|
|
}
|
|
};
|
|
var LRUCache = class _LRUCache {
|
|
// options that cannot be changed without disaster
|
|
#max;
|
|
#maxSize;
|
|
#dispose;
|
|
#disposeAfter;
|
|
#fetchMethod;
|
|
#memoMethod;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.ttl}
|
|
*/
|
|
ttl;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.ttlResolution}
|
|
*/
|
|
ttlResolution;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.ttlAutopurge}
|
|
*/
|
|
ttlAutopurge;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.updateAgeOnGet}
|
|
*/
|
|
updateAgeOnGet;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.updateAgeOnHas}
|
|
*/
|
|
updateAgeOnHas;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.allowStale}
|
|
*/
|
|
allowStale;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.noDisposeOnSet}
|
|
*/
|
|
noDisposeOnSet;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.noUpdateTTL}
|
|
*/
|
|
noUpdateTTL;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.maxEntrySize}
|
|
*/
|
|
maxEntrySize;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.sizeCalculation}
|
|
*/
|
|
sizeCalculation;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.noDeleteOnFetchRejection}
|
|
*/
|
|
noDeleteOnFetchRejection;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.noDeleteOnStaleGet}
|
|
*/
|
|
noDeleteOnStaleGet;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.allowStaleOnFetchAbort}
|
|
*/
|
|
allowStaleOnFetchAbort;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.allowStaleOnFetchRejection}
|
|
*/
|
|
allowStaleOnFetchRejection;
|
|
/**
|
|
* {@link LRUCache.OptionsBase.ignoreFetchAbort}
|
|
*/
|
|
ignoreFetchAbort;
|
|
// computed properties
|
|
#size;
|
|
#calculatedSize;
|
|
#keyMap;
|
|
#keyList;
|
|
#valList;
|
|
#next;
|
|
#prev;
|
|
#head;
|
|
#tail;
|
|
#free;
|
|
#disposed;
|
|
#sizes;
|
|
#starts;
|
|
#ttls;
|
|
#hasDispose;
|
|
#hasFetchMethod;
|
|
#hasDisposeAfter;
|
|
/**
|
|
* Do not call this method unless you need to inspect the
|
|
* inner workings of the cache. If anything returned by this
|
|
* object is modified in any way, strange breakage may occur.
|
|
*
|
|
* These fields are private for a reason!
|
|
*
|
|
* @internal
|
|
*/
|
|
static unsafeExposeInternals(c) {
|
|
return {
|
|
// properties
|
|
starts: c.#starts,
|
|
ttls: c.#ttls,
|
|
sizes: c.#sizes,
|
|
keyMap: c.#keyMap,
|
|
keyList: c.#keyList,
|
|
valList: c.#valList,
|
|
next: c.#next,
|
|
prev: c.#prev,
|
|
get head() {
|
|
return c.#head;
|
|
},
|
|
get tail() {
|
|
return c.#tail;
|
|
},
|
|
free: c.#free,
|
|
// methods
|
|
isBackgroundFetch: (p) => c.#isBackgroundFetch(p),
|
|
backgroundFetch: (k, index, options, context) => c.#backgroundFetch(k, index, options, context),
|
|
moveToTail: (index) => c.#moveToTail(index),
|
|
indexes: (options) => c.#indexes(options),
|
|
rindexes: (options) => c.#rindexes(options),
|
|
isStale: (index) => c.#isStale(index)
|
|
};
|
|
}
|
|
// Protected read-only members
|
|
/**
|
|
* {@link LRUCache.OptionsBase.max} (read-only)
|
|
*/
|
|
get max() {
|
|
return this.#max;
|
|
}
|
|
/**
|
|
* {@link LRUCache.OptionsBase.maxSize} (read-only)
|
|
*/
|
|
get maxSize() {
|
|
return this.#maxSize;
|
|
}
|
|
/**
|
|
* The total computed size of items in the cache (read-only)
|
|
*/
|
|
get calculatedSize() {
|
|
return this.#calculatedSize;
|
|
}
|
|
/**
|
|
* The number of items stored in the cache (read-only)
|
|
*/
|
|
get size() {
|
|
return this.#size;
|
|
}
|
|
/**
|
|
* {@link LRUCache.OptionsBase.fetchMethod} (read-only)
|
|
*/
|
|
get fetchMethod() {
|
|
return this.#fetchMethod;
|
|
}
|
|
get memoMethod() {
|
|
return this.#memoMethod;
|
|
}
|
|
/**
|
|
* {@link LRUCache.OptionsBase.dispose} (read-only)
|
|
*/
|
|
get dispose() {
|
|
return this.#dispose;
|
|
}
|
|
/**
|
|
* {@link LRUCache.OptionsBase.disposeAfter} (read-only)
|
|
*/
|
|
get disposeAfter() {
|
|
return this.#disposeAfter;
|
|
}
|
|
constructor(options) {
|
|
const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, memoMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort } = options;
|
|
if (max !== 0 && !isPosInt(max)) {
|
|
throw new TypeError("max option must be a nonnegative integer");
|
|
}
|
|
const UintArray = max ? getUintArray(max) : Array;
|
|
if (!UintArray) {
|
|
throw new Error("invalid max value: " + max);
|
|
}
|
|
this.#max = max;
|
|
this.#maxSize = maxSize;
|
|
this.maxEntrySize = maxEntrySize || this.#maxSize;
|
|
this.sizeCalculation = sizeCalculation;
|
|
if (this.sizeCalculation) {
|
|
if (!this.#maxSize && !this.maxEntrySize) {
|
|
throw new TypeError("cannot set sizeCalculation without setting maxSize or maxEntrySize");
|
|
}
|
|
if (typeof this.sizeCalculation !== "function") {
|
|
throw new TypeError("sizeCalculation set to non-function");
|
|
}
|
|
}
|
|
if (memoMethod !== void 0 && typeof memoMethod !== "function") {
|
|
throw new TypeError("memoMethod must be a function if defined");
|
|
}
|
|
this.#memoMethod = memoMethod;
|
|
if (fetchMethod !== void 0 && typeof fetchMethod !== "function") {
|
|
throw new TypeError("fetchMethod must be a function if specified");
|
|
}
|
|
this.#fetchMethod = fetchMethod;
|
|
this.#hasFetchMethod = !!fetchMethod;
|
|
this.#keyMap = /* @__PURE__ */ new Map();
|
|
this.#keyList = new Array(max).fill(void 0);
|
|
this.#valList = new Array(max).fill(void 0);
|
|
this.#next = new UintArray(max);
|
|
this.#prev = new UintArray(max);
|
|
this.#head = 0;
|
|
this.#tail = 0;
|
|
this.#free = Stack.create(max);
|
|
this.#size = 0;
|
|
this.#calculatedSize = 0;
|
|
if (typeof dispose === "function") {
|
|
this.#dispose = dispose;
|
|
}
|
|
if (typeof disposeAfter === "function") {
|
|
this.#disposeAfter = disposeAfter;
|
|
this.#disposed = [];
|
|
} else {
|
|
this.#disposeAfter = void 0;
|
|
this.#disposed = void 0;
|
|
}
|
|
this.#hasDispose = !!this.#dispose;
|
|
this.#hasDisposeAfter = !!this.#disposeAfter;
|
|
this.noDisposeOnSet = !!noDisposeOnSet;
|
|
this.noUpdateTTL = !!noUpdateTTL;
|
|
this.noDeleteOnFetchRejection = !!noDeleteOnFetchRejection;
|
|
this.allowStaleOnFetchRejection = !!allowStaleOnFetchRejection;
|
|
this.allowStaleOnFetchAbort = !!allowStaleOnFetchAbort;
|
|
this.ignoreFetchAbort = !!ignoreFetchAbort;
|
|
if (this.maxEntrySize !== 0) {
|
|
if (this.#maxSize !== 0) {
|
|
if (!isPosInt(this.#maxSize)) {
|
|
throw new TypeError("maxSize must be a positive integer if specified");
|
|
}
|
|
}
|
|
if (!isPosInt(this.maxEntrySize)) {
|
|
throw new TypeError("maxEntrySize must be a positive integer if specified");
|
|
}
|
|
this.#initializeSizeTracking();
|
|
}
|
|
this.allowStale = !!allowStale;
|
|
this.noDeleteOnStaleGet = !!noDeleteOnStaleGet;
|
|
this.updateAgeOnGet = !!updateAgeOnGet;
|
|
this.updateAgeOnHas = !!updateAgeOnHas;
|
|
this.ttlResolution = isPosInt(ttlResolution) || ttlResolution === 0 ? ttlResolution : 1;
|
|
this.ttlAutopurge = !!ttlAutopurge;
|
|
this.ttl = ttl || 0;
|
|
if (this.ttl) {
|
|
if (!isPosInt(this.ttl)) {
|
|
throw new TypeError("ttl must be a positive integer if specified");
|
|
}
|
|
this.#initializeTTLTracking();
|
|
}
|
|
if (this.#max === 0 && this.ttl === 0 && this.#maxSize === 0) {
|
|
throw new TypeError("At least one of max, maxSize, or ttl is required");
|
|
}
|
|
if (!this.ttlAutopurge && !this.#max && !this.#maxSize) {
|
|
const code = "LRU_CACHE_UNBOUNDED";
|
|
if (shouldWarn(code)) {
|
|
warned.add(code);
|
|
const msg = "TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.";
|
|
emitWarning(msg, "UnboundedCacheWarning", code, _LRUCache);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Return the number of ms left in the item's TTL. If item is not in cache,
|
|
* returns `0`. Returns `Infinity` if item is in cache without a defined TTL.
|
|
*/
|
|
getRemainingTTL(key) {
|
|
return this.#keyMap.has(key) ? Infinity : 0;
|
|
}
|
|
#initializeTTLTracking() {
|
|
const ttls = new ZeroArray(this.#max);
|
|
const starts = new ZeroArray(this.#max);
|
|
this.#ttls = ttls;
|
|
this.#starts = starts;
|
|
this.#setItemTTL = (index, ttl, start = perf.now()) => {
|
|
starts[index] = ttl !== 0 ? start : 0;
|
|
ttls[index] = ttl;
|
|
if (ttl !== 0 && this.ttlAutopurge) {
|
|
const t = setTimeout(() => {
|
|
if (this.#isStale(index)) {
|
|
this.#delete(this.#keyList[index], "expire");
|
|
}
|
|
}, ttl + 1);
|
|
if (t.unref) {
|
|
t.unref();
|
|
}
|
|
}
|
|
};
|
|
this.#updateItemAge = (index) => {
|
|
starts[index] = ttls[index] !== 0 ? perf.now() : 0;
|
|
};
|
|
this.#statusTTL = (status, index) => {
|
|
if (ttls[index]) {
|
|
const ttl = ttls[index];
|
|
const start = starts[index];
|
|
if (!ttl || !start)
|
|
return;
|
|
status.ttl = ttl;
|
|
status.start = start;
|
|
status.now = cachedNow || getNow();
|
|
const age = status.now - start;
|
|
status.remainingTTL = ttl - age;
|
|
}
|
|
};
|
|
let cachedNow = 0;
|
|
const getNow = () => {
|
|
const n = perf.now();
|
|
if (this.ttlResolution > 0) {
|
|
cachedNow = n;
|
|
const t = setTimeout(() => cachedNow = 0, this.ttlResolution);
|
|
if (t.unref) {
|
|
t.unref();
|
|
}
|
|
}
|
|
return n;
|
|
};
|
|
this.getRemainingTTL = (key) => {
|
|
const index = this.#keyMap.get(key);
|
|
if (index === void 0) {
|
|
return 0;
|
|
}
|
|
const ttl = ttls[index];
|
|
const start = starts[index];
|
|
if (!ttl || !start) {
|
|
return Infinity;
|
|
}
|
|
const age = (cachedNow || getNow()) - start;
|
|
return ttl - age;
|
|
};
|
|
this.#isStale = (index) => {
|
|
const s = starts[index];
|
|
const t = ttls[index];
|
|
return !!t && !!s && (cachedNow || getNow()) - s > t;
|
|
};
|
|
}
|
|
// conditionally set private methods related to TTL
|
|
#updateItemAge = () => {
|
|
};
|
|
#statusTTL = () => {
|
|
};
|
|
#setItemTTL = () => {
|
|
};
|
|
/* c8 ignore stop */
|
|
#isStale = () => false;
|
|
#initializeSizeTracking() {
|
|
const sizes = new ZeroArray(this.#max);
|
|
this.#calculatedSize = 0;
|
|
this.#sizes = sizes;
|
|
this.#removeItemSize = (index) => {
|
|
this.#calculatedSize -= sizes[index];
|
|
sizes[index] = 0;
|
|
};
|
|
this.#requireSize = (k, v, size, sizeCalculation) => {
|
|
if (this.#isBackgroundFetch(v)) {
|
|
return 0;
|
|
}
|
|
if (!isPosInt(size)) {
|
|
if (sizeCalculation) {
|
|
if (typeof sizeCalculation !== "function") {
|
|
throw new TypeError("sizeCalculation must be a function");
|
|
}
|
|
size = sizeCalculation(v, k);
|
|
if (!isPosInt(size)) {
|
|
throw new TypeError("sizeCalculation return invalid (expect positive integer)");
|
|
}
|
|
} else {
|
|
throw new TypeError("invalid size value (must be positive integer). When maxSize or maxEntrySize is used, sizeCalculation or size must be set.");
|
|
}
|
|
}
|
|
return size;
|
|
};
|
|
this.#addItemSize = (index, size, status) => {
|
|
sizes[index] = size;
|
|
if (this.#maxSize) {
|
|
const maxSize = this.#maxSize - sizes[index];
|
|
while (this.#calculatedSize > maxSize) {
|
|
this.#evict(true);
|
|
}
|
|
}
|
|
this.#calculatedSize += sizes[index];
|
|
if (status) {
|
|
status.entrySize = size;
|
|
status.totalCalculatedSize = this.#calculatedSize;
|
|
}
|
|
};
|
|
}
|
|
#removeItemSize = (_i) => {
|
|
};
|
|
#addItemSize = (_i, _s, _st) => {
|
|
};
|
|
#requireSize = (_k, _v, size, sizeCalculation) => {
|
|
if (size || sizeCalculation) {
|
|
throw new TypeError("cannot set size without setting maxSize or maxEntrySize on cache");
|
|
}
|
|
return 0;
|
|
};
|
|
*#indexes({ allowStale = this.allowStale } = {}) {
|
|
if (this.#size) {
|
|
for (let i = this.#tail; true; ) {
|
|
if (!this.#isValidIndex(i)) {
|
|
break;
|
|
}
|
|
if (allowStale || !this.#isStale(i)) {
|
|
yield i;
|
|
}
|
|
if (i === this.#head) {
|
|
break;
|
|
} else {
|
|
i = this.#prev[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*#rindexes({ allowStale = this.allowStale } = {}) {
|
|
if (this.#size) {
|
|
for (let i = this.#head; true; ) {
|
|
if (!this.#isValidIndex(i)) {
|
|
break;
|
|
}
|
|
if (allowStale || !this.#isStale(i)) {
|
|
yield i;
|
|
}
|
|
if (i === this.#tail) {
|
|
break;
|
|
} else {
|
|
i = this.#next[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#isValidIndex(index) {
|
|
return index !== void 0 && this.#keyMap.get(this.#keyList[index]) === index;
|
|
}
|
|
/**
|
|
* Return a generator yielding `[key, value]` pairs,
|
|
* in order from most recently used to least recently used.
|
|
*/
|
|
*entries() {
|
|
for (const i of this.#indexes()) {
|
|
if (this.#valList[i] !== void 0 && this.#keyList[i] !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) {
|
|
yield [this.#keyList[i], this.#valList[i]];
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Inverse order version of {@link LRUCache.entries}
|
|
*
|
|
* Return a generator yielding `[key, value]` pairs,
|
|
* in order from least recently used to most recently used.
|
|
*/
|
|
*rentries() {
|
|
for (const i of this.#rindexes()) {
|
|
if (this.#valList[i] !== void 0 && this.#keyList[i] !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) {
|
|
yield [this.#keyList[i], this.#valList[i]];
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Return a generator yielding the keys in the cache,
|
|
* in order from most recently used to least recently used.
|
|
*/
|
|
*keys() {
|
|
for (const i of this.#indexes()) {
|
|
const k = this.#keyList[i];
|
|
if (k !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) {
|
|
yield k;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Inverse order version of {@link LRUCache.keys}
|
|
*
|
|
* Return a generator yielding the keys in the cache,
|
|
* in order from least recently used to most recently used.
|
|
*/
|
|
*rkeys() {
|
|
for (const i of this.#rindexes()) {
|
|
const k = this.#keyList[i];
|
|
if (k !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) {
|
|
yield k;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Return a generator yielding the values in the cache,
|
|
* in order from most recently used to least recently used.
|
|
*/
|
|
*values() {
|
|
for (const i of this.#indexes()) {
|
|
const v = this.#valList[i];
|
|
if (v !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) {
|
|
yield this.#valList[i];
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Inverse order version of {@link LRUCache.values}
|
|
*
|
|
* Return a generator yielding the values in the cache,
|
|
* in order from least recently used to most recently used.
|
|
*/
|
|
*rvalues() {
|
|
for (const i of this.#rindexes()) {
|
|
const v = this.#valList[i];
|
|
if (v !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) {
|
|
yield this.#valList[i];
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Iterating over the cache itself yields the same results as
|
|
* {@link LRUCache.entries}
|
|
*/
|
|
[Symbol.iterator]() {
|
|
return this.entries();
|
|
}
|
|
/**
|
|
* A String value that is used in the creation of the default string
|
|
* description of an object. Called by the built-in method
|
|
* `Object.prototype.toString`.
|
|
*/
|
|
[Symbol.toStringTag] = "LRUCache";
|
|
/**
|
|
* Find a value for which the supplied fn method returns a truthy value,
|
|
* similar to `Array.find()`. fn is called as `fn(value, key, cache)`.
|
|
*/
|
|
find(fn, getOptions = {}) {
|
|
for (const i of this.#indexes()) {
|
|
const v = this.#valList[i];
|
|
const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
|
if (value === void 0)
|
|
continue;
|
|
if (fn(value, this.#keyList[i], this)) {
|
|
return this.get(this.#keyList[i], getOptions);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Call the supplied function on each item in the cache, in order from most
|
|
* recently used to least recently used.
|
|
*
|
|
* `fn` is called as `fn(value, key, cache)`.
|
|
*
|
|
* If `thisp` is provided, function will be called in the `this`-context of
|
|
* the provided object, or the cache if no `thisp` object is provided.
|
|
*
|
|
* Does not update age or recenty of use, or iterate over stale values.
|
|
*/
|
|
forEach(fn, thisp = this) {
|
|
for (const i of this.#indexes()) {
|
|
const v = this.#valList[i];
|
|
const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
|
if (value === void 0)
|
|
continue;
|
|
fn.call(thisp, value, this.#keyList[i], this);
|
|
}
|
|
}
|
|
/**
|
|
* The same as {@link LRUCache.forEach} but items are iterated over in
|
|
* reverse order. (ie, less recently used items are iterated over first.)
|
|
*/
|
|
rforEach(fn, thisp = this) {
|
|
for (const i of this.#rindexes()) {
|
|
const v = this.#valList[i];
|
|
const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
|
if (value === void 0)
|
|
continue;
|
|
fn.call(thisp, value, this.#keyList[i], this);
|
|
}
|
|
}
|
|
/**
|
|
* Delete any stale entries. Returns true if anything was removed,
|
|
* false otherwise.
|
|
*/
|
|
purgeStale() {
|
|
let deleted = false;
|
|
for (const i of this.#rindexes({ allowStale: true })) {
|
|
if (this.#isStale(i)) {
|
|
this.#delete(this.#keyList[i], "expire");
|
|
deleted = true;
|
|
}
|
|
}
|
|
return deleted;
|
|
}
|
|
/**
|
|
* Get the extended info about a given entry, to get its value, size, and
|
|
* TTL info simultaneously. Returns `undefined` if the key is not present.
|
|
*
|
|
* Unlike {@link LRUCache#dump}, which is designed to be portable and survive
|
|
* serialization, the `start` value is always the current timestamp, and the
|
|
* `ttl` is a calculated remaining time to live (negative if expired).
|
|
*
|
|
* Always returns stale values, if their info is found in the cache, so be
|
|
* sure to check for expirations (ie, a negative {@link LRUCache.Entry#ttl})
|
|
* if relevant.
|
|
*/
|
|
info(key) {
|
|
const i = this.#keyMap.get(key);
|
|
if (i === void 0)
|
|
return void 0;
|
|
const v = this.#valList[i];
|
|
const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
|
if (value === void 0)
|
|
return void 0;
|
|
const entry = { value };
|
|
if (this.#ttls && this.#starts) {
|
|
const ttl = this.#ttls[i];
|
|
const start = this.#starts[i];
|
|
if (ttl && start) {
|
|
const remain = ttl - (perf.now() - start);
|
|
entry.ttl = remain;
|
|
entry.start = Date.now();
|
|
}
|
|
}
|
|
if (this.#sizes) {
|
|
entry.size = this.#sizes[i];
|
|
}
|
|
return entry;
|
|
}
|
|
/**
|
|
* Return an array of [key, {@link LRUCache.Entry}] tuples which can be
|
|
* passed to {@link LRUCache#load}.
|
|
*
|
|
* The `start` fields are calculated relative to a portable `Date.now()`
|
|
* timestamp, even if `performance.now()` is available.
|
|
*
|
|
* Stale entries are always included in the `dump`, even if
|
|
* {@link LRUCache.OptionsBase.allowStale} is false.
|
|
*
|
|
* Note: this returns an actual array, not a generator, so it can be more
|
|
* easily passed around.
|
|
*/
|
|
dump() {
|
|
const arr = [];
|
|
for (const i of this.#indexes({ allowStale: true })) {
|
|
const key = this.#keyList[i];
|
|
const v = this.#valList[i];
|
|
const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
|
if (value === void 0 || key === void 0)
|
|
continue;
|
|
const entry = { value };
|
|
if (this.#ttls && this.#starts) {
|
|
entry.ttl = this.#ttls[i];
|
|
const age = perf.now() - this.#starts[i];
|
|
entry.start = Math.floor(Date.now() - age);
|
|
}
|
|
if (this.#sizes) {
|
|
entry.size = this.#sizes[i];
|
|
}
|
|
arr.unshift([key, entry]);
|
|
}
|
|
return arr;
|
|
}
|
|
/**
|
|
* Reset the cache and load in the items in entries in the order listed.
|
|
*
|
|
* The shape of the resulting cache may be different if the same options are
|
|
* not used in both caches.
|
|
*
|
|
* The `start` fields are assumed to be calculated relative to a portable
|
|
* `Date.now()` timestamp, even if `performance.now()` is available.
|
|
*/
|
|
load(arr) {
|
|
this.clear();
|
|
for (const [key, entry] of arr) {
|
|
if (entry.start) {
|
|
const age = Date.now() - entry.start;
|
|
entry.start = perf.now() - age;
|
|
}
|
|
this.set(key, entry.value, entry);
|
|
}
|
|
}
|
|
/**
|
|
* Add a value to the cache.
|
|
*
|
|
* Note: if `undefined` is specified as a value, this is an alias for
|
|
* {@link LRUCache#delete}
|
|
*
|
|
* Fields on the {@link LRUCache.SetOptions} options param will override
|
|
* their corresponding values in the constructor options for the scope
|
|
* of this single `set()` operation.
|
|
*
|
|
* If `start` is provided, then that will set the effective start
|
|
* time for the TTL calculation. Note that this must be a previous
|
|
* value of `performance.now()` if supported, or a previous value of
|
|
* `Date.now()` if not.
|
|
*
|
|
* Options object may also include `size`, which will prevent
|
|
* calling the `sizeCalculation` function and just use the specified
|
|
* number if it is a positive integer, and `noDisposeOnSet` which
|
|
* will prevent calling a `dispose` function in the case of
|
|
* overwrites.
|
|
*
|
|
* If the `size` (or return value of `sizeCalculation`) for a given
|
|
* entry is greater than `maxEntrySize`, then the item will not be
|
|
* added to the cache.
|
|
*
|
|
* Will update the recency of the entry.
|
|
*
|
|
* If the value is `undefined`, then this is an alias for
|
|
* `cache.delete(key)`. `undefined` is never stored in the cache.
|
|
*/
|
|
set(k, v, setOptions = {}) {
|
|
if (v === void 0) {
|
|
this.delete(k);
|
|
return this;
|
|
}
|
|
const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status } = setOptions;
|
|
let { noUpdateTTL = this.noUpdateTTL } = setOptions;
|
|
const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation);
|
|
if (this.maxEntrySize && size > this.maxEntrySize) {
|
|
if (status) {
|
|
status.set = "miss";
|
|
status.maxEntrySizeExceeded = true;
|
|
}
|
|
this.#delete(k, "set");
|
|
return this;
|
|
}
|
|
let index = this.#size === 0 ? void 0 : this.#keyMap.get(k);
|
|
if (index === void 0) {
|
|
index = this.#size === 0 ? this.#tail : this.#free.length !== 0 ? this.#free.pop() : this.#size === this.#max ? this.#evict(false) : this.#size;
|
|
this.#keyList[index] = k;
|
|
this.#valList[index] = v;
|
|
this.#keyMap.set(k, index);
|
|
this.#next[this.#tail] = index;
|
|
this.#prev[index] = this.#tail;
|
|
this.#tail = index;
|
|
this.#size++;
|
|
this.#addItemSize(index, size, status);
|
|
if (status)
|
|
status.set = "add";
|
|
noUpdateTTL = false;
|
|
} else {
|
|
this.#moveToTail(index);
|
|
const oldVal = this.#valList[index];
|
|
if (v !== oldVal) {
|
|
if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) {
|
|
oldVal.__abortController.abort(new Error("replaced"));
|
|
const { __staleWhileFetching: s } = oldVal;
|
|
if (s !== void 0 && !noDisposeOnSet) {
|
|
if (this.#hasDispose) {
|
|
this.#dispose?.(s, k, "set");
|
|
}
|
|
if (this.#hasDisposeAfter) {
|
|
this.#disposed?.push([s, k, "set"]);
|
|
}
|
|
}
|
|
} else if (!noDisposeOnSet) {
|
|
if (this.#hasDispose) {
|
|
this.#dispose?.(oldVal, k, "set");
|
|
}
|
|
if (this.#hasDisposeAfter) {
|
|
this.#disposed?.push([oldVal, k, "set"]);
|
|
}
|
|
}
|
|
this.#removeItemSize(index);
|
|
this.#addItemSize(index, size, status);
|
|
this.#valList[index] = v;
|
|
if (status) {
|
|
status.set = "replace";
|
|
const oldValue = oldVal && this.#isBackgroundFetch(oldVal) ? oldVal.__staleWhileFetching : oldVal;
|
|
if (oldValue !== void 0)
|
|
status.oldValue = oldValue;
|
|
}
|
|
} else if (status) {
|
|
status.set = "update";
|
|
}
|
|
}
|
|
if (ttl !== 0 && !this.#ttls) {
|
|
this.#initializeTTLTracking();
|
|
}
|
|
if (this.#ttls) {
|
|
if (!noUpdateTTL) {
|
|
this.#setItemTTL(index, ttl, start);
|
|
}
|
|
if (status)
|
|
this.#statusTTL(status, index);
|
|
}
|
|
if (!noDisposeOnSet && this.#hasDisposeAfter && this.#disposed) {
|
|
const dt = this.#disposed;
|
|
let task;
|
|
while (task = dt?.shift()) {
|
|
this.#disposeAfter?.(...task);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
/**
|
|
* Evict the least recently used item, returning its value or
|
|
* `undefined` if cache is empty.
|
|
*/
|
|
pop() {
|
|
try {
|
|
while (this.#size) {
|
|
const val = this.#valList[this.#head];
|
|
this.#evict(true);
|
|
if (this.#isBackgroundFetch(val)) {
|
|
if (val.__staleWhileFetching) {
|
|
return val.__staleWhileFetching;
|
|
}
|
|
} else if (val !== void 0) {
|
|
return val;
|
|
}
|
|
}
|
|
} finally {
|
|
if (this.#hasDisposeAfter && this.#disposed) {
|
|
const dt = this.#disposed;
|
|
let task;
|
|
while (task = dt?.shift()) {
|
|
this.#disposeAfter?.(...task);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#evict(free) {
|
|
const head = this.#head;
|
|
const k = this.#keyList[head];
|
|
const v = this.#valList[head];
|
|
if (this.#hasFetchMethod && this.#isBackgroundFetch(v)) {
|
|
v.__abortController.abort(new Error("evicted"));
|
|
} else if (this.#hasDispose || this.#hasDisposeAfter) {
|
|
if (this.#hasDispose) {
|
|
this.#dispose?.(v, k, "evict");
|
|
}
|
|
if (this.#hasDisposeAfter) {
|
|
this.#disposed?.push([v, k, "evict"]);
|
|
}
|
|
}
|
|
this.#removeItemSize(head);
|
|
if (free) {
|
|
this.#keyList[head] = void 0;
|
|
this.#valList[head] = void 0;
|
|
this.#free.push(head);
|
|
}
|
|
if (this.#size === 1) {
|
|
this.#head = this.#tail = 0;
|
|
this.#free.length = 0;
|
|
} else {
|
|
this.#head = this.#next[head];
|
|
}
|
|
this.#keyMap.delete(k);
|
|
this.#size--;
|
|
return head;
|
|
}
|
|
/**
|
|
* Check if a key is in the cache, without updating the recency of use.
|
|
* Will return false if the item is stale, even though it is technically
|
|
* in the cache.
|
|
*
|
|
* Check if a key is in the cache, without updating the recency of
|
|
* use. Age is updated if {@link LRUCache.OptionsBase.updateAgeOnHas} is set
|
|
* to `true` in either the options or the constructor.
|
|
*
|
|
* Will return `false` if the item is stale, even though it is technically in
|
|
* the cache. The difference can be determined (if it matters) by using a
|
|
* `status` argument, and inspecting the `has` field.
|
|
*
|
|
* Will not update item age unless
|
|
* {@link LRUCache.OptionsBase.updateAgeOnHas} is set.
|
|
*/
|
|
has(k, hasOptions = {}) {
|
|
const { updateAgeOnHas = this.updateAgeOnHas, status } = hasOptions;
|
|
const index = this.#keyMap.get(k);
|
|
if (index !== void 0) {
|
|
const v = this.#valList[index];
|
|
if (this.#isBackgroundFetch(v) && v.__staleWhileFetching === void 0) {
|
|
return false;
|
|
}
|
|
if (!this.#isStale(index)) {
|
|
if (updateAgeOnHas) {
|
|
this.#updateItemAge(index);
|
|
}
|
|
if (status) {
|
|
status.has = "hit";
|
|
this.#statusTTL(status, index);
|
|
}
|
|
return true;
|
|
} else if (status) {
|
|
status.has = "stale";
|
|
this.#statusTTL(status, index);
|
|
}
|
|
} else if (status) {
|
|
status.has = "miss";
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Like {@link LRUCache#get} but doesn't update recency or delete stale
|
|
* items.
|
|
*
|
|
* Returns `undefined` if the item is stale, unless
|
|
* {@link LRUCache.OptionsBase.allowStale} is set.
|
|
*/
|
|
peek(k, peekOptions = {}) {
|
|
const { allowStale = this.allowStale } = peekOptions;
|
|
const index = this.#keyMap.get(k);
|
|
if (index === void 0 || !allowStale && this.#isStale(index)) {
|
|
return;
|
|
}
|
|
const v = this.#valList[index];
|
|
return this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
|
|
}
|
|
#backgroundFetch(k, index, options, context) {
|
|
const v = index === void 0 ? void 0 : this.#valList[index];
|
|
if (this.#isBackgroundFetch(v)) {
|
|
return v;
|
|
}
|
|
const ac = new AC();
|
|
const { signal } = options;
|
|
signal?.addEventListener("abort", () => ac.abort(signal.reason), {
|
|
signal: ac.signal
|
|
});
|
|
const fetchOpts = {
|
|
signal: ac.signal,
|
|
options,
|
|
context
|
|
};
|
|
const cb = (v2, updateCache = false) => {
|
|
const { aborted } = ac.signal;
|
|
const ignoreAbort = options.ignoreFetchAbort && v2 !== void 0;
|
|
if (options.status) {
|
|
if (aborted && !updateCache) {
|
|
options.status.fetchAborted = true;
|
|
options.status.fetchError = ac.signal.reason;
|
|
if (ignoreAbort)
|
|
options.status.fetchAbortIgnored = true;
|
|
} else {
|
|
options.status.fetchResolved = true;
|
|
}
|
|
}
|
|
if (aborted && !ignoreAbort && !updateCache) {
|
|
return fetchFail(ac.signal.reason);
|
|
}
|
|
const bf2 = p;
|
|
if (this.#valList[index] === p) {
|
|
if (v2 === void 0) {
|
|
if (bf2.__staleWhileFetching) {
|
|
this.#valList[index] = bf2.__staleWhileFetching;
|
|
} else {
|
|
this.#delete(k, "fetch");
|
|
}
|
|
} else {
|
|
if (options.status)
|
|
options.status.fetchUpdated = true;
|
|
this.set(k, v2, fetchOpts.options);
|
|
}
|
|
}
|
|
return v2;
|
|
};
|
|
const eb = (er) => {
|
|
if (options.status) {
|
|
options.status.fetchRejected = true;
|
|
options.status.fetchError = er;
|
|
}
|
|
return fetchFail(er);
|
|
};
|
|
const fetchFail = (er) => {
|
|
const { aborted } = ac.signal;
|
|
const allowStaleAborted = aborted && options.allowStaleOnFetchAbort;
|
|
const allowStale = allowStaleAborted || options.allowStaleOnFetchRejection;
|
|
const noDelete = allowStale || options.noDeleteOnFetchRejection;
|
|
const bf2 = p;
|
|
if (this.#valList[index] === p) {
|
|
const del = !noDelete || bf2.__staleWhileFetching === void 0;
|
|
if (del) {
|
|
this.#delete(k, "fetch");
|
|
} else if (!allowStaleAborted) {
|
|
this.#valList[index] = bf2.__staleWhileFetching;
|
|
}
|
|
}
|
|
if (allowStale) {
|
|
if (options.status && bf2.__staleWhileFetching !== void 0) {
|
|
options.status.returnedStale = true;
|
|
}
|
|
return bf2.__staleWhileFetching;
|
|
} else if (bf2.__returned === bf2) {
|
|
throw er;
|
|
}
|
|
};
|
|
const pcall = (res, rej) => {
|
|
const fmp = this.#fetchMethod?.(k, v, fetchOpts);
|
|
if (fmp && fmp instanceof Promise) {
|
|
fmp.then((v2) => res(v2 === void 0 ? void 0 : v2), rej);
|
|
}
|
|
ac.signal.addEventListener("abort", () => {
|
|
if (!options.ignoreFetchAbort || options.allowStaleOnFetchAbort) {
|
|
res(void 0);
|
|
if (options.allowStaleOnFetchAbort) {
|
|
res = (v2) => cb(v2, true);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
if (options.status)
|
|
options.status.fetchDispatched = true;
|
|
const p = new Promise(pcall).then(cb, eb);
|
|
const bf = Object.assign(p, {
|
|
__abortController: ac,
|
|
__staleWhileFetching: v,
|
|
__returned: void 0
|
|
});
|
|
if (index === void 0) {
|
|
this.set(k, bf, { ...fetchOpts.options, status: void 0 });
|
|
index = this.#keyMap.get(k);
|
|
} else {
|
|
this.#valList[index] = bf;
|
|
}
|
|
return bf;
|
|
}
|
|
#isBackgroundFetch(p) {
|
|
if (!this.#hasFetchMethod)
|
|
return false;
|
|
const b = p;
|
|
return !!b && b instanceof Promise && b.hasOwnProperty("__staleWhileFetching") && b.__abortController instanceof AC;
|
|
}
|
|
async fetch(k, fetchOptions = {}) {
|
|
const {
|
|
// get options
|
|
allowStale = this.allowStale,
|
|
updateAgeOnGet = this.updateAgeOnGet,
|
|
noDeleteOnStaleGet = this.noDeleteOnStaleGet,
|
|
// set options
|
|
ttl = this.ttl,
|
|
noDisposeOnSet = this.noDisposeOnSet,
|
|
size = 0,
|
|
sizeCalculation = this.sizeCalculation,
|
|
noUpdateTTL = this.noUpdateTTL,
|
|
// fetch exclusive options
|
|
noDeleteOnFetchRejection = this.noDeleteOnFetchRejection,
|
|
allowStaleOnFetchRejection = this.allowStaleOnFetchRejection,
|
|
ignoreFetchAbort = this.ignoreFetchAbort,
|
|
allowStaleOnFetchAbort = this.allowStaleOnFetchAbort,
|
|
context,
|
|
forceRefresh = false,
|
|
status,
|
|
signal
|
|
} = fetchOptions;
|
|
if (!this.#hasFetchMethod) {
|
|
if (status)
|
|
status.fetch = "get";
|
|
return this.get(k, {
|
|
allowStale,
|
|
updateAgeOnGet,
|
|
noDeleteOnStaleGet,
|
|
status
|
|
});
|
|
}
|
|
const options = {
|
|
allowStale,
|
|
updateAgeOnGet,
|
|
noDeleteOnStaleGet,
|
|
ttl,
|
|
noDisposeOnSet,
|
|
size,
|
|
sizeCalculation,
|
|
noUpdateTTL,
|
|
noDeleteOnFetchRejection,
|
|
allowStaleOnFetchRejection,
|
|
allowStaleOnFetchAbort,
|
|
ignoreFetchAbort,
|
|
status,
|
|
signal
|
|
};
|
|
let index = this.#keyMap.get(k);
|
|
if (index === void 0) {
|
|
if (status)
|
|
status.fetch = "miss";
|
|
const p = this.#backgroundFetch(k, index, options, context);
|
|
return p.__returned = p;
|
|
} else {
|
|
const v = this.#valList[index];
|
|
if (this.#isBackgroundFetch(v)) {
|
|
const stale = allowStale && v.__staleWhileFetching !== void 0;
|
|
if (status) {
|
|
status.fetch = "inflight";
|
|
if (stale)
|
|
status.returnedStale = true;
|
|
}
|
|
return stale ? v.__staleWhileFetching : v.__returned = v;
|
|
}
|
|
const isStale = this.#isStale(index);
|
|
if (!forceRefresh && !isStale) {
|
|
if (status)
|
|
status.fetch = "hit";
|
|
this.#moveToTail(index);
|
|
if (updateAgeOnGet) {
|
|
this.#updateItemAge(index);
|
|
}
|
|
if (status)
|
|
this.#statusTTL(status, index);
|
|
return v;
|
|
}
|
|
const p = this.#backgroundFetch(k, index, options, context);
|
|
const hasStale = p.__staleWhileFetching !== void 0;
|
|
const staleVal = hasStale && allowStale;
|
|
if (status) {
|
|
status.fetch = isStale ? "stale" : "refresh";
|
|
if (staleVal && isStale)
|
|
status.returnedStale = true;
|
|
}
|
|
return staleVal ? p.__staleWhileFetching : p.__returned = p;
|
|
}
|
|
}
|
|
async forceFetch(k, fetchOptions = {}) {
|
|
const v = await this.fetch(k, fetchOptions);
|
|
if (v === void 0)
|
|
throw new Error("fetch() returned undefined");
|
|
return v;
|
|
}
|
|
memo(k, memoOptions = {}) {
|
|
const memoMethod = this.#memoMethod;
|
|
if (!memoMethod) {
|
|
throw new Error("no memoMethod provided to constructor");
|
|
}
|
|
const { context, forceRefresh, ...options } = memoOptions;
|
|
const v = this.get(k, options);
|
|
if (!forceRefresh && v !== void 0)
|
|
return v;
|
|
const vv = memoMethod(k, v, {
|
|
options,
|
|
context
|
|
});
|
|
this.set(k, vv, options);
|
|
return vv;
|
|
}
|
|
/**
|
|
* Return a value from the cache. Will update the recency of the cache
|
|
* entry found.
|
|
*
|
|
* If the key is not found, get() will return `undefined`.
|
|
*/
|
|
get(k, getOptions = {}) {
|
|
const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, status } = getOptions;
|
|
const index = this.#keyMap.get(k);
|
|
if (index !== void 0) {
|
|
const value = this.#valList[index];
|
|
const fetching = this.#isBackgroundFetch(value);
|
|
if (status)
|
|
this.#statusTTL(status, index);
|
|
if (this.#isStale(index)) {
|
|
if (status)
|
|
status.get = "stale";
|
|
if (!fetching) {
|
|
if (!noDeleteOnStaleGet) {
|
|
this.#delete(k, "expire");
|
|
}
|
|
if (status && allowStale)
|
|
status.returnedStale = true;
|
|
return allowStale ? value : void 0;
|
|
} else {
|
|
if (status && allowStale && value.__staleWhileFetching !== void 0) {
|
|
status.returnedStale = true;
|
|
}
|
|
return allowStale ? value.__staleWhileFetching : void 0;
|
|
}
|
|
} else {
|
|
if (status)
|
|
status.get = "hit";
|
|
if (fetching) {
|
|
return value.__staleWhileFetching;
|
|
}
|
|
this.#moveToTail(index);
|
|
if (updateAgeOnGet) {
|
|
this.#updateItemAge(index);
|
|
}
|
|
return value;
|
|
}
|
|
} else if (status) {
|
|
status.get = "miss";
|
|
}
|
|
}
|
|
#connect(p, n) {
|
|
this.#prev[n] = p;
|
|
this.#next[p] = n;
|
|
}
|
|
#moveToTail(index) {
|
|
if (index !== this.#tail) {
|
|
if (index === this.#head) {
|
|
this.#head = this.#next[index];
|
|
} else {
|
|
this.#connect(this.#prev[index], this.#next[index]);
|
|
}
|
|
this.#connect(this.#tail, index);
|
|
this.#tail = index;
|
|
}
|
|
}
|
|
/**
|
|
* Deletes a key out of the cache.
|
|
*
|
|
* Returns true if the key was deleted, false otherwise.
|
|
*/
|
|
delete(k) {
|
|
return this.#delete(k, "delete");
|
|
}
|
|
#delete(k, reason) {
|
|
let deleted = false;
|
|
if (this.#size !== 0) {
|
|
const index = this.#keyMap.get(k);
|
|
if (index !== void 0) {
|
|
deleted = true;
|
|
if (this.#size === 1) {
|
|
this.#clear(reason);
|
|
} else {
|
|
this.#removeItemSize(index);
|
|
const v = this.#valList[index];
|
|
if (this.#isBackgroundFetch(v)) {
|
|
v.__abortController.abort(new Error("deleted"));
|
|
} else if (this.#hasDispose || this.#hasDisposeAfter) {
|
|
if (this.#hasDispose) {
|
|
this.#dispose?.(v, k, reason);
|
|
}
|
|
if (this.#hasDisposeAfter) {
|
|
this.#disposed?.push([v, k, reason]);
|
|
}
|
|
}
|
|
this.#keyMap.delete(k);
|
|
this.#keyList[index] = void 0;
|
|
this.#valList[index] = void 0;
|
|
if (index === this.#tail) {
|
|
this.#tail = this.#prev[index];
|
|
} else if (index === this.#head) {
|
|
this.#head = this.#next[index];
|
|
} else {
|
|
const pi = this.#prev[index];
|
|
this.#next[pi] = this.#next[index];
|
|
const ni = this.#next[index];
|
|
this.#prev[ni] = this.#prev[index];
|
|
}
|
|
this.#size--;
|
|
this.#free.push(index);
|
|
}
|
|
}
|
|
}
|
|
if (this.#hasDisposeAfter && this.#disposed?.length) {
|
|
const dt = this.#disposed;
|
|
let task;
|
|
while (task = dt?.shift()) {
|
|
this.#disposeAfter?.(...task);
|
|
}
|
|
}
|
|
return deleted;
|
|
}
|
|
/**
|
|
* Clear the cache entirely, throwing away all values.
|
|
*/
|
|
clear() {
|
|
return this.#clear("delete");
|
|
}
|
|
#clear(reason) {
|
|
for (const index of this.#rindexes({ allowStale: true })) {
|
|
const v = this.#valList[index];
|
|
if (this.#isBackgroundFetch(v)) {
|
|
v.__abortController.abort(new Error("deleted"));
|
|
} else {
|
|
const k = this.#keyList[index];
|
|
if (this.#hasDispose) {
|
|
this.#dispose?.(v, k, reason);
|
|
}
|
|
if (this.#hasDisposeAfter) {
|
|
this.#disposed?.push([v, k, reason]);
|
|
}
|
|
}
|
|
}
|
|
this.#keyMap.clear();
|
|
this.#valList.fill(void 0);
|
|
this.#keyList.fill(void 0);
|
|
if (this.#ttls && this.#starts) {
|
|
this.#ttls.fill(0);
|
|
this.#starts.fill(0);
|
|
}
|
|
if (this.#sizes) {
|
|
this.#sizes.fill(0);
|
|
}
|
|
this.#head = 0;
|
|
this.#tail = 0;
|
|
this.#free.length = 0;
|
|
this.#calculatedSize = 0;
|
|
this.#size = 0;
|
|
if (this.#hasDisposeAfter && this.#disposed) {
|
|
const dt = this.#disposed;
|
|
let task;
|
|
while (task = dt?.shift()) {
|
|
this.#disposeAfter?.(...task);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var proc = typeof process === "object" && process ? process : {
|
|
stdout: null,
|
|
stderr: null
|
|
};
|
|
var isStream = (s) => !!s && typeof s === "object" && (s instanceof Minipass || s instanceof import_node_stream.default || isReadable(s) || isWritable(s));
|
|
var isReadable = (s) => !!s && typeof s === "object" && s instanceof import_node_events.EventEmitter && typeof s.pipe === "function" && // node core Writable streams have a pipe() method, but it throws
|
|
s.pipe !== import_node_stream.default.Writable.prototype.pipe;
|
|
var isWritable = (s) => !!s && typeof s === "object" && s instanceof import_node_events.EventEmitter && typeof s.write === "function" && typeof s.end === "function";
|
|
var EOF = Symbol("EOF");
|
|
var MAYBE_EMIT_END = Symbol("maybeEmitEnd");
|
|
var EMITTED_END = Symbol("emittedEnd");
|
|
var EMITTING_END = Symbol("emittingEnd");
|
|
var EMITTED_ERROR = Symbol("emittedError");
|
|
var CLOSED = Symbol("closed");
|
|
var READ = Symbol("read");
|
|
var FLUSH = Symbol("flush");
|
|
var FLUSHCHUNK = Symbol("flushChunk");
|
|
var ENCODING = Symbol("encoding");
|
|
var DECODER = Symbol("decoder");
|
|
var FLOWING = Symbol("flowing");
|
|
var PAUSED = Symbol("paused");
|
|
var RESUME = Symbol("resume");
|
|
var BUFFER = Symbol("buffer");
|
|
var PIPES = Symbol("pipes");
|
|
var BUFFERLENGTH = Symbol("bufferLength");
|
|
var BUFFERPUSH = Symbol("bufferPush");
|
|
var BUFFERSHIFT = Symbol("bufferShift");
|
|
var OBJECTMODE = Symbol("objectMode");
|
|
var DESTROYED = Symbol("destroyed");
|
|
var ERROR = Symbol("error");
|
|
var EMITDATA = Symbol("emitData");
|
|
var EMITEND = Symbol("emitEnd");
|
|
var EMITEND2 = Symbol("emitEnd2");
|
|
var ASYNC = Symbol("async");
|
|
var ABORT = Symbol("abort");
|
|
var ABORTED = Symbol("aborted");
|
|
var SIGNAL = Symbol("signal");
|
|
var DATALISTENERS = Symbol("dataListeners");
|
|
var DISCARDED = Symbol("discarded");
|
|
var defer = (fn) => Promise.resolve().then(fn);
|
|
var nodefer = (fn) => fn();
|
|
var isEndish = (ev) => ev === "end" || ev === "finish" || ev === "prefinish";
|
|
var isArrayBufferLike = (b) => b instanceof ArrayBuffer || !!b && typeof b === "object" && b.constructor && b.constructor.name === "ArrayBuffer" && b.byteLength >= 0;
|
|
var isArrayBufferView = (b) => !Buffer.isBuffer(b) && ArrayBuffer.isView(b);
|
|
var Pipe = class {
|
|
src;
|
|
dest;
|
|
opts;
|
|
ondrain;
|
|
constructor(src, dest, opts) {
|
|
this.src = src;
|
|
this.dest = dest;
|
|
this.opts = opts;
|
|
this.ondrain = () => src[RESUME]();
|
|
this.dest.on("drain", this.ondrain);
|
|
}
|
|
unpipe() {
|
|
this.dest.removeListener("drain", this.ondrain);
|
|
}
|
|
// only here for the prototype
|
|
/* c8 ignore start */
|
|
proxyErrors(_er) {
|
|
}
|
|
/* c8 ignore stop */
|
|
end() {
|
|
this.unpipe();
|
|
if (this.opts.end)
|
|
this.dest.end();
|
|
}
|
|
};
|
|
var PipeProxyErrors = class extends Pipe {
|
|
unpipe() {
|
|
this.src.removeListener("error", this.proxyErrors);
|
|
super.unpipe();
|
|
}
|
|
constructor(src, dest, opts) {
|
|
super(src, dest, opts);
|
|
this.proxyErrors = (er) => dest.emit("error", er);
|
|
src.on("error", this.proxyErrors);
|
|
}
|
|
};
|
|
var isObjectModeOptions = (o) => !!o.objectMode;
|
|
var isEncodingOptions = (o) => !o.objectMode && !!o.encoding && o.encoding !== "buffer";
|
|
var Minipass = class extends import_node_events.EventEmitter {
|
|
[FLOWING] = false;
|
|
[PAUSED] = false;
|
|
[PIPES] = [];
|
|
[BUFFER] = [];
|
|
[OBJECTMODE];
|
|
[ENCODING];
|
|
[ASYNC];
|
|
[DECODER];
|
|
[EOF] = false;
|
|
[EMITTED_END] = false;
|
|
[EMITTING_END] = false;
|
|
[CLOSED] = false;
|
|
[EMITTED_ERROR] = null;
|
|
[BUFFERLENGTH] = 0;
|
|
[DESTROYED] = false;
|
|
[SIGNAL];
|
|
[ABORTED] = false;
|
|
[DATALISTENERS] = 0;
|
|
[DISCARDED] = false;
|
|
/**
|
|
* true if the stream can be written
|
|
*/
|
|
writable = true;
|
|
/**
|
|
* true if the stream can be read
|
|
*/
|
|
readable = true;
|
|
/**
|
|
* If `RType` is Buffer, then options do not need to be provided.
|
|
* Otherwise, an options object must be provided to specify either
|
|
* {@link Minipass.SharedOptions.objectMode} or
|
|
* {@link Minipass.SharedOptions.encoding}, as appropriate.
|
|
*/
|
|
constructor(...args) {
|
|
const options = args[0] || {};
|
|
super();
|
|
if (options.objectMode && typeof options.encoding === "string") {
|
|
throw new TypeError("Encoding and objectMode may not be used together");
|
|
}
|
|
if (isObjectModeOptions(options)) {
|
|
this[OBJECTMODE] = true;
|
|
this[ENCODING] = null;
|
|
} else if (isEncodingOptions(options)) {
|
|
this[ENCODING] = options.encoding;
|
|
this[OBJECTMODE] = false;
|
|
} else {
|
|
this[OBJECTMODE] = false;
|
|
this[ENCODING] = null;
|
|
}
|
|
this[ASYNC] = !!options.async;
|
|
this[DECODER] = this[ENCODING] ? new import_node_string_decoder.StringDecoder(this[ENCODING]) : null;
|
|
if (options && options.debugExposeBuffer === true) {
|
|
Object.defineProperty(this, "buffer", { get: () => this[BUFFER] });
|
|
}
|
|
if (options && options.debugExposePipes === true) {
|
|
Object.defineProperty(this, "pipes", { get: () => this[PIPES] });
|
|
}
|
|
const { signal } = options;
|
|
if (signal) {
|
|
this[SIGNAL] = signal;
|
|
if (signal.aborted) {
|
|
this[ABORT]();
|
|
} else {
|
|
signal.addEventListener("abort", () => this[ABORT]());
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* The amount of data stored in the buffer waiting to be read.
|
|
*
|
|
* For Buffer strings, this will be the total byte length.
|
|
* For string encoding streams, this will be the string character length,
|
|
* according to JavaScript's `string.length` logic.
|
|
* For objectMode streams, this is a count of the items waiting to be
|
|
* emitted.
|
|
*/
|
|
get bufferLength() {
|
|
return this[BUFFERLENGTH];
|
|
}
|
|
/**
|
|
* The `BufferEncoding` currently in use, or `null`
|
|
*/
|
|
get encoding() {
|
|
return this[ENCODING];
|
|
}
|
|
/**
|
|
* @deprecated - This is a read only property
|
|
*/
|
|
set encoding(_enc) {
|
|
throw new Error("Encoding must be set at instantiation time");
|
|
}
|
|
/**
|
|
* @deprecated - Encoding may only be set at instantiation time
|
|
*/
|
|
setEncoding(_enc) {
|
|
throw new Error("Encoding must be set at instantiation time");
|
|
}
|
|
/**
|
|
* True if this is an objectMode stream
|
|
*/
|
|
get objectMode() {
|
|
return this[OBJECTMODE];
|
|
}
|
|
/**
|
|
* @deprecated - This is a read-only property
|
|
*/
|
|
set objectMode(_om) {
|
|
throw new Error("objectMode must be set at instantiation time");
|
|
}
|
|
/**
|
|
* true if this is an async stream
|
|
*/
|
|
get ["async"]() {
|
|
return this[ASYNC];
|
|
}
|
|
/**
|
|
* Set to true to make this stream async.
|
|
*
|
|
* Once set, it cannot be unset, as this would potentially cause incorrect
|
|
* behavior. Ie, a sync stream can be made async, but an async stream
|
|
* cannot be safely made sync.
|
|
*/
|
|
set ["async"](a) {
|
|
this[ASYNC] = this[ASYNC] || !!a;
|
|
}
|
|
// drop everything and get out of the flow completely
|
|
[ABORT]() {
|
|
this[ABORTED] = true;
|
|
this.emit("abort", this[SIGNAL]?.reason);
|
|
this.destroy(this[SIGNAL]?.reason);
|
|
}
|
|
/**
|
|
* True if the stream has been aborted.
|
|
*/
|
|
get aborted() {
|
|
return this[ABORTED];
|
|
}
|
|
/**
|
|
* No-op setter. Stream aborted status is set via the AbortSignal provided
|
|
* in the constructor options.
|
|
*/
|
|
set aborted(_) {
|
|
}
|
|
write(chunk, encoding, cb) {
|
|
if (this[ABORTED])
|
|
return false;
|
|
if (this[EOF])
|
|
throw new Error("write after end");
|
|
if (this[DESTROYED]) {
|
|
this.emit("error", Object.assign(new Error("Cannot call write after a stream was destroyed"), { code: "ERR_STREAM_DESTROYED" }));
|
|
return true;
|
|
}
|
|
if (typeof encoding === "function") {
|
|
cb = encoding;
|
|
encoding = "utf8";
|
|
}
|
|
if (!encoding)
|
|
encoding = "utf8";
|
|
const fn = this[ASYNC] ? defer : nodefer;
|
|
if (!this[OBJECTMODE] && !Buffer.isBuffer(chunk)) {
|
|
if (isArrayBufferView(chunk)) {
|
|
chunk = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
} else if (isArrayBufferLike(chunk)) {
|
|
chunk = Buffer.from(chunk);
|
|
} else if (typeof chunk !== "string") {
|
|
throw new Error("Non-contiguous data written to non-objectMode stream");
|
|
}
|
|
}
|
|
if (this[OBJECTMODE]) {
|
|
if (this[FLOWING] && this[BUFFERLENGTH] !== 0)
|
|
this[FLUSH](true);
|
|
if (this[FLOWING])
|
|
this.emit("data", chunk);
|
|
else
|
|
this[BUFFERPUSH](chunk);
|
|
if (this[BUFFERLENGTH] !== 0)
|
|
this.emit("readable");
|
|
if (cb)
|
|
fn(cb);
|
|
return this[FLOWING];
|
|
}
|
|
if (!chunk.length) {
|
|
if (this[BUFFERLENGTH] !== 0)
|
|
this.emit("readable");
|
|
if (cb)
|
|
fn(cb);
|
|
return this[FLOWING];
|
|
}
|
|
if (typeof chunk === "string" && // unless it is a string already ready for us to use
|
|
!(encoding === this[ENCODING] && !this[DECODER]?.lastNeed)) {
|
|
chunk = Buffer.from(chunk, encoding);
|
|
}
|
|
if (Buffer.isBuffer(chunk) && this[ENCODING]) {
|
|
chunk = this[DECODER].write(chunk);
|
|
}
|
|
if (this[FLOWING] && this[BUFFERLENGTH] !== 0)
|
|
this[FLUSH](true);
|
|
if (this[FLOWING])
|
|
this.emit("data", chunk);
|
|
else
|
|
this[BUFFERPUSH](chunk);
|
|
if (this[BUFFERLENGTH] !== 0)
|
|
this.emit("readable");
|
|
if (cb)
|
|
fn(cb);
|
|
return this[FLOWING];
|
|
}
|
|
/**
|
|
* Low-level explicit read method.
|
|
*
|
|
* In objectMode, the argument is ignored, and one item is returned if
|
|
* available.
|
|
*
|
|
* `n` is the number of bytes (or in the case of encoding streams,
|
|
* characters) to consume. If `n` is not provided, then the entire buffer
|
|
* is returned, or `null` is returned if no data is available.
|
|
*
|
|
* If `n` is greater that the amount of data in the internal buffer,
|
|
* then `null` is returned.
|
|
*/
|
|
read(n) {
|
|
if (this[DESTROYED])
|
|
return null;
|
|
this[DISCARDED] = false;
|
|
if (this[BUFFERLENGTH] === 0 || n === 0 || n && n > this[BUFFERLENGTH]) {
|
|
this[MAYBE_EMIT_END]();
|
|
return null;
|
|
}
|
|
if (this[OBJECTMODE])
|
|
n = null;
|
|
if (this[BUFFER].length > 1 && !this[OBJECTMODE]) {
|
|
this[BUFFER] = [
|
|
this[ENCODING] ? this[BUFFER].join("") : Buffer.concat(this[BUFFER], this[BUFFERLENGTH])
|
|
];
|
|
}
|
|
const ret = this[READ](n || null, this[BUFFER][0]);
|
|
this[MAYBE_EMIT_END]();
|
|
return ret;
|
|
}
|
|
[READ](n, chunk) {
|
|
if (this[OBJECTMODE])
|
|
this[BUFFERSHIFT]();
|
|
else {
|
|
const c = chunk;
|
|
if (n === c.length || n === null)
|
|
this[BUFFERSHIFT]();
|
|
else if (typeof c === "string") {
|
|
this[BUFFER][0] = c.slice(n);
|
|
chunk = c.slice(0, n);
|
|
this[BUFFERLENGTH] -= n;
|
|
} else {
|
|
this[BUFFER][0] = c.subarray(n);
|
|
chunk = c.subarray(0, n);
|
|
this[BUFFERLENGTH] -= n;
|
|
}
|
|
}
|
|
this.emit("data", chunk);
|
|
if (!this[BUFFER].length && !this[EOF])
|
|
this.emit("drain");
|
|
return chunk;
|
|
}
|
|
end(chunk, encoding, cb) {
|
|
if (typeof chunk === "function") {
|
|
cb = chunk;
|
|
chunk = void 0;
|
|
}
|
|
if (typeof encoding === "function") {
|
|
cb = encoding;
|
|
encoding = "utf8";
|
|
}
|
|
if (chunk !== void 0)
|
|
this.write(chunk, encoding);
|
|
if (cb)
|
|
this.once("end", cb);
|
|
this[EOF] = true;
|
|
this.writable = false;
|
|
if (this[FLOWING] || !this[PAUSED])
|
|
this[MAYBE_EMIT_END]();
|
|
return this;
|
|
}
|
|
// don't let the internal resume be overwritten
|
|
[RESUME]() {
|
|
if (this[DESTROYED])
|
|
return;
|
|
if (!this[DATALISTENERS] && !this[PIPES].length) {
|
|
this[DISCARDED] = true;
|
|
}
|
|
this[PAUSED] = false;
|
|
this[FLOWING] = true;
|
|
this.emit("resume");
|
|
if (this[BUFFER].length)
|
|
this[FLUSH]();
|
|
else if (this[EOF])
|
|
this[MAYBE_EMIT_END]();
|
|
else
|
|
this.emit("drain");
|
|
}
|
|
/**
|
|
* Resume the stream if it is currently in a paused state
|
|
*
|
|
* If called when there are no pipe destinations or `data` event listeners,
|
|
* this will place the stream in a "discarded" state, where all data will
|
|
* be thrown away. The discarded state is removed if a pipe destination or
|
|
* data handler is added, if pause() is called, or if any synchronous or
|
|
* asynchronous iteration is started.
|
|
*/
|
|
resume() {
|
|
return this[RESUME]();
|
|
}
|
|
/**
|
|
* Pause the stream
|
|
*/
|
|
pause() {
|
|
this[FLOWING] = false;
|
|
this[PAUSED] = true;
|
|
this[DISCARDED] = false;
|
|
}
|
|
/**
|
|
* true if the stream has been forcibly destroyed
|
|
*/
|
|
get destroyed() {
|
|
return this[DESTROYED];
|
|
}
|
|
/**
|
|
* true if the stream is currently in a flowing state, meaning that
|
|
* any writes will be immediately emitted.
|
|
*/
|
|
get flowing() {
|
|
return this[FLOWING];
|
|
}
|
|
/**
|
|
* true if the stream is currently in a paused state
|
|
*/
|
|
get paused() {
|
|
return this[PAUSED];
|
|
}
|
|
[BUFFERPUSH](chunk) {
|
|
if (this[OBJECTMODE])
|
|
this[BUFFERLENGTH] += 1;
|
|
else
|
|
this[BUFFERLENGTH] += chunk.length;
|
|
this[BUFFER].push(chunk);
|
|
}
|
|
[BUFFERSHIFT]() {
|
|
if (this[OBJECTMODE])
|
|
this[BUFFERLENGTH] -= 1;
|
|
else
|
|
this[BUFFERLENGTH] -= this[BUFFER][0].length;
|
|
return this[BUFFER].shift();
|
|
}
|
|
[FLUSH](noDrain = false) {
|
|
do {
|
|
} while (this[FLUSHCHUNK](this[BUFFERSHIFT]()) && this[BUFFER].length);
|
|
if (!noDrain && !this[BUFFER].length && !this[EOF])
|
|
this.emit("drain");
|
|
}
|
|
[FLUSHCHUNK](chunk) {
|
|
this.emit("data", chunk);
|
|
return this[FLOWING];
|
|
}
|
|
/**
|
|
* Pipe all data emitted by this stream into the destination provided.
|
|
*
|
|
* Triggers the flow of data.
|
|
*/
|
|
pipe(dest, opts) {
|
|
if (this[DESTROYED])
|
|
return dest;
|
|
this[DISCARDED] = false;
|
|
const ended = this[EMITTED_END];
|
|
opts = opts || {};
|
|
if (dest === proc.stdout || dest === proc.stderr)
|
|
opts.end = false;
|
|
else
|
|
opts.end = opts.end !== false;
|
|
opts.proxyErrors = !!opts.proxyErrors;
|
|
if (ended) {
|
|
if (opts.end)
|
|
dest.end();
|
|
} else {
|
|
this[PIPES].push(!opts.proxyErrors ? new Pipe(this, dest, opts) : new PipeProxyErrors(this, dest, opts));
|
|
if (this[ASYNC])
|
|
defer(() => this[RESUME]());
|
|
else
|
|
this[RESUME]();
|
|
}
|
|
return dest;
|
|
}
|
|
/**
|
|
* Fully unhook a piped destination stream.
|
|
*
|
|
* If the destination stream was the only consumer of this stream (ie,
|
|
* there are no other piped destinations or `'data'` event listeners)
|
|
* then the flow of data will stop until there is another consumer or
|
|
* {@link Minipass#resume} is explicitly called.
|
|
*/
|
|
unpipe(dest) {
|
|
const p = this[PIPES].find((p2) => p2.dest === dest);
|
|
if (p) {
|
|
if (this[PIPES].length === 1) {
|
|
if (this[FLOWING] && this[DATALISTENERS] === 0) {
|
|
this[FLOWING] = false;
|
|
}
|
|
this[PIPES] = [];
|
|
} else
|
|
this[PIPES].splice(this[PIPES].indexOf(p), 1);
|
|
p.unpipe();
|
|
}
|
|
}
|
|
/**
|
|
* Alias for {@link Minipass#on}
|
|
*/
|
|
addListener(ev, handler) {
|
|
return this.on(ev, handler);
|
|
}
|
|
/**
|
|
* Mostly identical to `EventEmitter.on`, with the following
|
|
* behavior differences to prevent data loss and unnecessary hangs:
|
|
*
|
|
* - Adding a 'data' event handler will trigger the flow of data
|
|
*
|
|
* - Adding a 'readable' event handler when there is data waiting to be read
|
|
* will cause 'readable' to be emitted immediately.
|
|
*
|
|
* - Adding an 'endish' event handler ('end', 'finish', etc.) which has
|
|
* already passed will cause the event to be emitted immediately and all
|
|
* handlers removed.
|
|
*
|
|
* - Adding an 'error' event handler after an error has been emitted will
|
|
* cause the event to be re-emitted immediately with the error previously
|
|
* raised.
|
|
*/
|
|
on(ev, handler) {
|
|
const ret = super.on(ev, handler);
|
|
if (ev === "data") {
|
|
this[DISCARDED] = false;
|
|
this[DATALISTENERS]++;
|
|
if (!this[PIPES].length && !this[FLOWING]) {
|
|
this[RESUME]();
|
|
}
|
|
} else if (ev === "readable" && this[BUFFERLENGTH] !== 0) {
|
|
super.emit("readable");
|
|
} else if (isEndish(ev) && this[EMITTED_END]) {
|
|
super.emit(ev);
|
|
this.removeAllListeners(ev);
|
|
} else if (ev === "error" && this[EMITTED_ERROR]) {
|
|
const h = handler;
|
|
if (this[ASYNC])
|
|
defer(() => h.call(this, this[EMITTED_ERROR]));
|
|
else
|
|
h.call(this, this[EMITTED_ERROR]);
|
|
}
|
|
return ret;
|
|
}
|
|
/**
|
|
* Alias for {@link Minipass#off}
|
|
*/
|
|
removeListener(ev, handler) {
|
|
return this.off(ev, handler);
|
|
}
|
|
/**
|
|
* Mostly identical to `EventEmitter.off`
|
|
*
|
|
* If a 'data' event handler is removed, and it was the last consumer
|
|
* (ie, there are no pipe destinations or other 'data' event listeners),
|
|
* then the flow of data will stop until there is another consumer or
|
|
* {@link Minipass#resume} is explicitly called.
|
|
*/
|
|
off(ev, handler) {
|
|
const ret = super.off(ev, handler);
|
|
if (ev === "data") {
|
|
this[DATALISTENERS] = this.listeners("data").length;
|
|
if (this[DATALISTENERS] === 0 && !this[DISCARDED] && !this[PIPES].length) {
|
|
this[FLOWING] = false;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
/**
|
|
* Mostly identical to `EventEmitter.removeAllListeners`
|
|
*
|
|
* If all 'data' event handlers are removed, and they were the last consumer
|
|
* (ie, there are no pipe destinations), then the flow of data will stop
|
|
* until there is another consumer or {@link Minipass#resume} is explicitly
|
|
* called.
|
|
*/
|
|
removeAllListeners(ev) {
|
|
const ret = super.removeAllListeners(ev);
|
|
if (ev === "data" || ev === void 0) {
|
|
this[DATALISTENERS] = 0;
|
|
if (!this[DISCARDED] && !this[PIPES].length) {
|
|
this[FLOWING] = false;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
/**
|
|
* true if the 'end' event has been emitted
|
|
*/
|
|
get emittedEnd() {
|
|
return this[EMITTED_END];
|
|
}
|
|
[MAYBE_EMIT_END]() {
|
|
if (!this[EMITTING_END] && !this[EMITTED_END] && !this[DESTROYED] && this[BUFFER].length === 0 && this[EOF]) {
|
|
this[EMITTING_END] = true;
|
|
this.emit("end");
|
|
this.emit("prefinish");
|
|
this.emit("finish");
|
|
if (this[CLOSED])
|
|
this.emit("close");
|
|
this[EMITTING_END] = false;
|
|
}
|
|
}
|
|
/**
|
|
* Mostly identical to `EventEmitter.emit`, with the following
|
|
* behavior differences to prevent data loss and unnecessary hangs:
|
|
*
|
|
* If the stream has been destroyed, and the event is something other
|
|
* than 'close' or 'error', then `false` is returned and no handlers
|
|
* are called.
|
|
*
|
|
* If the event is 'end', and has already been emitted, then the event
|
|
* is ignored. If the stream is in a paused or non-flowing state, then
|
|
* the event will be deferred until data flow resumes. If the stream is
|
|
* async, then handlers will be called on the next tick rather than
|
|
* immediately.
|
|
*
|
|
* If the event is 'close', and 'end' has not yet been emitted, then
|
|
* the event will be deferred until after 'end' is emitted.
|
|
*
|
|
* If the event is 'error', and an AbortSignal was provided for the stream,
|
|
* and there are no listeners, then the event is ignored, matching the
|
|
* behavior of node core streams in the presense of an AbortSignal.
|
|
*
|
|
* If the event is 'finish' or 'prefinish', then all listeners will be
|
|
* removed after emitting the event, to prevent double-firing.
|
|
*/
|
|
emit(ev, ...args) {
|
|
const data = args[0];
|
|
if (ev !== "error" && ev !== "close" && ev !== DESTROYED && this[DESTROYED]) {
|
|
return false;
|
|
} else if (ev === "data") {
|
|
return !this[OBJECTMODE] && !data ? false : this[ASYNC] ? (defer(() => this[EMITDATA](data)), true) : this[EMITDATA](data);
|
|
} else if (ev === "end") {
|
|
return this[EMITEND]();
|
|
} else if (ev === "close") {
|
|
this[CLOSED] = true;
|
|
if (!this[EMITTED_END] && !this[DESTROYED])
|
|
return false;
|
|
const ret2 = super.emit("close");
|
|
this.removeAllListeners("close");
|
|
return ret2;
|
|
} else if (ev === "error") {
|
|
this[EMITTED_ERROR] = data;
|
|
super.emit(ERROR, data);
|
|
const ret2 = !this[SIGNAL] || this.listeners("error").length ? super.emit("error", data) : false;
|
|
this[MAYBE_EMIT_END]();
|
|
return ret2;
|
|
} else if (ev === "resume") {
|
|
const ret2 = super.emit("resume");
|
|
this[MAYBE_EMIT_END]();
|
|
return ret2;
|
|
} else if (ev === "finish" || ev === "prefinish") {
|
|
const ret2 = super.emit(ev);
|
|
this.removeAllListeners(ev);
|
|
return ret2;
|
|
}
|
|
const ret = super.emit(ev, ...args);
|
|
this[MAYBE_EMIT_END]();
|
|
return ret;
|
|
}
|
|
[EMITDATA](data) {
|
|
for (const p of this[PIPES]) {
|
|
if (p.dest.write(data) === false)
|
|
this.pause();
|
|
}
|
|
const ret = this[DISCARDED] ? false : super.emit("data", data);
|
|
this[MAYBE_EMIT_END]();
|
|
return ret;
|
|
}
|
|
[EMITEND]() {
|
|
if (this[EMITTED_END])
|
|
return false;
|
|
this[EMITTED_END] = true;
|
|
this.readable = false;
|
|
return this[ASYNC] ? (defer(() => this[EMITEND2]()), true) : this[EMITEND2]();
|
|
}
|
|
[EMITEND2]() {
|
|
if (this[DECODER]) {
|
|
const data = this[DECODER].end();
|
|
if (data) {
|
|
for (const p of this[PIPES]) {
|
|
p.dest.write(data);
|
|
}
|
|
if (!this[DISCARDED])
|
|
super.emit("data", data);
|
|
}
|
|
}
|
|
for (const p of this[PIPES]) {
|
|
p.end();
|
|
}
|
|
const ret = super.emit("end");
|
|
this.removeAllListeners("end");
|
|
return ret;
|
|
}
|
|
/**
|
|
* Return a Promise that resolves to an array of all emitted data once
|
|
* the stream ends.
|
|
*/
|
|
async collect() {
|
|
const buf = Object.assign([], {
|
|
dataLength: 0
|
|
});
|
|
if (!this[OBJECTMODE])
|
|
buf.dataLength = 0;
|
|
const p = this.promise();
|
|
this.on("data", (c) => {
|
|
buf.push(c);
|
|
if (!this[OBJECTMODE])
|
|
buf.dataLength += c.length;
|
|
});
|
|
await p;
|
|
return buf;
|
|
}
|
|
/**
|
|
* Return a Promise that resolves to the concatenation of all emitted data
|
|
* once the stream ends.
|
|
*
|
|
* Not allowed on objectMode streams.
|
|
*/
|
|
async concat() {
|
|
if (this[OBJECTMODE]) {
|
|
throw new Error("cannot concat in objectMode");
|
|
}
|
|
const buf = await this.collect();
|
|
return this[ENCODING] ? buf.join("") : Buffer.concat(buf, buf.dataLength);
|
|
}
|
|
/**
|
|
* Return a void Promise that resolves once the stream ends.
|
|
*/
|
|
async promise() {
|
|
return new Promise((resolve6, reject) => {
|
|
this.on(DESTROYED, () => reject(new Error("stream destroyed")));
|
|
this.on("error", (er) => reject(er));
|
|
this.on("end", () => resolve6());
|
|
});
|
|
}
|
|
/**
|
|
* Asynchronous `for await of` iteration.
|
|
*
|
|
* This will continue emitting all chunks until the stream terminates.
|
|
*/
|
|
[Symbol.asyncIterator]() {
|
|
this[DISCARDED] = false;
|
|
let stopped = false;
|
|
const stop = async () => {
|
|
this.pause();
|
|
stopped = true;
|
|
return { value: void 0, done: true };
|
|
};
|
|
const next = () => {
|
|
if (stopped)
|
|
return stop();
|
|
const res = this.read();
|
|
if (res !== null)
|
|
return Promise.resolve({ done: false, value: res });
|
|
if (this[EOF])
|
|
return stop();
|
|
let resolve6;
|
|
let reject;
|
|
const onerr = (er) => {
|
|
this.off("data", ondata);
|
|
this.off("end", onend);
|
|
this.off(DESTROYED, ondestroy);
|
|
stop();
|
|
reject(er);
|
|
};
|
|
const ondata = (value) => {
|
|
this.off("error", onerr);
|
|
this.off("end", onend);
|
|
this.off(DESTROYED, ondestroy);
|
|
this.pause();
|
|
resolve6({ value, done: !!this[EOF] });
|
|
};
|
|
const onend = () => {
|
|
this.off("error", onerr);
|
|
this.off("data", ondata);
|
|
this.off(DESTROYED, ondestroy);
|
|
stop();
|
|
resolve6({ done: true, value: void 0 });
|
|
};
|
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
return new Promise((res2, rej) => {
|
|
reject = rej;
|
|
resolve6 = res2;
|
|
this.once(DESTROYED, ondestroy);
|
|
this.once("error", onerr);
|
|
this.once("end", onend);
|
|
this.once("data", ondata);
|
|
});
|
|
};
|
|
return {
|
|
next,
|
|
throw: stop,
|
|
return: stop,
|
|
[Symbol.asyncIterator]() {
|
|
return this;
|
|
}
|
|
};
|
|
}
|
|
/**
|
|
* Synchronous `for of` iteration.
|
|
*
|
|
* The iteration will terminate when the internal buffer runs out, even
|
|
* if the stream has not yet terminated.
|
|
*/
|
|
[Symbol.iterator]() {
|
|
this[DISCARDED] = false;
|
|
let stopped = false;
|
|
const stop = () => {
|
|
this.pause();
|
|
this.off(ERROR, stop);
|
|
this.off(DESTROYED, stop);
|
|
this.off("end", stop);
|
|
stopped = true;
|
|
return { done: true, value: void 0 };
|
|
};
|
|
const next = () => {
|
|
if (stopped)
|
|
return stop();
|
|
const value = this.read();
|
|
return value === null ? stop() : { done: false, value };
|
|
};
|
|
this.once("end", stop);
|
|
this.once(ERROR, stop);
|
|
this.once(DESTROYED, stop);
|
|
return {
|
|
next,
|
|
throw: stop,
|
|
return: stop,
|
|
[Symbol.iterator]() {
|
|
return this;
|
|
}
|
|
};
|
|
}
|
|
/**
|
|
* Destroy a stream, preventing it from being used for any further purpose.
|
|
*
|
|
* If the stream has a `close()` method, then it will be called on
|
|
* destruction.
|
|
*
|
|
* After destruction, any attempt to write data, read data, or emit most
|
|
* events will be ignored.
|
|
*
|
|
* If an error argument is provided, then it will be emitted in an
|
|
* 'error' event.
|
|
*/
|
|
destroy(er) {
|
|
if (this[DESTROYED]) {
|
|
if (er)
|
|
this.emit("error", er);
|
|
else
|
|
this.emit(DESTROYED);
|
|
return this;
|
|
}
|
|
this[DESTROYED] = true;
|
|
this[DISCARDED] = true;
|
|
this[BUFFER].length = 0;
|
|
this[BUFFERLENGTH] = 0;
|
|
const wc = this;
|
|
if (typeof wc.close === "function" && !this[CLOSED])
|
|
wc.close();
|
|
if (er)
|
|
this.emit("error", er);
|
|
else
|
|
this.emit(DESTROYED);
|
|
return this;
|
|
}
|
|
/**
|
|
* Alias for {@link isStream}
|
|
*
|
|
* Former export location, maintained for backwards compatibility.
|
|
*
|
|
* @deprecated
|
|
*/
|
|
static get isStream() {
|
|
return isStream;
|
|
}
|
|
};
|
|
var realpathSync = import_fs.realpathSync.native;
|
|
var defaultFS = {
|
|
lstatSync: import_fs.lstatSync,
|
|
readdir: import_fs.readdir,
|
|
readdirSync: import_fs.readdirSync,
|
|
readlinkSync: import_fs.readlinkSync,
|
|
realpathSync,
|
|
promises: {
|
|
lstat: import_promises.lstat,
|
|
readdir: import_promises.readdir,
|
|
readlink: import_promises.readlink,
|
|
realpath: import_promises.realpath
|
|
}
|
|
};
|
|
var fsFromOption = (fsOption) => !fsOption || fsOption === defaultFS || fsOption === actualFS ? defaultFS : {
|
|
...defaultFS,
|
|
...fsOption,
|
|
promises: {
|
|
...defaultFS.promises,
|
|
...fsOption.promises || {}
|
|
}
|
|
};
|
|
var uncDriveRegexp = /^\\\\\?\\([a-z]:)\\?$/i;
|
|
var uncToDrive = (rootPath) => rootPath.replace(/\//g, "\\").replace(uncDriveRegexp, "$1\\");
|
|
var eitherSep = /[\\\/]/;
|
|
var UNKNOWN = 0;
|
|
var IFIFO = 1;
|
|
var IFCHR = 2;
|
|
var IFDIR = 4;
|
|
var IFBLK = 6;
|
|
var IFREG = 8;
|
|
var IFLNK = 10;
|
|
var IFSOCK = 12;
|
|
var IFMT = 15;
|
|
var IFMT_UNKNOWN = ~IFMT;
|
|
var READDIR_CALLED = 16;
|
|
var LSTAT_CALLED = 32;
|
|
var ENOTDIR = 64;
|
|
var ENOENT = 128;
|
|
var ENOREADLINK = 256;
|
|
var ENOREALPATH = 512;
|
|
var ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
|
|
var TYPEMASK = 1023;
|
|
var entToType = (s) => s.isFile() ? IFREG : s.isDirectory() ? IFDIR : s.isSymbolicLink() ? IFLNK : s.isCharacterDevice() ? IFCHR : s.isBlockDevice() ? IFBLK : s.isSocket() ? IFSOCK : s.isFIFO() ? IFIFO : UNKNOWN;
|
|
var normalizeCache = /* @__PURE__ */ new Map();
|
|
var normalize = (s) => {
|
|
const c = normalizeCache.get(s);
|
|
if (c)
|
|
return c;
|
|
const n = s.normalize("NFKD");
|
|
normalizeCache.set(s, n);
|
|
return n;
|
|
};
|
|
var normalizeNocaseCache = /* @__PURE__ */ new Map();
|
|
var normalizeNocase = (s) => {
|
|
const c = normalizeNocaseCache.get(s);
|
|
if (c)
|
|
return c;
|
|
const n = normalize(s.toLowerCase());
|
|
normalizeNocaseCache.set(s, n);
|
|
return n;
|
|
};
|
|
var ResolveCache = class extends LRUCache {
|
|
constructor() {
|
|
super({ max: 256 });
|
|
}
|
|
};
|
|
var ChildrenCache = class extends LRUCache {
|
|
constructor(maxSize = 16 * 1024) {
|
|
super({
|
|
maxSize,
|
|
// parent + children
|
|
sizeCalculation: (a) => a.length + 1
|
|
});
|
|
}
|
|
};
|
|
var setAsCwd = Symbol("PathScurry setAsCwd");
|
|
var PathBase = class {
|
|
/**
|
|
* the basename of this path
|
|
*
|
|
* **Important**: *always* test the path name against any test string
|
|
* usingthe {@link isNamed} method, and not by directly comparing this
|
|
* string. Otherwise, unicode path strings that the system sees as identical
|
|
* will not be properly treated as the same path, leading to incorrect
|
|
* behavior and possible security issues.
|
|
*/
|
|
name;
|
|
/**
|
|
* the Path entry corresponding to the path root.
|
|
*
|
|
* @internal
|
|
*/
|
|
root;
|
|
/**
|
|
* All roots found within the current PathScurry family
|
|
*
|
|
* @internal
|
|
*/
|
|
roots;
|
|
/**
|
|
* a reference to the parent path, or undefined in the case of root entries
|
|
*
|
|
* @internal
|
|
*/
|
|
parent;
|
|
/**
|
|
* boolean indicating whether paths are compared case-insensitively
|
|
* @internal
|
|
*/
|
|
nocase;
|
|
/**
|
|
* boolean indicating that this path is the current working directory
|
|
* of the PathScurry collection that contains it.
|
|
*/
|
|
isCWD = false;
|
|
// potential default fs override
|
|
#fs;
|
|
// Stats fields
|
|
#dev;
|
|
get dev() {
|
|
return this.#dev;
|
|
}
|
|
#mode;
|
|
get mode() {
|
|
return this.#mode;
|
|
}
|
|
#nlink;
|
|
get nlink() {
|
|
return this.#nlink;
|
|
}
|
|
#uid;
|
|
get uid() {
|
|
return this.#uid;
|
|
}
|
|
#gid;
|
|
get gid() {
|
|
return this.#gid;
|
|
}
|
|
#rdev;
|
|
get rdev() {
|
|
return this.#rdev;
|
|
}
|
|
#blksize;
|
|
get blksize() {
|
|
return this.#blksize;
|
|
}
|
|
#ino;
|
|
get ino() {
|
|
return this.#ino;
|
|
}
|
|
#size;
|
|
get size() {
|
|
return this.#size;
|
|
}
|
|
#blocks;
|
|
get blocks() {
|
|
return this.#blocks;
|
|
}
|
|
#atimeMs;
|
|
get atimeMs() {
|
|
return this.#atimeMs;
|
|
}
|
|
#mtimeMs;
|
|
get mtimeMs() {
|
|
return this.#mtimeMs;
|
|
}
|
|
#ctimeMs;
|
|
get ctimeMs() {
|
|
return this.#ctimeMs;
|
|
}
|
|
#birthtimeMs;
|
|
get birthtimeMs() {
|
|
return this.#birthtimeMs;
|
|
}
|
|
#atime;
|
|
get atime() {
|
|
return this.#atime;
|
|
}
|
|
#mtime;
|
|
get mtime() {
|
|
return this.#mtime;
|
|
}
|
|
#ctime;
|
|
get ctime() {
|
|
return this.#ctime;
|
|
}
|
|
#birthtime;
|
|
get birthtime() {
|
|
return this.#birthtime;
|
|
}
|
|
#matchName;
|
|
#depth;
|
|
#fullpath;
|
|
#fullpathPosix;
|
|
#relative;
|
|
#relativePosix;
|
|
#type;
|
|
#children;
|
|
#linkTarget;
|
|
#realpath;
|
|
/**
|
|
* This property is for compatibility with the Dirent class as of
|
|
* Node v20, where Dirent['parentPath'] refers to the path of the
|
|
* directory that was passed to readdir. For root entries, it's the path
|
|
* to the entry itself.
|
|
*/
|
|
get parentPath() {
|
|
return (this.parent || this).fullpath();
|
|
}
|
|
/**
|
|
* Deprecated alias for Dirent['parentPath'] Somewhat counterintuitively,
|
|
* this property refers to the *parent* path, not the path object itself.
|
|
*
|
|
* @deprecated
|
|
*/
|
|
get path() {
|
|
return this.parentPath;
|
|
}
|
|
/**
|
|
* Do not create new Path objects directly. They should always be accessed
|
|
* via the PathScurry class or other methods on the Path class.
|
|
*
|
|
* @internal
|
|
*/
|
|
constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) {
|
|
this.name = name;
|
|
this.#matchName = nocase ? normalizeNocase(name) : normalize(name);
|
|
this.#type = type & TYPEMASK;
|
|
this.nocase = nocase;
|
|
this.roots = roots;
|
|
this.root = root || this;
|
|
this.#children = children;
|
|
this.#fullpath = opts.fullpath;
|
|
this.#relative = opts.relative;
|
|
this.#relativePosix = opts.relativePosix;
|
|
this.parent = opts.parent;
|
|
if (this.parent) {
|
|
this.#fs = this.parent.#fs;
|
|
} else {
|
|
this.#fs = fsFromOption(opts.fs);
|
|
}
|
|
}
|
|
/**
|
|
* Returns the depth of the Path object from its root.
|
|
*
|
|
* For example, a path at `/foo/bar` would have a depth of 2.
|
|
*/
|
|
depth() {
|
|
if (this.#depth !== void 0)
|
|
return this.#depth;
|
|
if (!this.parent)
|
|
return this.#depth = 0;
|
|
return this.#depth = this.parent.depth() + 1;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
childrenCache() {
|
|
return this.#children;
|
|
}
|
|
/**
|
|
* Get the Path object referenced by the string path, resolved from this Path
|
|
*/
|
|
resolve(path2) {
|
|
if (!path2) {
|
|
return this;
|
|
}
|
|
const rootPath = this.getRootString(path2);
|
|
const dir = path2.substring(rootPath.length);
|
|
const dirParts = dir.split(this.splitSep);
|
|
const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
|
|
return result;
|
|
}
|
|
#resolveParts(dirParts) {
|
|
let p = this;
|
|
for (const part of dirParts) {
|
|
p = p.child(part);
|
|
}
|
|
return p;
|
|
}
|
|
/**
|
|
* Returns the cached children Path objects, if still available. If they
|
|
* have fallen out of the cache, then returns an empty array, and resets the
|
|
* READDIR_CALLED bit, so that future calls to readdir() will require an fs
|
|
* lookup.
|
|
*
|
|
* @internal
|
|
*/
|
|
children() {
|
|
const cached = this.#children.get(this);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
const children = Object.assign([], { provisional: 0 });
|
|
this.#children.set(this, children);
|
|
this.#type &= ~READDIR_CALLED;
|
|
return children;
|
|
}
|
|
/**
|
|
* Resolves a path portion and returns or creates the child Path.
|
|
*
|
|
* Returns `this` if pathPart is `''` or `'.'`, or `parent` if pathPart is
|
|
* `'..'`.
|
|
*
|
|
* This should not be called directly. If `pathPart` contains any path
|
|
* separators, it will lead to unsafe undefined behavior.
|
|
*
|
|
* Use `Path.resolve()` instead.
|
|
*
|
|
* @internal
|
|
*/
|
|
child(pathPart, opts) {
|
|
if (pathPart === "" || pathPart === ".") {
|
|
return this;
|
|
}
|
|
if (pathPart === "..") {
|
|
return this.parent || this;
|
|
}
|
|
const children = this.children();
|
|
const name = this.nocase ? normalizeNocase(pathPart) : normalize(pathPart);
|
|
for (const p of children) {
|
|
if (p.#matchName === name) {
|
|
return p;
|
|
}
|
|
}
|
|
const s = this.parent ? this.sep : "";
|
|
const fullpath = this.#fullpath ? this.#fullpath + s + pathPart : void 0;
|
|
const pchild = this.newChild(pathPart, UNKNOWN, {
|
|
...opts,
|
|
parent: this,
|
|
fullpath
|
|
});
|
|
if (!this.canReaddir()) {
|
|
pchild.#type |= ENOENT;
|
|
}
|
|
children.push(pchild);
|
|
return pchild;
|
|
}
|
|
/**
|
|
* The relative path from the cwd. If it does not share an ancestor with
|
|
* the cwd, then this ends up being equivalent to the fullpath()
|
|
*/
|
|
relative() {
|
|
if (this.isCWD)
|
|
return "";
|
|
if (this.#relative !== void 0) {
|
|
return this.#relative;
|
|
}
|
|
const name = this.name;
|
|
const p = this.parent;
|
|
if (!p) {
|
|
return this.#relative = this.name;
|
|
}
|
|
const pv = p.relative();
|
|
return pv + (!pv || !p.parent ? "" : this.sep) + name;
|
|
}
|
|
/**
|
|
* The relative path from the cwd, using / as the path separator.
|
|
* If it does not share an ancestor with
|
|
* the cwd, then this ends up being equivalent to the fullpathPosix()
|
|
* On posix systems, this is identical to relative().
|
|
*/
|
|
relativePosix() {
|
|
if (this.sep === "/")
|
|
return this.relative();
|
|
if (this.isCWD)
|
|
return "";
|
|
if (this.#relativePosix !== void 0)
|
|
return this.#relativePosix;
|
|
const name = this.name;
|
|
const p = this.parent;
|
|
if (!p) {
|
|
return this.#relativePosix = this.fullpathPosix();
|
|
}
|
|
const pv = p.relativePosix();
|
|
return pv + (!pv || !p.parent ? "" : "/") + name;
|
|
}
|
|
/**
|
|
* The fully resolved path string for this Path entry
|
|
*/
|
|
fullpath() {
|
|
if (this.#fullpath !== void 0) {
|
|
return this.#fullpath;
|
|
}
|
|
const name = this.name;
|
|
const p = this.parent;
|
|
if (!p) {
|
|
return this.#fullpath = this.name;
|
|
}
|
|
const pv = p.fullpath();
|
|
const fp = pv + (!p.parent ? "" : this.sep) + name;
|
|
return this.#fullpath = fp;
|
|
}
|
|
/**
|
|
* On platforms other than windows, this is identical to fullpath.
|
|
*
|
|
* On windows, this is overridden to return the forward-slash form of the
|
|
* full UNC path.
|
|
*/
|
|
fullpathPosix() {
|
|
if (this.#fullpathPosix !== void 0)
|
|
return this.#fullpathPosix;
|
|
if (this.sep === "/")
|
|
return this.#fullpathPosix = this.fullpath();
|
|
if (!this.parent) {
|
|
const p2 = this.fullpath().replace(/\\/g, "/");
|
|
if (/^[a-z]:\//i.test(p2)) {
|
|
return this.#fullpathPosix = `//?/${p2}`;
|
|
} else {
|
|
return this.#fullpathPosix = p2;
|
|
}
|
|
}
|
|
const p = this.parent;
|
|
const pfpp = p.fullpathPosix();
|
|
const fpp = pfpp + (!pfpp || !p.parent ? "" : "/") + this.name;
|
|
return this.#fullpathPosix = fpp;
|
|
}
|
|
/**
|
|
* Is the Path of an unknown type?
|
|
*
|
|
* Note that we might know *something* about it if there has been a previous
|
|
* filesystem operation, for example that it does not exist, or is not a
|
|
* link, or whether it has child entries.
|
|
*/
|
|
isUnknown() {
|
|
return (this.#type & IFMT) === UNKNOWN;
|
|
}
|
|
isType(type) {
|
|
return this[`is${type}`]();
|
|
}
|
|
getType() {
|
|
return this.isUnknown() ? "Unknown" : this.isDirectory() ? "Directory" : this.isFile() ? "File" : this.isSymbolicLink() ? "SymbolicLink" : this.isFIFO() ? "FIFO" : this.isCharacterDevice() ? "CharacterDevice" : this.isBlockDevice() ? "BlockDevice" : (
|
|
/* c8 ignore start */
|
|
this.isSocket() ? "Socket" : "Unknown"
|
|
);
|
|
}
|
|
/**
|
|
* Is the Path a regular file?
|
|
*/
|
|
isFile() {
|
|
return (this.#type & IFMT) === IFREG;
|
|
}
|
|
/**
|
|
* Is the Path a directory?
|
|
*/
|
|
isDirectory() {
|
|
return (this.#type & IFMT) === IFDIR;
|
|
}
|
|
/**
|
|
* Is the path a character device?
|
|
*/
|
|
isCharacterDevice() {
|
|
return (this.#type & IFMT) === IFCHR;
|
|
}
|
|
/**
|
|
* Is the path a block device?
|
|
*/
|
|
isBlockDevice() {
|
|
return (this.#type & IFMT) === IFBLK;
|
|
}
|
|
/**
|
|
* Is the path a FIFO pipe?
|
|
*/
|
|
isFIFO() {
|
|
return (this.#type & IFMT) === IFIFO;
|
|
}
|
|
/**
|
|
* Is the path a socket?
|
|
*/
|
|
isSocket() {
|
|
return (this.#type & IFMT) === IFSOCK;
|
|
}
|
|
/**
|
|
* Is the path a symbolic link?
|
|
*/
|
|
isSymbolicLink() {
|
|
return (this.#type & IFLNK) === IFLNK;
|
|
}
|
|
/**
|
|
* Return the entry if it has been subject of a successful lstat, or
|
|
* undefined otherwise.
|
|
*
|
|
* Does not read the filesystem, so an undefined result *could* simply
|
|
* mean that we haven't called lstat on it.
|
|
*/
|
|
lstatCached() {
|
|
return this.#type & LSTAT_CALLED ? this : void 0;
|
|
}
|
|
/**
|
|
* Return the cached link target if the entry has been the subject of a
|
|
* successful readlink, or undefined otherwise.
|
|
*
|
|
* Does not read the filesystem, so an undefined result *could* just mean we
|
|
* don't have any cached data. Only use it if you are very sure that a
|
|
* readlink() has been called at some point.
|
|
*/
|
|
readlinkCached() {
|
|
return this.#linkTarget;
|
|
}
|
|
/**
|
|
* Returns the cached realpath target if the entry has been the subject
|
|
* of a successful realpath, or undefined otherwise.
|
|
*
|
|
* Does not read the filesystem, so an undefined result *could* just mean we
|
|
* don't have any cached data. Only use it if you are very sure that a
|
|
* realpath() has been called at some point.
|
|
*/
|
|
realpathCached() {
|
|
return this.#realpath;
|
|
}
|
|
/**
|
|
* Returns the cached child Path entries array if the entry has been the
|
|
* subject of a successful readdir(), or [] otherwise.
|
|
*
|
|
* Does not read the filesystem, so an empty array *could* just mean we
|
|
* don't have any cached data. Only use it if you are very sure that a
|
|
* readdir() has been called recently enough to still be valid.
|
|
*/
|
|
readdirCached() {
|
|
const children = this.children();
|
|
return children.slice(0, children.provisional);
|
|
}
|
|
/**
|
|
* Return true if it's worth trying to readlink. Ie, we don't (yet) have
|
|
* any indication that readlink will definitely fail.
|
|
*
|
|
* Returns false if the path is known to not be a symlink, if a previous
|
|
* readlink failed, or if the entry does not exist.
|
|
*/
|
|
canReadlink() {
|
|
if (this.#linkTarget)
|
|
return true;
|
|
if (!this.parent)
|
|
return false;
|
|
const ifmt = this.#type & IFMT;
|
|
return !(ifmt !== UNKNOWN && ifmt !== IFLNK || this.#type & ENOREADLINK || this.#type & ENOENT);
|
|
}
|
|
/**
|
|
* Return true if readdir has previously been successfully called on this
|
|
* path, indicating that cachedReaddir() is likely valid.
|
|
*/
|
|
calledReaddir() {
|
|
return !!(this.#type & READDIR_CALLED);
|
|
}
|
|
/**
|
|
* Returns true if the path is known to not exist. That is, a previous lstat
|
|
* or readdir failed to verify its existence when that would have been
|
|
* expected, or a parent entry was marked either enoent or enotdir.
|
|
*/
|
|
isENOENT() {
|
|
return !!(this.#type & ENOENT);
|
|
}
|
|
/**
|
|
* Return true if the path is a match for the given path name. This handles
|
|
* case sensitivity and unicode normalization.
|
|
*
|
|
* Note: even on case-sensitive systems, it is **not** safe to test the
|
|
* equality of the `.name` property to determine whether a given pathname
|
|
* matches, due to unicode normalization mismatches.
|
|
*
|
|
* Always use this method instead of testing the `path.name` property
|
|
* directly.
|
|
*/
|
|
isNamed(n) {
|
|
return !this.nocase ? this.#matchName === normalize(n) : this.#matchName === normalizeNocase(n);
|
|
}
|
|
/**
|
|
* Return the Path object corresponding to the target of a symbolic link.
|
|
*
|
|
* If the Path is not a symbolic link, or if the readlink call fails for any
|
|
* reason, `undefined` is returned.
|
|
*
|
|
* Result is cached, and thus may be outdated if the filesystem is mutated.
|
|
*/
|
|
async readlink() {
|
|
const target = this.#linkTarget;
|
|
if (target) {
|
|
return target;
|
|
}
|
|
if (!this.canReadlink()) {
|
|
return void 0;
|
|
}
|
|
if (!this.parent) {
|
|
return void 0;
|
|
}
|
|
try {
|
|
const read = await this.#fs.promises.readlink(this.fullpath());
|
|
const linkTarget = (await this.parent.realpath())?.resolve(read);
|
|
if (linkTarget) {
|
|
return this.#linkTarget = linkTarget;
|
|
}
|
|
} catch (er) {
|
|
this.#readlinkFail(er.code);
|
|
return void 0;
|
|
}
|
|
}
|
|
/**
|
|
* Synchronous {@link PathBase.readlink}
|
|
*/
|
|
readlinkSync() {
|
|
const target = this.#linkTarget;
|
|
if (target) {
|
|
return target;
|
|
}
|
|
if (!this.canReadlink()) {
|
|
return void 0;
|
|
}
|
|
if (!this.parent) {
|
|
return void 0;
|
|
}
|
|
try {
|
|
const read = this.#fs.readlinkSync(this.fullpath());
|
|
const linkTarget = this.parent.realpathSync()?.resolve(read);
|
|
if (linkTarget) {
|
|
return this.#linkTarget = linkTarget;
|
|
}
|
|
} catch (er) {
|
|
this.#readlinkFail(er.code);
|
|
return void 0;
|
|
}
|
|
}
|
|
#readdirSuccess(children) {
|
|
this.#type |= READDIR_CALLED;
|
|
for (let p = children.provisional; p < children.length; p++) {
|
|
const c = children[p];
|
|
if (c)
|
|
c.#markENOENT();
|
|
}
|
|
}
|
|
#markENOENT() {
|
|
if (this.#type & ENOENT)
|
|
return;
|
|
this.#type = (this.#type | ENOENT) & IFMT_UNKNOWN;
|
|
this.#markChildrenENOENT();
|
|
}
|
|
#markChildrenENOENT() {
|
|
const children = this.children();
|
|
children.provisional = 0;
|
|
for (const p of children) {
|
|
p.#markENOENT();
|
|
}
|
|
}
|
|
#markENOREALPATH() {
|
|
this.#type |= ENOREALPATH;
|
|
this.#markENOTDIR();
|
|
}
|
|
// save the information when we know the entry is not a dir
|
|
#markENOTDIR() {
|
|
if (this.#type & ENOTDIR)
|
|
return;
|
|
let t = this.#type;
|
|
if ((t & IFMT) === IFDIR)
|
|
t &= IFMT_UNKNOWN;
|
|
this.#type = t | ENOTDIR;
|
|
this.#markChildrenENOENT();
|
|
}
|
|
#readdirFail(code = "") {
|
|
if (code === "ENOTDIR" || code === "EPERM") {
|
|
this.#markENOTDIR();
|
|
} else if (code === "ENOENT") {
|
|
this.#markENOENT();
|
|
} else {
|
|
this.children().provisional = 0;
|
|
}
|
|
}
|
|
#lstatFail(code = "") {
|
|
if (code === "ENOTDIR") {
|
|
const p = this.parent;
|
|
p.#markENOTDIR();
|
|
} else if (code === "ENOENT") {
|
|
this.#markENOENT();
|
|
}
|
|
}
|
|
#readlinkFail(code = "") {
|
|
let ter = this.#type;
|
|
ter |= ENOREADLINK;
|
|
if (code === "ENOENT")
|
|
ter |= ENOENT;
|
|
if (code === "EINVAL" || code === "UNKNOWN") {
|
|
ter &= IFMT_UNKNOWN;
|
|
}
|
|
this.#type = ter;
|
|
if (code === "ENOTDIR" && this.parent) {
|
|
this.parent.#markENOTDIR();
|
|
}
|
|
}
|
|
#readdirAddChild(e, c) {
|
|
return this.#readdirMaybePromoteChild(e, c) || this.#readdirAddNewChild(e, c);
|
|
}
|
|
#readdirAddNewChild(e, c) {
|
|
const type = entToType(e);
|
|
const child = this.newChild(e.name, type, { parent: this });
|
|
const ifmt = child.#type & IFMT;
|
|
if (ifmt !== IFDIR && ifmt !== IFLNK && ifmt !== UNKNOWN) {
|
|
child.#type |= ENOTDIR;
|
|
}
|
|
c.unshift(child);
|
|
c.provisional++;
|
|
return child;
|
|
}
|
|
#readdirMaybePromoteChild(e, c) {
|
|
for (let p = c.provisional; p < c.length; p++) {
|
|
const pchild = c[p];
|
|
const name = this.nocase ? normalizeNocase(e.name) : normalize(e.name);
|
|
if (name !== pchild.#matchName) {
|
|
continue;
|
|
}
|
|
return this.#readdirPromoteChild(e, pchild, p, c);
|
|
}
|
|
}
|
|
#readdirPromoteChild(e, p, index, c) {
|
|
const v = p.name;
|
|
p.#type = p.#type & IFMT_UNKNOWN | entToType(e);
|
|
if (v !== e.name)
|
|
p.name = e.name;
|
|
if (index !== c.provisional) {
|
|
if (index === c.length - 1)
|
|
c.pop();
|
|
else
|
|
c.splice(index, 1);
|
|
c.unshift(p);
|
|
}
|
|
c.provisional++;
|
|
return p;
|
|
}
|
|
/**
|
|
* Call lstat() on this Path, and update all known information that can be
|
|
* determined.
|
|
*
|
|
* Note that unlike `fs.lstat()`, the returned value does not contain some
|
|
* information, such as `mode`, `dev`, `nlink`, and `ino`. If that
|
|
* information is required, you will need to call `fs.lstat` yourself.
|
|
*
|
|
* If the Path refers to a nonexistent file, or if the lstat call fails for
|
|
* any reason, `undefined` is returned. Otherwise the updated Path object is
|
|
* returned.
|
|
*
|
|
* Results are cached, and thus may be out of date if the filesystem is
|
|
* mutated.
|
|
*/
|
|
async lstat() {
|
|
if ((this.#type & ENOENT) === 0) {
|
|
try {
|
|
this.#applyStat(await this.#fs.promises.lstat(this.fullpath()));
|
|
return this;
|
|
} catch (er) {
|
|
this.#lstatFail(er.code);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* synchronous {@link PathBase.lstat}
|
|
*/
|
|
lstatSync() {
|
|
if ((this.#type & ENOENT) === 0) {
|
|
try {
|
|
this.#applyStat(this.#fs.lstatSync(this.fullpath()));
|
|
return this;
|
|
} catch (er) {
|
|
this.#lstatFail(er.code);
|
|
}
|
|
}
|
|
}
|
|
#applyStat(st) {
|
|
const { atime, atimeMs, birthtime, birthtimeMs, blksize, blocks, ctime, ctimeMs, dev, gid, ino, mode, mtime, mtimeMs, nlink, rdev, size, uid } = st;
|
|
this.#atime = atime;
|
|
this.#atimeMs = atimeMs;
|
|
this.#birthtime = birthtime;
|
|
this.#birthtimeMs = birthtimeMs;
|
|
this.#blksize = blksize;
|
|
this.#blocks = blocks;
|
|
this.#ctime = ctime;
|
|
this.#ctimeMs = ctimeMs;
|
|
this.#dev = dev;
|
|
this.#gid = gid;
|
|
this.#ino = ino;
|
|
this.#mode = mode;
|
|
this.#mtime = mtime;
|
|
this.#mtimeMs = mtimeMs;
|
|
this.#nlink = nlink;
|
|
this.#rdev = rdev;
|
|
this.#size = size;
|
|
this.#uid = uid;
|
|
const ifmt = entToType(st);
|
|
this.#type = this.#type & IFMT_UNKNOWN | ifmt | LSTAT_CALLED;
|
|
if (ifmt !== UNKNOWN && ifmt !== IFDIR && ifmt !== IFLNK) {
|
|
this.#type |= ENOTDIR;
|
|
}
|
|
}
|
|
#onReaddirCB = [];
|
|
#readdirCBInFlight = false;
|
|
#callOnReaddirCB(children) {
|
|
this.#readdirCBInFlight = false;
|
|
const cbs = this.#onReaddirCB.slice();
|
|
this.#onReaddirCB.length = 0;
|
|
cbs.forEach((cb) => cb(null, children));
|
|
}
|
|
/**
|
|
* Standard node-style callback interface to get list of directory entries.
|
|
*
|
|
* If the Path cannot or does not contain any children, then an empty array
|
|
* is returned.
|
|
*
|
|
* Results are cached, and thus may be out of date if the filesystem is
|
|
* mutated.
|
|
*
|
|
* @param cb The callback called with (er, entries). Note that the `er`
|
|
* param is somewhat extraneous, as all readdir() errors are handled and
|
|
* simply result in an empty set of entries being returned.
|
|
* @param allowZalgo Boolean indicating that immediately known results should
|
|
* *not* be deferred with `queueMicrotask`. Defaults to `false`. Release
|
|
* zalgo at your peril, the dark pony lord is devious and unforgiving.
|
|
*/
|
|
readdirCB(cb, allowZalgo = false) {
|
|
if (!this.canReaddir()) {
|
|
if (allowZalgo)
|
|
cb(null, []);
|
|
else
|
|
queueMicrotask(() => cb(null, []));
|
|
return;
|
|
}
|
|
const children = this.children();
|
|
if (this.calledReaddir()) {
|
|
const c = children.slice(0, children.provisional);
|
|
if (allowZalgo)
|
|
cb(null, c);
|
|
else
|
|
queueMicrotask(() => cb(null, c));
|
|
return;
|
|
}
|
|
this.#onReaddirCB.push(cb);
|
|
if (this.#readdirCBInFlight) {
|
|
return;
|
|
}
|
|
this.#readdirCBInFlight = true;
|
|
const fullpath = this.fullpath();
|
|
this.#fs.readdir(fullpath, { withFileTypes: true }, (er, entries) => {
|
|
if (er) {
|
|
this.#readdirFail(er.code);
|
|
children.provisional = 0;
|
|
} else {
|
|
for (const e of entries) {
|
|
this.#readdirAddChild(e, children);
|
|
}
|
|
this.#readdirSuccess(children);
|
|
}
|
|
this.#callOnReaddirCB(children.slice(0, children.provisional));
|
|
return;
|
|
});
|
|
}
|
|
#asyncReaddirInFlight;
|
|
/**
|
|
* Return an array of known child entries.
|
|
*
|
|
* If the Path cannot or does not contain any children, then an empty array
|
|
* is returned.
|
|
*
|
|
* Results are cached, and thus may be out of date if the filesystem is
|
|
* mutated.
|
|
*/
|
|
async readdir() {
|
|
if (!this.canReaddir()) {
|
|
return [];
|
|
}
|
|
const children = this.children();
|
|
if (this.calledReaddir()) {
|
|
return children.slice(0, children.provisional);
|
|
}
|
|
const fullpath = this.fullpath();
|
|
if (this.#asyncReaddirInFlight) {
|
|
await this.#asyncReaddirInFlight;
|
|
} else {
|
|
let resolve6 = () => {
|
|
};
|
|
this.#asyncReaddirInFlight = new Promise((res) => resolve6 = res);
|
|
try {
|
|
for (const e of await this.#fs.promises.readdir(fullpath, {
|
|
withFileTypes: true
|
|
})) {
|
|
this.#readdirAddChild(e, children);
|
|
}
|
|
this.#readdirSuccess(children);
|
|
} catch (er) {
|
|
this.#readdirFail(er.code);
|
|
children.provisional = 0;
|
|
}
|
|
this.#asyncReaddirInFlight = void 0;
|
|
resolve6();
|
|
}
|
|
return children.slice(0, children.provisional);
|
|
}
|
|
/**
|
|
* synchronous {@link PathBase.readdir}
|
|
*/
|
|
readdirSync() {
|
|
if (!this.canReaddir()) {
|
|
return [];
|
|
}
|
|
const children = this.children();
|
|
if (this.calledReaddir()) {
|
|
return children.slice(0, children.provisional);
|
|
}
|
|
const fullpath = this.fullpath();
|
|
try {
|
|
for (const e of this.#fs.readdirSync(fullpath, {
|
|
withFileTypes: true
|
|
})) {
|
|
this.#readdirAddChild(e, children);
|
|
}
|
|
this.#readdirSuccess(children);
|
|
} catch (er) {
|
|
this.#readdirFail(er.code);
|
|
children.provisional = 0;
|
|
}
|
|
return children.slice(0, children.provisional);
|
|
}
|
|
canReaddir() {
|
|
if (this.#type & ENOCHILD)
|
|
return false;
|
|
const ifmt = IFMT & this.#type;
|
|
if (!(ifmt === UNKNOWN || ifmt === IFDIR || ifmt === IFLNK)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
shouldWalk(dirs, walkFilter) {
|
|
return (this.#type & IFDIR) === IFDIR && !(this.#type & ENOCHILD) && !dirs.has(this) && (!walkFilter || walkFilter(this));
|
|
}
|
|
/**
|
|
* Return the Path object corresponding to path as resolved
|
|
* by realpath(3).
|
|
*
|
|
* If the realpath call fails for any reason, `undefined` is returned.
|
|
*
|
|
* Result is cached, and thus may be outdated if the filesystem is mutated.
|
|
* On success, returns a Path object.
|
|
*/
|
|
async realpath() {
|
|
if (this.#realpath)
|
|
return this.#realpath;
|
|
if ((ENOREALPATH | ENOREADLINK | ENOENT) & this.#type)
|
|
return void 0;
|
|
try {
|
|
const rp = await this.#fs.promises.realpath(this.fullpath());
|
|
return this.#realpath = this.resolve(rp);
|
|
} catch (_) {
|
|
this.#markENOREALPATH();
|
|
}
|
|
}
|
|
/**
|
|
* Synchronous {@link realpath}
|
|
*/
|
|
realpathSync() {
|
|
if (this.#realpath)
|
|
return this.#realpath;
|
|
if ((ENOREALPATH | ENOREADLINK | ENOENT) & this.#type)
|
|
return void 0;
|
|
try {
|
|
const rp = this.#fs.realpathSync(this.fullpath());
|
|
return this.#realpath = this.resolve(rp);
|
|
} catch (_) {
|
|
this.#markENOREALPATH();
|
|
}
|
|
}
|
|
/**
|
|
* Internal method to mark this Path object as the scurry cwd,
|
|
* called by {@link PathScurry#chdir}
|
|
*
|
|
* @internal
|
|
*/
|
|
[setAsCwd](oldCwd) {
|
|
if (oldCwd === this)
|
|
return;
|
|
oldCwd.isCWD = false;
|
|
this.isCWD = true;
|
|
const changed = /* @__PURE__ */ new Set([]);
|
|
let rp = [];
|
|
let p = this;
|
|
while (p && p.parent) {
|
|
changed.add(p);
|
|
p.#relative = rp.join(this.sep);
|
|
p.#relativePosix = rp.join("/");
|
|
p = p.parent;
|
|
rp.push("..");
|
|
}
|
|
p = oldCwd;
|
|
while (p && p.parent && !changed.has(p)) {
|
|
p.#relative = void 0;
|
|
p.#relativePosix = void 0;
|
|
p = p.parent;
|
|
}
|
|
}
|
|
};
|
|
var PathWin32 = class _PathWin32 extends PathBase {
|
|
/**
|
|
* Separator for generating path strings.
|
|
*/
|
|
sep = "\\";
|
|
/**
|
|
* Separator for parsing path strings.
|
|
*/
|
|
splitSep = eitherSep;
|
|
/**
|
|
* Do not create new Path objects directly. They should always be accessed
|
|
* via the PathScurry class or other methods on the Path class.
|
|
*
|
|
* @internal
|
|
*/
|
|
constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) {
|
|
super(name, type, root, roots, nocase, children, opts);
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
newChild(name, type = UNKNOWN, opts = {}) {
|
|
return new _PathWin32(name, type, this.root, this.roots, this.nocase, this.childrenCache(), opts);
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
getRootString(path2) {
|
|
return import_node_path.win32.parse(path2).root;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
getRoot(rootPath) {
|
|
rootPath = uncToDrive(rootPath.toUpperCase());
|
|
if (rootPath === this.root.name) {
|
|
return this.root;
|
|
}
|
|
for (const [compare, root] of Object.entries(this.roots)) {
|
|
if (this.sameRoot(rootPath, compare)) {
|
|
return this.roots[rootPath] = root;
|
|
}
|
|
}
|
|
return this.roots[rootPath] = new PathScurryWin32(rootPath, this).root;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
sameRoot(rootPath, compare = this.root.name) {
|
|
rootPath = rootPath.toUpperCase().replace(/\//g, "\\").replace(uncDriveRegexp, "$1\\");
|
|
return rootPath === compare;
|
|
}
|
|
};
|
|
var PathPosix = class _PathPosix extends PathBase {
|
|
/**
|
|
* separator for parsing path strings
|
|
*/
|
|
splitSep = "/";
|
|
/**
|
|
* separator for generating path strings
|
|
*/
|
|
sep = "/";
|
|
/**
|
|
* Do not create new Path objects directly. They should always be accessed
|
|
* via the PathScurry class or other methods on the Path class.
|
|
*
|
|
* @internal
|
|
*/
|
|
constructor(name, type = UNKNOWN, root, roots, nocase, children, opts) {
|
|
super(name, type, root, roots, nocase, children, opts);
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
getRootString(path2) {
|
|
return path2.startsWith("/") ? "/" : "";
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
getRoot(_rootPath) {
|
|
return this.root;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
newChild(name, type = UNKNOWN, opts = {}) {
|
|
return new _PathPosix(name, type, this.root, this.roots, this.nocase, this.childrenCache(), opts);
|
|
}
|
|
};
|
|
var PathScurryBase = class {
|
|
/**
|
|
* The root Path entry for the current working directory of this Scurry
|
|
*/
|
|
root;
|
|
/**
|
|
* The string path for the root of this Scurry's current working directory
|
|
*/
|
|
rootPath;
|
|
/**
|
|
* A collection of all roots encountered, referenced by rootPath
|
|
*/
|
|
roots;
|
|
/**
|
|
* The Path entry corresponding to this PathScurry's current working directory.
|
|
*/
|
|
cwd;
|
|
#resolveCache;
|
|
#resolvePosixCache;
|
|
#children;
|
|
/**
|
|
* Perform path comparisons case-insensitively.
|
|
*
|
|
* Defaults true on Darwin and Windows systems, false elsewhere.
|
|
*/
|
|
nocase;
|
|
#fs;
|
|
/**
|
|
* This class should not be instantiated directly.
|
|
*
|
|
* Use PathScurryWin32, PathScurryDarwin, PathScurryPosix, or PathScurry
|
|
*
|
|
* @internal
|
|
*/
|
|
constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs: fs2 = defaultFS } = {}) {
|
|
this.#fs = fsFromOption(fs2);
|
|
if (cwd instanceof URL || cwd.startsWith("file://")) {
|
|
cwd = (0, import_node_url2.fileURLToPath)(cwd);
|
|
}
|
|
const cwdPath = pathImpl.resolve(cwd);
|
|
this.roots = /* @__PURE__ */ Object.create(null);
|
|
this.rootPath = this.parseRootPath(cwdPath);
|
|
this.#resolveCache = new ResolveCache();
|
|
this.#resolvePosixCache = new ResolveCache();
|
|
this.#children = new ChildrenCache(childrenCacheSize);
|
|
const split = cwdPath.substring(this.rootPath.length).split(sep2);
|
|
if (split.length === 1 && !split[0]) {
|
|
split.pop();
|
|
}
|
|
if (nocase === void 0) {
|
|
throw new TypeError("must provide nocase setting to PathScurryBase ctor");
|
|
}
|
|
this.nocase = nocase;
|
|
this.root = this.newRoot(this.#fs);
|
|
this.roots[this.rootPath] = this.root;
|
|
let prev = this.root;
|
|
let len = split.length - 1;
|
|
const joinSep = pathImpl.sep;
|
|
let abs = this.rootPath;
|
|
let sawFirst = false;
|
|
for (const part of split) {
|
|
const l = len--;
|
|
prev = prev.child(part, {
|
|
relative: new Array(l).fill("..").join(joinSep),
|
|
relativePosix: new Array(l).fill("..").join("/"),
|
|
fullpath: abs += (sawFirst ? "" : joinSep) + part
|
|
});
|
|
sawFirst = true;
|
|
}
|
|
this.cwd = prev;
|
|
}
|
|
/**
|
|
* Get the depth of a provided path, string, or the cwd
|
|
*/
|
|
depth(path2 = this.cwd) {
|
|
if (typeof path2 === "string") {
|
|
path2 = this.cwd.resolve(path2);
|
|
}
|
|
return path2.depth();
|
|
}
|
|
/**
|
|
* Return the cache of child entries. Exposed so subclasses can create
|
|
* child Path objects in a platform-specific way.
|
|
*
|
|
* @internal
|
|
*/
|
|
childrenCache() {
|
|
return this.#children;
|
|
}
|
|
/**
|
|
* Resolve one or more path strings to a resolved string
|
|
*
|
|
* Same interface as require('path').resolve.
|
|
*
|
|
* Much faster than path.resolve() when called multiple times for the same
|
|
* path, because the resolved Path objects are cached. Much slower
|
|
* otherwise.
|
|
*/
|
|
resolve(...paths) {
|
|
let r = "";
|
|
for (let i = paths.length - 1; i >= 0; i--) {
|
|
const p = paths[i];
|
|
if (!p || p === ".")
|
|
continue;
|
|
r = r ? `${p}/${r}` : p;
|
|
if (this.isAbsolute(p)) {
|
|
break;
|
|
}
|
|
}
|
|
const cached = this.#resolveCache.get(r);
|
|
if (cached !== void 0) {
|
|
return cached;
|
|
}
|
|
const result = this.cwd.resolve(r).fullpath();
|
|
this.#resolveCache.set(r, result);
|
|
return result;
|
|
}
|
|
/**
|
|
* Resolve one or more path strings to a resolved string, returning
|
|
* the posix path. Identical to .resolve() on posix systems, but on
|
|
* windows will return a forward-slash separated UNC path.
|
|
*
|
|
* Same interface as require('path').resolve.
|
|
*
|
|
* Much faster than path.resolve() when called multiple times for the same
|
|
* path, because the resolved Path objects are cached. Much slower
|
|
* otherwise.
|
|
*/
|
|
resolvePosix(...paths) {
|
|
let r = "";
|
|
for (let i = paths.length - 1; i >= 0; i--) {
|
|
const p = paths[i];
|
|
if (!p || p === ".")
|
|
continue;
|
|
r = r ? `${p}/${r}` : p;
|
|
if (this.isAbsolute(p)) {
|
|
break;
|
|
}
|
|
}
|
|
const cached = this.#resolvePosixCache.get(r);
|
|
if (cached !== void 0) {
|
|
return cached;
|
|
}
|
|
const result = this.cwd.resolve(r).fullpathPosix();
|
|
this.#resolvePosixCache.set(r, result);
|
|
return result;
|
|
}
|
|
/**
|
|
* find the relative path from the cwd to the supplied path string or entry
|
|
*/
|
|
relative(entry = this.cwd) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
}
|
|
return entry.relative();
|
|
}
|
|
/**
|
|
* find the relative path from the cwd to the supplied path string or
|
|
* entry, using / as the path delimiter, even on Windows.
|
|
*/
|
|
relativePosix(entry = this.cwd) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
}
|
|
return entry.relativePosix();
|
|
}
|
|
/**
|
|
* Return the basename for the provided string or Path object
|
|
*/
|
|
basename(entry = this.cwd) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
}
|
|
return entry.name;
|
|
}
|
|
/**
|
|
* Return the dirname for the provided string or Path object
|
|
*/
|
|
dirname(entry = this.cwd) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
}
|
|
return (entry.parent || entry).fullpath();
|
|
}
|
|
async readdir(entry = this.cwd, opts = {
|
|
withFileTypes: true
|
|
}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
opts = entry;
|
|
entry = this.cwd;
|
|
}
|
|
const { withFileTypes } = opts;
|
|
if (!entry.canReaddir()) {
|
|
return [];
|
|
} else {
|
|
const p = await entry.readdir();
|
|
return withFileTypes ? p : p.map((e) => e.name);
|
|
}
|
|
}
|
|
readdirSync(entry = this.cwd, opts = {
|
|
withFileTypes: true
|
|
}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
opts = entry;
|
|
entry = this.cwd;
|
|
}
|
|
const { withFileTypes = true } = opts;
|
|
if (!entry.canReaddir()) {
|
|
return [];
|
|
} else if (withFileTypes) {
|
|
return entry.readdirSync();
|
|
} else {
|
|
return entry.readdirSync().map((e) => e.name);
|
|
}
|
|
}
|
|
/**
|
|
* Call lstat() on the string or Path object, and update all known
|
|
* information that can be determined.
|
|
*
|
|
* Note that unlike `fs.lstat()`, the returned value does not contain some
|
|
* information, such as `mode`, `dev`, `nlink`, and `ino`. If that
|
|
* information is required, you will need to call `fs.lstat` yourself.
|
|
*
|
|
* If the Path refers to a nonexistent file, or if the lstat call fails for
|
|
* any reason, `undefined` is returned. Otherwise the updated Path object is
|
|
* returned.
|
|
*
|
|
* Results are cached, and thus may be out of date if the filesystem is
|
|
* mutated.
|
|
*/
|
|
async lstat(entry = this.cwd) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
}
|
|
return entry.lstat();
|
|
}
|
|
/**
|
|
* synchronous {@link PathScurryBase.lstat}
|
|
*/
|
|
lstatSync(entry = this.cwd) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
}
|
|
return entry.lstatSync();
|
|
}
|
|
async readlink(entry = this.cwd, { withFileTypes } = {
|
|
withFileTypes: false
|
|
}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
withFileTypes = entry.withFileTypes;
|
|
entry = this.cwd;
|
|
}
|
|
const e = await entry.readlink();
|
|
return withFileTypes ? e : e?.fullpath();
|
|
}
|
|
readlinkSync(entry = this.cwd, { withFileTypes } = {
|
|
withFileTypes: false
|
|
}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
withFileTypes = entry.withFileTypes;
|
|
entry = this.cwd;
|
|
}
|
|
const e = entry.readlinkSync();
|
|
return withFileTypes ? e : e?.fullpath();
|
|
}
|
|
async realpath(entry = this.cwd, { withFileTypes } = {
|
|
withFileTypes: false
|
|
}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
withFileTypes = entry.withFileTypes;
|
|
entry = this.cwd;
|
|
}
|
|
const e = await entry.realpath();
|
|
return withFileTypes ? e : e?.fullpath();
|
|
}
|
|
realpathSync(entry = this.cwd, { withFileTypes } = {
|
|
withFileTypes: false
|
|
}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
withFileTypes = entry.withFileTypes;
|
|
entry = this.cwd;
|
|
}
|
|
const e = entry.realpathSync();
|
|
return withFileTypes ? e : e?.fullpath();
|
|
}
|
|
async walk(entry = this.cwd, opts = {}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
opts = entry;
|
|
entry = this.cwd;
|
|
}
|
|
const { withFileTypes = true, follow = false, filter: filter2, walkFilter } = opts;
|
|
const results = [];
|
|
if (!filter2 || filter2(entry)) {
|
|
results.push(withFileTypes ? entry : entry.fullpath());
|
|
}
|
|
const dirs = /* @__PURE__ */ new Set();
|
|
const walk = (dir, cb) => {
|
|
dirs.add(dir);
|
|
dir.readdirCB((er, entries) => {
|
|
if (er) {
|
|
return cb(er);
|
|
}
|
|
let len = entries.length;
|
|
if (!len)
|
|
return cb();
|
|
const next = () => {
|
|
if (--len === 0) {
|
|
cb();
|
|
}
|
|
};
|
|
for (const e of entries) {
|
|
if (!filter2 || filter2(e)) {
|
|
results.push(withFileTypes ? e : e.fullpath());
|
|
}
|
|
if (follow && e.isSymbolicLink()) {
|
|
e.realpath().then((r) => r?.isUnknown() ? r.lstat() : r).then((r) => r?.shouldWalk(dirs, walkFilter) ? walk(r, next) : next());
|
|
} else {
|
|
if (e.shouldWalk(dirs, walkFilter)) {
|
|
walk(e, next);
|
|
} else {
|
|
next();
|
|
}
|
|
}
|
|
}
|
|
}, true);
|
|
};
|
|
const start = entry;
|
|
return new Promise((res, rej) => {
|
|
walk(start, (er) => {
|
|
if (er)
|
|
return rej(er);
|
|
res(results);
|
|
});
|
|
});
|
|
}
|
|
walkSync(entry = this.cwd, opts = {}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
opts = entry;
|
|
entry = this.cwd;
|
|
}
|
|
const { withFileTypes = true, follow = false, filter: filter2, walkFilter } = opts;
|
|
const results = [];
|
|
if (!filter2 || filter2(entry)) {
|
|
results.push(withFileTypes ? entry : entry.fullpath());
|
|
}
|
|
const dirs = /* @__PURE__ */ new Set([entry]);
|
|
for (const dir of dirs) {
|
|
const entries = dir.readdirSync();
|
|
for (const e of entries) {
|
|
if (!filter2 || filter2(e)) {
|
|
results.push(withFileTypes ? e : e.fullpath());
|
|
}
|
|
let r = e;
|
|
if (e.isSymbolicLink()) {
|
|
if (!(follow && (r = e.realpathSync())))
|
|
continue;
|
|
if (r.isUnknown())
|
|
r.lstatSync();
|
|
}
|
|
if (r.shouldWalk(dirs, walkFilter)) {
|
|
dirs.add(r);
|
|
}
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
/**
|
|
* Support for `for await`
|
|
*
|
|
* Alias for {@link PathScurryBase.iterate}
|
|
*
|
|
* Note: As of Node 19, this is very slow, compared to other methods of
|
|
* walking. Consider using {@link PathScurryBase.stream} if memory overhead
|
|
* and backpressure are concerns, or {@link PathScurryBase.walk} if not.
|
|
*/
|
|
[Symbol.asyncIterator]() {
|
|
return this.iterate();
|
|
}
|
|
iterate(entry = this.cwd, options = {}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
options = entry;
|
|
entry = this.cwd;
|
|
}
|
|
return this.stream(entry, options)[Symbol.asyncIterator]();
|
|
}
|
|
/**
|
|
* Iterating over a PathScurry performs a synchronous walk.
|
|
*
|
|
* Alias for {@link PathScurryBase.iterateSync}
|
|
*/
|
|
[Symbol.iterator]() {
|
|
return this.iterateSync();
|
|
}
|
|
*iterateSync(entry = this.cwd, opts = {}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
opts = entry;
|
|
entry = this.cwd;
|
|
}
|
|
const { withFileTypes = true, follow = false, filter: filter2, walkFilter } = opts;
|
|
if (!filter2 || filter2(entry)) {
|
|
yield withFileTypes ? entry : entry.fullpath();
|
|
}
|
|
const dirs = /* @__PURE__ */ new Set([entry]);
|
|
for (const dir of dirs) {
|
|
const entries = dir.readdirSync();
|
|
for (const e of entries) {
|
|
if (!filter2 || filter2(e)) {
|
|
yield withFileTypes ? e : e.fullpath();
|
|
}
|
|
let r = e;
|
|
if (e.isSymbolicLink()) {
|
|
if (!(follow && (r = e.realpathSync())))
|
|
continue;
|
|
if (r.isUnknown())
|
|
r.lstatSync();
|
|
}
|
|
if (r.shouldWalk(dirs, walkFilter)) {
|
|
dirs.add(r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
stream(entry = this.cwd, opts = {}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
opts = entry;
|
|
entry = this.cwd;
|
|
}
|
|
const { withFileTypes = true, follow = false, filter: filter2, walkFilter } = opts;
|
|
const results = new Minipass({ objectMode: true });
|
|
if (!filter2 || filter2(entry)) {
|
|
results.write(withFileTypes ? entry : entry.fullpath());
|
|
}
|
|
const dirs = /* @__PURE__ */ new Set();
|
|
const queue = [entry];
|
|
let processing = 0;
|
|
const process2 = () => {
|
|
let paused = false;
|
|
while (!paused) {
|
|
const dir = queue.shift();
|
|
if (!dir) {
|
|
if (processing === 0)
|
|
results.end();
|
|
return;
|
|
}
|
|
processing++;
|
|
dirs.add(dir);
|
|
const onReaddir = (er, entries, didRealpaths = false) => {
|
|
if (er)
|
|
return results.emit("error", er);
|
|
if (follow && !didRealpaths) {
|
|
const promises2 = [];
|
|
for (const e of entries) {
|
|
if (e.isSymbolicLink()) {
|
|
promises2.push(e.realpath().then((r) => r?.isUnknown() ? r.lstat() : r));
|
|
}
|
|
}
|
|
if (promises2.length) {
|
|
Promise.all(promises2).then(() => onReaddir(null, entries, true));
|
|
return;
|
|
}
|
|
}
|
|
for (const e of entries) {
|
|
if (e && (!filter2 || filter2(e))) {
|
|
if (!results.write(withFileTypes ? e : e.fullpath())) {
|
|
paused = true;
|
|
}
|
|
}
|
|
}
|
|
processing--;
|
|
for (const e of entries) {
|
|
const r = e.realpathCached() || e;
|
|
if (r.shouldWalk(dirs, walkFilter)) {
|
|
queue.push(r);
|
|
}
|
|
}
|
|
if (paused && !results.flowing) {
|
|
results.once("drain", process2);
|
|
} else if (!sync2) {
|
|
process2();
|
|
}
|
|
};
|
|
let sync2 = true;
|
|
dir.readdirCB(onReaddir, true);
|
|
sync2 = false;
|
|
}
|
|
};
|
|
process2();
|
|
return results;
|
|
}
|
|
streamSync(entry = this.cwd, opts = {}) {
|
|
if (typeof entry === "string") {
|
|
entry = this.cwd.resolve(entry);
|
|
} else if (!(entry instanceof PathBase)) {
|
|
opts = entry;
|
|
entry = this.cwd;
|
|
}
|
|
const { withFileTypes = true, follow = false, filter: filter2, walkFilter } = opts;
|
|
const results = new Minipass({ objectMode: true });
|
|
const dirs = /* @__PURE__ */ new Set();
|
|
if (!filter2 || filter2(entry)) {
|
|
results.write(withFileTypes ? entry : entry.fullpath());
|
|
}
|
|
const queue = [entry];
|
|
let processing = 0;
|
|
const process2 = () => {
|
|
let paused = false;
|
|
while (!paused) {
|
|
const dir = queue.shift();
|
|
if (!dir) {
|
|
if (processing === 0)
|
|
results.end();
|
|
return;
|
|
}
|
|
processing++;
|
|
dirs.add(dir);
|
|
const entries = dir.readdirSync();
|
|
for (const e of entries) {
|
|
if (!filter2 || filter2(e)) {
|
|
if (!results.write(withFileTypes ? e : e.fullpath())) {
|
|
paused = true;
|
|
}
|
|
}
|
|
}
|
|
processing--;
|
|
for (const e of entries) {
|
|
let r = e;
|
|
if (e.isSymbolicLink()) {
|
|
if (!(follow && (r = e.realpathSync())))
|
|
continue;
|
|
if (r.isUnknown())
|
|
r.lstatSync();
|
|
}
|
|
if (r.shouldWalk(dirs, walkFilter)) {
|
|
queue.push(r);
|
|
}
|
|
}
|
|
}
|
|
if (paused && !results.flowing)
|
|
results.once("drain", process2);
|
|
};
|
|
process2();
|
|
return results;
|
|
}
|
|
chdir(path2 = this.cwd) {
|
|
const oldCwd = this.cwd;
|
|
this.cwd = typeof path2 === "string" ? this.cwd.resolve(path2) : path2;
|
|
this.cwd[setAsCwd](oldCwd);
|
|
}
|
|
};
|
|
var PathScurryWin32 = class extends PathScurryBase {
|
|
/**
|
|
* separator for generating path strings
|
|
*/
|
|
sep = "\\";
|
|
constructor(cwd = process.cwd(), opts = {}) {
|
|
const { nocase = true } = opts;
|
|
super(cwd, import_node_path.win32, "\\", { ...opts, nocase });
|
|
this.nocase = nocase;
|
|
for (let p = this.cwd; p; p = p.parent) {
|
|
p.nocase = this.nocase;
|
|
}
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
parseRootPath(dir) {
|
|
return import_node_path.win32.parse(dir).root.toUpperCase();
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
newRoot(fs2) {
|
|
return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs2 });
|
|
}
|
|
/**
|
|
* Return true if the provided path string is an absolute path
|
|
*/
|
|
isAbsolute(p) {
|
|
return p.startsWith("/") || p.startsWith("\\") || /^[a-z]:(\/|\\)/i.test(p);
|
|
}
|
|
};
|
|
var PathScurryPosix = class extends PathScurryBase {
|
|
/**
|
|
* separator for generating path strings
|
|
*/
|
|
sep = "/";
|
|
constructor(cwd = process.cwd(), opts = {}) {
|
|
const { nocase = false } = opts;
|
|
super(cwd, import_node_path.posix, "/", { ...opts, nocase });
|
|
this.nocase = nocase;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
parseRootPath(_dir) {
|
|
return "/";
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
newRoot(fs2) {
|
|
return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs2 });
|
|
}
|
|
/**
|
|
* Return true if the provided path string is an absolute path
|
|
*/
|
|
isAbsolute(p) {
|
|
return p.startsWith("/");
|
|
}
|
|
};
|
|
var PathScurryDarwin = class extends PathScurryPosix {
|
|
constructor(cwd = process.cwd(), opts = {}) {
|
|
const { nocase = true } = opts;
|
|
super(cwd, { ...opts, nocase });
|
|
}
|
|
};
|
|
var Path = process.platform === "win32" ? PathWin32 : PathPosix;
|
|
var PathScurry = process.platform === "win32" ? PathScurryWin32 : process.platform === "darwin" ? PathScurryDarwin : PathScurryPosix;
|
|
var isPatternList = (pl) => pl.length >= 1;
|
|
var isGlobList = (gl) => gl.length >= 1;
|
|
var Pattern = class _Pattern {
|
|
#patternList;
|
|
#globList;
|
|
#index;
|
|
length;
|
|
#platform;
|
|
#rest;
|
|
#globString;
|
|
#isDrive;
|
|
#isUNC;
|
|
#isAbsolute;
|
|
#followGlobstar = true;
|
|
constructor(patternList, globList, index, platform) {
|
|
if (!isPatternList(patternList)) {
|
|
throw new TypeError("empty pattern list");
|
|
}
|
|
if (!isGlobList(globList)) {
|
|
throw new TypeError("empty glob list");
|
|
}
|
|
if (globList.length !== patternList.length) {
|
|
throw new TypeError("mismatched pattern list and glob list lengths");
|
|
}
|
|
this.length = patternList.length;
|
|
if (index < 0 || index >= this.length) {
|
|
throw new TypeError("index out of range");
|
|
}
|
|
this.#patternList = patternList;
|
|
this.#globList = globList;
|
|
this.#index = index;
|
|
this.#platform = platform;
|
|
if (this.#index === 0) {
|
|
if (this.isUNC()) {
|
|
const [p0, p1, p2, p3, ...prest] = this.#patternList;
|
|
const [g0, g1, g2, g3, ...grest] = this.#globList;
|
|
if (prest[0] === "") {
|
|
prest.shift();
|
|
grest.shift();
|
|
}
|
|
const p = [p0, p1, p2, p3, ""].join("/");
|
|
const g = [g0, g1, g2, g3, ""].join("/");
|
|
this.#patternList = [p, ...prest];
|
|
this.#globList = [g, ...grest];
|
|
this.length = this.#patternList.length;
|
|
} else if (this.isDrive() || this.isAbsolute()) {
|
|
const [p1, ...prest] = this.#patternList;
|
|
const [g1, ...grest] = this.#globList;
|
|
if (prest[0] === "") {
|
|
prest.shift();
|
|
grest.shift();
|
|
}
|
|
const p = p1 + "/";
|
|
const g = g1 + "/";
|
|
this.#patternList = [p, ...prest];
|
|
this.#globList = [g, ...grest];
|
|
this.length = this.#patternList.length;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* The first entry in the parsed list of patterns
|
|
*/
|
|
pattern() {
|
|
return this.#patternList[this.#index];
|
|
}
|
|
/**
|
|
* true of if pattern() returns a string
|
|
*/
|
|
isString() {
|
|
return typeof this.#patternList[this.#index] === "string";
|
|
}
|
|
/**
|
|
* true of if pattern() returns GLOBSTAR
|
|
*/
|
|
isGlobstar() {
|
|
return this.#patternList[this.#index] === GLOBSTAR;
|
|
}
|
|
/**
|
|
* true if pattern() returns a regexp
|
|
*/
|
|
isRegExp() {
|
|
return this.#patternList[this.#index] instanceof RegExp;
|
|
}
|
|
/**
|
|
* The /-joined set of glob parts that make up this pattern
|
|
*/
|
|
globString() {
|
|
return this.#globString = this.#globString || (this.#index === 0 ? this.isAbsolute() ? this.#globList[0] + this.#globList.slice(1).join("/") : this.#globList.join("/") : this.#globList.slice(this.#index).join("/"));
|
|
}
|
|
/**
|
|
* true if there are more pattern parts after this one
|
|
*/
|
|
hasMore() {
|
|
return this.length > this.#index + 1;
|
|
}
|
|
/**
|
|
* The rest of the pattern after this part, or null if this is the end
|
|
*/
|
|
rest() {
|
|
if (this.#rest !== void 0)
|
|
return this.#rest;
|
|
if (!this.hasMore())
|
|
return this.#rest = null;
|
|
this.#rest = new _Pattern(this.#patternList, this.#globList, this.#index + 1, this.#platform);
|
|
this.#rest.#isAbsolute = this.#isAbsolute;
|
|
this.#rest.#isUNC = this.#isUNC;
|
|
this.#rest.#isDrive = this.#isDrive;
|
|
return this.#rest;
|
|
}
|
|
/**
|
|
* true if the pattern represents a //unc/path/ on windows
|
|
*/
|
|
isUNC() {
|
|
const pl = this.#patternList;
|
|
return this.#isUNC !== void 0 ? this.#isUNC : this.#isUNC = this.#platform === "win32" && this.#index === 0 && pl[0] === "" && pl[1] === "" && typeof pl[2] === "string" && !!pl[2] && typeof pl[3] === "string" && !!pl[3];
|
|
}
|
|
// pattern like C:/...
|
|
// split = ['C:', ...]
|
|
// XXX: would be nice to handle patterns like `c:*` to test the cwd
|
|
// in c: for *, but I don't know of a way to even figure out what that
|
|
// cwd is without actually chdir'ing into it?
|
|
/**
|
|
* True if the pattern starts with a drive letter on Windows
|
|
*/
|
|
isDrive() {
|
|
const pl = this.#patternList;
|
|
return this.#isDrive !== void 0 ? this.#isDrive : this.#isDrive = this.#platform === "win32" && this.#index === 0 && this.length > 1 && typeof pl[0] === "string" && /^[a-z]:$/i.test(pl[0]);
|
|
}
|
|
// pattern = '/' or '/...' or '/x/...'
|
|
// split = ['', ''] or ['', ...] or ['', 'x', ...]
|
|
// Drive and UNC both considered absolute on windows
|
|
/**
|
|
* True if the pattern is rooted on an absolute path
|
|
*/
|
|
isAbsolute() {
|
|
const pl = this.#patternList;
|
|
return this.#isAbsolute !== void 0 ? this.#isAbsolute : this.#isAbsolute = pl[0] === "" && pl.length > 1 || this.isDrive() || this.isUNC();
|
|
}
|
|
/**
|
|
* consume the root of the pattern, and return it
|
|
*/
|
|
root() {
|
|
const p = this.#patternList[0];
|
|
return typeof p === "string" && this.isAbsolute() && this.#index === 0 ? p : "";
|
|
}
|
|
/**
|
|
* Check to see if the current globstar pattern is allowed to follow
|
|
* a symbolic link.
|
|
*/
|
|
checkFollowGlobstar() {
|
|
return !(this.#index === 0 || !this.isGlobstar() || !this.#followGlobstar);
|
|
}
|
|
/**
|
|
* Mark that the current globstar pattern is following a symbolic link
|
|
*/
|
|
markFollowGlobstar() {
|
|
if (this.#index === 0 || !this.isGlobstar() || !this.#followGlobstar)
|
|
return false;
|
|
this.#followGlobstar = false;
|
|
return true;
|
|
}
|
|
};
|
|
var defaultPlatform2 = typeof process === "object" && process && typeof process.platform === "string" ? process.platform : "linux";
|
|
var Ignore = class {
|
|
relative;
|
|
relativeChildren;
|
|
absolute;
|
|
absoluteChildren;
|
|
platform;
|
|
mmopts;
|
|
constructor(ignored, { nobrace, nocase, noext, noglobstar, platform = defaultPlatform2 }) {
|
|
this.relative = [];
|
|
this.absolute = [];
|
|
this.relativeChildren = [];
|
|
this.absoluteChildren = [];
|
|
this.platform = platform;
|
|
this.mmopts = {
|
|
dot: true,
|
|
nobrace,
|
|
nocase,
|
|
noext,
|
|
noglobstar,
|
|
optimizationLevel: 2,
|
|
platform,
|
|
nocomment: true,
|
|
nonegate: true
|
|
};
|
|
for (const ign of ignored)
|
|
this.add(ign);
|
|
}
|
|
add(ign) {
|
|
const mm = new Minimatch(ign, this.mmopts);
|
|
for (let i = 0; i < mm.set.length; i++) {
|
|
const parsed = mm.set[i];
|
|
const globParts = mm.globParts[i];
|
|
if (!parsed || !globParts) {
|
|
throw new Error("invalid pattern object");
|
|
}
|
|
while (parsed[0] === "." && globParts[0] === ".") {
|
|
parsed.shift();
|
|
globParts.shift();
|
|
}
|
|
const p = new Pattern(parsed, globParts, 0, this.platform);
|
|
const m = new Minimatch(p.globString(), this.mmopts);
|
|
const children = globParts[globParts.length - 1] === "**";
|
|
const absolute = p.isAbsolute();
|
|
if (absolute)
|
|
this.absolute.push(m);
|
|
else
|
|
this.relative.push(m);
|
|
if (children) {
|
|
if (absolute)
|
|
this.absoluteChildren.push(m);
|
|
else
|
|
this.relativeChildren.push(m);
|
|
}
|
|
}
|
|
}
|
|
ignored(p) {
|
|
const fullpath = p.fullpath();
|
|
const fullpaths = `${fullpath}/`;
|
|
const relative = p.relative() || ".";
|
|
const relatives = `${relative}/`;
|
|
for (const m of this.relative) {
|
|
if (m.match(relative) || m.match(relatives))
|
|
return true;
|
|
}
|
|
for (const m of this.absolute) {
|
|
if (m.match(fullpath) || m.match(fullpaths))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
childrenIgnored(p) {
|
|
const fullpath = p.fullpath() + "/";
|
|
const relative = (p.relative() || ".") + "/";
|
|
for (const m of this.relativeChildren) {
|
|
if (m.match(relative))
|
|
return true;
|
|
}
|
|
for (const m of this.absoluteChildren) {
|
|
if (m.match(fullpath))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
var HasWalkedCache = class _HasWalkedCache {
|
|
store;
|
|
constructor(store = /* @__PURE__ */ new Map()) {
|
|
this.store = store;
|
|
}
|
|
copy() {
|
|
return new _HasWalkedCache(new Map(this.store));
|
|
}
|
|
hasWalked(target, pattern) {
|
|
return this.store.get(target.fullpath())?.has(pattern.globString());
|
|
}
|
|
storeWalked(target, pattern) {
|
|
const fullpath = target.fullpath();
|
|
const cached = this.store.get(fullpath);
|
|
if (cached)
|
|
cached.add(pattern.globString());
|
|
else
|
|
this.store.set(fullpath, /* @__PURE__ */ new Set([pattern.globString()]));
|
|
}
|
|
};
|
|
var MatchRecord = class {
|
|
store = /* @__PURE__ */ new Map();
|
|
add(target, absolute, ifDir) {
|
|
const n = (absolute ? 2 : 0) | (ifDir ? 1 : 0);
|
|
const current = this.store.get(target);
|
|
this.store.set(target, current === void 0 ? n : n & current);
|
|
}
|
|
// match, absolute, ifdir
|
|
entries() {
|
|
return [...this.store.entries()].map(([path2, n]) => [
|
|
path2,
|
|
!!(n & 2),
|
|
!!(n & 1)
|
|
]);
|
|
}
|
|
};
|
|
var SubWalks = class {
|
|
store = /* @__PURE__ */ new Map();
|
|
add(target, pattern) {
|
|
if (!target.canReaddir()) {
|
|
return;
|
|
}
|
|
const subs = this.store.get(target);
|
|
if (subs) {
|
|
if (!subs.find((p) => p.globString() === pattern.globString())) {
|
|
subs.push(pattern);
|
|
}
|
|
} else
|
|
this.store.set(target, [pattern]);
|
|
}
|
|
get(target) {
|
|
const subs = this.store.get(target);
|
|
if (!subs) {
|
|
throw new Error("attempting to walk unknown path");
|
|
}
|
|
return subs;
|
|
}
|
|
entries() {
|
|
return this.keys().map((k) => [k, this.store.get(k)]);
|
|
}
|
|
keys() {
|
|
return [...this.store.keys()].filter((t) => t.canReaddir());
|
|
}
|
|
};
|
|
var Processor = class _Processor {
|
|
hasWalkedCache;
|
|
matches = new MatchRecord();
|
|
subwalks = new SubWalks();
|
|
patterns;
|
|
follow;
|
|
dot;
|
|
opts;
|
|
constructor(opts, hasWalkedCache) {
|
|
this.opts = opts;
|
|
this.follow = !!opts.follow;
|
|
this.dot = !!opts.dot;
|
|
this.hasWalkedCache = hasWalkedCache ? hasWalkedCache.copy() : new HasWalkedCache();
|
|
}
|
|
processPatterns(target, patterns) {
|
|
this.patterns = patterns;
|
|
const processingSet = patterns.map((p) => [target, p]);
|
|
for (let [t, pattern] of processingSet) {
|
|
this.hasWalkedCache.storeWalked(t, pattern);
|
|
const root = pattern.root();
|
|
const absolute = pattern.isAbsolute() && this.opts.absolute !== false;
|
|
if (root) {
|
|
t = t.resolve(root === "/" && this.opts.root !== void 0 ? this.opts.root : root);
|
|
const rest2 = pattern.rest();
|
|
if (!rest2) {
|
|
this.matches.add(t, true, false);
|
|
continue;
|
|
} else {
|
|
pattern = rest2;
|
|
}
|
|
}
|
|
if (t.isENOENT())
|
|
continue;
|
|
let p;
|
|
let rest;
|
|
let changed = false;
|
|
while (typeof (p = pattern.pattern()) === "string" && (rest = pattern.rest())) {
|
|
const c = t.resolve(p);
|
|
t = c;
|
|
pattern = rest;
|
|
changed = true;
|
|
}
|
|
p = pattern.pattern();
|
|
rest = pattern.rest();
|
|
if (changed) {
|
|
if (this.hasWalkedCache.hasWalked(t, pattern))
|
|
continue;
|
|
this.hasWalkedCache.storeWalked(t, pattern);
|
|
}
|
|
if (typeof p === "string") {
|
|
const ifDir = p === ".." || p === "" || p === ".";
|
|
this.matches.add(t.resolve(p), absolute, ifDir);
|
|
continue;
|
|
} else if (p === GLOBSTAR) {
|
|
if (!t.isSymbolicLink() || this.follow || pattern.checkFollowGlobstar()) {
|
|
this.subwalks.add(t, pattern);
|
|
}
|
|
const rp = rest?.pattern();
|
|
const rrest = rest?.rest();
|
|
if (!rest || (rp === "" || rp === ".") && !rrest) {
|
|
this.matches.add(t, absolute, rp === "" || rp === ".");
|
|
} else {
|
|
if (rp === "..") {
|
|
const tp = t.parent || t;
|
|
if (!rrest)
|
|
this.matches.add(tp, absolute, true);
|
|
else if (!this.hasWalkedCache.hasWalked(tp, rrest)) {
|
|
this.subwalks.add(tp, rrest);
|
|
}
|
|
}
|
|
}
|
|
} else if (p instanceof RegExp) {
|
|
this.subwalks.add(t, pattern);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
subwalkTargets() {
|
|
return this.subwalks.keys();
|
|
}
|
|
child() {
|
|
return new _Processor(this.opts, this.hasWalkedCache);
|
|
}
|
|
// return a new Processor containing the subwalks for each
|
|
// child entry, and a set of matches, and
|
|
// a hasWalkedCache that's a copy of this one
|
|
// then we're going to call
|
|
filterEntries(parent, entries) {
|
|
const patterns = this.subwalks.get(parent);
|
|
const results = this.child();
|
|
for (const e of entries) {
|
|
for (const pattern of patterns) {
|
|
const absolute = pattern.isAbsolute();
|
|
const p = pattern.pattern();
|
|
const rest = pattern.rest();
|
|
if (p === GLOBSTAR) {
|
|
results.testGlobstar(e, pattern, rest, absolute);
|
|
} else if (p instanceof RegExp) {
|
|
results.testRegExp(e, p, rest, absolute);
|
|
} else {
|
|
results.testString(e, p, rest, absolute);
|
|
}
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
testGlobstar(e, pattern, rest, absolute) {
|
|
if (this.dot || !e.name.startsWith(".")) {
|
|
if (!pattern.hasMore()) {
|
|
this.matches.add(e, absolute, false);
|
|
}
|
|
if (e.canReaddir()) {
|
|
if (this.follow || !e.isSymbolicLink()) {
|
|
this.subwalks.add(e, pattern);
|
|
} else if (e.isSymbolicLink()) {
|
|
if (rest && pattern.checkFollowGlobstar()) {
|
|
this.subwalks.add(e, rest);
|
|
} else if (pattern.markFollowGlobstar()) {
|
|
this.subwalks.add(e, pattern);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (rest) {
|
|
const rp = rest.pattern();
|
|
if (typeof rp === "string" && // dots and empty were handled already
|
|
rp !== ".." && rp !== "" && rp !== ".") {
|
|
this.testString(e, rp, rest.rest(), absolute);
|
|
} else if (rp === "..") {
|
|
const ep = e.parent || e;
|
|
this.subwalks.add(ep, rest);
|
|
} else if (rp instanceof RegExp) {
|
|
this.testRegExp(e, rp, rest.rest(), absolute);
|
|
}
|
|
}
|
|
}
|
|
testRegExp(e, p, rest, absolute) {
|
|
if (!p.test(e.name))
|
|
return;
|
|
if (!rest) {
|
|
this.matches.add(e, absolute, false);
|
|
} else {
|
|
this.subwalks.add(e, rest);
|
|
}
|
|
}
|
|
testString(e, p, rest, absolute) {
|
|
if (!e.isNamed(p))
|
|
return;
|
|
if (!rest) {
|
|
this.matches.add(e, absolute, false);
|
|
} else {
|
|
this.subwalks.add(e, rest);
|
|
}
|
|
}
|
|
};
|
|
var makeIgnore = (ignore, opts) => typeof ignore === "string" ? new Ignore([ignore], opts) : Array.isArray(ignore) ? new Ignore(ignore, opts) : ignore;
|
|
var GlobUtil = class {
|
|
path;
|
|
patterns;
|
|
opts;
|
|
seen = /* @__PURE__ */ new Set();
|
|
paused = false;
|
|
aborted = false;
|
|
#onResume = [];
|
|
#ignore;
|
|
#sep;
|
|
signal;
|
|
maxDepth;
|
|
includeChildMatches;
|
|
constructor(patterns, path2, opts) {
|
|
this.patterns = patterns;
|
|
this.path = path2;
|
|
this.opts = opts;
|
|
this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
|
|
this.includeChildMatches = opts.includeChildMatches !== false;
|
|
if (opts.ignore || !this.includeChildMatches) {
|
|
this.#ignore = makeIgnore(opts.ignore ?? [], opts);
|
|
if (!this.includeChildMatches && typeof this.#ignore.add !== "function") {
|
|
const m = "cannot ignore child matches, ignore lacks add() method.";
|
|
throw new Error(m);
|
|
}
|
|
}
|
|
this.maxDepth = opts.maxDepth || Infinity;
|
|
if (opts.signal) {
|
|
this.signal = opts.signal;
|
|
this.signal.addEventListener("abort", () => {
|
|
this.#onResume.length = 0;
|
|
});
|
|
}
|
|
}
|
|
#ignored(path2) {
|
|
return this.seen.has(path2) || !!this.#ignore?.ignored?.(path2);
|
|
}
|
|
#childrenIgnored(path2) {
|
|
return !!this.#ignore?.childrenIgnored?.(path2);
|
|
}
|
|
// backpressure mechanism
|
|
pause() {
|
|
this.paused = true;
|
|
}
|
|
resume() {
|
|
if (this.signal?.aborted)
|
|
return;
|
|
this.paused = false;
|
|
let fn = void 0;
|
|
while (!this.paused && (fn = this.#onResume.shift())) {
|
|
fn();
|
|
}
|
|
}
|
|
onResume(fn) {
|
|
if (this.signal?.aborted)
|
|
return;
|
|
if (!this.paused) {
|
|
fn();
|
|
} else {
|
|
this.#onResume.push(fn);
|
|
}
|
|
}
|
|
// do the requisite realpath/stat checking, and return the path
|
|
// to add or undefined to filter it out.
|
|
async matchCheck(e, ifDir) {
|
|
if (ifDir && this.opts.nodir)
|
|
return void 0;
|
|
let rpc;
|
|
if (this.opts.realpath) {
|
|
rpc = e.realpathCached() || await e.realpath();
|
|
if (!rpc)
|
|
return void 0;
|
|
e = rpc;
|
|
}
|
|
const needStat = e.isUnknown() || this.opts.stat;
|
|
const s = needStat ? await e.lstat() : e;
|
|
if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) {
|
|
const target = await s.realpath();
|
|
if (target && (target.isUnknown() || this.opts.stat)) {
|
|
await target.lstat();
|
|
}
|
|
}
|
|
return this.matchCheckTest(s, ifDir);
|
|
}
|
|
matchCheckTest(e, ifDir) {
|
|
return e && (this.maxDepth === Infinity || e.depth() <= this.maxDepth) && (!ifDir || e.canReaddir()) && (!this.opts.nodir || !e.isDirectory()) && (!this.opts.nodir || !this.opts.follow || !e.isSymbolicLink() || !e.realpathCached()?.isDirectory()) && !this.#ignored(e) ? e : void 0;
|
|
}
|
|
matchCheckSync(e, ifDir) {
|
|
if (ifDir && this.opts.nodir)
|
|
return void 0;
|
|
let rpc;
|
|
if (this.opts.realpath) {
|
|
rpc = e.realpathCached() || e.realpathSync();
|
|
if (!rpc)
|
|
return void 0;
|
|
e = rpc;
|
|
}
|
|
const needStat = e.isUnknown() || this.opts.stat;
|
|
const s = needStat ? e.lstatSync() : e;
|
|
if (this.opts.follow && this.opts.nodir && s?.isSymbolicLink()) {
|
|
const target = s.realpathSync();
|
|
if (target && (target?.isUnknown() || this.opts.stat)) {
|
|
target.lstatSync();
|
|
}
|
|
}
|
|
return this.matchCheckTest(s, ifDir);
|
|
}
|
|
matchFinish(e, absolute) {
|
|
if (this.#ignored(e))
|
|
return;
|
|
if (!this.includeChildMatches && this.#ignore?.add) {
|
|
const ign = `${e.relativePosix()}/**`;
|
|
this.#ignore.add(ign);
|
|
}
|
|
const abs = this.opts.absolute === void 0 ? absolute : this.opts.absolute;
|
|
this.seen.add(e);
|
|
const mark = this.opts.mark && e.isDirectory() ? this.#sep : "";
|
|
if (this.opts.withFileTypes) {
|
|
this.matchEmit(e);
|
|
} else if (abs) {
|
|
const abs2 = this.opts.posix ? e.fullpathPosix() : e.fullpath();
|
|
this.matchEmit(abs2 + mark);
|
|
} else {
|
|
const rel = this.opts.posix ? e.relativePosix() : e.relative();
|
|
const pre = this.opts.dotRelative && !rel.startsWith(".." + this.#sep) ? "." + this.#sep : "";
|
|
this.matchEmit(!rel ? "." + mark : pre + rel + mark);
|
|
}
|
|
}
|
|
async match(e, absolute, ifDir) {
|
|
const p = await this.matchCheck(e, ifDir);
|
|
if (p)
|
|
this.matchFinish(p, absolute);
|
|
}
|
|
matchSync(e, absolute, ifDir) {
|
|
const p = this.matchCheckSync(e, ifDir);
|
|
if (p)
|
|
this.matchFinish(p, absolute);
|
|
}
|
|
walkCB(target, patterns, cb) {
|
|
if (this.signal?.aborted)
|
|
cb();
|
|
this.walkCB2(target, patterns, new Processor(this.opts), cb);
|
|
}
|
|
walkCB2(target, patterns, processor, cb) {
|
|
if (this.#childrenIgnored(target))
|
|
return cb();
|
|
if (this.signal?.aborted)
|
|
cb();
|
|
if (this.paused) {
|
|
this.onResume(() => this.walkCB2(target, patterns, processor, cb));
|
|
return;
|
|
}
|
|
processor.processPatterns(target, patterns);
|
|
let tasks = 1;
|
|
const next = () => {
|
|
if (--tasks === 0)
|
|
cb();
|
|
};
|
|
for (const [m, absolute, ifDir] of processor.matches.entries()) {
|
|
if (this.#ignored(m))
|
|
continue;
|
|
tasks++;
|
|
this.match(m, absolute, ifDir).then(() => next());
|
|
}
|
|
for (const t of processor.subwalkTargets()) {
|
|
if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
|
|
continue;
|
|
}
|
|
tasks++;
|
|
const childrenCached = t.readdirCached();
|
|
if (t.calledReaddir())
|
|
this.walkCB3(t, childrenCached, processor, next);
|
|
else {
|
|
t.readdirCB((_, entries) => this.walkCB3(t, entries, processor, next), true);
|
|
}
|
|
}
|
|
next();
|
|
}
|
|
walkCB3(target, entries, processor, cb) {
|
|
processor = processor.filterEntries(target, entries);
|
|
let tasks = 1;
|
|
const next = () => {
|
|
if (--tasks === 0)
|
|
cb();
|
|
};
|
|
for (const [m, absolute, ifDir] of processor.matches.entries()) {
|
|
if (this.#ignored(m))
|
|
continue;
|
|
tasks++;
|
|
this.match(m, absolute, ifDir).then(() => next());
|
|
}
|
|
for (const [target2, patterns] of processor.subwalks.entries()) {
|
|
tasks++;
|
|
this.walkCB2(target2, patterns, processor.child(), next);
|
|
}
|
|
next();
|
|
}
|
|
walkCBSync(target, patterns, cb) {
|
|
if (this.signal?.aborted)
|
|
cb();
|
|
this.walkCB2Sync(target, patterns, new Processor(this.opts), cb);
|
|
}
|
|
walkCB2Sync(target, patterns, processor, cb) {
|
|
if (this.#childrenIgnored(target))
|
|
return cb();
|
|
if (this.signal?.aborted)
|
|
cb();
|
|
if (this.paused) {
|
|
this.onResume(() => this.walkCB2Sync(target, patterns, processor, cb));
|
|
return;
|
|
}
|
|
processor.processPatterns(target, patterns);
|
|
let tasks = 1;
|
|
const next = () => {
|
|
if (--tasks === 0)
|
|
cb();
|
|
};
|
|
for (const [m, absolute, ifDir] of processor.matches.entries()) {
|
|
if (this.#ignored(m))
|
|
continue;
|
|
this.matchSync(m, absolute, ifDir);
|
|
}
|
|
for (const t of processor.subwalkTargets()) {
|
|
if (this.maxDepth !== Infinity && t.depth() >= this.maxDepth) {
|
|
continue;
|
|
}
|
|
tasks++;
|
|
const children = t.readdirSync();
|
|
this.walkCB3Sync(t, children, processor, next);
|
|
}
|
|
next();
|
|
}
|
|
walkCB3Sync(target, entries, processor, cb) {
|
|
processor = processor.filterEntries(target, entries);
|
|
let tasks = 1;
|
|
const next = () => {
|
|
if (--tasks === 0)
|
|
cb();
|
|
};
|
|
for (const [m, absolute, ifDir] of processor.matches.entries()) {
|
|
if (this.#ignored(m))
|
|
continue;
|
|
this.matchSync(m, absolute, ifDir);
|
|
}
|
|
for (const [target2, patterns] of processor.subwalks.entries()) {
|
|
tasks++;
|
|
this.walkCB2Sync(target2, patterns, processor.child(), next);
|
|
}
|
|
next();
|
|
}
|
|
};
|
|
var GlobWalker = class extends GlobUtil {
|
|
matches = /* @__PURE__ */ new Set();
|
|
constructor(patterns, path2, opts) {
|
|
super(patterns, path2, opts);
|
|
}
|
|
matchEmit(e) {
|
|
this.matches.add(e);
|
|
}
|
|
async walk() {
|
|
if (this.signal?.aborted)
|
|
throw this.signal.reason;
|
|
if (this.path.isUnknown()) {
|
|
await this.path.lstat();
|
|
}
|
|
await new Promise((res, rej) => {
|
|
this.walkCB(this.path, this.patterns, () => {
|
|
if (this.signal?.aborted) {
|
|
rej(this.signal.reason);
|
|
} else {
|
|
res(this.matches);
|
|
}
|
|
});
|
|
});
|
|
return this.matches;
|
|
}
|
|
walkSync() {
|
|
if (this.signal?.aborted)
|
|
throw this.signal.reason;
|
|
if (this.path.isUnknown()) {
|
|
this.path.lstatSync();
|
|
}
|
|
this.walkCBSync(this.path, this.patterns, () => {
|
|
if (this.signal?.aborted)
|
|
throw this.signal.reason;
|
|
});
|
|
return this.matches;
|
|
}
|
|
};
|
|
var GlobStream = class extends GlobUtil {
|
|
results;
|
|
constructor(patterns, path2, opts) {
|
|
super(patterns, path2, opts);
|
|
this.results = new Minipass({
|
|
signal: this.signal,
|
|
objectMode: true
|
|
});
|
|
this.results.on("drain", () => this.resume());
|
|
this.results.on("resume", () => this.resume());
|
|
}
|
|
matchEmit(e) {
|
|
this.results.write(e);
|
|
if (!this.results.flowing)
|
|
this.pause();
|
|
}
|
|
stream() {
|
|
const target = this.path;
|
|
if (target.isUnknown()) {
|
|
target.lstat().then(() => {
|
|
this.walkCB(target, this.patterns, () => this.results.end());
|
|
});
|
|
} else {
|
|
this.walkCB(target, this.patterns, () => this.results.end());
|
|
}
|
|
return this.results;
|
|
}
|
|
streamSync() {
|
|
if (this.path.isUnknown()) {
|
|
this.path.lstatSync();
|
|
}
|
|
this.walkCBSync(this.path, this.patterns, () => this.results.end());
|
|
return this.results;
|
|
}
|
|
};
|
|
var defaultPlatform3 = typeof process === "object" && process && typeof process.platform === "string" ? process.platform : "linux";
|
|
var Glob = class {
|
|
absolute;
|
|
cwd;
|
|
root;
|
|
dot;
|
|
dotRelative;
|
|
follow;
|
|
ignore;
|
|
magicalBraces;
|
|
mark;
|
|
matchBase;
|
|
maxDepth;
|
|
nobrace;
|
|
nocase;
|
|
nodir;
|
|
noext;
|
|
noglobstar;
|
|
pattern;
|
|
platform;
|
|
realpath;
|
|
scurry;
|
|
stat;
|
|
signal;
|
|
windowsPathsNoEscape;
|
|
withFileTypes;
|
|
includeChildMatches;
|
|
/**
|
|
* The options provided to the constructor.
|
|
*/
|
|
opts;
|
|
/**
|
|
* An array of parsed immutable {@link Pattern} objects.
|
|
*/
|
|
patterns;
|
|
/**
|
|
* All options are stored as properties on the `Glob` object.
|
|
*
|
|
* See {@link GlobOptions} for full options descriptions.
|
|
*
|
|
* Note that a previous `Glob` object can be passed as the
|
|
* `GlobOptions` to another `Glob` instantiation to re-use settings
|
|
* and caches with a new pattern.
|
|
*
|
|
* Traversal functions can be called multiple times to run the walk
|
|
* again.
|
|
*/
|
|
constructor(pattern, opts) {
|
|
if (!opts)
|
|
throw new TypeError("glob options required");
|
|
this.withFileTypes = !!opts.withFileTypes;
|
|
this.signal = opts.signal;
|
|
this.follow = !!opts.follow;
|
|
this.dot = !!opts.dot;
|
|
this.dotRelative = !!opts.dotRelative;
|
|
this.nodir = !!opts.nodir;
|
|
this.mark = !!opts.mark;
|
|
if (!opts.cwd) {
|
|
this.cwd = "";
|
|
} else if (opts.cwd instanceof URL || opts.cwd.startsWith("file://")) {
|
|
opts.cwd = (0, import_node_url.fileURLToPath)(opts.cwd);
|
|
}
|
|
this.cwd = opts.cwd || "";
|
|
this.root = opts.root;
|
|
this.magicalBraces = !!opts.magicalBraces;
|
|
this.nobrace = !!opts.nobrace;
|
|
this.noext = !!opts.noext;
|
|
this.realpath = !!opts.realpath;
|
|
this.absolute = opts.absolute;
|
|
this.includeChildMatches = opts.includeChildMatches !== false;
|
|
this.noglobstar = !!opts.noglobstar;
|
|
this.matchBase = !!opts.matchBase;
|
|
this.maxDepth = typeof opts.maxDepth === "number" ? opts.maxDepth : Infinity;
|
|
this.stat = !!opts.stat;
|
|
this.ignore = opts.ignore;
|
|
if (this.withFileTypes && this.absolute !== void 0) {
|
|
throw new Error("cannot set absolute and withFileTypes:true");
|
|
}
|
|
if (typeof pattern === "string") {
|
|
pattern = [pattern];
|
|
}
|
|
this.windowsPathsNoEscape = !!opts.windowsPathsNoEscape || opts.allowWindowsEscape === false;
|
|
if (this.windowsPathsNoEscape) {
|
|
pattern = pattern.map((p) => p.replace(/\\/g, "/"));
|
|
}
|
|
if (this.matchBase) {
|
|
if (opts.noglobstar) {
|
|
throw new TypeError("base matching requires globstar");
|
|
}
|
|
pattern = pattern.map((p) => p.includes("/") ? p : `./**/${p}`);
|
|
}
|
|
this.pattern = pattern;
|
|
this.platform = opts.platform || defaultPlatform3;
|
|
this.opts = { ...opts, platform: this.platform };
|
|
if (opts.scurry) {
|
|
this.scurry = opts.scurry;
|
|
if (opts.nocase !== void 0 && opts.nocase !== opts.scurry.nocase) {
|
|
throw new Error("nocase option contradicts provided scurry option");
|
|
}
|
|
} else {
|
|
const Scurry = opts.platform === "win32" ? PathScurryWin32 : opts.platform === "darwin" ? PathScurryDarwin : opts.platform ? PathScurryPosix : PathScurry;
|
|
this.scurry = new Scurry(this.cwd, {
|
|
nocase: opts.nocase,
|
|
fs: opts.fs
|
|
});
|
|
}
|
|
this.nocase = this.scurry.nocase;
|
|
const nocaseMagicOnly = this.platform === "darwin" || this.platform === "win32";
|
|
const mmo = {
|
|
// default nocase based on platform
|
|
...opts,
|
|
dot: this.dot,
|
|
matchBase: this.matchBase,
|
|
nobrace: this.nobrace,
|
|
nocase: this.nocase,
|
|
nocaseMagicOnly,
|
|
nocomment: true,
|
|
noext: this.noext,
|
|
nonegate: true,
|
|
optimizationLevel: 2,
|
|
platform: this.platform,
|
|
windowsPathsNoEscape: this.windowsPathsNoEscape,
|
|
debug: !!this.opts.debug
|
|
};
|
|
const mms = this.pattern.map((p) => new Minimatch(p, mmo));
|
|
const [matchSet, globParts] = mms.reduce((set, m) => {
|
|
set[0].push(...m.set);
|
|
set[1].push(...m.globParts);
|
|
return set;
|
|
}, [[], []]);
|
|
this.patterns = matchSet.map((set, i) => {
|
|
const g = globParts[i];
|
|
if (!g)
|
|
throw new Error("invalid pattern object");
|
|
return new Pattern(set, g, 0, this.platform);
|
|
});
|
|
}
|
|
async walk() {
|
|
return [
|
|
...await new GlobWalker(this.patterns, this.scurry.cwd, {
|
|
...this.opts,
|
|
maxDepth: this.maxDepth !== Infinity ? this.maxDepth + this.scurry.cwd.depth() : Infinity,
|
|
platform: this.platform,
|
|
nocase: this.nocase,
|
|
includeChildMatches: this.includeChildMatches
|
|
}).walk()
|
|
];
|
|
}
|
|
walkSync() {
|
|
return [
|
|
...new GlobWalker(this.patterns, this.scurry.cwd, {
|
|
...this.opts,
|
|
maxDepth: this.maxDepth !== Infinity ? this.maxDepth + this.scurry.cwd.depth() : Infinity,
|
|
platform: this.platform,
|
|
nocase: this.nocase,
|
|
includeChildMatches: this.includeChildMatches
|
|
}).walkSync()
|
|
];
|
|
}
|
|
stream() {
|
|
return new GlobStream(this.patterns, this.scurry.cwd, {
|
|
...this.opts,
|
|
maxDepth: this.maxDepth !== Infinity ? this.maxDepth + this.scurry.cwd.depth() : Infinity,
|
|
platform: this.platform,
|
|
nocase: this.nocase,
|
|
includeChildMatches: this.includeChildMatches
|
|
}).stream();
|
|
}
|
|
streamSync() {
|
|
return new GlobStream(this.patterns, this.scurry.cwd, {
|
|
...this.opts,
|
|
maxDepth: this.maxDepth !== Infinity ? this.maxDepth + this.scurry.cwd.depth() : Infinity,
|
|
platform: this.platform,
|
|
nocase: this.nocase,
|
|
includeChildMatches: this.includeChildMatches
|
|
}).streamSync();
|
|
}
|
|
/**
|
|
* Default sync iteration function. Returns a Generator that
|
|
* iterates over the results.
|
|
*/
|
|
iterateSync() {
|
|
return this.streamSync()[Symbol.iterator]();
|
|
}
|
|
[Symbol.iterator]() {
|
|
return this.iterateSync();
|
|
}
|
|
/**
|
|
* Default async iteration function. Returns an AsyncGenerator that
|
|
* iterates over the results.
|
|
*/
|
|
iterate() {
|
|
return this.stream()[Symbol.asyncIterator]();
|
|
}
|
|
[Symbol.asyncIterator]() {
|
|
return this.iterate();
|
|
}
|
|
};
|
|
var hasMagic = (pattern, options = {}) => {
|
|
if (!Array.isArray(pattern)) {
|
|
pattern = [pattern];
|
|
}
|
|
for (const p of pattern) {
|
|
if (new Minimatch(p, options).hasMagic())
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
function globStreamSync(pattern, options = {}) {
|
|
return new Glob(pattern, options).streamSync();
|
|
}
|
|
function globStream(pattern, options = {}) {
|
|
return new Glob(pattern, options).stream();
|
|
}
|
|
function globSync(pattern, options = {}) {
|
|
return new Glob(pattern, options).walkSync();
|
|
}
|
|
async function glob_(pattern, options = {}) {
|
|
return new Glob(pattern, options).walk();
|
|
}
|
|
function globIterateSync(pattern, options = {}) {
|
|
return new Glob(pattern, options).iterateSync();
|
|
}
|
|
function globIterate(pattern, options = {}) {
|
|
return new Glob(pattern, options).iterate();
|
|
}
|
|
var streamSync = globStreamSync;
|
|
var stream = Object.assign(globStream, { sync: globStreamSync });
|
|
var iterateSync = globIterateSync;
|
|
var iterate = Object.assign(globIterate, {
|
|
sync: globIterateSync
|
|
});
|
|
var sync = Object.assign(globSync, {
|
|
stream: globStreamSync,
|
|
iterate: globIterateSync
|
|
});
|
|
var glob = Object.assign(glob_, {
|
|
glob: glob_,
|
|
globSync,
|
|
sync,
|
|
globStream,
|
|
stream,
|
|
globStreamSync,
|
|
streamSync,
|
|
globIterate,
|
|
iterate,
|
|
globIterateSync,
|
|
iterateSync,
|
|
Glob,
|
|
hasMagic,
|
|
escape,
|
|
unescape
|
|
});
|
|
glob.glob = glob;
|
|
var typeOrUndef = (val, t) => typeof val === "undefined" || typeof val === t;
|
|
var isRimrafOptions = (o) => !!o && typeof o === "object" && typeOrUndef(o.preserveRoot, "boolean") && typeOrUndef(o.tmp, "string") && typeOrUndef(o.maxRetries, "number") && typeOrUndef(o.retryDelay, "number") && typeOrUndef(o.backoff, "number") && typeOrUndef(o.maxBackoff, "number") && (typeOrUndef(o.glob, "boolean") || o.glob && typeof o.glob === "object") && typeOrUndef(o.filter, "function");
|
|
var assertRimrafOptions = (o) => {
|
|
if (!isRimrafOptions(o)) {
|
|
throw new Error("invalid rimraf options");
|
|
}
|
|
};
|
|
var optArgT = (opt) => {
|
|
assertRimrafOptions(opt);
|
|
const { glob: glob2, ...options } = opt;
|
|
if (!glob2) {
|
|
return options;
|
|
}
|
|
const globOpt = glob2 === true ? opt.signal ? { signal: opt.signal } : {} : opt.signal ? {
|
|
signal: opt.signal,
|
|
...glob2
|
|
} : glob2;
|
|
return {
|
|
...options,
|
|
glob: {
|
|
...globOpt,
|
|
// always get absolute paths from glob, to ensure
|
|
// that we are referencing the correct thing.
|
|
absolute: true,
|
|
withFileTypes: false
|
|
}
|
|
};
|
|
};
|
|
var optArg = (opt = {}) => optArgT(opt);
|
|
var optArgSync = (opt = {}) => optArgT(opt);
|
|
var platform_default = process.env.__TESTING_RIMRAF_PLATFORM__ || process.platform;
|
|
var pathArg = (path2, opt = {}) => {
|
|
const type = typeof path2;
|
|
if (type !== "string") {
|
|
const ctor = path2 && type === "object" && path2.constructor;
|
|
const received = ctor && ctor.name ? `an instance of ${ctor.name}` : type === "object" ? (0, import_util.inspect)(path2) : `type ${type} ${path2}`;
|
|
const msg = `The "path" argument must be of type string. Received ${received}`;
|
|
throw Object.assign(new TypeError(msg), {
|
|
path: path2,
|
|
code: "ERR_INVALID_ARG_TYPE"
|
|
});
|
|
}
|
|
if (/\0/.test(path2)) {
|
|
const msg = "path must be a string without null bytes";
|
|
throw Object.assign(new TypeError(msg), {
|
|
path: path2,
|
|
code: "ERR_INVALID_ARG_VALUE"
|
|
});
|
|
}
|
|
path2 = (0, import_path.resolve)(path2);
|
|
const { root } = (0, import_path.parse)(path2);
|
|
if (path2 === root && opt.preserveRoot !== false) {
|
|
const msg = "refusing to remove root directory without preserveRoot:false";
|
|
throw Object.assign(new Error(msg), {
|
|
path: path2,
|
|
code: "ERR_PRESERVE_ROOT"
|
|
});
|
|
}
|
|
if (platform_default === "win32") {
|
|
const badWinChars = /[*|"<>?:]/;
|
|
const { root: root2 } = (0, import_path.parse)(path2);
|
|
if (badWinChars.test(path2.substring(root2.length))) {
|
|
throw Object.assign(new Error("Illegal characters in path."), {
|
|
path: path2,
|
|
code: "EINVAL"
|
|
});
|
|
}
|
|
}
|
|
return path2;
|
|
};
|
|
var path_arg_default = pathArg;
|
|
var readdirSync2 = (path2) => (0, import_fs4.readdirSync)(path2, { withFileTypes: true });
|
|
var chmod = (path2, mode) => new Promise((res, rej) => import_fs2.default.chmod(path2, mode, (er, ...d) => er ? rej(er) : res(...d)));
|
|
var mkdir = (path2, options) => new Promise((res, rej) => import_fs2.default.mkdir(path2, options, (er, made) => er ? rej(er) : res(made)));
|
|
var readdir2 = (path2) => new Promise((res, rej) => import_fs2.default.readdir(path2, { withFileTypes: true }, (er, data) => er ? rej(er) : res(data)));
|
|
var rename = (oldPath, newPath) => new Promise((res, rej) => import_fs2.default.rename(oldPath, newPath, (er, ...d) => er ? rej(er) : res(...d)));
|
|
var rm = (path2, options) => new Promise((res, rej) => import_fs2.default.rm(path2, options, (er, ...d) => er ? rej(er) : res(...d)));
|
|
var rmdir = (path2) => new Promise((res, rej) => import_fs2.default.rmdir(path2, (er, ...d) => er ? rej(er) : res(...d)));
|
|
var stat = (path2) => new Promise((res, rej) => import_fs2.default.stat(path2, (er, data) => er ? rej(er) : res(data)));
|
|
var lstat2 = (path2) => new Promise((res, rej) => import_fs2.default.lstat(path2, (er, data) => er ? rej(er) : res(data)));
|
|
var unlink = (path2) => new Promise((res, rej) => import_fs2.default.unlink(path2, (er, ...d) => er ? rej(er) : res(...d)));
|
|
var promises = {
|
|
chmod,
|
|
mkdir,
|
|
readdir: readdir2,
|
|
rename,
|
|
rm,
|
|
rmdir,
|
|
stat,
|
|
lstat: lstat2,
|
|
unlink
|
|
};
|
|
var { readdir: readdir3 } = promises;
|
|
var readdirOrError = (path2) => readdir3(path2).catch((er) => er);
|
|
var readdirOrErrorSync = (path2) => {
|
|
try {
|
|
return readdirSync2(path2);
|
|
} catch (er) {
|
|
return er;
|
|
}
|
|
};
|
|
var ignoreENOENT = async (p) => p.catch((er) => {
|
|
if (er.code !== "ENOENT") {
|
|
throw er;
|
|
}
|
|
});
|
|
var ignoreENOENTSync = (fn) => {
|
|
try {
|
|
return fn();
|
|
} catch (er) {
|
|
if (er?.code !== "ENOENT") {
|
|
throw er;
|
|
}
|
|
}
|
|
};
|
|
var { lstat: lstat3, rmdir: rmdir2, unlink: unlink2 } = promises;
|
|
var rimrafPosix = async (path2, opt) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
try {
|
|
return await rimrafPosixDir(path2, opt, await lstat3(path2));
|
|
} catch (er) {
|
|
if (er?.code === "ENOENT")
|
|
return true;
|
|
throw er;
|
|
}
|
|
};
|
|
var rimrafPosixSync = (path2, opt) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
try {
|
|
return rimrafPosixDirSync(path2, opt, (0, import_fs3.lstatSync)(path2));
|
|
} catch (er) {
|
|
if (er?.code === "ENOENT")
|
|
return true;
|
|
throw er;
|
|
}
|
|
};
|
|
var rimrafPosixDir = async (path2, opt, ent) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
const entries = ent.isDirectory() ? await readdirOrError(path2) : null;
|
|
if (!Array.isArray(entries)) {
|
|
if (entries) {
|
|
if (entries.code === "ENOENT") {
|
|
return true;
|
|
}
|
|
if (entries.code !== "ENOTDIR") {
|
|
throw entries;
|
|
}
|
|
}
|
|
if (opt.filter && !await opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
await ignoreENOENT(unlink2(path2));
|
|
return true;
|
|
}
|
|
const removedAll = (await Promise.all(entries.map((ent2) => rimrafPosixDir((0, import_path2.resolve)(path2, ent2.name), opt, ent2)))).reduce((a, b) => a && b, true);
|
|
if (!removedAll) {
|
|
return false;
|
|
}
|
|
if (opt.preserveRoot === false && path2 === (0, import_path2.parse)(path2).root) {
|
|
return false;
|
|
}
|
|
if (opt.filter && !await opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
await ignoreENOENT(rmdir2(path2));
|
|
return true;
|
|
};
|
|
var rimrafPosixDirSync = (path2, opt, ent) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
const entries = ent.isDirectory() ? readdirOrErrorSync(path2) : null;
|
|
if (!Array.isArray(entries)) {
|
|
if (entries) {
|
|
if (entries.code === "ENOENT") {
|
|
return true;
|
|
}
|
|
if (entries.code !== "ENOTDIR") {
|
|
throw entries;
|
|
}
|
|
}
|
|
if (opt.filter && !opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
ignoreENOENTSync(() => (0, import_fs3.unlinkSync)(path2));
|
|
return true;
|
|
}
|
|
let removedAll = true;
|
|
for (const ent2 of entries) {
|
|
const p = (0, import_path2.resolve)(path2, ent2.name);
|
|
removedAll = rimrafPosixDirSync(p, opt, ent2) && removedAll;
|
|
}
|
|
if (opt.preserveRoot === false && path2 === (0, import_path2.parse)(path2).root) {
|
|
return false;
|
|
}
|
|
if (!removedAll) {
|
|
return false;
|
|
}
|
|
if (opt.filter && !opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
ignoreENOENTSync(() => (0, import_fs3.rmdirSync)(path2));
|
|
return true;
|
|
};
|
|
var { chmod: chmod2 } = promises;
|
|
var fixEPERM = (fn) => async (path2) => {
|
|
try {
|
|
return await fn(path2);
|
|
} catch (er) {
|
|
const fer = er;
|
|
if (fer?.code === "ENOENT") {
|
|
return;
|
|
}
|
|
if (fer?.code === "EPERM") {
|
|
try {
|
|
await chmod2(path2, 438);
|
|
} catch (er2) {
|
|
const fer2 = er2;
|
|
if (fer2?.code === "ENOENT") {
|
|
return;
|
|
}
|
|
throw er;
|
|
}
|
|
return await fn(path2);
|
|
}
|
|
throw er;
|
|
}
|
|
};
|
|
var fixEPERMSync = (fn) => (path2) => {
|
|
try {
|
|
return fn(path2);
|
|
} catch (er) {
|
|
const fer = er;
|
|
if (fer?.code === "ENOENT") {
|
|
return;
|
|
}
|
|
if (fer?.code === "EPERM") {
|
|
try {
|
|
(0, import_fs3.chmodSync)(path2, 438);
|
|
} catch (er2) {
|
|
const fer2 = er2;
|
|
if (fer2?.code === "ENOENT") {
|
|
return;
|
|
}
|
|
throw er;
|
|
}
|
|
return fn(path2);
|
|
}
|
|
throw er;
|
|
}
|
|
};
|
|
var MAXBACKOFF = 200;
|
|
var RATE = 1.2;
|
|
var MAXRETRIES = 10;
|
|
var codes = /* @__PURE__ */ new Set(["EMFILE", "ENFILE", "EBUSY"]);
|
|
var retryBusy = (fn) => {
|
|
const method = async (path2, opt, backoff = 1, total = 0) => {
|
|
const mbo = opt.maxBackoff || MAXBACKOFF;
|
|
const rate = opt.backoff || RATE;
|
|
const max = opt.maxRetries || MAXRETRIES;
|
|
let retries = 0;
|
|
while (true) {
|
|
try {
|
|
return await fn(path2);
|
|
} catch (er) {
|
|
const fer = er;
|
|
if (fer?.path === path2 && fer?.code && codes.has(fer.code)) {
|
|
backoff = Math.ceil(backoff * rate);
|
|
total = backoff + total;
|
|
if (total < mbo) {
|
|
return new Promise((res, rej) => {
|
|
setTimeout(() => {
|
|
method(path2, opt, backoff, total).then(res, rej);
|
|
}, backoff);
|
|
});
|
|
}
|
|
if (retries < max) {
|
|
retries++;
|
|
continue;
|
|
}
|
|
}
|
|
throw er;
|
|
}
|
|
}
|
|
};
|
|
return method;
|
|
};
|
|
var retryBusySync = (fn) => {
|
|
const method = (path2, opt) => {
|
|
const max = opt.maxRetries || MAXRETRIES;
|
|
let retries = 0;
|
|
while (true) {
|
|
try {
|
|
return fn(path2);
|
|
} catch (er) {
|
|
const fer = er;
|
|
if (fer?.path === path2 && fer?.code && codes.has(fer.code) && retries < max) {
|
|
retries++;
|
|
continue;
|
|
}
|
|
throw er;
|
|
}
|
|
}
|
|
};
|
|
return method;
|
|
};
|
|
var { stat: stat2 } = promises;
|
|
var isDirSync = (path2) => {
|
|
try {
|
|
return (0, import_fs3.statSync)(path2).isDirectory();
|
|
} catch (er) {
|
|
return false;
|
|
}
|
|
};
|
|
var isDir = (path2) => stat2(path2).then((st) => st.isDirectory(), () => false);
|
|
var win32DefaultTmp = async (path2) => {
|
|
const { root } = (0, import_path5.parse)(path2);
|
|
const tmp = (0, import_os.tmpdir)();
|
|
const { root: tmpRoot } = (0, import_path5.parse)(tmp);
|
|
if (root.toLowerCase() === tmpRoot.toLowerCase()) {
|
|
return tmp;
|
|
}
|
|
const driveTmp = (0, import_path5.resolve)(root, "/temp");
|
|
if (await isDir(driveTmp)) {
|
|
return driveTmp;
|
|
}
|
|
return root;
|
|
};
|
|
var win32DefaultTmpSync = (path2) => {
|
|
const { root } = (0, import_path5.parse)(path2);
|
|
const tmp = (0, import_os.tmpdir)();
|
|
const { root: tmpRoot } = (0, import_path5.parse)(tmp);
|
|
if (root.toLowerCase() === tmpRoot.toLowerCase()) {
|
|
return tmp;
|
|
}
|
|
const driveTmp = (0, import_path5.resolve)(root, "/temp");
|
|
if (isDirSync(driveTmp)) {
|
|
return driveTmp;
|
|
}
|
|
return root;
|
|
};
|
|
var posixDefaultTmp = async () => (0, import_os.tmpdir)();
|
|
var posixDefaultTmpSync = () => (0, import_os.tmpdir)();
|
|
var defaultTmp = platform_default === "win32" ? win32DefaultTmp : posixDefaultTmp;
|
|
var defaultTmpSync = platform_default === "win32" ? win32DefaultTmpSync : posixDefaultTmpSync;
|
|
var { lstat: lstat4, rename: rename2, unlink: unlink3, rmdir: rmdir3, chmod: chmod3 } = promises;
|
|
var uniqueFilename = (path2) => `.${(0, import_path4.basename)(path2)}.${Math.random()}`;
|
|
var unlinkFixEPERM = async (path2) => unlink3(path2).catch((er) => {
|
|
if (er.code === "EPERM") {
|
|
return chmod3(path2, 438).then(() => unlink3(path2), (er2) => {
|
|
if (er2.code === "ENOENT") {
|
|
return;
|
|
}
|
|
throw er;
|
|
});
|
|
} else if (er.code === "ENOENT") {
|
|
return;
|
|
}
|
|
throw er;
|
|
});
|
|
var unlinkFixEPERMSync = (path2) => {
|
|
try {
|
|
(0, import_fs3.unlinkSync)(path2);
|
|
} catch (er) {
|
|
if (er?.code === "EPERM") {
|
|
try {
|
|
return (0, import_fs3.chmodSync)(path2, 438);
|
|
} catch (er2) {
|
|
if (er2?.code === "ENOENT") {
|
|
return;
|
|
}
|
|
throw er;
|
|
}
|
|
} else if (er?.code === "ENOENT") {
|
|
return;
|
|
}
|
|
throw er;
|
|
}
|
|
};
|
|
var rimrafMoveRemove = async (path2, opt) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
try {
|
|
return await rimrafMoveRemoveDir(path2, opt, await lstat4(path2));
|
|
} catch (er) {
|
|
if (er?.code === "ENOENT")
|
|
return true;
|
|
throw er;
|
|
}
|
|
};
|
|
var rimrafMoveRemoveDir = async (path2, opt, ent) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
if (!opt.tmp) {
|
|
return rimrafMoveRemoveDir(path2, { ...opt, tmp: await defaultTmp(path2) }, ent);
|
|
}
|
|
if (path2 === opt.tmp && (0, import_path4.parse)(path2).root !== path2) {
|
|
throw new Error("cannot delete temp directory used for deletion");
|
|
}
|
|
const entries = ent.isDirectory() ? await readdirOrError(path2) : null;
|
|
if (!Array.isArray(entries)) {
|
|
if (entries) {
|
|
if (entries.code === "ENOENT") {
|
|
return true;
|
|
}
|
|
if (entries.code !== "ENOTDIR") {
|
|
throw entries;
|
|
}
|
|
}
|
|
if (opt.filter && !await opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
await ignoreENOENT(tmpUnlink(path2, opt.tmp, unlinkFixEPERM));
|
|
return true;
|
|
}
|
|
const removedAll = (await Promise.all(entries.map((ent2) => rimrafMoveRemoveDir((0, import_path4.resolve)(path2, ent2.name), opt, ent2)))).reduce((a, b) => a && b, true);
|
|
if (!removedAll) {
|
|
return false;
|
|
}
|
|
if (opt.preserveRoot === false && path2 === (0, import_path4.parse)(path2).root) {
|
|
return false;
|
|
}
|
|
if (opt.filter && !await opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
await ignoreENOENT(tmpUnlink(path2, opt.tmp, rmdir3));
|
|
return true;
|
|
};
|
|
var tmpUnlink = async (path2, tmp, rm3) => {
|
|
const tmpFile = (0, import_path4.resolve)(tmp, uniqueFilename(path2));
|
|
await rename2(path2, tmpFile);
|
|
return await rm3(tmpFile);
|
|
};
|
|
var rimrafMoveRemoveSync = (path2, opt) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
try {
|
|
return rimrafMoveRemoveDirSync(path2, opt, (0, import_fs3.lstatSync)(path2));
|
|
} catch (er) {
|
|
if (er?.code === "ENOENT")
|
|
return true;
|
|
throw er;
|
|
}
|
|
};
|
|
var rimrafMoveRemoveDirSync = (path2, opt, ent) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
if (!opt.tmp) {
|
|
return rimrafMoveRemoveDirSync(path2, { ...opt, tmp: defaultTmpSync(path2) }, ent);
|
|
}
|
|
const tmp = opt.tmp;
|
|
if (path2 === opt.tmp && (0, import_path4.parse)(path2).root !== path2) {
|
|
throw new Error("cannot delete temp directory used for deletion");
|
|
}
|
|
const entries = ent.isDirectory() ? readdirOrErrorSync(path2) : null;
|
|
if (!Array.isArray(entries)) {
|
|
if (entries) {
|
|
if (entries.code === "ENOENT") {
|
|
return true;
|
|
}
|
|
if (entries.code !== "ENOTDIR") {
|
|
throw entries;
|
|
}
|
|
}
|
|
if (opt.filter && !opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
ignoreENOENTSync(() => tmpUnlinkSync(path2, tmp, unlinkFixEPERMSync));
|
|
return true;
|
|
}
|
|
let removedAll = true;
|
|
for (const ent2 of entries) {
|
|
const p = (0, import_path4.resolve)(path2, ent2.name);
|
|
removedAll = rimrafMoveRemoveDirSync(p, opt, ent2) && removedAll;
|
|
}
|
|
if (!removedAll) {
|
|
return false;
|
|
}
|
|
if (opt.preserveRoot === false && path2 === (0, import_path4.parse)(path2).root) {
|
|
return false;
|
|
}
|
|
if (opt.filter && !opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
ignoreENOENTSync(() => tmpUnlinkSync(path2, tmp, import_fs3.rmdirSync));
|
|
return true;
|
|
};
|
|
var tmpUnlinkSync = (path2, tmp, rmSync2) => {
|
|
const tmpFile = (0, import_path4.resolve)(tmp, uniqueFilename(path2));
|
|
(0, import_fs3.renameSync)(path2, tmpFile);
|
|
return rmSync2(tmpFile);
|
|
};
|
|
var { unlink: unlink4, rmdir: rmdir4, lstat: lstat5 } = promises;
|
|
var rimrafWindowsFile = retryBusy(fixEPERM(unlink4));
|
|
var rimrafWindowsFileSync = retryBusySync(fixEPERMSync(import_fs3.unlinkSync));
|
|
var rimrafWindowsDirRetry = retryBusy(fixEPERM(rmdir4));
|
|
var rimrafWindowsDirRetrySync = retryBusySync(fixEPERMSync(import_fs3.rmdirSync));
|
|
var rimrafWindowsDirMoveRemoveFallback = async (path2, opt) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
const { filter: filter2, ...options } = opt;
|
|
try {
|
|
return await rimrafWindowsDirRetry(path2, options);
|
|
} catch (er) {
|
|
if (er?.code === "ENOTEMPTY") {
|
|
return await rimrafMoveRemove(path2, options);
|
|
}
|
|
throw er;
|
|
}
|
|
};
|
|
var rimrafWindowsDirMoveRemoveFallbackSync = (path2, opt) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
const { filter: filter2, ...options } = opt;
|
|
try {
|
|
return rimrafWindowsDirRetrySync(path2, options);
|
|
} catch (er) {
|
|
const fer = er;
|
|
if (fer?.code === "ENOTEMPTY") {
|
|
return rimrafMoveRemoveSync(path2, options);
|
|
}
|
|
throw er;
|
|
}
|
|
};
|
|
var START = Symbol("start");
|
|
var CHILD = Symbol("child");
|
|
var FINISH = Symbol("finish");
|
|
var rimrafWindows = async (path2, opt) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
try {
|
|
return await rimrafWindowsDir(path2, opt, await lstat5(path2), START);
|
|
} catch (er) {
|
|
if (er?.code === "ENOENT")
|
|
return true;
|
|
throw er;
|
|
}
|
|
};
|
|
var rimrafWindowsSync = (path2, opt) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
try {
|
|
return rimrafWindowsDirSync(path2, opt, (0, import_fs3.lstatSync)(path2), START);
|
|
} catch (er) {
|
|
if (er?.code === "ENOENT")
|
|
return true;
|
|
throw er;
|
|
}
|
|
};
|
|
var rimrafWindowsDir = async (path2, opt, ent, state = START) => {
|
|
if (opt?.signal?.aborted) {
|
|
throw opt.signal.reason;
|
|
}
|
|
const entries = ent.isDirectory() ? await readdirOrError(path2) : null;
|
|
if (!Array.isArray(entries)) {
|
|
if (entries) {
|
|
if (entries.code === "ENOENT") {
|
|
return true;
|
|
}
|
|
if (entries.code !== "ENOTDIR") {
|
|
throw entries;
|
|
}
|
|
}
|
|
if (opt.filter && !await opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
await ignoreENOENT(rimrafWindowsFile(path2, opt));
|
|
return true;
|
|
}
|
|
const s = state === START ? CHILD : state;
|
|
const removedAll = (await Promise.all(entries.map((ent2) => rimrafWindowsDir((0, import_path3.resolve)(path2, ent2.name), opt, ent2, s)))).reduce((a, b) => a && b, true);
|
|
if (state === START) {
|
|
return rimrafWindowsDir(path2, opt, ent, FINISH);
|
|
} else if (state === FINISH) {
|
|
if (opt.preserveRoot === false && path2 === (0, import_path3.parse)(path2).root) {
|
|
return false;
|
|
}
|
|
if (!removedAll) {
|
|
return false;
|
|
}
|
|
if (opt.filter && !await opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
await ignoreENOENT(rimrafWindowsDirMoveRemoveFallback(path2, opt));
|
|
}
|
|
return true;
|
|
};
|
|
var rimrafWindowsDirSync = (path2, opt, ent, state = START) => {
|
|
const entries = ent.isDirectory() ? readdirOrErrorSync(path2) : null;
|
|
if (!Array.isArray(entries)) {
|
|
if (entries) {
|
|
if (entries.code === "ENOENT") {
|
|
return true;
|
|
}
|
|
if (entries.code !== "ENOTDIR") {
|
|
throw entries;
|
|
}
|
|
}
|
|
if (opt.filter && !opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
ignoreENOENTSync(() => rimrafWindowsFileSync(path2, opt));
|
|
return true;
|
|
}
|
|
let removedAll = true;
|
|
for (const ent2 of entries) {
|
|
const s = state === START ? CHILD : state;
|
|
const p = (0, import_path3.resolve)(path2, ent2.name);
|
|
removedAll = rimrafWindowsDirSync(p, opt, ent2, s) && removedAll;
|
|
}
|
|
if (state === START) {
|
|
return rimrafWindowsDirSync(path2, opt, ent, FINISH);
|
|
} else if (state === FINISH) {
|
|
if (opt.preserveRoot === false && path2 === (0, import_path3.parse)(path2).root) {
|
|
return false;
|
|
}
|
|
if (!removedAll) {
|
|
return false;
|
|
}
|
|
if (opt.filter && !opt.filter(path2, ent)) {
|
|
return false;
|
|
}
|
|
ignoreENOENTSync(() => {
|
|
rimrafWindowsDirMoveRemoveFallbackSync(path2, opt);
|
|
});
|
|
}
|
|
return true;
|
|
};
|
|
var rimrafManual = platform_default === "win32" ? rimrafWindows : rimrafPosix;
|
|
var rimrafManualSync = platform_default === "win32" ? rimrafWindowsSync : rimrafPosixSync;
|
|
var { rm: rm2 } = promises;
|
|
var rimrafNative = async (path2, opt) => {
|
|
await rm2(path2, {
|
|
...opt,
|
|
force: true,
|
|
recursive: true
|
|
});
|
|
return true;
|
|
};
|
|
var rimrafNativeSync = (path2, opt) => {
|
|
(0, import_fs3.rmSync)(path2, {
|
|
...opt,
|
|
force: true,
|
|
recursive: true
|
|
});
|
|
return true;
|
|
};
|
|
var version = process.env.__TESTING_RIMRAF_NODE_VERSION__ || process.version;
|
|
var versArr = version.replace(/^v/, "").split(".");
|
|
var [major = 0, minor = 0] = versArr.map((v) => parseInt(v, 10));
|
|
var hasNative = major > 14 || major === 14 && minor >= 14;
|
|
var useNative = !hasNative || platform_default === "win32" ? () => false : (opt) => !opt?.signal && !opt?.filter;
|
|
var useNativeSync = !hasNative || platform_default === "win32" ? () => false : (opt) => !opt?.signal && !opt?.filter;
|
|
var wrap = (fn) => async (path2, opt) => {
|
|
const options = optArg(opt);
|
|
if (options.glob) {
|
|
path2 = await glob(path2, options.glob);
|
|
}
|
|
if (Array.isArray(path2)) {
|
|
return !!(await Promise.all(path2.map((p) => fn(path_arg_default(p, options), options)))).reduce((a, b) => a && b, true);
|
|
} else {
|
|
return !!await fn(path_arg_default(path2, options), options);
|
|
}
|
|
};
|
|
var wrapSync = (fn) => (path2, opt) => {
|
|
const options = optArgSync(opt);
|
|
if (options.glob) {
|
|
path2 = globSync(path2, options.glob);
|
|
}
|
|
if (Array.isArray(path2)) {
|
|
return !!path2.map((p) => fn(path_arg_default(p, options), options)).reduce((a, b) => a && b, true);
|
|
} else {
|
|
return !!fn(path_arg_default(path2, options), options);
|
|
}
|
|
};
|
|
var nativeSync = wrapSync(rimrafNativeSync);
|
|
var native = Object.assign(wrap(rimrafNative), { sync: nativeSync });
|
|
var manualSync = wrapSync(rimrafManualSync);
|
|
var manual = Object.assign(wrap(rimrafManual), { sync: manualSync });
|
|
var windowsSync = wrapSync(rimrafWindowsSync);
|
|
var windows = Object.assign(wrap(rimrafWindows), { sync: windowsSync });
|
|
var posixSync = wrapSync(rimrafPosixSync);
|
|
var posix2 = Object.assign(wrap(rimrafPosix), { sync: posixSync });
|
|
var moveRemoveSync = wrapSync(rimrafMoveRemoveSync);
|
|
var moveRemove = Object.assign(wrap(rimrafMoveRemove), {
|
|
sync: moveRemoveSync
|
|
});
|
|
var rimrafSync = wrapSync((path2, opt) => useNativeSync(opt) ? rimrafNativeSync(path2, opt) : rimrafManualSync(path2, opt));
|
|
var rimraf_ = wrap((path2, opt) => useNative(opt) ? rimrafNative(path2, opt) : rimrafManual(path2, opt));
|
|
var rimraf = Object.assign(rimraf_, {
|
|
rimraf: rimraf_,
|
|
sync: rimrafSync,
|
|
rimrafSync,
|
|
manual,
|
|
manualSync,
|
|
native,
|
|
nativeSync,
|
|
posix: posix2,
|
|
posixSync,
|
|
windows,
|
|
windowsSync,
|
|
moveRemove,
|
|
moveRemoveSync
|
|
});
|
|
rimraf.rimraf = rimraf;
|