2319 lines
76 KiB
JavaScript
Executable File
2319 lines
76 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
"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 __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
|
|
));
|
|
|
|
// src/cli.ts
|
|
var path4 = __toESM(require("path"));
|
|
var fs3 = __toESM(require("fs"));
|
|
var os2 = __toESM(require("os"));
|
|
var import_child_process2 = require("child_process");
|
|
|
|
// src/core/platform.ts
|
|
var os = __toESM(require("os"));
|
|
var systemPatterns = {
|
|
linux: "linux",
|
|
darwin: "(darwin|macos|mac|osx)",
|
|
win32: "(windows|win)"
|
|
};
|
|
var archPatterns = {
|
|
x64: "(x86_64|x64|amd64)",
|
|
arm64: "(aarch64|arm64)"
|
|
};
|
|
function getPlatformInfo(overrides) {
|
|
const system = (overrides?.system || os.platform()).toLowerCase();
|
|
const arch2 = (overrides?.arch || os.arch()).toLowerCase();
|
|
return {
|
|
system,
|
|
arch: arch2,
|
|
systemPattern: systemPatterns[system] || system,
|
|
archPattern: archPatterns[arch2] || arch2
|
|
};
|
|
}
|
|
|
|
// node_modules/balanced-match/dist/esm/index.js
|
|
var balanced = (a, b, str) => {
|
|
const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
|
|
const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
|
|
const r = ma !== null && mb != null && range(ma, mb, str);
|
|
return r && {
|
|
start: r[0],
|
|
end: r[1],
|
|
pre: str.slice(0, r[0]),
|
|
body: str.slice(r[0] + ma.length, r[1]),
|
|
post: str.slice(r[1] + mb.length)
|
|
};
|
|
};
|
|
var maybeMatch = (reg, str) => {
|
|
const m = str.match(reg);
|
|
return m ? m[0] : null;
|
|
};
|
|
var range = (a, b, str) => {
|
|
let begs, beg, left, right = void 0, result;
|
|
let ai = str.indexOf(a);
|
|
let bi = str.indexOf(b, ai + 1);
|
|
let 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) {
|
|
const r = begs.pop();
|
|
if (r !== void 0)
|
|
result = [r, bi];
|
|
} else {
|
|
beg = begs.pop();
|
|
if (beg !== void 0 && beg < left) {
|
|
left = beg;
|
|
right = bi;
|
|
}
|
|
bi = str.indexOf(b, i + 1);
|
|
}
|
|
i = ai < bi && ai >= 0 ? ai : bi;
|
|
}
|
|
if (begs.length && right !== void 0) {
|
|
result = [left, right];
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
|
|
// node_modules/brace-expansion/dist/esm/index.js
|
|
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";
|
|
var escSlashPattern = new RegExp(escSlash, "g");
|
|
var escOpenPattern = new RegExp(escOpen, "g");
|
|
var escClosePattern = new RegExp(escClose, "g");
|
|
var escCommaPattern = new RegExp(escComma, "g");
|
|
var escPeriodPattern = new RegExp(escPeriod, "g");
|
|
var slashPattern = /\\\\/g;
|
|
var openPattern = /\\{/g;
|
|
var closePattern = /\\}/g;
|
|
var commaPattern = /\\,/g;
|
|
var periodPattern = /\\\./g;
|
|
var EXPANSION_MAX = 1e5;
|
|
function numeric(str) {
|
|
return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
|
|
}
|
|
function escapeBraces(str) {
|
|
return str.replace(slashPattern, escSlash).replace(openPattern, escOpen).replace(closePattern, escClose).replace(commaPattern, escComma).replace(periodPattern, escPeriod);
|
|
}
|
|
function unescapeBraces(str) {
|
|
return str.replace(escSlashPattern, "\\").replace(escOpenPattern, "{").replace(escClosePattern, "}").replace(escCommaPattern, ",").replace(escPeriodPattern, ".");
|
|
}
|
|
function parseCommaParts(str) {
|
|
if (!str) {
|
|
return [""];
|
|
}
|
|
const parts = [];
|
|
const m = balanced("{", "}", str);
|
|
if (!m) {
|
|
return str.split(",");
|
|
}
|
|
const { pre, body, post } = m;
|
|
const p = pre.split(",");
|
|
p[p.length - 1] += "{" + body + "}";
|
|
const 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 expand(str, options = {}) {
|
|
if (!str) {
|
|
return [];
|
|
}
|
|
const { max = EXPANSION_MAX } = options;
|
|
if (str.slice(0, 2) === "{}") {
|
|
str = "\\{\\}" + str.slice(2);
|
|
}
|
|
return expand_(escapeBraces(str), max, 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 expand_(str, max, isTop) {
|
|
const expansions = [];
|
|
const m = balanced("{", "}", str);
|
|
if (!m)
|
|
return [str];
|
|
const pre = m.pre;
|
|
const post = m.post.length ? expand_(m.post, max, false) : [""];
|
|
if (/\$$/.test(m.pre)) {
|
|
for (let k = 0; k < post.length && k < max; k++) {
|
|
const expansion = pre + "{" + m.body + "}" + post[k];
|
|
expansions.push(expansion);
|
|
}
|
|
} else {
|
|
const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
|
|
const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
|
|
const isSequence = isNumericSequence || isAlphaSequence;
|
|
const isOptions = m.body.indexOf(",") >= 0;
|
|
if (!isSequence && !isOptions) {
|
|
if (m.post.match(/,(?!,).*\}/)) {
|
|
str = m.pre + "{" + m.body + escClose + m.post;
|
|
return expand_(str, max, true);
|
|
}
|
|
return [str];
|
|
}
|
|
let n;
|
|
if (isSequence) {
|
|
n = m.body.split(/\.\./);
|
|
} else {
|
|
n = parseCommaParts(m.body);
|
|
if (n.length === 1 && n[0] !== void 0) {
|
|
n = expand_(n[0], max, false).map(embrace);
|
|
if (n.length === 1) {
|
|
return post.map((p) => m.pre + n[0] + p);
|
|
}
|
|
}
|
|
}
|
|
let N;
|
|
if (isSequence && n[0] !== void 0 && n[1] !== void 0) {
|
|
const x = numeric(n[0]);
|
|
const y = numeric(n[1]);
|
|
const width = Math.max(n[0].length, n[1].length);
|
|
let incr = n.length === 3 && n[2] !== void 0 ? Math.max(Math.abs(numeric(n[2])), 1) : 1;
|
|
let test = lte;
|
|
const reverse = y < x;
|
|
if (reverse) {
|
|
incr *= -1;
|
|
test = gte;
|
|
}
|
|
const pad = n.some(isPadded);
|
|
N = [];
|
|
for (let i = x; test(i, y); i += incr) {
|
|
let c;
|
|
if (isAlphaSequence) {
|
|
c = String.fromCharCode(i);
|
|
if (c === "\\") {
|
|
c = "";
|
|
}
|
|
} else {
|
|
c = String(i);
|
|
if (pad) {
|
|
const need = width - c.length;
|
|
if (need > 0) {
|
|
const 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 (let j = 0; j < n.length; j++) {
|
|
N.push.apply(N, expand_(n[j], max, false));
|
|
}
|
|
}
|
|
for (let j = 0; j < N.length; j++) {
|
|
for (let k = 0; k < post.length && expansions.length < max; k++) {
|
|
const expansion = pre + N[j] + post[k];
|
|
if (!isTop || isSequence || expansion) {
|
|
expansions.push(expansion);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return expansions;
|
|
}
|
|
|
|
// node_modules/minimatch/dist/esm/assert-valid-pattern.js
|
|
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");
|
|
}
|
|
};
|
|
|
|
// node_modules/minimatch/dist/esm/brace-expressions.js
|
|
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 = (glob, position) => {
|
|
const pos = position;
|
|
if (glob.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 < glob.length) {
|
|
const c = glob.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 (glob.startsWith(cls, i)) {
|
|
if (rangeStart) {
|
|
return ["$.", false, glob.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 (glob.startsWith("-]", i + 1)) {
|
|
ranges.push(braceEscape(c + "-"));
|
|
i += 2;
|
|
continue;
|
|
}
|
|
if (glob.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, glob.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];
|
|
};
|
|
|
|
// node_modules/minimatch/dist/esm/unescape.js
|
|
var unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => {
|
|
if (magicalBraces) {
|
|
return windowsPathsNoEscape ? s.replace(/\[([^/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^/\\])\]/g, "$1$2").replace(/\\([^/])/g, "$1");
|
|
}
|
|
return windowsPathsNoEscape ? s.replace(/\[([^/\\{}])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^/\\{}])\]/g, "$1$2").replace(/\\([^/{}])/g, "$1");
|
|
};
|
|
|
|
// node_modules/minimatch/dist/esm/ast.js
|
|
var _a;
|
|
var types = /* @__PURE__ */ new Set(["!", "?", "+", "*", "@"]);
|
|
var isExtglobType = (c) => types.has(c);
|
|
var isExtglobAST = (c) => isExtglobType(c.type);
|
|
var adoptionMap = /* @__PURE__ */ new Map([
|
|
["!", ["@"]],
|
|
["?", ["?", "@"]],
|
|
["@", ["@"]],
|
|
["*", ["*", "+", "?", "@"]],
|
|
["+", ["+", "@"]]
|
|
]);
|
|
var adoptionWithSpaceMap = /* @__PURE__ */ new Map([
|
|
["!", ["?"]],
|
|
["@", ["?"]],
|
|
["+", ["?", "*"]]
|
|
]);
|
|
var adoptionAnyMap = /* @__PURE__ */ new Map([
|
|
["!", ["?", "@"]],
|
|
["?", ["?", "@"]],
|
|
["@", ["?", "@"]],
|
|
["*", ["*", "+", "?", "@"]],
|
|
["+", ["+", "@", "?", "*"]]
|
|
]);
|
|
var usurpMap = /* @__PURE__ */ new Map([
|
|
["!", /* @__PURE__ */ new Map([["!", "@"]])],
|
|
[
|
|
"?",
|
|
/* @__PURE__ */ new Map([
|
|
["*", "*"],
|
|
["+", "*"]
|
|
])
|
|
],
|
|
[
|
|
"@",
|
|
/* @__PURE__ */ new Map([
|
|
["!", "!"],
|
|
["?", "?"],
|
|
["@", "@"],
|
|
["*", "*"],
|
|
["+", "+"]
|
|
])
|
|
],
|
|
[
|
|
"+",
|
|
/* @__PURE__ */ new Map([
|
|
["?", "*"],
|
|
["*", "*"]
|
|
])
|
|
]
|
|
]);
|
|
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 ID = 0;
|
|
var AST = class {
|
|
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;
|
|
id = ++ID;
|
|
get depth() {
|
|
return (this.#parent?.depth ?? -1) + 1;
|
|
}
|
|
[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
|
|
return {
|
|
"@@type": "AST",
|
|
id: this.id,
|
|
type: this.type,
|
|
root: this.#root.id,
|
|
parent: this.#parent?.id,
|
|
depth: this.depth,
|
|
partsLength: this.#parts.length,
|
|
parts: this.#parts
|
|
};
|
|
}
|
|
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() {
|
|
return this.#toString !== void 0 ? this.#toString : !this.type ? this.#toString = this.#parts.map((p) => String(p)).join("") : 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 _a && 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 _a && 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 _a(this.type, parent);
|
|
for (const p of this.#parts) {
|
|
c.copyIn(p);
|
|
}
|
|
return c;
|
|
}
|
|
static #parseAST(str, ast, pos, opt, extDepth) {
|
|
const maxDepth = opt.maxExtglobRecursion ?? 2;
|
|
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;
|
|
}
|
|
const doRecurse = !opt.noext && isExtglobType(c) && str.charAt(i2) === "(" && extDepth <= maxDepth;
|
|
if (doRecurse) {
|
|
ast.push(acc2);
|
|
acc2 = "";
|
|
const ext2 = new _a(c, ast);
|
|
i2 = _a.#parseAST(str, ext2, i2, opt, extDepth + 1);
|
|
ast.push(ext2);
|
|
continue;
|
|
}
|
|
acc2 += c;
|
|
}
|
|
ast.push(acc2);
|
|
return i2;
|
|
}
|
|
let i = pos + 1;
|
|
let part = new _a(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;
|
|
}
|
|
const doRecurse = !opt.noext && isExtglobType(c) && str.charAt(i) === "(" && /* c8 ignore start - the maxDepth is sufficient here */
|
|
(extDepth <= maxDepth || ast && ast.#canAdoptType(c));
|
|
if (doRecurse) {
|
|
const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;
|
|
part.push(acc);
|
|
acc = "";
|
|
const ext2 = new _a(c, part);
|
|
part.push(ext2);
|
|
i = _a.#parseAST(str, ext2, i, opt, extDepth + depthAdd);
|
|
continue;
|
|
}
|
|
if (c === "|") {
|
|
part.push(acc);
|
|
acc = "";
|
|
parts.push(part);
|
|
part = new _a(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;
|
|
}
|
|
#canAdoptWithSpace(child) {
|
|
return this.#canAdopt(child, adoptionWithSpaceMap);
|
|
}
|
|
#canAdopt(child, map = adoptionMap) {
|
|
if (!child || typeof child !== "object" || child.type !== null || child.#parts.length !== 1 || this.type === null) {
|
|
return false;
|
|
}
|
|
const gc = child.#parts[0];
|
|
if (!gc || typeof gc !== "object" || gc.type === null) {
|
|
return false;
|
|
}
|
|
return this.#canAdoptType(gc.type, map);
|
|
}
|
|
#canAdoptType(c, map = adoptionAnyMap) {
|
|
return !!map.get(this.type)?.includes(c);
|
|
}
|
|
#adoptWithSpace(child, index) {
|
|
const gc = child.#parts[0];
|
|
const blank = new _a(null, gc, this.options);
|
|
blank.#parts.push("");
|
|
gc.push(blank);
|
|
this.#adopt(child, index);
|
|
}
|
|
#adopt(child, index) {
|
|
const gc = child.#parts[0];
|
|
this.#parts.splice(index, 1, ...gc.#parts);
|
|
for (const p of gc.#parts) {
|
|
if (typeof p === "object")
|
|
p.#parent = this;
|
|
}
|
|
this.#toString = void 0;
|
|
}
|
|
#canUsurpType(c) {
|
|
const m = usurpMap.get(this.type);
|
|
return !!m?.has(c);
|
|
}
|
|
#canUsurp(child) {
|
|
if (!child || typeof child !== "object" || child.type !== null || child.#parts.length !== 1 || this.type === null || this.#parts.length !== 1) {
|
|
return false;
|
|
}
|
|
const gc = child.#parts[0];
|
|
if (!gc || typeof gc !== "object" || gc.type === null) {
|
|
return false;
|
|
}
|
|
return this.#canUsurpType(gc.type);
|
|
}
|
|
#usurp(child) {
|
|
const m = usurpMap.get(this.type);
|
|
const gc = child.#parts[0];
|
|
const nt = m?.get(gc.type);
|
|
if (!nt)
|
|
return false;
|
|
this.#parts = gc.#parts;
|
|
for (const p of this.#parts) {
|
|
if (typeof p === "object") {
|
|
p.#parent = this;
|
|
}
|
|
}
|
|
this.type = nt;
|
|
this.#toString = void 0;
|
|
this.#emptyExt = false;
|
|
}
|
|
static fromGlob(pattern, options = {}) {
|
|
const ast = new _a(null, void 0, options);
|
|
_a.#parseAST(pattern, ast, 0, options, 0);
|
|
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 glob = this.toString();
|
|
const [re, body, hasMagic, uflag] = this.toRegExpSource();
|
|
const anyMagic = hasMagic || this.#hasMagic || this.#options.nocase && !this.#options.nocaseMagicOnly && glob.toUpperCase() !== glob.toLowerCase();
|
|
if (!anyMagic) {
|
|
return body;
|
|
}
|
|
const flags = (this.#options.nocase ? "i" : "") + (uflag ? "u" : "");
|
|
return Object.assign(new RegExp(`^${re}$`, flags), {
|
|
_src: re,
|
|
_glob: glob
|
|
});
|
|
}
|
|
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.#flatten();
|
|
this.#fillNegs();
|
|
}
|
|
if (!isExtglobAST(this)) {
|
|
const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string");
|
|
const src = this.#parts.map((p) => {
|
|
const [re, _, hasMagic, uflag] = typeof p === "string" ? _a.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
|
|
this.#hasMagic = this.#hasMagic || hasMagic;
|
|
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();
|
|
const me = this;
|
|
me.#parts = [s];
|
|
me.type = null;
|
|
me.#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
|
|
];
|
|
}
|
|
#flatten() {
|
|
if (!isExtglobAST(this)) {
|
|
for (const p of this.#parts) {
|
|
if (typeof p === "object") {
|
|
p.#flatten();
|
|
}
|
|
}
|
|
} else {
|
|
let iterations = 0;
|
|
let done = false;
|
|
do {
|
|
done = true;
|
|
for (let i = 0; i < this.#parts.length; i++) {
|
|
const c = this.#parts[i];
|
|
if (typeof c === "object") {
|
|
c.#flatten();
|
|
if (this.#canAdopt(c)) {
|
|
done = false;
|
|
this.#adopt(c, i);
|
|
} else if (this.#canAdoptWithSpace(c)) {
|
|
done = false;
|
|
this.#adoptWithSpace(c, i);
|
|
} else if (this.#canUsurp(c)) {
|
|
done = false;
|
|
this.#usurp(c);
|
|
}
|
|
}
|
|
}
|
|
} while (!done && ++iterations < 10);
|
|
}
|
|
this.#toString = void 0;
|
|
}
|
|
#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(glob, hasMagic, noEmpty = false) {
|
|
let escaping = false;
|
|
let re = "";
|
|
let uflag = false;
|
|
let inStar = false;
|
|
for (let i = 0; i < glob.length; i++) {
|
|
const c = glob.charAt(i);
|
|
if (escaping) {
|
|
escaping = false;
|
|
re += (reSpecials.has(c) ? "\\" : "") + c;
|
|
continue;
|
|
}
|
|
if (c === "*") {
|
|
if (inStar)
|
|
continue;
|
|
inStar = true;
|
|
re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star;
|
|
hasMagic = true;
|
|
continue;
|
|
} else {
|
|
inStar = false;
|
|
}
|
|
if (c === "\\") {
|
|
if (i === glob.length - 1) {
|
|
re += "\\\\";
|
|
} else {
|
|
escaping = true;
|
|
}
|
|
continue;
|
|
}
|
|
if (c === "[") {
|
|
const [src, needUflag, consumed, magic] = parseClass(glob, i);
|
|
if (consumed) {
|
|
re += src;
|
|
uflag = uflag || needUflag;
|
|
i += consumed - 1;
|
|
hasMagic = hasMagic || magic;
|
|
continue;
|
|
}
|
|
}
|
|
if (c === "?") {
|
|
re += qmark;
|
|
hasMagic = true;
|
|
continue;
|
|
}
|
|
re += regExpEscape(c);
|
|
}
|
|
return [re, unescape(glob), !!hasMagic, uflag];
|
|
}
|
|
};
|
|
_a = AST;
|
|
|
|
// node_modules/minimatch/dist/esm/escape.js
|
|
var escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => {
|
|
if (magicalBraces) {
|
|
return windowsPathsNoEscape ? s.replace(/[?*()[\]{}]/g, "[$&]") : s.replace(/[?*()[\]\\{}]/g, "\\$&");
|
|
}
|
|
return windowsPathsNoEscape ? s.replace(/[?*()[\]]/g, "[$&]") : s.replace(/[?*()[\]\\]/g, "\\$&");
|
|
};
|
|
|
|
// node_modules/minimatch/dist/esm/index.js
|
|
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 = /* @__PURE__ */ 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 expand(pattern, { max: options.braceExpandMax });
|
|
};
|
|
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;
|
|
maxGlobstarRecursion;
|
|
regexp;
|
|
constructor(pattern, options = {}) {
|
|
assertValidPattern(pattern);
|
|
options = options || {};
|
|
this.options = options;
|
|
this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200;
|
|
this.pattern = pattern;
|
|
this.platform = options.platform || defaultPlatform;
|
|
this.isWindows = this.platform === "win32";
|
|
const awe = "allowWindowsEscape";
|
|
this.windowsPathsNoEscape = !!options.windowsPathsNoEscape || options[awe] === 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 (const partset of globParts) {
|
|
for (let j = 0; j < partset.length; j++) {
|
|
if (partset[j] === "**") {
|
|
partset[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 !== "**" && !(this.isWindows && /^[a-z]:$/i.test(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) {
|
|
let fileStartIndex = 0;
|
|
let patternStartIndex = 0;
|
|
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;
|
|
patternStartIndex = pdi;
|
|
fileStartIndex = fdi;
|
|
}
|
|
}
|
|
}
|
|
const { optimizationLevel = 1 } = this.options;
|
|
if (optimizationLevel >= 2) {
|
|
file = this.levelTwoFileOptimize(file);
|
|
}
|
|
if (pattern.includes(GLOBSTAR)) {
|
|
return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
|
|
}
|
|
return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
|
|
}
|
|
#matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
|
|
const firstgs = pattern.indexOf(GLOBSTAR, patternIndex);
|
|
const lastgs = pattern.lastIndexOf(GLOBSTAR);
|
|
const [head, body, tail] = partial ? [
|
|
pattern.slice(patternIndex, firstgs),
|
|
pattern.slice(firstgs + 1),
|
|
[]
|
|
] : [
|
|
pattern.slice(patternIndex, firstgs),
|
|
pattern.slice(firstgs + 1, lastgs),
|
|
pattern.slice(lastgs + 1)
|
|
];
|
|
if (head.length) {
|
|
const fileHead = file.slice(fileIndex, fileIndex + head.length);
|
|
if (!this.#matchOne(fileHead, head, partial, 0, 0)) {
|
|
return false;
|
|
}
|
|
fileIndex += head.length;
|
|
patternIndex += head.length;
|
|
}
|
|
let fileTailMatch = 0;
|
|
if (tail.length) {
|
|
if (tail.length + fileIndex > file.length)
|
|
return false;
|
|
let tailStart = file.length - tail.length;
|
|
if (this.#matchOne(file, tail, partial, tailStart, 0)) {
|
|
fileTailMatch = tail.length;
|
|
} else {
|
|
if (file[file.length - 1] !== "" || fileIndex + tail.length === file.length) {
|
|
return false;
|
|
}
|
|
tailStart--;
|
|
if (!this.#matchOne(file, tail, partial, tailStart, 0)) {
|
|
return false;
|
|
}
|
|
fileTailMatch = tail.length + 1;
|
|
}
|
|
}
|
|
if (!body.length) {
|
|
let sawSome = !!fileTailMatch;
|
|
for (let i2 = fileIndex; i2 < file.length - fileTailMatch; i2++) {
|
|
const f = String(file[i2]);
|
|
sawSome = true;
|
|
if (f === "." || f === ".." || !this.options.dot && f.startsWith(".")) {
|
|
return false;
|
|
}
|
|
}
|
|
return partial || sawSome;
|
|
}
|
|
const bodySegments = [[[], 0]];
|
|
let currentBody = bodySegments[0];
|
|
let nonGsParts = 0;
|
|
const nonGsPartsSums = [0];
|
|
for (const b of body) {
|
|
if (b === GLOBSTAR) {
|
|
nonGsPartsSums.push(nonGsParts);
|
|
currentBody = [[], 0];
|
|
bodySegments.push(currentBody);
|
|
} else {
|
|
currentBody[0].push(b);
|
|
nonGsParts++;
|
|
}
|
|
}
|
|
let i = bodySegments.length - 1;
|
|
const fileLength = file.length - fileTailMatch;
|
|
for (const b of bodySegments) {
|
|
b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
|
|
}
|
|
return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
|
|
}
|
|
// return false for "nope, not matching"
|
|
// return null for "not matching, cannot keep trying"
|
|
#matchGlobStarBodySections(file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
|
|
const bs = bodySegments[bodyIndex];
|
|
if (!bs) {
|
|
for (let i = fileIndex; i < file.length; i++) {
|
|
sawTail = true;
|
|
const f = file[i];
|
|
if (f === "." || f === ".." || !this.options.dot && f.startsWith(".")) {
|
|
return false;
|
|
}
|
|
}
|
|
return sawTail;
|
|
}
|
|
const [body, after] = bs;
|
|
while (fileIndex <= after) {
|
|
const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
|
|
if (m && globStarDepth < this.maxGlobstarRecursion) {
|
|
const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
|
|
if (sub !== false) {
|
|
return sub;
|
|
}
|
|
}
|
|
const f = file[fileIndex];
|
|
if (f === "." || f === ".." || !this.options.dot && f.startsWith(".")) {
|
|
return false;
|
|
}
|
|
fileIndex++;
|
|
}
|
|
return partial || null;
|
|
}
|
|
#matchOne(file, pattern, partial, fileIndex, patternIndex) {
|
|
let fi;
|
|
let pi;
|
|
let pl;
|
|
let fl;
|
|
for (fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
|
|
this.debug("matchOne loop");
|
|
let p = pattern[pi];
|
|
let f = file[fi];
|
|
this.debug(pattern, p, f);
|
|
if (p === false || p === GLOBSTAR) {
|
|
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;
|
|
}
|
|
});
|
|
const filtered = pp.filter((p) => p !== GLOBSTAR);
|
|
if (this.partial && filtered.length >= 1) {
|
|
const prefixes = [];
|
|
for (let i = 1; i <= filtered.length; i++) {
|
|
prefixes.push(filtered.slice(0, i).join("/"));
|
|
}
|
|
return "(?:" + prefixes.join("|") + ")";
|
|
}
|
|
return filtered.join("/");
|
|
}).join("|");
|
|
const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
|
|
re = "^" + open + re + close + "$";
|
|
if (this.partial) {
|
|
re = "^(?:\\/|" + open + re.slice(1, -1) + close + ")$";
|
|
}
|
|
if (this.negate)
|
|
re = "^(?!" + re + ").+$";
|
|
try {
|
|
this.regexp = new RegExp(re, [...flags].join(""));
|
|
} catch {
|
|
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 (const pattern of set) {
|
|
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;
|
|
|
|
// src/core/matcher.ts
|
|
var knownFileTypes = {
|
|
archive: "*.{zip,tar.gz,tgz}",
|
|
package: "*.{deb,pkg,rpm}",
|
|
linux: "*.{deb,rpm}",
|
|
macos: "*.pkg",
|
|
targz: "*.{tgz,tar.gz}"
|
|
};
|
|
function filterByRegex(assets, pattern) {
|
|
const regex = new RegExp(pattern, "i");
|
|
return assets.filter((asset) => regex.test(asset.name));
|
|
}
|
|
function replacePlatformPlaceholders(pattern, platform2) {
|
|
return pattern.replace(/{{SYSTEM}}/g, platform2.systemPattern).replace(/{{ARCH}}/g, platform2.archPattern);
|
|
}
|
|
function getMatchingAsset(assets, platform2, fileName, fileType) {
|
|
if (fileName && !fileName.startsWith("~")) {
|
|
const exactMatches = assets.filter((asset) => asset.name === fileName);
|
|
if (exactMatches.length !== 1) {
|
|
throw new Error(`Expected exactly one asset to match the provided filename, matched: ${exactMatches.length}`);
|
|
}
|
|
return exactMatches[0];
|
|
}
|
|
let fileTypeFilteredAssets = assets;
|
|
if (fileType) {
|
|
if (Object.hasOwn(knownFileTypes, fileType)) {
|
|
const fileTypeGlob = knownFileTypes[fileType];
|
|
fileTypeFilteredAssets = assets.filter((asset) => minimatch(asset.name, fileTypeGlob, { nocase: true }));
|
|
} else if (fileType.startsWith("~")) {
|
|
const fileTypeRegex = `${fileType.substring(1)}$`;
|
|
fileTypeFilteredAssets = filterByRegex(assets, fileTypeRegex);
|
|
} else {
|
|
const extension = fileType.replace(/^\./, "");
|
|
const fileTypeGlob = `*.${extension}`;
|
|
fileTypeFilteredAssets = assets.filter((asset) => minimatch(asset.name, fileTypeGlob, { nocase: true }));
|
|
}
|
|
}
|
|
if (fileName && fileName.startsWith("~")) {
|
|
const fileNamePattern = replacePlatformPlaceholders(fileName.substring(1), platform2);
|
|
const fileNameFilteredAssets = filterByRegex(fileTypeFilteredAssets, fileNamePattern);
|
|
if (fileNameFilteredAssets.length !== 1) {
|
|
throw new Error(`Expected exactly one asset to match the filename regex, matched: ${fileNameFilteredAssets.length}`);
|
|
}
|
|
return fileNameFilteredAssets[0];
|
|
}
|
|
const defaultPattern = replacePlatformPlaceholders("{{SYSTEM}}[_-]{{ARCH}}", platform2);
|
|
const defaultFilteredAssets = filterByRegex(fileTypeFilteredAssets, defaultPattern);
|
|
if (defaultFilteredAssets.length !== 1) {
|
|
const errorMessage = defaultFilteredAssets.length === 0 ? `No assets matched the default criteria: ${defaultPattern}` : `Multiple assets matched the default criteria: ${defaultFilteredAssets.map((asset) => asset.name).join(", ")}`;
|
|
throw new Error(errorMessage);
|
|
}
|
|
return defaultFilteredAssets[0];
|
|
}
|
|
|
|
// src/core/finder.ts
|
|
var fs = __toESM(require("fs"));
|
|
var path2 = __toESM(require("path"));
|
|
function findBinary(dir, pattern, debug, logger) {
|
|
const items = fs.readdirSync(dir);
|
|
if (debug) {
|
|
logger(`Searching for binary in ${dir}...`);
|
|
items.forEach((item) => logger(` - ${item}`));
|
|
}
|
|
for (const item of items) {
|
|
const fullPath = path2.join(dir, item);
|
|
const stat = fs.statSync(fullPath);
|
|
if (stat.isDirectory()) {
|
|
const found = findBinary(fullPath, pattern, debug, logger);
|
|
if (found) return found;
|
|
} else {
|
|
let isMatch = false;
|
|
if (pattern instanceof RegExp) {
|
|
isMatch = pattern.test(item);
|
|
} else {
|
|
isMatch = item === pattern;
|
|
if (!isMatch && process.platform === "win32" && !pattern.toLowerCase().endsWith(".exe")) {
|
|
isMatch = item.toLowerCase() === `${pattern.toLowerCase()}.exe`;
|
|
}
|
|
}
|
|
if (isMatch) return fullPath;
|
|
}
|
|
}
|
|
return void 0;
|
|
}
|
|
|
|
// src/core/downloader.ts
|
|
function getGithubApiHeaders(token) {
|
|
const headers = {
|
|
"Accept": "application/vnd.github.v3+json",
|
|
"User-Agent": "setup-github-release-action"
|
|
};
|
|
if (token) {
|
|
headers["Authorization"] = `token ${token}`;
|
|
}
|
|
return headers;
|
|
}
|
|
async function fetchLatestRelease(repository, token) {
|
|
const url = `https://api.github.com/repos/${repository}/releases/latest`;
|
|
const headers = getGithubApiHeaders(token);
|
|
const response = await fetch(url, { headers });
|
|
if (!response.ok) {
|
|
const errorBody = await response.text();
|
|
throw new Error(`Failed to fetch latest release for ${repository}: ${response.statusText}. ${errorBody}`);
|
|
}
|
|
return await response.json();
|
|
}
|
|
async function fetchLatestReleaseRaw(repository, token) {
|
|
const url = `https://api.github.com/repos/${repository}/releases/latest`;
|
|
const headers = getGithubApiHeaders(token);
|
|
const response = await fetch(url, { headers });
|
|
const body = await response.text();
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch latest release for ${repository}: ${response.statusText}. ${body}`);
|
|
}
|
|
return body;
|
|
}
|
|
async function downloadAsset(url, destPath, token) {
|
|
const headers = {
|
|
"User-Agent": "setup-github-release-action"
|
|
};
|
|
if (token) {
|
|
headers["Authorization"] = `token ${token}`;
|
|
}
|
|
const response = await fetch(url, { headers });
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to download asset: ${response.statusText}`);
|
|
}
|
|
const fs4 = await import("fs");
|
|
const { Readable } = await import("stream");
|
|
const { finished } = await import("stream/promises");
|
|
const fileStream = fs4.createWriteStream(destPath);
|
|
await finished(Readable.fromWeb(response.body).pipe(fileStream));
|
|
}
|
|
|
|
// src/core/extractor.ts
|
|
var import_child_process = require("child_process");
|
|
var path3 = __toESM(require("path"));
|
|
var fs2 = __toESM(require("fs"));
|
|
async function extractAsset(filePath, destDir) {
|
|
const ext2 = path3.extname(filePath).toLowerCase();
|
|
const name = path3.basename(filePath).toLowerCase();
|
|
if (!fs2.existsSync(destDir)) {
|
|
fs2.mkdirSync(destDir, { recursive: true });
|
|
}
|
|
if (name.endsWith(".tar.gz") || name.endsWith(".tgz") || name.endsWith(".tar")) {
|
|
const args = ["-xf", filePath, "-C", destDir];
|
|
const result = (0, import_child_process.spawnSync)("tar", args);
|
|
if (result.status !== 0) {
|
|
throw new Error(`tar failed with status ${result.status}: ${result.stderr.toString()}`);
|
|
}
|
|
} else if (name.endsWith(".zip")) {
|
|
if (process.platform === "win32") {
|
|
const tarResult = (0, import_child_process.spawnSync)("tar", ["-xf", filePath, "-C", destDir]);
|
|
if (tarResult.status === 0) return;
|
|
const escapedFilePath = filePath.replace(/'/g, "''");
|
|
const escapedDestDir = destDir.replace(/'/g, "''");
|
|
const dotNetCommand = `Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFilePath}', '${escapedDestDir}')`;
|
|
for (const shell of ["pwsh", "powershell"]) {
|
|
const result = (0, import_child_process.spawnSync)(shell, ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", dotNetCommand]);
|
|
if (result.status === 0) return;
|
|
}
|
|
throw new Error(`Extraction failed: Both tar and PowerShell fallback failed. Make sure your system can extract ZIP files.`);
|
|
} else {
|
|
const result = (0, import_child_process.spawnSync)("unzip", ["-q", filePath, "-d", destDir]);
|
|
if (result.status !== 0) {
|
|
throw new Error(`unzip failed with status ${result.status}: ${result.stderr.toString()}`);
|
|
}
|
|
}
|
|
} else if (name.endsWith(".7z")) {
|
|
const result = (0, import_child_process.spawnSync)("7z", ["x", filePath, `-o${destDir}`, "-y"]);
|
|
if (result.status !== 0) {
|
|
throw new Error(`7z failed with status ${result.status}. Make sure 7z is installed.`);
|
|
}
|
|
} else {
|
|
const destPath = path3.join(destDir, path3.basename(filePath));
|
|
fs2.copyFileSync(filePath, destPath);
|
|
}
|
|
}
|
|
|
|
// src/cli.ts
|
|
function usage() {
|
|
return `
|
|
Usage: install-github-release [options] <repository>
|
|
|
|
Arguments:
|
|
repository The GitHub repository (owner/repo)
|
|
|
|
Options:
|
|
--dry-run [level] Run in test mode (default level: 1)
|
|
-l, --list [repository] List available assets from latest release and exit
|
|
-a, --app-name <name> Application name (optional, for output messages)
|
|
-f, --file-name <name> Asset file name or regex pattern (prefixed with ~)
|
|
-b, --binary-name <name> Binary name (supports source:destination form)
|
|
-t, --file-type <type> Known: archive|package|linux|macos|targz
|
|
Or custom: ~<regex> (end-of-string match) or extension (e.g. zip, .tar.gz)
|
|
-p, --install-path <path> Custom installation directory
|
|
-o, --output-directory <path>
|
|
Only download selected asset to the specified directory
|
|
-j, --releases-json Download latest release JSON only
|
|
--system <name> Override detected system for asset matching
|
|
--arch <name> Override detected architecture for asset matching
|
|
-k, --token <token> GitHub token
|
|
-d, --debug Enable debug logging
|
|
-h, --help Show this help message
|
|
`;
|
|
}
|
|
function ensureOptionValue(argv, index, option) {
|
|
const value = argv[index + 1];
|
|
if (!value || value.startsWith("-")) {
|
|
throw new Error(`Missing value for ${option}.`);
|
|
}
|
|
return value;
|
|
}
|
|
function parseCliArgs(argv) {
|
|
const envDryRun = process.env.TEST_MODE;
|
|
const dryRunLevelFromEnv = envDryRun && /^\d+$/.test(envDryRun) ? parseInt(envDryRun, 10) : 0;
|
|
const opts = {
|
|
releasesJsonOnly: false,
|
|
listOnly: false,
|
|
debug: false,
|
|
help: false,
|
|
dryRunLevel: dryRunLevelFromEnv,
|
|
positionals: []
|
|
};
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const arg = argv[i];
|
|
switch (arg) {
|
|
case "-h":
|
|
case "--help":
|
|
opts.help = true;
|
|
break;
|
|
case "--dry-run": {
|
|
const next = argv[i + 1];
|
|
if (next && /^\d+$/.test(next)) {
|
|
opts.dryRunLevel = parseInt(next, 10);
|
|
i++;
|
|
} else {
|
|
opts.dryRunLevel = 1;
|
|
}
|
|
break;
|
|
}
|
|
case "-l":
|
|
case "--list": {
|
|
opts.listOnly = true;
|
|
const next = argv[i + 1];
|
|
if (next && !next.startsWith("-")) {
|
|
opts.listRepo = next;
|
|
i++;
|
|
}
|
|
break;
|
|
}
|
|
case "-a":
|
|
case "--app-name":
|
|
opts.appName = ensureOptionValue(argv, i, arg);
|
|
i++;
|
|
break;
|
|
case "-f":
|
|
case "--file-name":
|
|
opts.fileName = ensureOptionValue(argv, i, arg);
|
|
i++;
|
|
break;
|
|
case "-b":
|
|
case "--binary-name":
|
|
opts.binaryName = ensureOptionValue(argv, i, arg);
|
|
i++;
|
|
break;
|
|
case "-t":
|
|
case "--file-type": {
|
|
const fileType = ensureOptionValue(argv, i, arg);
|
|
if (!fileType.trim()) {
|
|
throw new Error(`Unknown asset type: ${fileType}`);
|
|
}
|
|
opts.fileType = fileType;
|
|
i++;
|
|
break;
|
|
}
|
|
case "-p":
|
|
case "--install-path":
|
|
opts.installPath = ensureOptionValue(argv, i, arg);
|
|
i++;
|
|
break;
|
|
case "-o":
|
|
case "--output-directory":
|
|
opts.outputDirectory = ensureOptionValue(argv, i, arg);
|
|
i++;
|
|
break;
|
|
case "-j":
|
|
case "--releases-json":
|
|
opts.releasesJsonOnly = true;
|
|
break;
|
|
case "--system":
|
|
opts.systemOverride = ensureOptionValue(argv, i, arg);
|
|
i++;
|
|
break;
|
|
case "--arch":
|
|
opts.archOverride = ensureOptionValue(argv, i, arg);
|
|
i++;
|
|
break;
|
|
case "-k":
|
|
case "--token":
|
|
opts.token = ensureOptionValue(argv, i, arg);
|
|
i++;
|
|
break;
|
|
case "-d":
|
|
case "--debug":
|
|
opts.debug = true;
|
|
break;
|
|
default:
|
|
if (arg.startsWith("-")) {
|
|
throw new Error(`Unknown option: ${arg}`);
|
|
}
|
|
opts.positionals.push(arg);
|
|
break;
|
|
}
|
|
}
|
|
return opts;
|
|
}
|
|
function validateOutputDirectory(outputDirectory) {
|
|
const resolvedPath = path4.resolve(outputDirectory);
|
|
if (!fs3.existsSync(resolvedPath) || !fs3.statSync(resolvedPath).isDirectory()) {
|
|
throw new Error(`Output directory "${resolvedPath}" does not exist.`);
|
|
}
|
|
return resolvedPath;
|
|
}
|
|
function getInstallDir(installPath) {
|
|
if (installPath) {
|
|
return path4.resolve(installPath);
|
|
}
|
|
if (process.platform === "win32") {
|
|
const localAppData = process.env.LOCALAPPDATA || path4.join(os2.homedir(), "AppData", "Local");
|
|
return path4.join(localAppData, "bin");
|
|
}
|
|
const isRoot = process.getuid && process.getuid() === 0;
|
|
if (isRoot) {
|
|
return "/usr/local/bin";
|
|
}
|
|
const homeBin = path4.join(os2.homedir(), "bin");
|
|
if (fs3.existsSync(homeBin)) {
|
|
return homeBin;
|
|
}
|
|
return "/usr/local/bin";
|
|
}
|
|
function installSystemPackage(downloadPath) {
|
|
const fileName = path4.basename(downloadPath).toLowerCase();
|
|
const command = fileName.endsWith(".deb") ? { binary: "dpkg", args: ["-i", downloadPath] } : fileName.endsWith(".pkg") ? { binary: "installer", args: ["-pkg", downloadPath, "-target", "/"] } : fileName.endsWith(".rpm") ? { binary: "rpm", args: ["-i", downloadPath] } : void 0;
|
|
if (!command) {
|
|
throw new Error(`Unsupported package type: ${fileName}`);
|
|
}
|
|
const isRoot = process.getuid && process.getuid() === 0;
|
|
const commandToRun = isRoot ? command.binary : "sudo";
|
|
const argsToRun = isRoot ? command.args : [command.binary, ...command.args];
|
|
const result = (0, import_child_process2.spawnSync)(commandToRun, argsToRun, { stdio: "inherit" });
|
|
if (result.status !== 0) {
|
|
throw new Error(`Failed to install package using ${commandToRun} ${argsToRun.join(" ")}.`);
|
|
}
|
|
}
|
|
async function run() {
|
|
let tempDir;
|
|
try {
|
|
const options = parseCliArgs(process.argv.slice(2));
|
|
const repository = options.listRepo || options.positionals[0];
|
|
if (options.help || !repository) {
|
|
console.log(usage());
|
|
process.exit(options.help ? 0 : 1);
|
|
}
|
|
const token = options.token || process.env.GITHUB_TOKEN;
|
|
if (options.listOnly) {
|
|
const release2 = await fetchLatestRelease(repository, token);
|
|
release2.assets.forEach((asset2) => console.log(`- ${asset2.browser_download_url}`));
|
|
process.exit(0);
|
|
}
|
|
const toolName = repository.split("/").pop() || repository;
|
|
const appName = options.appName || toolName.charAt(0).toUpperCase() + toolName.slice(1);
|
|
const binaryOption = options.binaryName || toolName;
|
|
const [binarySource, binaryDestination] = binaryOption.includes(":") ? [binaryOption.split(":")[0], binaryOption.split(":")[1]] : [binaryOption, binaryOption];
|
|
if (options.releasesJsonOnly) {
|
|
const rawRelease = await fetchLatestReleaseRaw(repository, token);
|
|
const outputBase = binaryDestination || toolName;
|
|
const outputName = `${outputBase}.releases.json`;
|
|
const outputPath = options.outputDirectory ? path4.join(validateOutputDirectory(options.outputDirectory), outputName) : outputName;
|
|
fs3.writeFileSync(outputPath, rawRelease, "utf8");
|
|
console.log(`Downloaded GitHub releases to ${outputPath}.`);
|
|
process.exit(0);
|
|
}
|
|
const platformInfo = getPlatformInfo({
|
|
system: options.systemOverride,
|
|
arch: options.archOverride
|
|
});
|
|
console.log(`Fetching latest release for ${repository}...`);
|
|
const release = await fetchLatestRelease(repository, token);
|
|
const asset = getMatchingAsset(release.assets, platformInfo, options.fileName, options.fileType);
|
|
const version = release.tag_name.replace(/^v/i, "");
|
|
const downloadUrl = asset.browser_download_url;
|
|
console.log(`Will download '${appName}' version: ${version}`);
|
|
console.log(`Download URL: "${downloadUrl}".`);
|
|
if (options.dryRunLevel > 0) {
|
|
process.exit(0);
|
|
}
|
|
if (options.outputDirectory) {
|
|
const outputDir = validateOutputDirectory(options.outputDirectory);
|
|
const outputPath = path4.join(outputDir, path4.basename(downloadUrl));
|
|
console.log(`Downloading '${appName}' version ${version} to '${outputPath}'...`);
|
|
await downloadAsset(downloadUrl, outputPath, token);
|
|
process.exit(0);
|
|
}
|
|
tempDir = fs3.mkdtempSync(path4.join(os2.tmpdir(), "setup-gh-release-"));
|
|
const downloadPath = path4.join(tempDir, asset.name);
|
|
await downloadAsset(downloadUrl, downloadPath, token);
|
|
if (/\.(deb|pkg|rpm)$/i.test(asset.name)) {
|
|
installSystemPackage(downloadPath);
|
|
console.log("Installation successful!");
|
|
process.exit(0);
|
|
}
|
|
const extractDir = path4.join(tempDir, "extract");
|
|
console.log(`Extracting ${asset.name}...`);
|
|
await extractAsset(downloadPath, extractDir);
|
|
let binaryPattern;
|
|
if (binarySource.startsWith("~")) {
|
|
binaryPattern = new RegExp(binarySource.substring(1), "i");
|
|
} else {
|
|
binaryPattern = binarySource;
|
|
}
|
|
const binaryPath = findBinary(extractDir, binaryPattern, options.debug, console.log);
|
|
if (!binaryPath) {
|
|
throw new Error(`Could not find binary "${binarySource}" in the extracted asset.`);
|
|
}
|
|
const installDir = getInstallDir(options.installPath);
|
|
if (!fs3.existsSync(installDir)) {
|
|
fs3.mkdirSync(installDir, { recursive: true });
|
|
}
|
|
const finalName = binaryDestination || path4.basename(binaryPath);
|
|
const destPath = path4.join(installDir, finalName);
|
|
console.log(`Installing ${finalName} to ${destPath}...`);
|
|
try {
|
|
fs3.copyFileSync(binaryPath, destPath);
|
|
} catch (err) {
|
|
if (err.code === "EBUSY") {
|
|
throw new Error(`The file ${destPath} is currently in use. Please close any running instances and try again.`);
|
|
}
|
|
if (err.code === "EACCES" || err.code === "EPERM") {
|
|
throw new Error(`Permission denied while installing to ${destPath}. Try running with sudo or as administrator, or use -p to specify a custom path.`);
|
|
}
|
|
throw err;
|
|
}
|
|
if (process.platform !== "win32") {
|
|
fs3.chmodSync(destPath, "755");
|
|
}
|
|
console.log("Installation successful!");
|
|
process.exit(0);
|
|
} catch (error) {
|
|
if (error?.message) {
|
|
console.error(`Error: ${error.message}`);
|
|
} else {
|
|
console.error("Error: Unknown failure.");
|
|
}
|
|
process.exit(1);
|
|
} finally {
|
|
if (tempDir && fs3.existsSync(tempDir)) {
|
|
fs3.rmSync(tempDir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
}
|
|
run();
|