You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
11450 lines
344 KiB
11450 lines
344 KiB
2 years ago
|
import { j as join, c as basename, d as dirname, r as relative, e as extname, b as resolve, f as distDir, g as rootDir, h as isAbsolute, i as isNode, k as configFiles, l as defaultPort, n as normalize, t as toNamespacedPath, E as EXIT_CODE_RESTART } from './chunk-utils-env.b861e3a0.js';
|
||
|
import { p as pLimit, g as generateHash, c as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, a as getCoverageProvider, C as CoverageProviderMap } from './chunk-integrations-coverage.44413252.js';
|
||
|
import { g as getEnvPackageName } from './chunk-env-node.b3664da2.js';
|
||
|
import { y as ensurePackageInstalled, A as AggregateErrorPonyfill, s as slash$1, k as relativePath, z as getTests, e as getFullName, u as hasFailed, B as hasFailedSnapshot, C as getSuites, j as notNullish, l as shuffle, t as toArray, n as noop$1, D as deepMerge, b as getCallLastIndex, E as removeUndefinedValues, F as isWindows, G as stdout } from './chunk-mock-date.a1c85759.js';
|
||
|
import { loadConfigFromFile, normalizePath, createServer, mergeConfig } from 'vite';
|
||
|
import path$a from 'node:path';
|
||
|
import url, { fileURLToPath } from 'node:url';
|
||
|
import process$1 from 'node:process';
|
||
|
import fs$8, { promises, existsSync } from 'node:fs';
|
||
|
import g from 'path';
|
||
|
import require$$0 from 'os';
|
||
|
import util$2 from 'util';
|
||
|
import require$$0$1 from 'stream';
|
||
|
import require$$2 from 'events';
|
||
|
import require$$0$2, { existsSync as existsSync$1, readFileSync } from 'fs';
|
||
|
import { c as commonjsGlobal } from './vendor-_commonjsHelpers.addc3445.js';
|
||
|
import c from 'picocolors';
|
||
|
import { ViteNodeRunner } from 'vite-node/client';
|
||
|
import { ViteNodeServer } from 'vite-node/server';
|
||
|
import { writeFile, rm } from 'node:fs/promises';
|
||
|
import { e as execa } from './vendor-index.b2fdde54.js';
|
||
|
import { SourceMapConsumer } from 'source-map';
|
||
|
import H from 'module';
|
||
|
import { parse as parse$4 } from 'acorn';
|
||
|
import { ancestor } from 'acorn-walk';
|
||
|
import { MessageChannel } from 'node:worker_threads';
|
||
|
import { cpus, hostname } from 'node:os';
|
||
|
import { Tinypool } from 'tinypool';
|
||
|
import { c as createBirpc } from './vendor-index.783e7f3e.js';
|
||
|
import { performance } from 'perf_hooks';
|
||
|
import { s as stripAnsi, c as cliTruncate, b as parseStacktrace, d as stringWidth, e as sliceAnsi, a as positionToOffset, l as lineSplitRE } from './chunk-utils-source-map.60562959.js';
|
||
|
import { g as safeSetInterval, h as safeClearInterval, s as safeSetTimeout, d as ansiStyles, c as createPatch, a as stringify$5, b as safeClearTimeout } from './chunk-utils-timers.52534f96.js';
|
||
|
import { resolveModule } from 'local-pkg';
|
||
|
import { createHash } from 'crypto';
|
||
|
import { slash as slash$2, cleanUrl } from 'vite-node/utils';
|
||
|
import { o as onetime } from './vendor-index.9c919048.js';
|
||
|
import { s as signalExit } from './vendor-index.7a2cebfe.js';
|
||
|
import { createHash as createHash$1 } from 'node:crypto';
|
||
|
import MagicString from './chunk-magic-string.3a794426.js';
|
||
|
import { stripLiteral } from 'strip-literal';
|
||
|
import require$$0$3 from 'readline';
|
||
|
import { p as prompts } from './vendor-index.9f20a9be.js';
|
||
|
|
||
|
var version$1 = "0.27.0";
|
||
|
|
||
|
class EndError extends Error {
|
||
|
constructor(value) {
|
||
|
super();
|
||
|
this.value = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The input can also be a promise, so we await it.
|
||
|
const testElement = async (element, tester) => tester(await element);
|
||
|
|
||
|
// The input can also be a promise, so we `Promise.all()` them both.
|
||
|
const finder = async element => {
|
||
|
const values = await Promise.all(element);
|
||
|
if (values[1] === true) {
|
||
|
throw new EndError(values[0]);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
async function pLocate(
|
||
|
iterable,
|
||
|
tester,
|
||
|
{
|
||
|
concurrency = Number.POSITIVE_INFINITY,
|
||
|
preserveOrder = true,
|
||
|
} = {},
|
||
|
) {
|
||
|
const limit = pLimit(concurrency);
|
||
|
|
||
|
// Start all the promises concurrently with optional limit.
|
||
|
const items = [...iterable].map(element => [element, limit(testElement, element, tester)]);
|
||
|
|
||
|
// Check the promises either serially or concurrently.
|
||
|
const checkLimit = pLimit(preserveOrder ? 1 : Number.POSITIVE_INFINITY);
|
||
|
|
||
|
try {
|
||
|
await Promise.all(items.map(element => checkLimit(finder, element)));
|
||
|
} catch (error) {
|
||
|
if (error instanceof EndError) {
|
||
|
return error.value;
|
||
|
}
|
||
|
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const typeMappings = {
|
||
|
directory: 'isDirectory',
|
||
|
file: 'isFile',
|
||
|
};
|
||
|
|
||
|
function checkType(type) {
|
||
|
if (Object.hasOwnProperty.call(typeMappings, type)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
throw new Error(`Invalid type specified: ${type}`);
|
||
|
}
|
||
|
|
||
|
const matchType = (type, stat) => stat[typeMappings[type]]();
|
||
|
|
||
|
const toPath$1 = urlOrPath => urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;
|
||
|
|
||
|
async function locatePath(
|
||
|
paths,
|
||
|
{
|
||
|
cwd = process$1.cwd(),
|
||
|
type = 'file',
|
||
|
allowSymlinks = true,
|
||
|
concurrency,
|
||
|
preserveOrder,
|
||
|
} = {},
|
||
|
) {
|
||
|
checkType(type);
|
||
|
cwd = toPath$1(cwd);
|
||
|
|
||
|
const statFunction = allowSymlinks ? promises.stat : promises.lstat;
|
||
|
|
||
|
return pLocate(paths, async path_ => {
|
||
|
try {
|
||
|
const stat = await statFunction(path$a.resolve(cwd, path_));
|
||
|
return matchType(type, stat);
|
||
|
} catch {
|
||
|
return false;
|
||
|
}
|
||
|
}, {concurrency, preserveOrder});
|
||
|
}
|
||
|
|
||
|
const toPath = urlOrPath => urlOrPath instanceof URL ? fileURLToPath(urlOrPath) : urlOrPath;
|
||
|
|
||
|
const findUpStop = Symbol('findUpStop');
|
||
|
|
||
|
async function findUpMultiple(name, options = {}) {
|
||
|
let directory = path$a.resolve(toPath(options.cwd) || '');
|
||
|
const {root} = path$a.parse(directory);
|
||
|
const stopAt = path$a.resolve(directory, options.stopAt || root);
|
||
|
const limit = options.limit || Number.POSITIVE_INFINITY;
|
||
|
const paths = [name].flat();
|
||
|
|
||
|
const runMatcher = async locateOptions => {
|
||
|
if (typeof name !== 'function') {
|
||
|
return locatePath(paths, locateOptions);
|
||
|
}
|
||
|
|
||
|
const foundPath = await name(locateOptions.cwd);
|
||
|
if (typeof foundPath === 'string') {
|
||
|
return locatePath([foundPath], locateOptions);
|
||
|
}
|
||
|
|
||
|
return foundPath;
|
||
|
};
|
||
|
|
||
|
const matches = [];
|
||
|
// eslint-disable-next-line no-constant-condition
|
||
|
while (true) {
|
||
|
// eslint-disable-next-line no-await-in-loop
|
||
|
const foundPath = await runMatcher({...options, cwd: directory});
|
||
|
|
||
|
if (foundPath === findUpStop) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (foundPath) {
|
||
|
matches.push(path$a.resolve(directory, foundPath));
|
||
|
}
|
||
|
|
||
|
if (directory === stopAt || matches.length >= limit) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
directory = path$a.dirname(directory);
|
||
|
}
|
||
|
|
||
|
return matches;
|
||
|
}
|
||
|
|
||
|
async function findUp(name, options = {}) {
|
||
|
const matches = await findUpMultiple(name, {...options, limit: 1});
|
||
|
return matches[0];
|
||
|
}
|
||
|
|
||
|
var tasks = {};
|
||
|
|
||
|
var utils$k = {};
|
||
|
|
||
|
var array$1 = {};
|
||
|
|
||
|
Object.defineProperty(array$1, "__esModule", { value: true });
|
||
|
array$1.splitWhen = array$1.flatten = void 0;
|
||
|
function flatten(items) {
|
||
|
return items.reduce((collection, item) => [].concat(collection, item), []);
|
||
|
}
|
||
|
array$1.flatten = flatten;
|
||
|
function splitWhen(items, predicate) {
|
||
|
const result = [[]];
|
||
|
let groupIndex = 0;
|
||
|
for (const item of items) {
|
||
|
if (predicate(item)) {
|
||
|
groupIndex++;
|
||
|
result[groupIndex] = [];
|
||
|
}
|
||
|
else {
|
||
|
result[groupIndex].push(item);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
array$1.splitWhen = splitWhen;
|
||
|
|
||
|
var errno$1 = {};
|
||
|
|
||
|
Object.defineProperty(errno$1, "__esModule", { value: true });
|
||
|
errno$1.isEnoentCodeError = void 0;
|
||
|
function isEnoentCodeError(error) {
|
||
|
return error.code === 'ENOENT';
|
||
|
}
|
||
|
errno$1.isEnoentCodeError = isEnoentCodeError;
|
||
|
|
||
|
var fs$7 = {};
|
||
|
|
||
|
Object.defineProperty(fs$7, "__esModule", { value: true });
|
||
|
fs$7.createDirentFromStats = void 0;
|
||
|
class DirentFromStats$1 {
|
||
|
constructor(name, stats) {
|
||
|
this.name = name;
|
||
|
this.isBlockDevice = stats.isBlockDevice.bind(stats);
|
||
|
this.isCharacterDevice = stats.isCharacterDevice.bind(stats);
|
||
|
this.isDirectory = stats.isDirectory.bind(stats);
|
||
|
this.isFIFO = stats.isFIFO.bind(stats);
|
||
|
this.isFile = stats.isFile.bind(stats);
|
||
|
this.isSocket = stats.isSocket.bind(stats);
|
||
|
this.isSymbolicLink = stats.isSymbolicLink.bind(stats);
|
||
|
}
|
||
|
}
|
||
|
function createDirentFromStats$1(name, stats) {
|
||
|
return new DirentFromStats$1(name, stats);
|
||
|
}
|
||
|
fs$7.createDirentFromStats = createDirentFromStats$1;
|
||
|
|
||
|
var path$9 = {};
|
||
|
|
||
|
Object.defineProperty(path$9, "__esModule", { value: true });
|
||
|
path$9.removeLeadingDotSegment = path$9.escape = path$9.makeAbsolute = path$9.unixify = void 0;
|
||
|
const path$8 = g;
|
||
|
const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\
|
||
|
const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g;
|
||
|
/**
|
||
|
* Designed to work only with simple paths: `dir\\file`.
|
||
|
*/
|
||
|
function unixify(filepath) {
|
||
|
return filepath.replace(/\\/g, '/');
|
||
|
}
|
||
|
path$9.unixify = unixify;
|
||
|
function makeAbsolute(cwd, filepath) {
|
||
|
return path$8.resolve(cwd, filepath);
|
||
|
}
|
||
|
path$9.makeAbsolute = makeAbsolute;
|
||
|
function escape(pattern) {
|
||
|
return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2');
|
||
|
}
|
||
|
path$9.escape = escape;
|
||
|
function removeLeadingDotSegment(entry) {
|
||
|
// We do not use `startsWith` because this is 10x slower than current implementation for some cases.
|
||
|
// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
|
||
|
if (entry.charAt(0) === '.') {
|
||
|
const secondCharactery = entry.charAt(1);
|
||
|
if (secondCharactery === '/' || secondCharactery === '\\') {
|
||
|
return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT);
|
||
|
}
|
||
|
}
|
||
|
return entry;
|
||
|
}
|
||
|
path$9.removeLeadingDotSegment = removeLeadingDotSegment;
|
||
|
|
||
|
var pattern$1 = {};
|
||
|
|
||
|
/*!
|
||
|
* is-extglob <https://github.com/jonschlinkert/is-extglob>
|
||
|
*
|
||
|
* Copyright (c) 2014-2016, Jon Schlinkert.
|
||
|
* Licensed under the MIT License.
|
||
|
*/
|
||
|
|
||
|
var isExtglob$1 = function isExtglob(str) {
|
||
|
if (typeof str !== 'string' || str === '') {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
var match;
|
||
|
while ((match = /(\\).|([@?!+*]\(.*\))/g.exec(str))) {
|
||
|
if (match[2]) return true;
|
||
|
str = str.slice(match.index + match[0].length);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/*!
|
||
|
* is-glob <https://github.com/jonschlinkert/is-glob>
|
||
|
*
|
||
|
* Copyright (c) 2014-2017, Jon Schlinkert.
|
||
|
* Released under the MIT License.
|
||
|
*/
|
||
|
|
||
|
var isExtglob = isExtglob$1;
|
||
|
var chars = { '{': '}', '(': ')', '[': ']'};
|
||
|
var strictCheck = function(str) {
|
||
|
if (str[0] === '!') {
|
||
|
return true;
|
||
|
}
|
||
|
var index = 0;
|
||
|
var pipeIndex = -2;
|
||
|
var closeSquareIndex = -2;
|
||
|
var closeCurlyIndex = -2;
|
||
|
var closeParenIndex = -2;
|
||
|
var backSlashIndex = -2;
|
||
|
while (index < str.length) {
|
||
|
if (str[index] === '*') {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (str[index + 1] === '?' && /[\].+)]/.test(str[index])) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (closeSquareIndex !== -1 && str[index] === '[' && str[index + 1] !== ']') {
|
||
|
if (closeSquareIndex < index) {
|
||
|
closeSquareIndex = str.indexOf(']', index);
|
||
|
}
|
||
|
if (closeSquareIndex > index) {
|
||
|
if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
|
||
|
return true;
|
||
|
}
|
||
|
backSlashIndex = str.indexOf('\\', index);
|
||
|
if (backSlashIndex === -1 || backSlashIndex > closeSquareIndex) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (closeCurlyIndex !== -1 && str[index] === '{' && str[index + 1] !== '}') {
|
||
|
closeCurlyIndex = str.indexOf('}', index);
|
||
|
if (closeCurlyIndex > index) {
|
||
|
backSlashIndex = str.indexOf('\\', index);
|
||
|
if (backSlashIndex === -1 || backSlashIndex > closeCurlyIndex) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (closeParenIndex !== -1 && str[index] === '(' && str[index + 1] === '?' && /[:!=]/.test(str[index + 2]) && str[index + 3] !== ')') {
|
||
|
closeParenIndex = str.indexOf(')', index);
|
||
|
if (closeParenIndex > index) {
|
||
|
backSlashIndex = str.indexOf('\\', index);
|
||
|
if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pipeIndex !== -1 && str[index] === '(' && str[index + 1] !== '|') {
|
||
|
if (pipeIndex < index) {
|
||
|
pipeIndex = str.indexOf('|', index);
|
||
|
}
|
||
|
if (pipeIndex !== -1 && str[pipeIndex + 1] !== ')') {
|
||
|
closeParenIndex = str.indexOf(')', pipeIndex);
|
||
|
if (closeParenIndex > pipeIndex) {
|
||
|
backSlashIndex = str.indexOf('\\', pipeIndex);
|
||
|
if (backSlashIndex === -1 || backSlashIndex > closeParenIndex) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (str[index] === '\\') {
|
||
|
var open = str[index + 1];
|
||
|
index += 2;
|
||
|
var close = chars[open];
|
||
|
|
||
|
if (close) {
|
||
|
var n = str.indexOf(close, index);
|
||
|
if (n !== -1) {
|
||
|
index = n + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (str[index] === '!') {
|
||
|
return true;
|
||
|
}
|
||
|
} else {
|
||
|
index++;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
var relaxedCheck = function(str) {
|
||
|
if (str[0] === '!') {
|
||
|
return true;
|
||
|
}
|
||
|
var index = 0;
|
||
|
while (index < str.length) {
|
||
|
if (/[*?{}()[\]]/.test(str[index])) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (str[index] === '\\') {
|
||
|
var open = str[index + 1];
|
||
|
index += 2;
|
||
|
var close = chars[open];
|
||
|
|
||
|
if (close) {
|
||
|
var n = str.indexOf(close, index);
|
||
|
if (n !== -1) {
|
||
|
index = n + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (str[index] === '!') {
|
||
|
return true;
|
||
|
}
|
||
|
} else {
|
||
|
index++;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
var isGlob$1 = function isGlob(str, options) {
|
||
|
if (typeof str !== 'string' || str === '') {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (isExtglob(str)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
var check = strictCheck;
|
||
|
|
||
|
// optionally relax check
|
||
|
if (options && options.strict === false) {
|
||
|
check = relaxedCheck;
|
||
|
}
|
||
|
|
||
|
return check(str);
|
||
|
};
|
||
|
|
||
|
var isGlob = isGlob$1;
|
||
|
var pathPosixDirname = g.posix.dirname;
|
||
|
var isWin32 = require$$0.platform() === 'win32';
|
||
|
|
||
|
var slash = '/';
|
||
|
var backslash = /\\/g;
|
||
|
var enclosure = /[\{\[].*[\}\]]$/;
|
||
|
var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/;
|
||
|
var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g;
|
||
|
|
||
|
/**
|
||
|
* @param {string} str
|
||
|
* @param {Object} opts
|
||
|
* @param {boolean} [opts.flipBackslashes=true]
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
var globParent$1 = function globParent(str, opts) {
|
||
|
var options = Object.assign({ flipBackslashes: true }, opts);
|
||
|
|
||
|
// flip windows path separators
|
||
|
if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) {
|
||
|
str = str.replace(backslash, slash);
|
||
|
}
|
||
|
|
||
|
// special case for strings ending in enclosure containing path separator
|
||
|
if (enclosure.test(str)) {
|
||
|
str += slash;
|
||
|
}
|
||
|
|
||
|
// preserves full path in case of trailing path separator
|
||
|
str += 'a';
|
||
|
|
||
|
// remove path parts that are globby
|
||
|
do {
|
||
|
str = pathPosixDirname(str);
|
||
|
} while (isGlob(str) || globby.test(str));
|
||
|
|
||
|
// remove escape chars and return result
|
||
|
return str.replace(escaped, '$1');
|
||
|
};
|
||
|
|
||
|
var utils$j = {};
|
||
|
|
||
|
(function (exports) {
|
||
|
|
||
|
exports.isInteger = num => {
|
||
|
if (typeof num === 'number') {
|
||
|
return Number.isInteger(num);
|
||
|
}
|
||
|
if (typeof num === 'string' && num.trim() !== '') {
|
||
|
return Number.isInteger(Number(num));
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Find a node of the given type
|
||
|
*/
|
||
|
|
||
|
exports.find = (node, type) => node.nodes.find(node => node.type === type);
|
||
|
|
||
|
/**
|
||
|
* Find a node of the given type
|
||
|
*/
|
||
|
|
||
|
exports.exceedsLimit = (min, max, step = 1, limit) => {
|
||
|
if (limit === false) return false;
|
||
|
if (!exports.isInteger(min) || !exports.isInteger(max)) return false;
|
||
|
return ((Number(max) - Number(min)) / Number(step)) >= limit;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Escape the given node with '\\' before node.value
|
||
|
*/
|
||
|
|
||
|
exports.escapeNode = (block, n = 0, type) => {
|
||
|
let node = block.nodes[n];
|
||
|
if (!node) return;
|
||
|
|
||
|
if ((type && node.type === type) || node.type === 'open' || node.type === 'close') {
|
||
|
if (node.escaped !== true) {
|
||
|
node.value = '\\' + node.value;
|
||
|
node.escaped = true;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns true if the given brace node should be enclosed in literal braces
|
||
|
*/
|
||
|
|
||
|
exports.encloseBrace = node => {
|
||
|
if (node.type !== 'brace') return false;
|
||
|
if ((node.commas >> 0 + node.ranges >> 0) === 0) {
|
||
|
node.invalid = true;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns true if a brace node is invalid.
|
||
|
*/
|
||
|
|
||
|
exports.isInvalidBrace = block => {
|
||
|
if (block.type !== 'brace') return false;
|
||
|
if (block.invalid === true || block.dollar) return true;
|
||
|
if ((block.commas >> 0 + block.ranges >> 0) === 0) {
|
||
|
block.invalid = true;
|
||
|
return true;
|
||
|
}
|
||
|
if (block.open !== true || block.close !== true) {
|
||
|
block.invalid = true;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns true if a node is an open or close node
|
||
|
*/
|
||
|
|
||
|
exports.isOpenOrClose = node => {
|
||
|
if (node.type === 'open' || node.type === 'close') {
|
||
|
return true;
|
||
|
}
|
||
|
return node.open === true || node.close === true;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Reduce an array of text nodes.
|
||
|
*/
|
||
|
|
||
|
exports.reduce = nodes => nodes.reduce((acc, node) => {
|
||
|
if (node.type === 'text') acc.push(node.value);
|
||
|
if (node.type === 'range') node.type = 'text';
|
||
|
return acc;
|
||
|
}, []);
|
||
|
|
||
|
/**
|
||
|
* Flatten an array
|
||
|
*/
|
||
|
|
||
|
exports.flatten = (...args) => {
|
||
|
const result = [];
|
||
|
const flat = arr => {
|
||
|
for (let i = 0; i < arr.length; i++) {
|
||
|
let ele = arr[i];
|
||
|
Array.isArray(ele) ? flat(ele) : ele !== void 0 && result.push(ele);
|
||
|
}
|
||
|
return result;
|
||
|
};
|
||
|
flat(args);
|
||
|
return result;
|
||
|
};
|
||
|
} (utils$j));
|
||
|
|
||
|
const utils$i = utils$j;
|
||
|
|
||
|
var stringify$4 = (ast, options = {}) => {
|
||
|
let stringify = (node, parent = {}) => {
|
||
|
let invalidBlock = options.escapeInvalid && utils$i.isInvalidBrace(parent);
|
||
|
let invalidNode = node.invalid === true && options.escapeInvalid === true;
|
||
|
let output = '';
|
||
|
|
||
|
if (node.value) {
|
||
|
if ((invalidBlock || invalidNode) && utils$i.isOpenOrClose(node)) {
|
||
|
return '\\' + node.value;
|
||
|
}
|
||
|
return node.value;
|
||
|
}
|
||
|
|
||
|
if (node.value) {
|
||
|
return node.value;
|
||
|
}
|
||
|
|
||
|
if (node.nodes) {
|
||
|
for (let child of node.nodes) {
|
||
|
output += stringify(child);
|
||
|
}
|
||
|
}
|
||
|
return output;
|
||
|
};
|
||
|
|
||
|
return stringify(ast);
|
||
|
};
|
||
|
|
||
|
/*!
|
||
|
* is-number <https://github.com/jonschlinkert/is-number>
|
||
|
*
|
||
|
* Copyright (c) 2014-present, Jon Schlinkert.
|
||
|
* Released under the MIT License.
|
||
|
*/
|
||
|
|
||
|
var isNumber$2 = function(num) {
|
||
|
if (typeof num === 'number') {
|
||
|
return num - num === 0;
|
||
|
}
|
||
|
if (typeof num === 'string' && num.trim() !== '') {
|
||
|
return Number.isFinite ? Number.isFinite(+num) : isFinite(+num);
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/*!
|
||
|
* to-regex-range <https://github.com/micromatch/to-regex-range>
|
||
|
*
|
||
|
* Copyright (c) 2015-present, Jon Schlinkert.
|
||
|
* Released under the MIT License.
|
||
|
*/
|
||
|
|
||
|
const isNumber$1 = isNumber$2;
|
||
|
|
||
|
const toRegexRange$1 = (min, max, options) => {
|
||
|
if (isNumber$1(min) === false) {
|
||
|
throw new TypeError('toRegexRange: expected the first argument to be a number');
|
||
|
}
|
||
|
|
||
|
if (max === void 0 || min === max) {
|
||
|
return String(min);
|
||
|
}
|
||
|
|
||
|
if (isNumber$1(max) === false) {
|
||
|
throw new TypeError('toRegexRange: expected the second argument to be a number.');
|
||
|
}
|
||
|
|
||
|
let opts = { relaxZeros: true, ...options };
|
||
|
if (typeof opts.strictZeros === 'boolean') {
|
||
|
opts.relaxZeros = opts.strictZeros === false;
|
||
|
}
|
||
|
|
||
|
let relax = String(opts.relaxZeros);
|
||
|
let shorthand = String(opts.shorthand);
|
||
|
let capture = String(opts.capture);
|
||
|
let wrap = String(opts.wrap);
|
||
|
let cacheKey = min + ':' + max + '=' + relax + shorthand + capture + wrap;
|
||
|
|
||
|
if (toRegexRange$1.cache.hasOwnProperty(cacheKey)) {
|
||
|
return toRegexRange$1.cache[cacheKey].result;
|
||
|
}
|
||
|
|
||
|
let a = Math.min(min, max);
|
||
|
let b = Math.max(min, max);
|
||
|
|
||
|
if (Math.abs(a - b) === 1) {
|
||
|
let result = min + '|' + max;
|
||
|
if (opts.capture) {
|
||
|
return `(${result})`;
|
||
|
}
|
||
|
if (opts.wrap === false) {
|
||
|
return result;
|
||
|
}
|
||
|
return `(?:${result})`;
|
||
|
}
|
||
|
|
||
|
let isPadded = hasPadding(min) || hasPadding(max);
|
||
|
let state = { min, max, a, b };
|
||
|
let positives = [];
|
||
|
let negatives = [];
|
||
|
|
||
|
if (isPadded) {
|
||
|
state.isPadded = isPadded;
|
||
|
state.maxLen = String(state.max).length;
|
||
|
}
|
||
|
|
||
|
if (a < 0) {
|
||
|
let newMin = b < 0 ? Math.abs(b) : 1;
|
||
|
negatives = splitToPatterns(newMin, Math.abs(a), state, opts);
|
||
|
a = state.a = 0;
|
||
|
}
|
||
|
|
||
|
if (b >= 0) {
|
||
|
positives = splitToPatterns(a, b, state, opts);
|
||
|
}
|
||
|
|
||
|
state.negatives = negatives;
|
||
|
state.positives = positives;
|
||
|
state.result = collatePatterns(negatives, positives);
|
||
|
|
||
|
if (opts.capture === true) {
|
||
|
state.result = `(${state.result})`;
|
||
|
} else if (opts.wrap !== false && (positives.length + negatives.length) > 1) {
|
||
|
state.result = `(?:${state.result})`;
|
||
|
}
|
||
|
|
||
|
toRegexRange$1.cache[cacheKey] = state;
|
||
|
return state.result;
|
||
|
};
|
||
|
|
||
|
function collatePatterns(neg, pos, options) {
|
||
|
let onlyNegative = filterPatterns(neg, pos, '-', false) || [];
|
||
|
let onlyPositive = filterPatterns(pos, neg, '', false) || [];
|
||
|
let intersected = filterPatterns(neg, pos, '-?', true) || [];
|
||
|
let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive);
|
||
|
return subpatterns.join('|');
|
||
|
}
|
||
|
|
||
|
function splitToRanges(min, max) {
|
||
|
let nines = 1;
|
||
|
let zeros = 1;
|
||
|
|
||
|
let stop = countNines(min, nines);
|
||
|
let stops = new Set([max]);
|
||
|
|
||
|
while (min <= stop && stop <= max) {
|
||
|
stops.add(stop);
|
||
|
nines += 1;
|
||
|
stop = countNines(min, nines);
|
||
|
}
|
||
|
|
||
|
stop = countZeros(max + 1, zeros) - 1;
|
||
|
|
||
|
while (min < stop && stop <= max) {
|
||
|
stops.add(stop);
|
||
|
zeros += 1;
|
||
|
stop = countZeros(max + 1, zeros) - 1;
|
||
|
}
|
||
|
|
||
|
stops = [...stops];
|
||
|
stops.sort(compare);
|
||
|
return stops;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Convert a range to a regex pattern
|
||
|
* @param {Number} `start`
|
||
|
* @param {Number} `stop`
|
||
|
* @return {String}
|
||
|
*/
|
||
|
|
||
|
function rangeToPattern(start, stop, options) {
|
||
|
if (start === stop) {
|
||
|
return { pattern: start, count: [], digits: 0 };
|
||
|
}
|
||
|
|
||
|
let zipped = zip(start, stop);
|
||
|
let digits = zipped.length;
|
||
|
let pattern = '';
|
||
|
let count = 0;
|
||
|
|
||
|
for (let i = 0; i < digits; i++) {
|
||
|
let [startDigit, stopDigit] = zipped[i];
|
||
|
|
||
|
if (startDigit === stopDigit) {
|
||
|
pattern += startDigit;
|
||
|
|
||
|
} else if (startDigit !== '0' || stopDigit !== '9') {
|
||
|
pattern += toCharacterClass(startDigit, stopDigit);
|
||
|
|
||
|
} else {
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (count) {
|
||
|
pattern += options.shorthand === true ? '\\d' : '[0-9]';
|
||
|
}
|
||
|
|
||
|
return { pattern, count: [count], digits };
|
||
|
}
|
||
|
|
||
|
function splitToPatterns(min, max, tok, options) {
|
||
|
let ranges = splitToRanges(min, max);
|
||
|
let tokens = [];
|
||
|
let start = min;
|
||
|
let prev;
|
||
|
|
||
|
for (let i = 0; i < ranges.length; i++) {
|
||
|
let max = ranges[i];
|
||
|
let obj = rangeToPattern(String(start), String(max), options);
|
||
|
let zeros = '';
|
||
|
|
||
|
if (!tok.isPadded && prev && prev.pattern === obj.pattern) {
|
||
|
if (prev.count.length > 1) {
|
||
|
prev.count.pop();
|
||
|
}
|
||
|
|
||
|
prev.count.push(obj.count[0]);
|
||
|
prev.string = prev.pattern + toQuantifier(prev.count);
|
||
|
start = max + 1;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (tok.isPadded) {
|
||
|
zeros = padZeros(max, tok, options);
|
||
|
}
|
||
|
|
||
|
obj.string = zeros + obj.pattern + toQuantifier(obj.count);
|
||
|
tokens.push(obj);
|
||
|
start = max + 1;
|
||
|
prev = obj;
|
||
|
}
|
||
|
|
||
|
return tokens;
|
||
|
}
|
||
|
|
||
|
function filterPatterns(arr, comparison, prefix, intersection, options) {
|
||
|
let result = [];
|
||
|
|
||
|
for (let ele of arr) {
|
||
|
let { string } = ele;
|
||
|
|
||
|
// only push if _both_ are negative...
|
||
|
if (!intersection && !contains(comparison, 'string', string)) {
|
||
|
result.push(prefix + string);
|
||
|
}
|
||
|
|
||
|
// or _both_ are positive
|
||
|
if (intersection && contains(comparison, 'string', string)) {
|
||
|
result.push(prefix + string);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Zip strings
|
||
|
*/
|
||
|
|
||
|
function zip(a, b) {
|
||
|
let arr = [];
|
||
|
for (let i = 0; i < a.length; i++) arr.push([a[i], b[i]]);
|
||
|
return arr;
|
||
|
}
|
||
|
|
||
|
function compare(a, b) {
|
||
|
return a > b ? 1 : b > a ? -1 : 0;
|
||
|
}
|
||
|
|
||
|
function contains(arr, key, val) {
|
||
|
return arr.some(ele => ele[key] === val);
|
||
|
}
|
||
|
|
||
|
function countNines(min, len) {
|
||
|
return Number(String(min).slice(0, -len) + '9'.repeat(len));
|
||
|
}
|
||
|
|
||
|
function countZeros(integer, zeros) {
|
||
|
return integer - (integer % Math.pow(10, zeros));
|
||
|
}
|
||
|
|
||
|
function toQuantifier(digits) {
|
||
|
let [start = 0, stop = ''] = digits;
|
||
|
if (stop || start > 1) {
|
||
|
return `{${start + (stop ? ',' + stop : '')}}`;
|
||
|
}
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
function toCharacterClass(a, b, options) {
|
||
|
return `[${a}${(b - a === 1) ? '' : '-'}${b}]`;
|
||
|
}
|
||
|
|
||
|
function hasPadding(str) {
|
||
|
return /^-?(0+)\d/.test(str);
|
||
|
}
|
||
|
|
||
|
function padZeros(value, tok, options) {
|
||
|
if (!tok.isPadded) {
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
let diff = Math.abs(tok.maxLen - String(value).length);
|
||
|
let relax = options.relaxZeros !== false;
|
||
|
|
||
|
switch (diff) {
|
||
|
case 0:
|
||
|
return '';
|
||
|
case 1:
|
||
|
return relax ? '0?' : '0';
|
||
|
case 2:
|
||
|
return relax ? '0{0,2}' : '00';
|
||
|
default: {
|
||
|
return relax ? `0{0,${diff}}` : `0{${diff}}`;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Cache
|
||
|
*/
|
||
|
|
||
|
toRegexRange$1.cache = {};
|
||
|
toRegexRange$1.clearCache = () => (toRegexRange$1.cache = {});
|
||
|
|
||
|
/**
|
||
|
* Expose `toRegexRange`
|
||
|
*/
|
||
|
|
||
|
var toRegexRange_1 = toRegexRange$1;
|
||
|
|
||
|
/*!
|
||
|
* fill-range <https://github.com/jonschlinkert/fill-range>
|
||
|
*
|
||
|
* Copyright (c) 2014-present, Jon Schlinkert.
|
||
|
* Licensed under the MIT License.
|
||
|
*/
|
||
|
|
||
|
const util$1 = util$2;
|
||
|
const toRegexRange = toRegexRange_1;
|
||
|
|
||
|
const isObject$1 = val => val !== null && typeof val === 'object' && !Array.isArray(val);
|
||
|
|
||
|
const transform$1 = toNumber => {
|
||
|
return value => toNumber === true ? Number(value) : String(value);
|
||
|
};
|
||
|
|
||
|
const isValidValue = value => {
|
||
|
return typeof value === 'number' || (typeof value === 'string' && value !== '');
|
||
|
};
|
||
|
|
||
|
const isNumber = num => Number.isInteger(+num);
|
||
|
|
||
|
const zeros = input => {
|
||
|
let value = `${input}`;
|
||
|
let index = -1;
|
||
|
if (value[0] === '-') value = value.slice(1);
|
||
|
if (value === '0') return false;
|
||
|
while (value[++index] === '0');
|
||
|
return index > 0;
|
||
|
};
|
||
|
|
||
|
const stringify$3 = (start, end, options) => {
|
||
|
if (typeof start === 'string' || typeof end === 'string') {
|
||
|
return true;
|
||
|
}
|
||
|
return options.stringify === true;
|
||
|
};
|
||
|
|
||
|
const pad = (input, maxLength, toNumber) => {
|
||
|
if (maxLength > 0) {
|
||
|
let dash = input[0] === '-' ? '-' : '';
|
||
|
if (dash) input = input.slice(1);
|
||
|
input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0'));
|
||
|
}
|
||
|
if (toNumber === false) {
|
||
|
return String(input);
|
||
|
}
|
||
|
return input;
|
||
|
};
|
||
|
|
||
|
const toMaxLen = (input, maxLength) => {
|
||
|
let negative = input[0] === '-' ? '-' : '';
|
||
|
if (negative) {
|
||
|
input = input.slice(1);
|
||
|
maxLength--;
|
||
|
}
|
||
|
while (input.length < maxLength) input = '0' + input;
|
||
|
return negative ? ('-' + input) : input;
|
||
|
};
|
||
|
|
||
|
const toSequence = (parts, options) => {
|
||
|
parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
||
|
parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
||
|
|
||
|
let prefix = options.capture ? '' : '?:';
|
||
|
let positives = '';
|
||
|
let negatives = '';
|
||
|
let result;
|
||
|
|
||
|
if (parts.positives.length) {
|
||
|
positives = parts.positives.join('|');
|
||
|
}
|
||
|
|
||
|
if (parts.negatives.length) {
|
||
|
negatives = `-(${prefix}${parts.negatives.join('|')})`;
|
||
|
}
|
||
|
|
||
|
if (positives && negatives) {
|
||
|
result = `${positives}|${negatives}`;
|
||
|
} else {
|
||
|
result = positives || negatives;
|
||
|
}
|
||
|
|
||
|
if (options.wrap) {
|
||
|
return `(${prefix}${result})`;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
const toRange = (a, b, isNumbers, options) => {
|
||
|
if (isNumbers) {
|
||
|
return toRegexRange(a, b, { wrap: false, ...options });
|
||
|
}
|
||
|
|
||
|
let start = String.fromCharCode(a);
|
||
|
if (a === b) return start;
|
||
|
|
||
|
let stop = String.fromCharCode(b);
|
||
|
return `[${start}-${stop}]`;
|
||
|
};
|
||
|
|
||
|
const toRegex = (start, end, options) => {
|
||
|
if (Array.isArray(start)) {
|
||
|
let wrap = options.wrap === true;
|
||
|
let prefix = options.capture ? '' : '?:';
|
||
|
return wrap ? `(${prefix}${start.join('|')})` : start.join('|');
|
||
|
}
|
||
|
return toRegexRange(start, end, options);
|
||
|
};
|
||
|
|
||
|
const rangeError = (...args) => {
|
||
|
return new RangeError('Invalid range arguments: ' + util$1.inspect(...args));
|
||
|
};
|
||
|
|
||
|
const invalidRange = (start, end, options) => {
|
||
|
if (options.strictRanges === true) throw rangeError([start, end]);
|
||
|
return [];
|
||
|
};
|
||
|
|
||
|
const invalidStep = (step, options) => {
|
||
|
if (options.strictRanges === true) {
|
||
|
throw new TypeError(`Expected step "${step}" to be a number`);
|
||
|
}
|
||
|
return [];
|
||
|
};
|
||
|
|
||
|
const fillNumbers = (start, end, step = 1, options = {}) => {
|
||
|
let a = Number(start);
|
||
|
let b = Number(end);
|
||
|
|
||
|
if (!Number.isInteger(a) || !Number.isInteger(b)) {
|
||
|
if (options.strictRanges === true) throw rangeError([start, end]);
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
// fix negative zero
|
||
|
if (a === 0) a = 0;
|
||
|
if (b === 0) b = 0;
|
||
|
|
||
|
let descending = a > b;
|
||
|
let startString = String(start);
|
||
|
let endString = String(end);
|
||
|
let stepString = String(step);
|
||
|
step = Math.max(Math.abs(step), 1);
|
||
|
|
||
|
let padded = zeros(startString) || zeros(endString) || zeros(stepString);
|
||
|
let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0;
|
||
|
let toNumber = padded === false && stringify$3(start, end, options) === false;
|
||
|
let format = options.transform || transform$1(toNumber);
|
||
|
|
||
|
if (options.toRegex && step === 1) {
|
||
|
return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options);
|
||
|
}
|
||
|
|
||
|
let parts = { negatives: [], positives: [] };
|
||
|
let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num));
|
||
|
let range = [];
|
||
|
let index = 0;
|
||
|
|
||
|
while (descending ? a >= b : a <= b) {
|
||
|
if (options.toRegex === true && step > 1) {
|
||
|
push(a);
|
||
|
} else {
|
||
|
range.push(pad(format(a, index), maxLen, toNumber));
|
||
|
}
|
||
|
a = descending ? a - step : a + step;
|
||
|
index++;
|
||
|
}
|
||
|
|
||
|
if (options.toRegex === true) {
|
||
|
return step > 1
|
||
|
? toSequence(parts, options)
|
||
|
: toRegex(range, null, { wrap: false, ...options });
|
||
|
}
|
||
|
|
||
|
return range;
|
||
|
};
|
||
|
|
||
|
const fillLetters = (start, end, step = 1, options = {}) => {
|
||
|
if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) {
|
||
|
return invalidRange(start, end, options);
|
||
|
}
|
||
|
|
||
|
|
||
|
let format = options.transform || (val => String.fromCharCode(val));
|
||
|
let a = `${start}`.charCodeAt(0);
|
||
|
let b = `${end}`.charCodeAt(0);
|
||
|
|
||
|
let descending = a > b;
|
||
|
let min = Math.min(a, b);
|
||
|
let max = Math.max(a, b);
|
||
|
|
||
|
if (options.toRegex && step === 1) {
|
||
|
return toRange(min, max, false, options);
|
||
|
}
|
||
|
|
||
|
let range = [];
|
||
|
let index = 0;
|
||
|
|
||
|
while (descending ? a >= b : a <= b) {
|
||
|
range.push(format(a, index));
|
||
|
a = descending ? a - step : a + step;
|
||
|
index++;
|
||
|
}
|
||
|
|
||
|
if (options.toRegex === true) {
|
||
|
return toRegex(range, null, { wrap: false, options });
|
||
|
}
|
||
|
|
||
|
return range;
|
||
|
};
|
||
|
|
||
|
const fill$2 = (start, end, step, options = {}) => {
|
||
|
if (end == null && isValidValue(start)) {
|
||
|
return [start];
|
||
|
}
|
||
|
|
||
|
if (!isValidValue(start) || !isValidValue(end)) {
|
||
|
return invalidRange(start, end, options);
|
||
|
}
|
||
|
|
||
|
if (typeof step === 'function') {
|
||
|
return fill$2(start, end, 1, { transform: step });
|
||
|
}
|
||
|
|
||
|
if (isObject$1(step)) {
|
||
|
return fill$2(start, end, 0, step);
|
||
|
}
|
||
|
|
||
|
let opts = { ...options };
|
||
|
if (opts.capture === true) opts.wrap = true;
|
||
|
step = step || opts.step || 1;
|
||
|
|
||
|
if (!isNumber(step)) {
|
||
|
if (step != null && !isObject$1(step)) return invalidStep(step, opts);
|
||
|
return fill$2(start, end, 1, step);
|
||
|
}
|
||
|
|
||
|
if (isNumber(start) && isNumber(end)) {
|
||
|
return fillNumbers(start, end, step, opts);
|
||
|
}
|
||
|
|
||
|
return fillLetters(start, end, Math.max(Math.abs(step), 1), opts);
|
||
|
};
|
||
|
|
||
|
var fillRange = fill$2;
|
||
|
|
||
|
const fill$1 = fillRange;
|
||
|
const utils$h = utils$j;
|
||
|
|
||
|
const compile$1 = (ast, options = {}) => {
|
||
|
let walk = (node, parent = {}) => {
|
||
|
let invalidBlock = utils$h.isInvalidBrace(parent);
|
||
|
let invalidNode = node.invalid === true && options.escapeInvalid === true;
|
||
|
let invalid = invalidBlock === true || invalidNode === true;
|
||
|
let prefix = options.escapeInvalid === true ? '\\' : '';
|
||
|
let output = '';
|
||
|
|
||
|
if (node.isOpen === true) {
|
||
|
return prefix + node.value;
|
||
|
}
|
||
|
if (node.isClose === true) {
|
||
|
return prefix + node.value;
|
||
|
}
|
||
|
|
||
|
if (node.type === 'open') {
|
||
|
return invalid ? (prefix + node.value) : '(';
|
||
|
}
|
||
|
|
||
|
if (node.type === 'close') {
|
||
|
return invalid ? (prefix + node.value) : ')';
|
||
|
}
|
||
|
|
||
|
if (node.type === 'comma') {
|
||
|
return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|');
|
||
|
}
|
||
|
|
||
|
if (node.value) {
|
||
|
return node.value;
|
||
|
}
|
||
|
|
||
|
if (node.nodes && node.ranges > 0) {
|
||
|
let args = utils$h.reduce(node.nodes);
|
||
|
let range = fill$1(...args, { ...options, wrap: false, toRegex: true });
|
||
|
|
||
|
if (range.length !== 0) {
|
||
|
return args.length > 1 && range.length > 1 ? `(${range})` : range;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (node.nodes) {
|
||
|
for (let child of node.nodes) {
|
||
|
output += walk(child, node);
|
||
|
}
|
||
|
}
|
||
|
return output;
|
||
|
};
|
||
|
|
||
|
return walk(ast);
|
||
|
};
|
||
|
|
||
|
var compile_1 = compile$1;
|
||
|
|
||
|
const fill = fillRange;
|
||
|
const stringify$2 = stringify$4;
|
||
|
const utils$g = utils$j;
|
||
|
|
||
|
const append = (queue = '', stash = '', enclose = false) => {
|
||
|
let result = [];
|
||
|
|
||
|
queue = [].concat(queue);
|
||
|
stash = [].concat(stash);
|
||
|
|
||
|
if (!stash.length) return queue;
|
||
|
if (!queue.length) {
|
||
|
return enclose ? utils$g.flatten(stash).map(ele => `{${ele}}`) : stash;
|
||
|
}
|
||
|
|
||
|
for (let item of queue) {
|
||
|
if (Array.isArray(item)) {
|
||
|
for (let value of item) {
|
||
|
result.push(append(value, stash, enclose));
|
||
|
}
|
||
|
} else {
|
||
|
for (let ele of stash) {
|
||
|
if (enclose === true && typeof ele === 'string') ele = `{${ele}}`;
|
||
|
result.push(Array.isArray(ele) ? append(item, ele, enclose) : (item + ele));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return utils$g.flatten(result);
|
||
|
};
|
||
|
|
||
|
const expand$1 = (ast, options = {}) => {
|
||
|
let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit;
|
||
|
|
||
|
let walk = (node, parent = {}) => {
|
||
|
node.queue = [];
|
||
|
|
||
|
let p = parent;
|
||
|
let q = parent.queue;
|
||
|
|
||
|
while (p.type !== 'brace' && p.type !== 'root' && p.parent) {
|
||
|
p = p.parent;
|
||
|
q = p.queue;
|
||
|
}
|
||
|
|
||
|
if (node.invalid || node.dollar) {
|
||
|
q.push(append(q.pop(), stringify$2(node, options)));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) {
|
||
|
q.push(append(q.pop(), ['{}']));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (node.nodes && node.ranges > 0) {
|
||
|
let args = utils$g.reduce(node.nodes);
|
||
|
|
||
|
if (utils$g.exceedsLimit(...args, options.step, rangeLimit)) {
|
||
|
throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
|
||
|
}
|
||
|
|
||
|
let range = fill(...args, options);
|
||
|
if (range.length === 0) {
|
||
|
range = stringify$2(node, options);
|
||
|
}
|
||
|
|
||
|
q.push(append(q.pop(), range));
|
||
|
node.nodes = [];
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let enclose = utils$g.encloseBrace(node);
|
||
|
let queue = node.queue;
|
||
|
let block = node;
|
||
|
|
||
|
while (block.type !== 'brace' && block.type !== 'root' && block.parent) {
|
||
|
block = block.parent;
|
||
|
queue = block.queue;
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < node.nodes.length; i++) {
|
||
|
let child = node.nodes[i];
|
||
|
|
||
|
if (child.type === 'comma' && node.type === 'brace') {
|
||
|
if (i === 1) queue.push('');
|
||
|
queue.push('');
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (child.type === 'close') {
|
||
|
q.push(append(q.pop(), queue, enclose));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (child.value && child.type !== 'open') {
|
||
|
queue.push(append(queue.pop(), child.value));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (child.nodes) {
|
||
|
walk(child, node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return queue;
|
||
|
};
|
||
|
|
||
|
return utils$g.flatten(walk(ast));
|
||
|
};
|
||
|
|
||
|
var expand_1 = expand$1;
|
||
|
|
||
|
var constants$4 = {
|
||
|
MAX_LENGTH: 1024 * 64,
|
||
|
|
||
|
// Digits
|
||
|
CHAR_0: '0', /* 0 */
|
||
|
CHAR_9: '9', /* 9 */
|
||
|
|
||
|
// Alphabet chars.
|
||
|
CHAR_UPPERCASE_A: 'A', /* A */
|
||
|
CHAR_LOWERCASE_A: 'a', /* a */
|
||
|
CHAR_UPPERCASE_Z: 'Z', /* Z */
|
||
|
CHAR_LOWERCASE_Z: 'z', /* z */
|
||
|
|
||
|
CHAR_LEFT_PARENTHESES: '(', /* ( */
|
||
|
CHAR_RIGHT_PARENTHESES: ')', /* ) */
|
||
|
|
||
|
CHAR_ASTERISK: '*', /* * */
|
||
|
|
||
|
// Non-alphabetic chars.
|
||
|
CHAR_AMPERSAND: '&', /* & */
|
||
|
CHAR_AT: '@', /* @ */
|
||
|
CHAR_BACKSLASH: '\\', /* \ */
|
||
|
CHAR_BACKTICK: '`', /* ` */
|
||
|
CHAR_CARRIAGE_RETURN: '\r', /* \r */
|
||
|
CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */
|
||
|
CHAR_COLON: ':', /* : */
|
||
|
CHAR_COMMA: ',', /* , */
|
||
|
CHAR_DOLLAR: '$', /* . */
|
||
|
CHAR_DOT: '.', /* . */
|
||
|
CHAR_DOUBLE_QUOTE: '"', /* " */
|
||
|
CHAR_EQUAL: '=', /* = */
|
||
|
CHAR_EXCLAMATION_MARK: '!', /* ! */
|
||
|
CHAR_FORM_FEED: '\f', /* \f */
|
||
|
CHAR_FORWARD_SLASH: '/', /* / */
|
||
|
CHAR_HASH: '#', /* # */
|
||
|
CHAR_HYPHEN_MINUS: '-', /* - */
|
||
|
CHAR_LEFT_ANGLE_BRACKET: '<', /* < */
|
||
|
CHAR_LEFT_CURLY_BRACE: '{', /* { */
|
||
|
CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */
|
||
|
CHAR_LINE_FEED: '\n', /* \n */
|
||
|
CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */
|
||
|
CHAR_PERCENT: '%', /* % */
|
||
|
CHAR_PLUS: '+', /* + */
|
||
|
CHAR_QUESTION_MARK: '?', /* ? */
|
||
|
CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */
|
||
|
CHAR_RIGHT_CURLY_BRACE: '}', /* } */
|
||
|
CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */
|
||
|
CHAR_SEMICOLON: ';', /* ; */
|
||
|
CHAR_SINGLE_QUOTE: '\'', /* ' */
|
||
|
CHAR_SPACE: ' ', /* */
|
||
|
CHAR_TAB: '\t', /* \t */
|
||
|
CHAR_UNDERSCORE: '_', /* _ */
|
||
|
CHAR_VERTICAL_LINE: '|', /* | */
|
||
|
CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */
|
||
|
};
|
||
|
|
||
|
const stringify$1 = stringify$4;
|
||
|
|
||
|
/**
|
||
|
* Constants
|
||
|
*/
|
||
|
|
||
|
const {
|
||
|
MAX_LENGTH: MAX_LENGTH$1,
|
||
|
CHAR_BACKSLASH, /* \ */
|
||
|
CHAR_BACKTICK, /* ` */
|
||
|
CHAR_COMMA: CHAR_COMMA$1, /* , */
|
||
|
CHAR_DOT: CHAR_DOT$1, /* . */
|
||
|
CHAR_LEFT_PARENTHESES: CHAR_LEFT_PARENTHESES$1, /* ( */
|
||
|
CHAR_RIGHT_PARENTHESES: CHAR_RIGHT_PARENTHESES$1, /* ) */
|
||
|
CHAR_LEFT_CURLY_BRACE: CHAR_LEFT_CURLY_BRACE$1, /* { */
|
||
|
CHAR_RIGHT_CURLY_BRACE: CHAR_RIGHT_CURLY_BRACE$1, /* } */
|
||
|
CHAR_LEFT_SQUARE_BRACKET: CHAR_LEFT_SQUARE_BRACKET$1, /* [ */
|
||
|
CHAR_RIGHT_SQUARE_BRACKET: CHAR_RIGHT_SQUARE_BRACKET$1, /* ] */
|
||
|
CHAR_DOUBLE_QUOTE, /* " */
|
||
|
CHAR_SINGLE_QUOTE, /* ' */
|
||
|
CHAR_NO_BREAK_SPACE,
|
||
|
CHAR_ZERO_WIDTH_NOBREAK_SPACE
|
||
|
} = constants$4;
|
||
|
|
||
|
/**
|
||
|
* parse
|
||
|
*/
|
||
|
|
||
|
const parse$3 = (input, options = {}) => {
|
||
|
if (typeof input !== 'string') {
|
||
|
throw new TypeError('Expected a string');
|
||
|
}
|
||
|
|
||
|
let opts = options || {};
|
||
|
let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH$1, opts.maxLength) : MAX_LENGTH$1;
|
||
|
if (input.length > max) {
|
||
|
throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`);
|
||
|
}
|
||
|
|
||
|
let ast = { type: 'root', input, nodes: [] };
|
||
|
let stack = [ast];
|
||
|
let block = ast;
|
||
|
let prev = ast;
|
||
|
let brackets = 0;
|
||
|
let length = input.length;
|
||
|
let index = 0;
|
||
|
let depth = 0;
|
||
|
let value;
|
||
|
|
||
|
/**
|
||
|
* Helpers
|
||
|
*/
|
||
|
|
||
|
const advance = () => input[index++];
|
||
|
const push = node => {
|
||
|
if (node.type === 'text' && prev.type === 'dot') {
|
||
|
prev.type = 'text';
|
||
|
}
|
||
|
|
||
|
if (prev && prev.type === 'text' && node.type === 'text') {
|
||
|
prev.value += node.value;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
block.nodes.push(node);
|
||
|
node.parent = block;
|
||
|
node.prev = prev;
|
||
|
prev = node;
|
||
|
return node;
|
||
|
};
|
||
|
|
||
|
push({ type: 'bos' });
|
||
|
|
||
|
while (index < length) {
|
||
|
block = stack[stack.length - 1];
|
||
|
value = advance();
|
||
|
|
||
|
/**
|
||
|
* Invalid chars
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Escaped chars
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_BACKSLASH) {
|
||
|
push({ type: 'text', value: (options.keepEscaping ? value : '') + advance() });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Right square bracket (literal): ']'
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_RIGHT_SQUARE_BRACKET$1) {
|
||
|
push({ type: 'text', value: '\\' + value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Left square bracket: '['
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_LEFT_SQUARE_BRACKET$1) {
|
||
|
brackets++;
|
||
|
let next;
|
||
|
|
||
|
while (index < length && (next = advance())) {
|
||
|
value += next;
|
||
|
|
||
|
if (next === CHAR_LEFT_SQUARE_BRACKET$1) {
|
||
|
brackets++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (next === CHAR_BACKSLASH) {
|
||
|
value += advance();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (next === CHAR_RIGHT_SQUARE_BRACKET$1) {
|
||
|
brackets--;
|
||
|
|
||
|
if (brackets === 0) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parentheses
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_LEFT_PARENTHESES$1) {
|
||
|
block = push({ type: 'paren', nodes: [] });
|
||
|
stack.push(block);
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (value === CHAR_RIGHT_PARENTHESES$1) {
|
||
|
if (block.type !== 'paren') {
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
block = stack.pop();
|
||
|
push({ type: 'text', value });
|
||
|
block = stack[stack.length - 1];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Quotes: '|"|`
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) {
|
||
|
let open = value;
|
||
|
let next;
|
||
|
|
||
|
if (options.keepQuotes !== true) {
|
||
|
value = '';
|
||
|
}
|
||
|
|
||
|
while (index < length && (next = advance())) {
|
||
|
if (next === CHAR_BACKSLASH) {
|
||
|
value += next + advance();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (next === open) {
|
||
|
if (options.keepQuotes === true) value += next;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
value += next;
|
||
|
}
|
||
|
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Left curly brace: '{'
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_LEFT_CURLY_BRACE$1) {
|
||
|
depth++;
|
||
|
|
||
|
let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true;
|
||
|
let brace = {
|
||
|
type: 'brace',
|
||
|
open: true,
|
||
|
close: false,
|
||
|
dollar,
|
||
|
depth,
|
||
|
commas: 0,
|
||
|
ranges: 0,
|
||
|
nodes: []
|
||
|
};
|
||
|
|
||
|
block = push(brace);
|
||
|
stack.push(block);
|
||
|
push({ type: 'open', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Right curly brace: '}'
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_RIGHT_CURLY_BRACE$1) {
|
||
|
if (block.type !== 'brace') {
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
let type = 'close';
|
||
|
block = stack.pop();
|
||
|
block.close = true;
|
||
|
|
||
|
push({ type, value });
|
||
|
depth--;
|
||
|
|
||
|
block = stack[stack.length - 1];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Comma: ','
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_COMMA$1 && depth > 0) {
|
||
|
if (block.ranges > 0) {
|
||
|
block.ranges = 0;
|
||
|
let open = block.nodes.shift();
|
||
|
block.nodes = [open, { type: 'text', value: stringify$1(block) }];
|
||
|
}
|
||
|
|
||
|
push({ type: 'comma', value });
|
||
|
block.commas++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Dot: '.'
|
||
|
*/
|
||
|
|
||
|
if (value === CHAR_DOT$1 && depth > 0 && block.commas === 0) {
|
||
|
let siblings = block.nodes;
|
||
|
|
||
|
if (depth === 0 || siblings.length === 0) {
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (prev.type === 'dot') {
|
||
|
block.range = [];
|
||
|
prev.value += value;
|
||
|
prev.type = 'range';
|
||
|
|
||
|
if (block.nodes.length !== 3 && block.nodes.length !== 5) {
|
||
|
block.invalid = true;
|
||
|
block.ranges = 0;
|
||
|
prev.type = 'text';
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
block.ranges++;
|
||
|
block.args = [];
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (prev.type === 'range') {
|
||
|
siblings.pop();
|
||
|
|
||
|
let before = siblings[siblings.length - 1];
|
||
|
before.value += prev.value + value;
|
||
|
prev = before;
|
||
|
block.ranges--;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
push({ type: 'dot', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Text
|
||
|
*/
|
||
|
|
||
|
push({ type: 'text', value });
|
||
|
}
|
||
|
|
||
|
// Mark imbalanced braces and brackets as invalid
|
||
|
do {
|
||
|
block = stack.pop();
|
||
|
|
||
|
if (block.type !== 'root') {
|
||
|
block.nodes.forEach(node => {
|
||
|
if (!node.nodes) {
|
||
|
if (node.type === 'open') node.isOpen = true;
|
||
|
if (node.type === 'close') node.isClose = true;
|
||
|
if (!node.nodes) node.type = 'text';
|
||
|
node.invalid = true;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// get the location of the block on parent.nodes (block's siblings)
|
||
|
let parent = stack[stack.length - 1];
|
||
|
let index = parent.nodes.indexOf(block);
|
||
|
// replace the (invalid) block with it's nodes
|
||
|
parent.nodes.splice(index, 1, ...block.nodes);
|
||
|
}
|
||
|
} while (stack.length > 0);
|
||
|
|
||
|
push({ type: 'eos' });
|
||
|
return ast;
|
||
|
};
|
||
|
|
||
|
var parse_1$1 = parse$3;
|
||
|
|
||
|
const stringify = stringify$4;
|
||
|
const compile = compile_1;
|
||
|
const expand = expand_1;
|
||
|
const parse$2 = parse_1$1;
|
||
|
|
||
|
/**
|
||
|
* Expand the given pattern or create a regex-compatible string.
|
||
|
*
|
||
|
* ```js
|
||
|
* const braces = require('braces');
|
||
|
* console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)']
|
||
|
* console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c']
|
||
|
* ```
|
||
|
* @param {String} `str`
|
||
|
* @param {Object} `options`
|
||
|
* @return {String}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
const braces$1 = (input, options = {}) => {
|
||
|
let output = [];
|
||
|
|
||
|
if (Array.isArray(input)) {
|
||
|
for (let pattern of input) {
|
||
|
let result = braces$1.create(pattern, options);
|
||
|
if (Array.isArray(result)) {
|
||
|
output.push(...result);
|
||
|
} else {
|
||
|
output.push(result);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
output = [].concat(braces$1.create(input, options));
|
||
|
}
|
||
|
|
||
|
if (options && options.expand === true && options.nodupes === true) {
|
||
|
output = [...new Set(output)];
|
||
|
}
|
||
|
return output;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Parse the given `str` with the given `options`.
|
||
|
*
|
||
|
* ```js
|
||
|
* // braces.parse(pattern, [, options]);
|
||
|
* const ast = braces.parse('a/{b,c}/d');
|
||
|
* console.log(ast);
|
||
|
* ```
|
||
|
* @param {String} pattern Brace pattern to parse
|
||
|
* @param {Object} options
|
||
|
* @return {Object} Returns an AST
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
braces$1.parse = (input, options = {}) => parse$2(input, options);
|
||
|
|
||
|
/**
|
||
|
* Creates a braces string from an AST, or an AST node.
|
||
|
*
|
||
|
* ```js
|
||
|
* const braces = require('braces');
|
||
|
* let ast = braces.parse('foo/{a,b}/bar');
|
||
|
* console.log(stringify(ast.nodes[2])); //=> '{a,b}'
|
||
|
* ```
|
||
|
* @param {String} `input` Brace pattern or AST.
|
||
|
* @param {Object} `options`
|
||
|
* @return {Array} Returns an array of expanded values.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
braces$1.stringify = (input, options = {}) => {
|
||
|
if (typeof input === 'string') {
|
||
|
return stringify(braces$1.parse(input, options), options);
|
||
|
}
|
||
|
return stringify(input, options);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Compiles a brace pattern into a regex-compatible, optimized string.
|
||
|
* This method is called by the main [braces](#braces) function by default.
|
||
|
*
|
||
|
* ```js
|
||
|
* const braces = require('braces');
|
||
|
* console.log(braces.compile('a/{b,c}/d'));
|
||
|
* //=> ['a/(b|c)/d']
|
||
|
* ```
|
||
|
* @param {String} `input` Brace pattern or AST.
|
||
|
* @param {Object} `options`
|
||
|
* @return {Array} Returns an array of expanded values.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
braces$1.compile = (input, options = {}) => {
|
||
|
if (typeof input === 'string') {
|
||
|
input = braces$1.parse(input, options);
|
||
|
}
|
||
|
return compile(input, options);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Expands a brace pattern into an array. This method is called by the
|
||
|
* main [braces](#braces) function when `options.expand` is true. Before
|
||
|
* using this method it's recommended that you read the [performance notes](#performance))
|
||
|
* and advantages of using [.compile](#compile) instead.
|
||
|
*
|
||
|
* ```js
|
||
|
* const braces = require('braces');
|
||
|
* console.log(braces.expand('a/{b,c}/d'));
|
||
|
* //=> ['a/b/d', 'a/c/d'];
|
||
|
* ```
|
||
|
* @param {String} `pattern` Brace pattern
|
||
|
* @param {Object} `options`
|
||
|
* @return {Array} Returns an array of expanded values.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
braces$1.expand = (input, options = {}) => {
|
||
|
if (typeof input === 'string') {
|
||
|
input = braces$1.parse(input, options);
|
||
|
}
|
||
|
|
||
|
let result = expand(input, options);
|
||
|
|
||
|
// filter out empty strings if specified
|
||
|
if (options.noempty === true) {
|
||
|
result = result.filter(Boolean);
|
||
|
}
|
||
|
|
||
|
// filter out duplicates if specified
|
||
|
if (options.nodupes === true) {
|
||
|
result = [...new Set(result)];
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Processes a brace pattern and returns either an expanded array
|
||
|
* (if `options.expand` is true), a highly optimized regex-compatible string.
|
||
|
* This method is called by the main [braces](#braces) function.
|
||
|
*
|
||
|
* ```js
|
||
|
* const braces = require('braces');
|
||
|
* console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
|
||
|
* //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
|
||
|
* ```
|
||
|
* @param {String} `pattern` Brace pattern
|
||
|
* @param {Object} `options`
|
||
|
* @return {Array} Returns an array of expanded values.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
braces$1.create = (input, options = {}) => {
|
||
|
if (input === '' || input.length < 3) {
|
||
|
return [input];
|
||
|
}
|
||
|
|
||
|
return options.expand !== true
|
||
|
? braces$1.compile(input, options)
|
||
|
: braces$1.expand(input, options);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Expose "braces"
|
||
|
*/
|
||
|
|
||
|
var braces_1 = braces$1;
|
||
|
|
||
|
var picomatch$2 = {exports: {}};
|
||
|
|
||
|
var utils$f = {};
|
||
|
|
||
|
const path$7 = g;
|
||
|
const WIN_SLASH = '\\\\/';
|
||
|
const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
|
||
|
|
||
|
/**
|
||
|
* Posix glob regex
|
||
|
*/
|
||
|
|
||
|
const DOT_LITERAL = '\\.';
|
||
|
const PLUS_LITERAL = '\\+';
|
||
|
const QMARK_LITERAL = '\\?';
|
||
|
const SLASH_LITERAL = '\\/';
|
||
|
const ONE_CHAR = '(?=.)';
|
||
|
const QMARK = '[^/]';
|
||
|
const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`;
|
||
|
const START_ANCHOR = `(?:^|${SLASH_LITERAL})`;
|
||
|
const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`;
|
||
|
const NO_DOT = `(?!${DOT_LITERAL})`;
|
||
|
const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`;
|
||
|
const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`;
|
||
|
const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`;
|
||
|
const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`;
|
||
|
const STAR = `${QMARK}*?`;
|
||
|
|
||
|
const POSIX_CHARS = {
|
||
|
DOT_LITERAL,
|
||
|
PLUS_LITERAL,
|
||
|
QMARK_LITERAL,
|
||
|
SLASH_LITERAL,
|
||
|
ONE_CHAR,
|
||
|
QMARK,
|
||
|
END_ANCHOR,
|
||
|
DOTS_SLASH,
|
||
|
NO_DOT,
|
||
|
NO_DOTS,
|
||
|
NO_DOT_SLASH,
|
||
|
NO_DOTS_SLASH,
|
||
|
QMARK_NO_DOT,
|
||
|
STAR,
|
||
|
START_ANCHOR
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Windows glob regex
|
||
|
*/
|
||
|
|
||
|
const WINDOWS_CHARS = {
|
||
|
...POSIX_CHARS,
|
||
|
|
||
|
SLASH_LITERAL: `[${WIN_SLASH}]`,
|
||
|
QMARK: WIN_NO_SLASH,
|
||
|
STAR: `${WIN_NO_SLASH}*?`,
|
||
|
DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`,
|
||
|
NO_DOT: `(?!${DOT_LITERAL})`,
|
||
|
NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
|
||
|
NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`,
|
||
|
NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
|
||
|
QMARK_NO_DOT: `[^.${WIN_SLASH}]`,
|
||
|
START_ANCHOR: `(?:^|[${WIN_SLASH}])`,
|
||
|
END_ANCHOR: `(?:[${WIN_SLASH}]|$)`
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* POSIX Bracket Regex
|
||
|
*/
|
||
|
|
||
|
const POSIX_REGEX_SOURCE$1 = {
|
||
|
alnum: 'a-zA-Z0-9',
|
||
|
alpha: 'a-zA-Z',
|
||
|
ascii: '\\x00-\\x7F',
|
||
|
blank: ' \\t',
|
||
|
cntrl: '\\x00-\\x1F\\x7F',
|
||
|
digit: '0-9',
|
||
|
graph: '\\x21-\\x7E',
|
||
|
lower: 'a-z',
|
||
|
print: '\\x20-\\x7E ',
|
||
|
punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~',
|
||
|
space: ' \\t\\r\\n\\v\\f',
|
||
|
upper: 'A-Z',
|
||
|
word: 'A-Za-z0-9_',
|
||
|
xdigit: 'A-Fa-f0-9'
|
||
|
};
|
||
|
|
||
|
var constants$3 = {
|
||
|
MAX_LENGTH: 1024 * 64,
|
||
|
POSIX_REGEX_SOURCE: POSIX_REGEX_SOURCE$1,
|
||
|
|
||
|
// regular expressions
|
||
|
REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g,
|
||
|
REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/,
|
||
|
REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/,
|
||
|
REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g,
|
||
|
REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g,
|
||
|
REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g,
|
||
|
|
||
|
// Replace globs with equivalent patterns to reduce parsing time.
|
||
|
REPLACEMENTS: {
|
||
|
'***': '*',
|
||
|
'**/**': '**',
|
||
|
'**/**/**': '**'
|
||
|
},
|
||
|
|
||
|
// Digits
|
||
|
CHAR_0: 48, /* 0 */
|
||
|
CHAR_9: 57, /* 9 */
|
||
|
|
||
|
// Alphabet chars.
|
||
|
CHAR_UPPERCASE_A: 65, /* A */
|
||
|
CHAR_LOWERCASE_A: 97, /* a */
|
||
|
CHAR_UPPERCASE_Z: 90, /* Z */
|
||
|
CHAR_LOWERCASE_Z: 122, /* z */
|
||
|
|
||
|
CHAR_LEFT_PARENTHESES: 40, /* ( */
|
||
|
CHAR_RIGHT_PARENTHESES: 41, /* ) */
|
||
|
|
||
|
CHAR_ASTERISK: 42, /* * */
|
||
|
|
||
|
// Non-alphabetic chars.
|
||
|
CHAR_AMPERSAND: 38, /* & */
|
||
|
CHAR_AT: 64, /* @ */
|
||
|
CHAR_BACKWARD_SLASH: 92, /* \ */
|
||
|
CHAR_CARRIAGE_RETURN: 13, /* \r */
|
||
|
CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */
|
||
|
CHAR_COLON: 58, /* : */
|
||
|
CHAR_COMMA: 44, /* , */
|
||
|
CHAR_DOT: 46, /* . */
|
||
|
CHAR_DOUBLE_QUOTE: 34, /* " */
|
||
|
CHAR_EQUAL: 61, /* = */
|
||
|
CHAR_EXCLAMATION_MARK: 33, /* ! */
|
||
|
CHAR_FORM_FEED: 12, /* \f */
|
||
|
CHAR_FORWARD_SLASH: 47, /* / */
|
||
|
CHAR_GRAVE_ACCENT: 96, /* ` */
|
||
|
CHAR_HASH: 35, /* # */
|
||
|
CHAR_HYPHEN_MINUS: 45, /* - */
|
||
|
CHAR_LEFT_ANGLE_BRACKET: 60, /* < */
|
||
|
CHAR_LEFT_CURLY_BRACE: 123, /* { */
|
||
|
CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */
|
||
|
CHAR_LINE_FEED: 10, /* \n */
|
||
|
CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */
|
||
|
CHAR_PERCENT: 37, /* % */
|
||
|
CHAR_PLUS: 43, /* + */
|
||
|
CHAR_QUESTION_MARK: 63, /* ? */
|
||
|
CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */
|
||
|
CHAR_RIGHT_CURLY_BRACE: 125, /* } */
|
||
|
CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */
|
||
|
CHAR_SEMICOLON: 59, /* ; */
|
||
|
CHAR_SINGLE_QUOTE: 39, /* ' */
|
||
|
CHAR_SPACE: 32, /* */
|
||
|
CHAR_TAB: 9, /* \t */
|
||
|
CHAR_UNDERSCORE: 95, /* _ */
|
||
|
CHAR_VERTICAL_LINE: 124, /* | */
|
||
|
CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */
|
||
|
|
||
|
SEP: path$7.sep,
|
||
|
|
||
|
/**
|
||
|
* Create EXTGLOB_CHARS
|
||
|
*/
|
||
|
|
||
|
extglobChars(chars) {
|
||
|
return {
|
||
|
'!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` },
|
||
|
'?': { type: 'qmark', open: '(?:', close: ')?' },
|
||
|
'+': { type: 'plus', open: '(?:', close: ')+' },
|
||
|
'*': { type: 'star', open: '(?:', close: ')*' },
|
||
|
'@': { type: 'at', open: '(?:', close: ')' }
|
||
|
};
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Create GLOB_CHARS
|
||
|
*/
|
||
|
|
||
|
globChars(win32) {
|
||
|
return win32 === true ? WINDOWS_CHARS : POSIX_CHARS;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
(function (exports) {
|
||
|
|
||
|
const path = g;
|
||
|
const win32 = process.platform === 'win32';
|
||
|
const {
|
||
|
REGEX_BACKSLASH,
|
||
|
REGEX_REMOVE_BACKSLASH,
|
||
|
REGEX_SPECIAL_CHARS,
|
||
|
REGEX_SPECIAL_CHARS_GLOBAL
|
||
|
} = constants$3;
|
||
|
|
||
|
exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val);
|
||
|
exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str);
|
||
|
exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str);
|
||
|
exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1');
|
||
|
exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/');
|
||
|
|
||
|
exports.removeBackslashes = str => {
|
||
|
return str.replace(REGEX_REMOVE_BACKSLASH, match => {
|
||
|
return match === '\\' ? '' : match;
|
||
|
});
|
||
|
};
|
||
|
|
||
|
exports.supportsLookbehinds = () => {
|
||
|
const segs = process.version.slice(1).split('.').map(Number);
|
||
|
if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
exports.isWindows = options => {
|
||
|
if (options && typeof options.windows === 'boolean') {
|
||
|
return options.windows;
|
||
|
}
|
||
|
return win32 === true || path.sep === '\\';
|
||
|
};
|
||
|
|
||
|
exports.escapeLast = (input, char, lastIdx) => {
|
||
|
const idx = input.lastIndexOf(char, lastIdx);
|
||
|
if (idx === -1) return input;
|
||
|
if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1);
|
||
|
return `${input.slice(0, idx)}\\${input.slice(idx)}`;
|
||
|
};
|
||
|
|
||
|
exports.removePrefix = (input, state = {}) => {
|
||
|
let output = input;
|
||
|
if (output.startsWith('./')) {
|
||
|
output = output.slice(2);
|
||
|
state.prefix = './';
|
||
|
}
|
||
|
return output;
|
||
|
};
|
||
|
|
||
|
exports.wrapOutput = (input, state = {}, options = {}) => {
|
||
|
const prepend = options.contains ? '' : '^';
|
||
|
const append = options.contains ? '' : '$';
|
||
|
|
||
|
let output = `${prepend}(?:${input})${append}`;
|
||
|
if (state.negated === true) {
|
||
|
output = `(?:^(?!${output}).*$)`;
|
||
|
}
|
||
|
return output;
|
||
|
};
|
||
|
} (utils$f));
|
||
|
|
||
|
const utils$e = utils$f;
|
||
|
const {
|
||
|
CHAR_ASTERISK, /* * */
|
||
|
CHAR_AT, /* @ */
|
||
|
CHAR_BACKWARD_SLASH, /* \ */
|
||
|
CHAR_COMMA, /* , */
|
||
|
CHAR_DOT, /* . */
|
||
|
CHAR_EXCLAMATION_MARK, /* ! */
|
||
|
CHAR_FORWARD_SLASH, /* / */
|
||
|
CHAR_LEFT_CURLY_BRACE, /* { */
|
||
|
CHAR_LEFT_PARENTHESES, /* ( */
|
||
|
CHAR_LEFT_SQUARE_BRACKET, /* [ */
|
||
|
CHAR_PLUS, /* + */
|
||
|
CHAR_QUESTION_MARK, /* ? */
|
||
|
CHAR_RIGHT_CURLY_BRACE, /* } */
|
||
|
CHAR_RIGHT_PARENTHESES, /* ) */
|
||
|
CHAR_RIGHT_SQUARE_BRACKET /* ] */
|
||
|
} = constants$3;
|
||
|
|
||
|
const isPathSeparator = code => {
|
||
|
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
|
||
|
};
|
||
|
|
||
|
const depth = token => {
|
||
|
if (token.isPrefix !== true) {
|
||
|
token.depth = token.isGlobstar ? Infinity : 1;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Quickly scans a glob pattern and returns an object with a handful of
|
||
|
* useful properties, like `isGlob`, `path` (the leading non-glob, if it exists),
|
||
|
* `glob` (the actual pattern), `negated` (true if the path starts with `!` but not
|
||
|
* with `!(`) and `negatedExtglob` (true if the path starts with `!(`).
|
||
|
*
|
||
|
* ```js
|
||
|
* const pm = require('picomatch');
|
||
|
* console.log(pm.scan('foo/bar/*.js'));
|
||
|
* { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' }
|
||
|
* ```
|
||
|
* @param {String} `str`
|
||
|
* @param {Object} `options`
|
||
|
* @return {Object} Returns an object with tokens and regex source string.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
const scan$1 = (input, options) => {
|
||
|
const opts = options || {};
|
||
|
|
||
|
const length = input.length - 1;
|
||
|
const scanToEnd = opts.parts === true || opts.scanToEnd === true;
|
||
|
const slashes = [];
|
||
|
const tokens = [];
|
||
|
const parts = [];
|
||
|
|
||
|
let str = input;
|
||
|
let index = -1;
|
||
|
let start = 0;
|
||
|
let lastIndex = 0;
|
||
|
let isBrace = false;
|
||
|
let isBracket = false;
|
||
|
let isGlob = false;
|
||
|
let isExtglob = false;
|
||
|
let isGlobstar = false;
|
||
|
let braceEscaped = false;
|
||
|
let backslashes = false;
|
||
|
let negated = false;
|
||
|
let negatedExtglob = false;
|
||
|
let finished = false;
|
||
|
let braces = 0;
|
||
|
let prev;
|
||
|
let code;
|
||
|
let token = { value: '', depth: 0, isGlob: false };
|
||
|
|
||
|
const eos = () => index >= length;
|
||
|
const peek = () => str.charCodeAt(index + 1);
|
||
|
const advance = () => {
|
||
|
prev = code;
|
||
|
return str.charCodeAt(++index);
|
||
|
};
|
||
|
|
||
|
while (index < length) {
|
||
|
code = advance();
|
||
|
let next;
|
||
|
|
||
|
if (code === CHAR_BACKWARD_SLASH) {
|
||
|
backslashes = token.backslashes = true;
|
||
|
code = advance();
|
||
|
|
||
|
if (code === CHAR_LEFT_CURLY_BRACE) {
|
||
|
braceEscaped = true;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) {
|
||
|
braces++;
|
||
|
|
||
|
while (eos() !== true && (code = advance())) {
|
||
|
if (code === CHAR_BACKWARD_SLASH) {
|
||
|
backslashes = token.backslashes = true;
|
||
|
advance();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (code === CHAR_LEFT_CURLY_BRACE) {
|
||
|
braces++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) {
|
||
|
isBrace = token.isBrace = true;
|
||
|
isGlob = token.isGlob = true;
|
||
|
finished = true;
|
||
|
|
||
|
if (scanToEnd === true) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (braceEscaped !== true && code === CHAR_COMMA) {
|
||
|
isBrace = token.isBrace = true;
|
||
|
isGlob = token.isGlob = true;
|
||
|
finished = true;
|
||
|
|
||
|
if (scanToEnd === true) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (code === CHAR_RIGHT_CURLY_BRACE) {
|
||
|
braces--;
|
||
|
|
||
|
if (braces === 0) {
|
||
|
braceEscaped = false;
|
||
|
isBrace = token.isBrace = true;
|
||
|
finished = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (scanToEnd === true) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (code === CHAR_FORWARD_SLASH) {
|
||
|
slashes.push(index);
|
||
|
tokens.push(token);
|
||
|
token = { value: '', depth: 0, isGlob: false };
|
||
|
|
||
|
if (finished === true) continue;
|
||
|
if (prev === CHAR_DOT && index === (start + 1)) {
|
||
|
start += 2;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
lastIndex = index + 1;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (opts.noext !== true) {
|
||
|
const isExtglobChar = code === CHAR_PLUS
|
||
|
|| code === CHAR_AT
|
||
|
|| code === CHAR_ASTERISK
|
||
|
|| code === CHAR_QUESTION_MARK
|
||
|
|| code === CHAR_EXCLAMATION_MARK;
|
||
|
|
||
|
if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) {
|
||
|
isGlob = token.isGlob = true;
|
||
|
isExtglob = token.isExtglob = true;
|
||
|
finished = true;
|
||
|
if (code === CHAR_EXCLAMATION_MARK && index === start) {
|
||
|
negatedExtglob = true;
|
||
|
}
|
||
|
|
||
|
if (scanToEnd === true) {
|
||
|
while (eos() !== true && (code = advance())) {
|
||
|
if (code === CHAR_BACKWARD_SLASH) {
|
||
|
backslashes = token.backslashes = true;
|
||
|
code = advance();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (code === CHAR_RIGHT_PARENTHESES) {
|
||
|
isGlob = token.isGlob = true;
|
||
|
finished = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (code === CHAR_ASTERISK) {
|
||
|
if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true;
|
||
|
isGlob = token.isGlob = true;
|
||
|
finished = true;
|
||
|
|
||
|
if (scanToEnd === true) {
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (code === CHAR_QUESTION_MARK) {
|
||
|
isGlob = token.isGlob = true;
|
||
|
finished = true;
|
||
|
|
||
|
if (scanToEnd === true) {
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (code === CHAR_LEFT_SQUARE_BRACKET) {
|
||
|
while (eos() !== true && (next = advance())) {
|
||
|
if (next === CHAR_BACKWARD_SLASH) {
|
||
|
backslashes = token.backslashes = true;
|
||
|
advance();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (next === CHAR_RIGHT_SQUARE_BRACKET) {
|
||
|
isBracket = token.isBracket = true;
|
||
|
isGlob = token.isGlob = true;
|
||
|
finished = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (scanToEnd === true) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) {
|
||
|
negated = token.negated = true;
|
||
|
start++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) {
|
||
|
isGlob = token.isGlob = true;
|
||
|
|
||
|
if (scanToEnd === true) {
|
||
|
while (eos() !== true && (code = advance())) {
|
||
|
if (code === CHAR_LEFT_PARENTHESES) {
|
||
|
backslashes = token.backslashes = true;
|
||
|
code = advance();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (code === CHAR_RIGHT_PARENTHESES) {
|
||
|
finished = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (isGlob === true) {
|
||
|
finished = true;
|
||
|
|
||
|
if (scanToEnd === true) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (opts.noext === true) {
|
||
|
isExtglob = false;
|
||
|
isGlob = false;
|
||
|
}
|
||
|
|
||
|
let base = str;
|
||
|
let prefix = '';
|
||
|
let glob = '';
|
||
|
|
||
|
if (start > 0) {
|
||
|
prefix = str.slice(0, start);
|
||
|
str = str.slice(start);
|
||
|
lastIndex -= start;
|
||
|
}
|
||
|
|
||
|
if (base && isGlob === true && lastIndex > 0) {
|
||
|
base = str.slice(0, lastIndex);
|
||
|
glob = str.slice(lastIndex);
|
||
|
} else if (isGlob === true) {
|
||
|
base = '';
|
||
|
glob = str;
|
||
|
} else {
|
||
|
base = str;
|
||
|
}
|
||
|
|
||
|
if (base && base !== '' && base !== '/' && base !== str) {
|
||
|
if (isPathSeparator(base.charCodeAt(base.length - 1))) {
|
||
|
base = base.slice(0, -1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (opts.unescape === true) {
|
||
|
if (glob) glob = utils$e.removeBackslashes(glob);
|
||
|
|
||
|
if (base && backslashes === true) {
|
||
|
base = utils$e.removeBackslashes(base);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const state = {
|
||
|
prefix,
|
||
|
input,
|
||
|
start,
|
||
|
base,
|
||
|
glob,
|
||
|
isBrace,
|
||
|
isBracket,
|
||
|
isGlob,
|
||
|
isExtglob,
|
||
|
isGlobstar,
|
||
|
negated,
|
||
|
negatedExtglob
|
||
|
};
|
||
|
|
||
|
if (opts.tokens === true) {
|
||
|
state.maxDepth = 0;
|
||
|
if (!isPathSeparator(code)) {
|
||
|
tokens.push(token);
|
||
|
}
|
||
|
state.tokens = tokens;
|
||
|
}
|
||
|
|
||
|
if (opts.parts === true || opts.tokens === true) {
|
||
|
let prevIndex;
|
||
|
|
||
|
for (let idx = 0; idx < slashes.length; idx++) {
|
||
|
const n = prevIndex ? prevIndex + 1 : start;
|
||
|
const i = slashes[idx];
|
||
|
const value = input.slice(n, i);
|
||
|
if (opts.tokens) {
|
||
|
if (idx === 0 && start !== 0) {
|
||
|
tokens[idx].isPrefix = true;
|
||
|
tokens[idx].value = prefix;
|
||
|
} else {
|
||
|
tokens[idx].value = value;
|
||
|
}
|
||
|
depth(tokens[idx]);
|
||
|
state.maxDepth += tokens[idx].depth;
|
||
|
}
|
||
|
if (idx !== 0 || value !== '') {
|
||
|
parts.push(value);
|
||
|
}
|
||
|
prevIndex = i;
|
||
|
}
|
||
|
|
||
|
if (prevIndex && prevIndex + 1 < input.length) {
|
||
|
const value = input.slice(prevIndex + 1);
|
||
|
parts.push(value);
|
||
|
|
||
|
if (opts.tokens) {
|
||
|
tokens[tokens.length - 1].value = value;
|
||
|
depth(tokens[tokens.length - 1]);
|
||
|
state.maxDepth += tokens[tokens.length - 1].depth;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
state.slashes = slashes;
|
||
|
state.parts = parts;
|
||
|
}
|
||
|
|
||
|
return state;
|
||
|
};
|
||
|
|
||
|
var scan_1 = scan$1;
|
||
|
|
||
|
const constants$2 = constants$3;
|
||
|
const utils$d = utils$f;
|
||
|
|
||
|
/**
|
||
|
* Constants
|
||
|
*/
|
||
|
|
||
|
const {
|
||
|
MAX_LENGTH,
|
||
|
POSIX_REGEX_SOURCE,
|
||
|
REGEX_NON_SPECIAL_CHARS,
|
||
|
REGEX_SPECIAL_CHARS_BACKREF,
|
||
|
REPLACEMENTS
|
||
|
} = constants$2;
|
||
|
|
||
|
/**
|
||
|
* Helpers
|
||
|
*/
|
||
|
|
||
|
const expandRange = (args, options) => {
|
||
|
if (typeof options.expandRange === 'function') {
|
||
|
return options.expandRange(...args, options);
|
||
|
}
|
||
|
|
||
|
args.sort();
|
||
|
const value = `[${args.join('-')}]`;
|
||
|
|
||
|
try {
|
||
|
/* eslint-disable-next-line no-new */
|
||
|
new RegExp(value);
|
||
|
} catch (ex) {
|
||
|
return args.map(v => utils$d.escapeRegex(v)).join('..');
|
||
|
}
|
||
|
|
||
|
return value;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Create the message for a syntax error
|
||
|
*/
|
||
|
|
||
|
const syntaxError = (type, char) => {
|
||
|
return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Parse the given input string.
|
||
|
* @param {String} input
|
||
|
* @param {Object} options
|
||
|
* @return {Object}
|
||
|
*/
|
||
|
|
||
|
const parse$1 = (input, options) => {
|
||
|
if (typeof input !== 'string') {
|
||
|
throw new TypeError('Expected a string');
|
||
|
}
|
||
|
|
||
|
input = REPLACEMENTS[input] || input;
|
||
|
|
||
|
const opts = { ...options };
|
||
|
const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
|
||
|
|
||
|
let len = input.length;
|
||
|
if (len > max) {
|
||
|
throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
|
||
|
}
|
||
|
|
||
|
const bos = { type: 'bos', value: '', output: opts.prepend || '' };
|
||
|
const tokens = [bos];
|
||
|
|
||
|
const capture = opts.capture ? '' : '?:';
|
||
|
const win32 = utils$d.isWindows(options);
|
||
|
|
||
|
// create constants based on platform, for windows or posix
|
||
|
const PLATFORM_CHARS = constants$2.globChars(win32);
|
||
|
const EXTGLOB_CHARS = constants$2.extglobChars(PLATFORM_CHARS);
|
||
|
|
||
|
const {
|
||
|
DOT_LITERAL,
|
||
|
PLUS_LITERAL,
|
||
|
SLASH_LITERAL,
|
||
|
ONE_CHAR,
|
||
|
DOTS_SLASH,
|
||
|
NO_DOT,
|
||
|
NO_DOT_SLASH,
|
||
|
NO_DOTS_SLASH,
|
||
|
QMARK,
|
||
|
QMARK_NO_DOT,
|
||
|
STAR,
|
||
|
START_ANCHOR
|
||
|
} = PLATFORM_CHARS;
|
||
|
|
||
|
const globstar = opts => {
|
||
|
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
|
||
|
};
|
||
|
|
||
|
const nodot = opts.dot ? '' : NO_DOT;
|
||
|
const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT;
|
||
|
let star = opts.bash === true ? globstar(opts) : STAR;
|
||
|
|
||
|
if (opts.capture) {
|
||
|
star = `(${star})`;
|
||
|
}
|
||
|
|
||
|
// minimatch options support
|
||
|
if (typeof opts.noext === 'boolean') {
|
||
|
opts.noextglob = opts.noext;
|
||
|
}
|
||
|
|
||
|
const state = {
|
||
|
input,
|
||
|
index: -1,
|
||
|
start: 0,
|
||
|
dot: opts.dot === true,
|
||
|
consumed: '',
|
||
|
output: '',
|
||
|
prefix: '',
|
||
|
backtrack: false,
|
||
|
negated: false,
|
||
|
brackets: 0,
|
||
|
braces: 0,
|
||
|
parens: 0,
|
||
|
quotes: 0,
|
||
|
globstar: false,
|
||
|
tokens
|
||
|
};
|
||
|
|
||
|
input = utils$d.removePrefix(input, state);
|
||
|
len = input.length;
|
||
|
|
||
|
const extglobs = [];
|
||
|
const braces = [];
|
||
|
const stack = [];
|
||
|
let prev = bos;
|
||
|
let value;
|
||
|
|
||
|
/**
|
||
|
* Tokenizing helpers
|
||
|
*/
|
||
|
|
||
|
const eos = () => state.index === len - 1;
|
||
|
const peek = state.peek = (n = 1) => input[state.index + n];
|
||
|
const advance = state.advance = () => input[++state.index] || '';
|
||
|
const remaining = () => input.slice(state.index + 1);
|
||
|
const consume = (value = '', num = 0) => {
|
||
|
state.consumed += value;
|
||
|
state.index += num;
|
||
|
};
|
||
|
|
||
|
const append = token => {
|
||
|
state.output += token.output != null ? token.output : token.value;
|
||
|
consume(token.value);
|
||
|
};
|
||
|
|
||
|
const negate = () => {
|
||
|
let count = 1;
|
||
|
|
||
|
while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) {
|
||
|
advance();
|
||
|
state.start++;
|
||
|
count++;
|
||
|
}
|
||
|
|
||
|
if (count % 2 === 0) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
state.negated = true;
|
||
|
state.start++;
|
||
|
return true;
|
||
|
};
|
||
|
|
||
|
const increment = type => {
|
||
|
state[type]++;
|
||
|
stack.push(type);
|
||
|
};
|
||
|
|
||
|
const decrement = type => {
|
||
|
state[type]--;
|
||
|
stack.pop();
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Push tokens onto the tokens array. This helper speeds up
|
||
|
* tokenizing by 1) helping us avoid backtracking as much as possible,
|
||
|
* and 2) helping us avoid creating extra tokens when consecutive
|
||
|
* characters are plain text. This improves performance and simplifies
|
||
|
* lookbehinds.
|
||
|
*/
|
||
|
|
||
|
const push = tok => {
|
||
|
if (prev.type === 'globstar') {
|
||
|
const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace');
|
||
|
const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren'));
|
||
|
|
||
|
if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) {
|
||
|
state.output = state.output.slice(0, -prev.output.length);
|
||
|
prev.type = 'star';
|
||
|
prev.value = '*';
|
||
|
prev.output = star;
|
||
|
state.output += prev.output;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (extglobs.length && tok.type !== 'paren') {
|
||
|
extglobs[extglobs.length - 1].inner += tok.value;
|
||
|
}
|
||
|
|
||
|
if (tok.value || tok.output) append(tok);
|
||
|
if (prev && prev.type === 'text' && tok.type === 'text') {
|
||
|
prev.value += tok.value;
|
||
|
prev.output = (prev.output || '') + tok.value;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
tok.prev = prev;
|
||
|
tokens.push(tok);
|
||
|
prev = tok;
|
||
|
};
|
||
|
|
||
|
const extglobOpen = (type, value) => {
|
||
|
const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' };
|
||
|
|
||
|
token.prev = prev;
|
||
|
token.parens = state.parens;
|
||
|
token.output = state.output;
|
||
|
const output = (opts.capture ? '(' : '') + token.open;
|
||
|
|
||
|
increment('parens');
|
||
|
push({ type, value, output: state.output ? '' : ONE_CHAR });
|
||
|
push({ type: 'paren', extglob: true, value: advance(), output });
|
||
|
extglobs.push(token);
|
||
|
};
|
||
|
|
||
|
const extglobClose = token => {
|
||
|
let output = token.close + (opts.capture ? ')' : '');
|
||
|
let rest;
|
||
|
|
||
|
if (token.type === 'negate') {
|
||
|
let extglobStar = star;
|
||
|
|
||
|
if (token.inner && token.inner.length > 1 && token.inner.includes('/')) {
|
||
|
extglobStar = globstar(opts);
|
||
|
}
|
||
|
|
||
|
if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) {
|
||
|
output = token.close = `)$))${extglobStar}`;
|
||
|
}
|
||
|
|
||
|
if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
|
||
|
// Any non-magical string (`.ts`) or even nested expression (`.{ts,tsx}`) can follow after the closing parenthesis.
|
||
|
// In this case, we need to parse the string and use it in the output of the original pattern.
|
||
|
// Suitable patterns: `/!(*.d).ts`, `/!(*.d).{ts,tsx}`, `**/!(*-dbg).@(js)`.
|
||
|
//
|
||
|
// Disabling the `fastpaths` option due to a problem with parsing strings as `.ts` in the pattern like `**/!(*.d).ts`.
|
||
|
const expression = parse$1(rest, { ...options, fastpaths: false }).output;
|
||
|
|
||
|
output = token.close = `)${expression})${extglobStar})`;
|
||
|
}
|
||
|
|
||
|
if (token.prev.type === 'bos') {
|
||
|
state.negatedExtglob = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
push({ type: 'paren', extglob: true, value, output });
|
||
|
decrement('parens');
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Fast paths
|
||
|
*/
|
||
|
|
||
|
if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) {
|
||
|
let backslashes = false;
|
||
|
|
||
|
let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => {
|
||
|
if (first === '\\') {
|
||
|
backslashes = true;
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
if (first === '?') {
|
||
|
if (esc) {
|
||
|
return esc + first + (rest ? QMARK.repeat(rest.length) : '');
|
||
|
}
|
||
|
if (index === 0) {
|
||
|
return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : '');
|
||
|
}
|
||
|
return QMARK.repeat(chars.length);
|
||
|
}
|
||
|
|
||
|
if (first === '.') {
|
||
|
return DOT_LITERAL.repeat(chars.length);
|
||
|
}
|
||
|
|
||
|
if (first === '*') {
|
||
|
if (esc) {
|
||
|
return esc + first + (rest ? star : '');
|
||
|
}
|
||
|
return star;
|
||
|
}
|
||
|
return esc ? m : `\\${m}`;
|
||
|
});
|
||
|
|
||
|
if (backslashes === true) {
|
||
|
if (opts.unescape === true) {
|
||
|
output = output.replace(/\\/g, '');
|
||
|
} else {
|
||
|
output = output.replace(/\\+/g, m => {
|
||
|
return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : '');
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (output === input && opts.contains === true) {
|
||
|
state.output = input;
|
||
|
return state;
|
||
|
}
|
||
|
|
||
|
state.output = utils$d.wrapOutput(output, state, options);
|
||
|
return state;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tokenize input until we reach end-of-string
|
||
|
*/
|
||
|
|
||
|
while (!eos()) {
|
||
|
value = advance();
|
||
|
|
||
|
if (value === '\u0000') {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Escaped characters
|
||
|
*/
|
||
|
|
||
|
if (value === '\\') {
|
||
|
const next = peek();
|
||
|
|
||
|
if (next === '/' && opts.bash !== true) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (next === '.' || next === ';') {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!next) {
|
||
|
value += '\\';
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// collapse slashes to reduce potential for exploits
|
||
|
const match = /^\\+/.exec(remaining());
|
||
|
let slashes = 0;
|
||
|
|
||
|
if (match && match[0].length > 2) {
|
||
|
slashes = match[0].length;
|
||
|
state.index += slashes;
|
||
|
if (slashes % 2 !== 0) {
|
||
|
value += '\\';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (opts.unescape === true) {
|
||
|
value = advance();
|
||
|
} else {
|
||
|
value += advance();
|
||
|
}
|
||
|
|
||
|
if (state.brackets === 0) {
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If we're inside a regex character class, continue
|
||
|
* until we reach the closing bracket.
|
||
|
*/
|
||
|
|
||
|
if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) {
|
||
|
if (opts.posix !== false && value === ':') {
|
||
|
const inner = prev.value.slice(1);
|
||
|
if (inner.includes('[')) {
|
||
|
prev.posix = true;
|
||
|
|
||
|
if (inner.includes(':')) {
|
||
|
const idx = prev.value.lastIndexOf('[');
|
||
|
const pre = prev.value.slice(0, idx);
|
||
|
const rest = prev.value.slice(idx + 2);
|
||
|
const posix = POSIX_REGEX_SOURCE[rest];
|
||
|
if (posix) {
|
||
|
prev.value = pre + posix;
|
||
|
state.backtrack = true;
|
||
|
advance();
|
||
|
|
||
|
if (!bos.output && tokens.indexOf(prev) === 1) {
|
||
|
bos.output = ONE_CHAR;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) {
|
||
|
value = `\\${value}`;
|
||
|
}
|
||
|
|
||
|
if (value === ']' && (prev.value === '[' || prev.value === '[^')) {
|
||
|
value = `\\${value}`;
|
||
|
}
|
||
|
|
||
|
if (opts.posix === true && value === '!' && prev.value === '[') {
|
||
|
value = '^';
|
||
|
}
|
||
|
|
||
|
prev.value += value;
|
||
|
append({ value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If we're inside a quoted string, continue
|
||
|
* until we reach the closing double quote.
|
||
|
*/
|
||
|
|
||
|
if (state.quotes === 1 && value !== '"') {
|
||
|
value = utils$d.escapeRegex(value);
|
||
|
prev.value += value;
|
||
|
append({ value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Double quotes
|
||
|
*/
|
||
|
|
||
|
if (value === '"') {
|
||
|
state.quotes = state.quotes === 1 ? 0 : 1;
|
||
|
if (opts.keepQuotes === true) {
|
||
|
push({ type: 'text', value });
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parentheses
|
||
|
*/
|
||
|
|
||
|
if (value === '(') {
|
||
|
increment('parens');
|
||
|
push({ type: 'paren', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (value === ')') {
|
||
|
if (state.parens === 0 && opts.strictBrackets === true) {
|
||
|
throw new SyntaxError(syntaxError('opening', '('));
|
||
|
}
|
||
|
|
||
|
const extglob = extglobs[extglobs.length - 1];
|
||
|
if (extglob && state.parens === extglob.parens + 1) {
|
||
|
extglobClose(extglobs.pop());
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
push({ type: 'paren', value, output: state.parens ? ')' : '\\)' });
|
||
|
decrement('parens');
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Square brackets
|
||
|
*/
|
||
|
|
||
|
if (value === '[') {
|
||
|
if (opts.nobracket === true || !remaining().includes(']')) {
|
||
|
if (opts.nobracket !== true && opts.strictBrackets === true) {
|
||
|
throw new SyntaxError(syntaxError('closing', ']'));
|
||
|
}
|
||
|
|
||
|
value = `\\${value}`;
|
||
|
} else {
|
||
|
increment('brackets');
|
||
|
}
|
||
|
|
||
|
push({ type: 'bracket', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (value === ']') {
|
||
|
if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) {
|
||
|
push({ type: 'text', value, output: `\\${value}` });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (state.brackets === 0) {
|
||
|
if (opts.strictBrackets === true) {
|
||
|
throw new SyntaxError(syntaxError('opening', '['));
|
||
|
}
|
||
|
|
||
|
push({ type: 'text', value, output: `\\${value}` });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
decrement('brackets');
|
||
|
|
||
|
const prevValue = prev.value.slice(1);
|
||
|
if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) {
|
||
|
value = `/${value}`;
|
||
|
}
|
||
|
|
||
|
prev.value += value;
|
||
|
append({ value });
|
||
|
|
||
|
// when literal brackets are explicitly disabled
|
||
|
// assume we should match with a regex character class
|
||
|
if (opts.literalBrackets === false || utils$d.hasRegexChars(prevValue)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const escaped = utils$d.escapeRegex(prev.value);
|
||
|
state.output = state.output.slice(0, -prev.value.length);
|
||
|
|
||
|
// when literal brackets are explicitly enabled
|
||
|
// assume we should escape the brackets to match literal characters
|
||
|
if (opts.literalBrackets === true) {
|
||
|
state.output += escaped;
|
||
|
prev.value = escaped;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// when the user specifies nothing, try to match both
|
||
|
prev.value = `(${capture}${escaped}|${prev.value})`;
|
||
|
state.output += prev.value;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Braces
|
||
|
*/
|
||
|
|
||
|
if (value === '{' && opts.nobrace !== true) {
|
||
|
increment('braces');
|
||
|
|
||
|
const open = {
|
||
|
type: 'brace',
|
||
|
value,
|
||
|
output: '(',
|
||
|
outputIndex: state.output.length,
|
||
|
tokensIndex: state.tokens.length
|
||
|
};
|
||
|
|
||
|
braces.push(open);
|
||
|
push(open);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (value === '}') {
|
||
|
const brace = braces[braces.length - 1];
|
||
|
|
||
|
if (opts.nobrace === true || !brace) {
|
||
|
push({ type: 'text', value, output: value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
let output = ')';
|
||
|
|
||
|
if (brace.dots === true) {
|
||
|
const arr = tokens.slice();
|
||
|
const range = [];
|
||
|
|
||
|
for (let i = arr.length - 1; i >= 0; i--) {
|
||
|
tokens.pop();
|
||
|
if (arr[i].type === 'brace') {
|
||
|
break;
|
||
|
}
|
||
|
if (arr[i].type !== 'dots') {
|
||
|
range.unshift(arr[i].value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
output = expandRange(range, opts);
|
||
|
state.backtrack = true;
|
||
|
}
|
||
|
|
||
|
if (brace.comma !== true && brace.dots !== true) {
|
||
|
const out = state.output.slice(0, brace.outputIndex);
|
||
|
const toks = state.tokens.slice(brace.tokensIndex);
|
||
|
brace.value = brace.output = '\\{';
|
||
|
value = output = '\\}';
|
||
|
state.output = out;
|
||
|
for (const t of toks) {
|
||
|
state.output += (t.output || t.value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
push({ type: 'brace', value, output });
|
||
|
decrement('braces');
|
||
|
braces.pop();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Pipes
|
||
|
*/
|
||
|
|
||
|
if (value === '|') {
|
||
|
if (extglobs.length > 0) {
|
||
|
extglobs[extglobs.length - 1].conditions++;
|
||
|
}
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Commas
|
||
|
*/
|
||
|
|
||
|
if (value === ',') {
|
||
|
let output = value;
|
||
|
|
||
|
const brace = braces[braces.length - 1];
|
||
|
if (brace && stack[stack.length - 1] === 'braces') {
|
||
|
brace.comma = true;
|
||
|
output = '|';
|
||
|
}
|
||
|
|
||
|
push({ type: 'comma', value, output });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Slashes
|
||
|
*/
|
||
|
|
||
|
if (value === '/') {
|
||
|
// if the beginning of the glob is "./", advance the start
|
||
|
// to the current index, and don't add the "./" characters
|
||
|
// to the state. This greatly simplifies lookbehinds when
|
||
|
// checking for BOS characters like "!" and "." (not "./")
|
||
|
if (prev.type === 'dot' && state.index === state.start + 1) {
|
||
|
state.start = state.index + 1;
|
||
|
state.consumed = '';
|
||
|
state.output = '';
|
||
|
tokens.pop();
|
||
|
prev = bos; // reset "prev" to the first token
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
push({ type: 'slash', value, output: SLASH_LITERAL });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Dots
|
||
|
*/
|
||
|
|
||
|
if (value === '.') {
|
||
|
if (state.braces > 0 && prev.type === 'dot') {
|
||
|
if (prev.value === '.') prev.output = DOT_LITERAL;
|
||
|
const brace = braces[braces.length - 1];
|
||
|
prev.type = 'dots';
|
||
|
prev.output += value;
|
||
|
prev.value += value;
|
||
|
brace.dots = true;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') {
|
||
|
push({ type: 'text', value, output: DOT_LITERAL });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
push({ type: 'dot', value, output: DOT_LITERAL });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Question marks
|
||
|
*/
|
||
|
|
||
|
if (value === '?') {
|
||
|
const isGroup = prev && prev.value === '(';
|
||
|
if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
|
||
|
extglobOpen('qmark', value);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (prev && prev.type === 'paren') {
|
||
|
const next = peek();
|
||
|
let output = value;
|
||
|
|
||
|
if (next === '<' && !utils$d.supportsLookbehinds()) {
|
||
|
throw new Error('Node.js v10 or higher is required for regex lookbehinds');
|
||
|
}
|
||
|
|
||
|
if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) {
|
||
|
output = `\\${value}`;
|
||
|
}
|
||
|
|
||
|
push({ type: 'text', value, output });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) {
|
||
|
push({ type: 'qmark', value, output: QMARK_NO_DOT });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
push({ type: 'qmark', value, output: QMARK });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Exclamation
|
||
|
*/
|
||
|
|
||
|
if (value === '!') {
|
||
|
if (opts.noextglob !== true && peek() === '(') {
|
||
|
if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) {
|
||
|
extglobOpen('negate', value);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (opts.nonegate !== true && state.index === 0) {
|
||
|
negate();
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Plus
|
||
|
*/
|
||
|
|
||
|
if (value === '+') {
|
||
|
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
|
||
|
extglobOpen('plus', value);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((prev && prev.value === '(') || opts.regex === false) {
|
||
|
push({ type: 'plus', value, output: PLUS_LITERAL });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) {
|
||
|
push({ type: 'plus', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
push({ type: 'plus', value: PLUS_LITERAL });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Plain text
|
||
|
*/
|
||
|
|
||
|
if (value === '@') {
|
||
|
if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
|
||
|
push({ type: 'at', extglob: true, value, output: '' });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Plain text
|
||
|
*/
|
||
|
|
||
|
if (value !== '*') {
|
||
|
if (value === '$' || value === '^') {
|
||
|
value = `\\${value}`;
|
||
|
}
|
||
|
|
||
|
const match = REGEX_NON_SPECIAL_CHARS.exec(remaining());
|
||
|
if (match) {
|
||
|
value += match[0];
|
||
|
state.index += match[0].length;
|
||
|
}
|
||
|
|
||
|
push({ type: 'text', value });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Stars
|
||
|
*/
|
||
|
|
||
|
if (prev && (prev.type === 'globstar' || prev.star === true)) {
|
||
|
prev.type = 'star';
|
||
|
prev.star = true;
|
||
|
prev.value += value;
|
||
|
prev.output = star;
|
||
|
state.backtrack = true;
|
||
|
state.globstar = true;
|
||
|
consume(value);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
let rest = remaining();
|
||
|
if (opts.noextglob !== true && /^\([^?]/.test(rest)) {
|
||
|
extglobOpen('star', value);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (prev.type === 'star') {
|
||
|
if (opts.noglobstar === true) {
|
||
|
consume(value);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const prior = prev.prev;
|
||
|
const before = prior.prev;
|
||
|
const isStart = prior.type === 'slash' || prior.type === 'bos';
|
||
|
const afterStar = before && (before.type === 'star' || before.type === 'globstar');
|
||
|
|
||
|
if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) {
|
||
|
push({ type: 'star', value, output: '' });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace');
|
||
|
const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren');
|
||
|
if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) {
|
||
|
push({ type: 'star', value, output: '' });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// strip consecutive `/**/`
|
||
|
while (rest.slice(0, 3) === '/**') {
|
||
|
const after = input[state.index + 4];
|
||
|
if (after && after !== '/') {
|
||
|
break;
|
||
|
}
|
||
|
rest = rest.slice(3);
|
||
|
consume('/**', 3);
|
||
|
}
|
||
|
|
||
|
if (prior.type === 'bos' && eos()) {
|
||
|
prev.type = 'globstar';
|
||
|
prev.value += value;
|
||
|
prev.output = globstar(opts);
|
||
|
state.output = prev.output;
|
||
|
state.globstar = true;
|
||
|
consume(value);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) {
|
||
|
state.output = state.output.slice(0, -(prior.output + prev.output).length);
|
||
|
prior.output = `(?:${prior.output}`;
|
||
|
|
||
|
prev.type = 'globstar';
|
||
|
prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)');
|
||
|
prev.value += value;
|
||
|
state.globstar = true;
|
||
|
state.output += prior.output + prev.output;
|
||
|
consume(value);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') {
|
||
|
const end = rest[1] !== void 0 ? '|$' : '';
|
||
|
|
||
|
state.output = state.output.slice(0, -(prior.output + prev.output).length);
|
||
|
prior.output = `(?:${prior.output}`;
|
||
|
|
||
|
prev.type = 'globstar';
|
||
|
prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`;
|
||
|
prev.value += value;
|
||
|
|
||
|
state.output += prior.output + prev.output;
|
||
|
state.globstar = true;
|
||
|
|
||
|
consume(value + advance());
|
||
|
|
||
|
push({ type: 'slash', value: '/', output: '' });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (prior.type === 'bos' && rest[0] === '/') {
|
||
|
prev.type = 'globstar';
|
||
|
prev.value += value;
|
||
|
prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`;
|
||
|
state.output = prev.output;
|
||
|
state.globstar = true;
|
||
|
consume(value + advance());
|
||
|
push({ type: 'slash', value: '/', output: '' });
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// remove single star from output
|
||
|
state.output = state.output.slice(0, -prev.output.length);
|
||
|
|
||
|
// reset previous token to globstar
|
||
|
prev.type = 'globstar';
|
||
|
prev.output = globstar(opts);
|
||
|
prev.value += value;
|
||
|
|
||
|
// reset output with globstar
|
||
|
state.output += prev.output;
|
||
|
state.globstar = true;
|
||
|
consume(value);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const token = { type: 'star', value, output: star };
|
||
|
|
||
|
if (opts.bash === true) {
|
||
|
token.output = '.*?';
|
||
|
if (prev.type === 'bos' || prev.type === 'slash') {
|
||
|
token.output = nodot + token.output;
|
||
|
}
|
||
|
push(token);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) {
|
||
|
token.output = value;
|
||
|
push(token);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') {
|
||
|
if (prev.type === 'dot') {
|
||
|
state.output += NO_DOT_SLASH;
|
||
|
prev.output += NO_DOT_SLASH;
|
||
|
|
||
|
} else if (opts.dot === true) {
|
||
|
state.output += NO_DOTS_SLASH;
|
||
|
prev.output += NO_DOTS_SLASH;
|
||
|
|
||
|
} else {
|
||
|
state.output += nodot;
|
||
|
prev.output += nodot;
|
||
|
}
|
||
|
|
||
|
if (peek() !== '*') {
|
||
|
state.output += ONE_CHAR;
|
||
|
prev.output += ONE_CHAR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
push(token);
|
||
|
}
|
||
|
|
||
|
while (state.brackets > 0) {
|
||
|
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']'));
|
||
|
state.output = utils$d.escapeLast(state.output, '[');
|
||
|
decrement('brackets');
|
||
|
}
|
||
|
|
||
|
while (state.parens > 0) {
|
||
|
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')'));
|
||
|
state.output = utils$d.escapeLast(state.output, '(');
|
||
|
decrement('parens');
|
||
|
}
|
||
|
|
||
|
while (state.braces > 0) {
|
||
|
if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}'));
|
||
|
state.output = utils$d.escapeLast(state.output, '{');
|
||
|
decrement('braces');
|
||
|
}
|
||
|
|
||
|
if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) {
|
||
|
push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` });
|
||
|
}
|
||
|
|
||
|
// rebuild the output if we had to backtrack at any point
|
||
|
if (state.backtrack === true) {
|
||
|
state.output = '';
|
||
|
|
||
|
for (const token of state.tokens) {
|
||
|
state.output += token.output != null ? token.output : token.value;
|
||
|
|
||
|
if (token.suffix) {
|
||
|
state.output += token.suffix;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return state;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Fast paths for creating regular expressions for common glob patterns.
|
||
|
* This can significantly speed up processing and has very little downside
|
||
|
* impact when none of the fast paths match.
|
||
|
*/
|
||
|
|
||
|
parse$1.fastpaths = (input, options) => {
|
||
|
const opts = { ...options };
|
||
|
const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
|
||
|
const len = input.length;
|
||
|
if (len > max) {
|
||
|
throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
|
||
|
}
|
||
|
|
||
|
input = REPLACEMENTS[input] || input;
|
||
|
const win32 = utils$d.isWindows(options);
|
||
|
|
||
|
// create constants based on platform, for windows or posix
|
||
|
const {
|
||
|
DOT_LITERAL,
|
||
|
SLASH_LITERAL,
|
||
|
ONE_CHAR,
|
||
|
DOTS_SLASH,
|
||
|
NO_DOT,
|
||
|
NO_DOTS,
|
||
|
NO_DOTS_SLASH,
|
||
|
STAR,
|
||
|
START_ANCHOR
|
||
|
} = constants$2.globChars(win32);
|
||
|
|
||
|
const nodot = opts.dot ? NO_DOTS : NO_DOT;
|
||
|
const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
|
||
|
const capture = opts.capture ? '' : '?:';
|
||
|
const state = { negated: false, prefix: '' };
|
||
|
let star = opts.bash === true ? '.*?' : STAR;
|
||
|
|
||
|
if (opts.capture) {
|
||
|
star = `(${star})`;
|
||
|
}
|
||
|
|
||
|
const globstar = opts => {
|
||
|
if (opts.noglobstar === true) return star;
|
||
|
return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
|
||
|
};
|
||
|
|
||
|
const create = str => {
|
||
|
switch (str) {
|
||
|
case '*':
|
||
|
return `${nodot}${ONE_CHAR}${star}`;
|
||
|
|
||
|
case '.*':
|
||
|
return `${DOT_LITERAL}${ONE_CHAR}${star}`;
|
||
|
|
||
|
case '*.*':
|
||
|
return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
|
||
|
|
||
|
case '*/*':
|
||
|
return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`;
|
||
|
|
||
|
case '**':
|
||
|
return nodot + globstar(opts);
|
||
|
|
||
|
case '**/*':
|
||
|
return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`;
|
||
|
|
||
|
case '**/*.*':
|
||
|
return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
|
||
|
|
||
|
case '**/.*':
|
||
|
return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
|
||
|
|
||
|
default: {
|
||
|
const match = /^(.*?)\.(\w+)$/.exec(str);
|
||
|
if (!match) return;
|
||
|
|
||
|
const source = create(match[1]);
|
||
|
if (!source) return;
|
||
|
|
||
|
return source + DOT_LITERAL + match[2];
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const output = utils$d.removePrefix(input, state);
|
||
|
let source = create(output);
|
||
|
|
||
|
if (source && opts.strictSlashes !== true) {
|
||
|
source += `${SLASH_LITERAL}?`;
|
||
|
}
|
||
|
|
||
|
return source;
|
||
|
};
|
||
|
|
||
|
var parse_1 = parse$1;
|
||
|
|
||
|
const path$6 = g;
|
||
|
const scan = scan_1;
|
||
|
const parse = parse_1;
|
||
|
const utils$c = utils$f;
|
||
|
const constants$1 = constants$3;
|
||
|
const isObject = val => val && typeof val === 'object' && !Array.isArray(val);
|
||
|
|
||
|
/**
|
||
|
* Creates a matcher function from one or more glob patterns. The
|
||
|
* returned function takes a string to match as its first argument,
|
||
|
* and returns true if the string is a match. The returned matcher
|
||
|
* function also takes a boolean as the second argument that, when true,
|
||
|
* returns an object with additional information.
|
||
|
*
|
||
|
* ```js
|
||
|
* const picomatch = require('picomatch');
|
||
|
* // picomatch(glob[, options]);
|
||
|
*
|
||
|
* const isMatch = picomatch('*.!(*a)');
|
||
|
* console.log(isMatch('a.a')); //=> false
|
||
|
* console.log(isMatch('a.b')); //=> true
|
||
|
* ```
|
||
|
* @name picomatch
|
||
|
* @param {String|Array} `globs` One or more glob patterns.
|
||
|
* @param {Object=} `options`
|
||
|
* @return {Function=} Returns a matcher function.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
const picomatch$1 = (glob, options, returnState = false) => {
|
||
|
if (Array.isArray(glob)) {
|
||
|
const fns = glob.map(input => picomatch$1(input, options, returnState));
|
||
|
const arrayMatcher = str => {
|
||
|
for (const isMatch of fns) {
|
||
|
const state = isMatch(str);
|
||
|
if (state) return state;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
return arrayMatcher;
|
||
|
}
|
||
|
|
||
|
const isState = isObject(glob) && glob.tokens && glob.input;
|
||
|
|
||
|
if (glob === '' || (typeof glob !== 'string' && !isState)) {
|
||
|
throw new TypeError('Expected pattern to be a non-empty string');
|
||
|
}
|
||
|
|
||
|
const opts = options || {};
|
||
|
const posix = utils$c.isWindows(options);
|
||
|
const regex = isState
|
||
|
? picomatch$1.compileRe(glob, options)
|
||
|
: picomatch$1.makeRe(glob, options, false, true);
|
||
|
|
||
|
const state = regex.state;
|
||
|
delete regex.state;
|
||
|
|
||
|
let isIgnored = () => false;
|
||
|
if (opts.ignore) {
|
||
|
const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null };
|
||
|
isIgnored = picomatch$1(opts.ignore, ignoreOpts, returnState);
|
||
|
}
|
||
|
|
||
|
const matcher = (input, returnObject = false) => {
|
||
|
const { isMatch, match, output } = picomatch$1.test(input, regex, options, { glob, posix });
|
||
|
const result = { glob, state, regex, posix, input, output, match, isMatch };
|
||
|
|
||
|
if (typeof opts.onResult === 'function') {
|
||
|
opts.onResult(result);
|
||
|
}
|
||
|
|
||
|
if (isMatch === false) {
|
||
|
result.isMatch = false;
|
||
|
return returnObject ? result : false;
|
||
|
}
|
||
|
|
||
|
if (isIgnored(input)) {
|
||
|
if (typeof opts.onIgnore === 'function') {
|
||
|
opts.onIgnore(result);
|
||
|
}
|
||
|
result.isMatch = false;
|
||
|
return returnObject ? result : false;
|
||
|
}
|
||
|
|
||
|
if (typeof opts.onMatch === 'function') {
|
||
|
opts.onMatch(result);
|
||
|
}
|
||
|
return returnObject ? result : true;
|
||
|
};
|
||
|
|
||
|
if (returnState) {
|
||
|
matcher.state = state;
|
||
|
}
|
||
|
|
||
|
return matcher;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Test `input` with the given `regex`. This is used by the main
|
||
|
* `picomatch()` function to test the input string.
|
||
|
*
|
||
|
* ```js
|
||
|
* const picomatch = require('picomatch');
|
||
|
* // picomatch.test(input, regex[, options]);
|
||
|
*
|
||
|
* console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/));
|
||
|
* // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' }
|
||
|
* ```
|
||
|
* @param {String} `input` String to test.
|
||
|
* @param {RegExp} `regex`
|
||
|
* @return {Object} Returns an object with matching info.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
picomatch$1.test = (input, regex, options, { glob, posix } = {}) => {
|
||
|
if (typeof input !== 'string') {
|
||
|
throw new TypeError('Expected input to be a string');
|
||
|
}
|
||
|
|
||
|
if (input === '') {
|
||
|
return { isMatch: false, output: '' };
|
||
|
}
|
||
|
|
||
|
const opts = options || {};
|
||
|
const format = opts.format || (posix ? utils$c.toPosixSlashes : null);
|
||
|
let match = input === glob;
|
||
|
let output = (match && format) ? format(input) : input;
|
||
|
|
||
|
if (match === false) {
|
||
|
output = format ? format(input) : input;
|
||
|
match = output === glob;
|
||
|
}
|
||
|
|
||
|
if (match === false || opts.capture === true) {
|
||
|
if (opts.matchBase === true || opts.basename === true) {
|
||
|
match = picomatch$1.matchBase(input, regex, options, posix);
|
||
|
} else {
|
||
|
match = regex.exec(output);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return { isMatch: Boolean(match), match, output };
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Match the basename of a filepath.
|
||
|
*
|
||
|
* ```js
|
||
|
* const picomatch = require('picomatch');
|
||
|
* // picomatch.matchBase(input, glob[, options]);
|
||
|
* console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true
|
||
|
* ```
|
||
|
* @param {String} `input` String to test.
|
||
|
* @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe).
|
||
|
* @return {Boolean}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
picomatch$1.matchBase = (input, glob, options, posix = utils$c.isWindows(options)) => {
|
||
|
const regex = glob instanceof RegExp ? glob : picomatch$1.makeRe(glob, options);
|
||
|
return regex.test(path$6.basename(input));
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns true if **any** of the given glob `patterns` match the specified `string`.
|
||
|
*
|
||
|
* ```js
|
||
|
* const picomatch = require('picomatch');
|
||
|
* // picomatch.isMatch(string, patterns[, options]);
|
||
|
*
|
||
|
* console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true
|
||
|
* console.log(picomatch.isMatch('a.a', 'b.*')); //=> false
|
||
|
* ```
|
||
|
* @param {String|Array} str The string to test.
|
||
|
* @param {String|Array} patterns One or more glob patterns to use for matching.
|
||
|
* @param {Object} [options] See available [options](#options).
|
||
|
* @return {Boolean} Returns true if any patterns match `str`
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
picomatch$1.isMatch = (str, patterns, options) => picomatch$1(patterns, options)(str);
|
||
|
|
||
|
/**
|
||
|
* Parse a glob pattern to create the source string for a regular
|
||
|
* expression.
|
||
|
*
|
||
|
* ```js
|
||
|
* const picomatch = require('picomatch');
|
||
|
* const result = picomatch.parse(pattern[, options]);
|
||
|
* ```
|
||
|
* @param {String} `pattern`
|
||
|
* @param {Object} `options`
|
||
|
* @return {Object} Returns an object with useful properties and output to be used as a regex source string.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
picomatch$1.parse = (pattern, options) => {
|
||
|
if (Array.isArray(pattern)) return pattern.map(p => picomatch$1.parse(p, options));
|
||
|
return parse(pattern, { ...options, fastpaths: false });
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Scan a glob pattern to separate the pattern into segments.
|
||
|
*
|
||
|
* ```js
|
||
|
* const picomatch = require('picomatch');
|
||
|
* // picomatch.scan(input[, options]);
|
||
|
*
|
||
|
* const result = picomatch.scan('!./foo/*.js');
|
||
|
* console.log(result);
|
||
|
* { prefix: '!./',
|
||
|
* input: '!./foo/*.js',
|
||
|
* start: 3,
|
||
|
* base: 'foo',
|
||
|
* glob: '*.js',
|
||
|
* isBrace: false,
|
||
|
* isBracket: false,
|
||
|
* isGlob: true,
|
||
|
* isExtglob: false,
|
||
|
* isGlobstar: false,
|
||
|
* negated: true }
|
||
|
* ```
|
||
|
* @param {String} `input` Glob pattern to scan.
|
||
|
* @param {Object} `options`
|
||
|
* @return {Object} Returns an object with
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
picomatch$1.scan = (input, options) => scan(input, options);
|
||
|
|
||
|
/**
|
||
|
* Compile a regular expression from the `state` object returned by the
|
||
|
* [parse()](#parse) method.
|
||
|
*
|
||
|
* @param {Object} `state`
|
||
|
* @param {Object} `options`
|
||
|
* @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser.
|
||
|
* @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging.
|
||
|
* @return {RegExp}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
picomatch$1.compileRe = (state, options, returnOutput = false, returnState = false) => {
|
||
|
if (returnOutput === true) {
|
||
|
return state.output;
|
||
|
}
|
||
|
|
||
|
const opts = options || {};
|
||
|
const prepend = opts.contains ? '' : '^';
|
||
|
const append = opts.contains ? '' : '$';
|
||
|
|
||
|
let source = `${prepend}(?:${state.output})${append}`;
|
||
|
if (state && state.negated === true) {
|
||
|
source = `^(?!${source}).*$`;
|
||
|
}
|
||
|
|
||
|
const regex = picomatch$1.toRegex(source, options);
|
||
|
if (returnState === true) {
|
||
|
regex.state = state;
|
||
|
}
|
||
|
|
||
|
return regex;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Create a regular expression from a parsed glob pattern.
|
||
|
*
|
||
|
* ```js
|
||
|
* const picomatch = require('picomatch');
|
||
|
* const state = picomatch.parse('*.js');
|
||
|
* // picomatch.compileRe(state[, options]);
|
||
|
*
|
||
|
* console.log(picomatch.compileRe(state));
|
||
|
* //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
|
||
|
* ```
|
||
|
* @param {String} `state` The object returned from the `.parse` method.
|
||
|
* @param {Object} `options`
|
||
|
* @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result.
|
||
|
* @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression.
|
||
|
* @return {RegExp} Returns a regex created from the given pattern.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
picomatch$1.makeRe = (input, options = {}, returnOutput = false, returnState = false) => {
|
||
|
if (!input || typeof input !== 'string') {
|
||
|
throw new TypeError('Expected a non-empty string');
|
||
|
}
|
||
|
|
||
|
let parsed = { negated: false, fastpaths: true };
|
||
|
|
||
|
if (options.fastpaths !== false && (input[0] === '.' || input[0] === '*')) {
|
||
|
parsed.output = parse.fastpaths(input, options);
|
||
|
}
|
||
|
|
||
|
if (!parsed.output) {
|
||
|
parsed = parse(input, options);
|
||
|
}
|
||
|
|
||
|
return picomatch$1.compileRe(parsed, options, returnOutput, returnState);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Create a regular expression from the given regex source string.
|
||
|
*
|
||
|
* ```js
|
||
|
* const picomatch = require('picomatch');
|
||
|
* // picomatch.toRegex(source[, options]);
|
||
|
*
|
||
|
* const { output } = picomatch.parse('*.js');
|
||
|
* console.log(picomatch.toRegex(output));
|
||
|
* //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
|
||
|
* ```
|
||
|
* @param {String} `source` Regular expression source string.
|
||
|
* @param {Object} `options`
|
||
|
* @return {RegExp}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
picomatch$1.toRegex = (source, options) => {
|
||
|
try {
|
||
|
const opts = options || {};
|
||
|
return new RegExp(source, opts.flags || (opts.nocase ? 'i' : ''));
|
||
|
} catch (err) {
|
||
|
if (options && options.debug === true) throw err;
|
||
|
return /$^/;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Picomatch constants.
|
||
|
* @return {Object}
|
||
|
*/
|
||
|
|
||
|
picomatch$1.constants = constants$1;
|
||
|
|
||
|
/**
|
||
|
* Expose "picomatch"
|
||
|
*/
|
||
|
|
||
|
var picomatch_1 = picomatch$1;
|
||
|
|
||
|
(function (module) {
|
||
|
|
||
|
module.exports = picomatch_1;
|
||
|
} (picomatch$2));
|
||
|
|
||
|
const util = util$2;
|
||
|
const braces = braces_1;
|
||
|
const picomatch = picomatch$2.exports;
|
||
|
const utils$b = utils$f;
|
||
|
const isEmptyString = val => val === '' || val === './';
|
||
|
|
||
|
/**
|
||
|
* Returns an array of strings that match one or more glob patterns.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm(list, patterns[, options]);
|
||
|
*
|
||
|
* console.log(mm(['a.js', 'a.txt'], ['*.js']));
|
||
|
* //=> [ 'a.js' ]
|
||
|
* ```
|
||
|
* @param {String|Array<string>} `list` List of strings to match.
|
||
|
* @param {String|Array<string>} `patterns` One or more glob patterns to use for matching.
|
||
|
* @param {Object} `options` See available [options](#options)
|
||
|
* @return {Array} Returns an array of matches
|
||
|
* @summary false
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
const micromatch$1 = (list, patterns, options) => {
|
||
|
patterns = [].concat(patterns);
|
||
|
list = [].concat(list);
|
||
|
|
||
|
let omit = new Set();
|
||
|
let keep = new Set();
|
||
|
let items = new Set();
|
||
|
let negatives = 0;
|
||
|
|
||
|
let onResult = state => {
|
||
|
items.add(state.output);
|
||
|
if (options && options.onResult) {
|
||
|
options.onResult(state);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
for (let i = 0; i < patterns.length; i++) {
|
||
|
let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true);
|
||
|
let negated = isMatch.state.negated || isMatch.state.negatedExtglob;
|
||
|
if (negated) negatives++;
|
||
|
|
||
|
for (let item of list) {
|
||
|
let matched = isMatch(item, true);
|
||
|
|
||
|
let match = negated ? !matched.isMatch : matched.isMatch;
|
||
|
if (!match) continue;
|
||
|
|
||
|
if (negated) {
|
||
|
omit.add(matched.output);
|
||
|
} else {
|
||
|
omit.delete(matched.output);
|
||
|
keep.add(matched.output);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let result = negatives === patterns.length ? [...items] : [...keep];
|
||
|
let matches = result.filter(item => !omit.has(item));
|
||
|
|
||
|
if (options && matches.length === 0) {
|
||
|
if (options.failglob === true) {
|
||
|
throw new Error(`No matches found for "${patterns.join(', ')}"`);
|
||
|
}
|
||
|
|
||
|
if (options.nonull === true || options.nullglob === true) {
|
||
|
return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return matches;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Backwards compatibility
|
||
|
*/
|
||
|
|
||
|
micromatch$1.match = micromatch$1;
|
||
|
|
||
|
/**
|
||
|
* Returns a matcher function from the given glob `pattern` and `options`.
|
||
|
* The returned function takes a string to match as its only argument and returns
|
||
|
* true if the string is a match.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm.matcher(pattern[, options]);
|
||
|
*
|
||
|
* const isMatch = mm.matcher('*.!(*a)');
|
||
|
* console.log(isMatch('a.a')); //=> false
|
||
|
* console.log(isMatch('a.b')); //=> true
|
||
|
* ```
|
||
|
* @param {String} `pattern` Glob pattern
|
||
|
* @param {Object} `options`
|
||
|
* @return {Function} Returns a matcher function.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.matcher = (pattern, options) => picomatch(pattern, options);
|
||
|
|
||
|
/**
|
||
|
* Returns true if **any** of the given glob `patterns` match the specified `string`.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm.isMatch(string, patterns[, options]);
|
||
|
*
|
||
|
* console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true
|
||
|
* console.log(mm.isMatch('a.a', 'b.*')); //=> false
|
||
|
* ```
|
||
|
* @param {String} `str` The string to test.
|
||
|
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
|
||
|
* @param {Object} `[options]` See available [options](#options).
|
||
|
* @return {Boolean} Returns true if any patterns match `str`
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
|
||
|
|
||
|
/**
|
||
|
* Backwards compatibility
|
||
|
*/
|
||
|
|
||
|
micromatch$1.any = micromatch$1.isMatch;
|
||
|
|
||
|
/**
|
||
|
* Returns a list of strings that _**do not match any**_ of the given `patterns`.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm.not(list, patterns[, options]);
|
||
|
*
|
||
|
* console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a'));
|
||
|
* //=> ['b.b', 'c.c']
|
||
|
* ```
|
||
|
* @param {Array} `list` Array of strings to match.
|
||
|
* @param {String|Array} `patterns` One or more glob pattern to use for matching.
|
||
|
* @param {Object} `options` See available [options](#options) for changing how matches are performed
|
||
|
* @return {Array} Returns an array of strings that **do not match** the given patterns.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.not = (list, patterns, options = {}) => {
|
||
|
patterns = [].concat(patterns).map(String);
|
||
|
let result = new Set();
|
||
|
let items = [];
|
||
|
|
||
|
let onResult = state => {
|
||
|
if (options.onResult) options.onResult(state);
|
||
|
items.push(state.output);
|
||
|
};
|
||
|
|
||
|
let matches = new Set(micromatch$1(list, patterns, { ...options, onResult }));
|
||
|
|
||
|
for (let item of items) {
|
||
|
if (!matches.has(item)) {
|
||
|
result.add(item);
|
||
|
}
|
||
|
}
|
||
|
return [...result];
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns true if the given `string` contains the given pattern. Similar
|
||
|
* to [.isMatch](#isMatch) but the pattern can match any part of the string.
|
||
|
*
|
||
|
* ```js
|
||
|
* var mm = require('micromatch');
|
||
|
* // mm.contains(string, pattern[, options]);
|
||
|
*
|
||
|
* console.log(mm.contains('aa/bb/cc', '*b'));
|
||
|
* //=> true
|
||
|
* console.log(mm.contains('aa/bb/cc', '*d'));
|
||
|
* //=> false
|
||
|
* ```
|
||
|
* @param {String} `str` The string to match.
|
||
|
* @param {String|Array} `patterns` Glob pattern to use for matching.
|
||
|
* @param {Object} `options` See available [options](#options) for changing how matches are performed
|
||
|
* @return {Boolean} Returns true if any of the patterns matches any part of `str`.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.contains = (str, pattern, options) => {
|
||
|
if (typeof str !== 'string') {
|
||
|
throw new TypeError(`Expected a string: "${util.inspect(str)}"`);
|
||
|
}
|
||
|
|
||
|
if (Array.isArray(pattern)) {
|
||
|
return pattern.some(p => micromatch$1.contains(str, p, options));
|
||
|
}
|
||
|
|
||
|
if (typeof pattern === 'string') {
|
||
|
if (isEmptyString(str) || isEmptyString(pattern)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return micromatch$1.isMatch(str, pattern, { ...options, contains: true });
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Filter the keys of the given object with the given `glob` pattern
|
||
|
* and `options`. Does not attempt to match nested keys. If you need this feature,
|
||
|
* use [glob-object][] instead.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm.matchKeys(object, patterns[, options]);
|
||
|
*
|
||
|
* const obj = { aa: 'a', ab: 'b', ac: 'c' };
|
||
|
* console.log(mm.matchKeys(obj, '*b'));
|
||
|
* //=> { ab: 'b' }
|
||
|
* ```
|
||
|
* @param {Object} `object` The object with keys to filter.
|
||
|
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
|
||
|
* @param {Object} `options` See available [options](#options) for changing how matches are performed
|
||
|
* @return {Object} Returns an object with only keys that match the given patterns.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.matchKeys = (obj, patterns, options) => {
|
||
|
if (!utils$b.isObject(obj)) {
|
||
|
throw new TypeError('Expected the first argument to be an object');
|
||
|
}
|
||
|
let keys = micromatch$1(Object.keys(obj), patterns, options);
|
||
|
let res = {};
|
||
|
for (let key of keys) res[key] = obj[key];
|
||
|
return res;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns true if some of the strings in the given `list` match any of the given glob `patterns`.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm.some(list, patterns[, options]);
|
||
|
*
|
||
|
* console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
|
||
|
* // true
|
||
|
* console.log(mm.some(['foo.js'], ['*.js', '!foo.js']));
|
||
|
* // false
|
||
|
* ```
|
||
|
* @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found.
|
||
|
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
|
||
|
* @param {Object} `options` See available [options](#options) for changing how matches are performed
|
||
|
* @return {Boolean} Returns true if any `patterns` matches any of the strings in `list`
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.some = (list, patterns, options) => {
|
||
|
let items = [].concat(list);
|
||
|
|
||
|
for (let pattern of [].concat(patterns)) {
|
||
|
let isMatch = picomatch(String(pattern), options);
|
||
|
if (items.some(item => isMatch(item))) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns true if every string in the given `list` matches
|
||
|
* any of the given glob `patterns`.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm.every(list, patterns[, options]);
|
||
|
*
|
||
|
* console.log(mm.every('foo.js', ['foo.js']));
|
||
|
* // true
|
||
|
* console.log(mm.every(['foo.js', 'bar.js'], ['*.js']));
|
||
|
* // true
|
||
|
* console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
|
||
|
* // false
|
||
|
* console.log(mm.every(['foo.js'], ['*.js', '!foo.js']));
|
||
|
* // false
|
||
|
* ```
|
||
|
* @param {String|Array} `list` The string or array of strings to test.
|
||
|
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
|
||
|
* @param {Object} `options` See available [options](#options) for changing how matches are performed
|
||
|
* @return {Boolean} Returns true if all `patterns` matches all of the strings in `list`
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.every = (list, patterns, options) => {
|
||
|
let items = [].concat(list);
|
||
|
|
||
|
for (let pattern of [].concat(patterns)) {
|
||
|
let isMatch = picomatch(String(pattern), options);
|
||
|
if (!items.every(item => isMatch(item))) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns true if **all** of the given `patterns` match
|
||
|
* the specified string.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm.all(string, patterns[, options]);
|
||
|
*
|
||
|
* console.log(mm.all('foo.js', ['foo.js']));
|
||
|
* // true
|
||
|
*
|
||
|
* console.log(mm.all('foo.js', ['*.js', '!foo.js']));
|
||
|
* // false
|
||
|
*
|
||
|
* console.log(mm.all('foo.js', ['*.js', 'foo.js']));
|
||
|
* // true
|
||
|
*
|
||
|
* console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js']));
|
||
|
* // true
|
||
|
* ```
|
||
|
* @param {String|Array} `str` The string to test.
|
||
|
* @param {String|Array} `patterns` One or more glob patterns to use for matching.
|
||
|
* @param {Object} `options` See available [options](#options) for changing how matches are performed
|
||
|
* @return {Boolean} Returns true if any patterns match `str`
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.all = (str, patterns, options) => {
|
||
|
if (typeof str !== 'string') {
|
||
|
throw new TypeError(`Expected a string: "${util.inspect(str)}"`);
|
||
|
}
|
||
|
|
||
|
return [].concat(patterns).every(p => picomatch(p, options)(str));
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm.capture(pattern, string[, options]);
|
||
|
*
|
||
|
* console.log(mm.capture('test/*.js', 'test/foo.js'));
|
||
|
* //=> ['foo']
|
||
|
* console.log(mm.capture('test/*.js', 'foo/bar.css'));
|
||
|
* //=> null
|
||
|
* ```
|
||
|
* @param {String} `glob` Glob pattern to use for matching.
|
||
|
* @param {String} `input` String to match
|
||
|
* @param {Object} `options` See available [options](#options) for changing how matches are performed
|
||
|
* @return {Array|null} Returns an array of captures if the input matches the glob pattern, otherwise `null`.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.capture = (glob, input, options) => {
|
||
|
let posix = utils$b.isWindows(options);
|
||
|
let regex = picomatch.makeRe(String(glob), { ...options, capture: true });
|
||
|
let match = regex.exec(posix ? utils$b.toPosixSlashes(input) : input);
|
||
|
|
||
|
if (match) {
|
||
|
return match.slice(1).map(v => v === void 0 ? '' : v);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Create a regular expression from the given glob `pattern`.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* // mm.makeRe(pattern[, options]);
|
||
|
*
|
||
|
* console.log(mm.makeRe('*.js'));
|
||
|
* //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/
|
||
|
* ```
|
||
|
* @param {String} `pattern` A glob pattern to convert to regex.
|
||
|
* @param {Object} `options`
|
||
|
* @return {RegExp} Returns a regex created from the given pattern.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.makeRe = (...args) => picomatch.makeRe(...args);
|
||
|
|
||
|
/**
|
||
|
* Scan a glob pattern to separate the pattern into segments. Used
|
||
|
* by the [split](#split) method.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* const state = mm.scan(pattern[, options]);
|
||
|
* ```
|
||
|
* @param {String} `pattern`
|
||
|
* @param {Object} `options`
|
||
|
* @return {Object} Returns an object with
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.scan = (...args) => picomatch.scan(...args);
|
||
|
|
||
|
/**
|
||
|
* Parse a glob pattern to create the source string for a regular
|
||
|
* expression.
|
||
|
*
|
||
|
* ```js
|
||
|
* const mm = require('micromatch');
|
||
|
* const state = mm.parse(pattern[, options]);
|
||
|
* ```
|
||
|
* @param {String} `glob`
|
||
|
* @param {Object} `options`
|
||
|
* @return {Object} Returns an object with useful properties and output to be used as regex source string.
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.parse = (patterns, options) => {
|
||
|
let res = [];
|
||
|
for (let pattern of [].concat(patterns || [])) {
|
||
|
for (let str of braces(String(pattern), options)) {
|
||
|
res.push(picomatch.parse(str, options));
|
||
|
}
|
||
|
}
|
||
|
return res;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Process the given brace `pattern`.
|
||
|
*
|
||
|
* ```js
|
||
|
* const { braces } = require('micromatch');
|
||
|
* console.log(braces('foo/{a,b,c}/bar'));
|
||
|
* //=> [ 'foo/(a|b|c)/bar' ]
|
||
|
*
|
||
|
* console.log(braces('foo/{a,b,c}/bar', { expand: true }));
|
||
|
* //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ]
|
||
|
* ```
|
||
|
* @param {String} `pattern` String with brace pattern to process.
|
||
|
* @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options.
|
||
|
* @return {Array}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
micromatch$1.braces = (pattern, options) => {
|
||
|
if (typeof pattern !== 'string') throw new TypeError('Expected a string');
|
||
|
if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) {
|
||
|
return [pattern];
|
||
|
}
|
||
|
return braces(pattern, options);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Expand braces
|
||
|
*/
|
||
|
|
||
|
micromatch$1.braceExpand = (pattern, options) => {
|
||
|
if (typeof pattern !== 'string') throw new TypeError('Expected a string');
|
||
|
return micromatch$1.braces(pattern, { ...options, expand: true });
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Expose micromatch
|
||
|
*/
|
||
|
|
||
|
var micromatch_1 = micromatch$1;
|
||
|
|
||
|
Object.defineProperty(pattern$1, "__esModule", { value: true });
|
||
|
pattern$1.matchAny = pattern$1.convertPatternsToRe = pattern$1.makeRe = pattern$1.getPatternParts = pattern$1.expandBraceExpansion = pattern$1.expandPatternsWithBraceExpansion = pattern$1.isAffectDepthOfReadingPattern = pattern$1.endsWithSlashGlobStar = pattern$1.hasGlobStar = pattern$1.getBaseDirectory = pattern$1.isPatternRelatedToParentDirectory = pattern$1.getPatternsOutsideCurrentDirectory = pattern$1.getPatternsInsideCurrentDirectory = pattern$1.getPositivePatterns = pattern$1.getNegativePatterns = pattern$1.isPositivePattern = pattern$1.isNegativePattern = pattern$1.convertToNegativePattern = pattern$1.convertToPositivePattern = pattern$1.isDynamicPattern = pattern$1.isStaticPattern = void 0;
|
||
|
const path$5 = g;
|
||
|
const globParent = globParent$1;
|
||
|
const micromatch = micromatch_1;
|
||
|
const GLOBSTAR = '**';
|
||
|
const ESCAPE_SYMBOL = '\\';
|
||
|
const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/;
|
||
|
const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[[^[]*]/;
|
||
|
const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\([^(]*\|[^|]*\)/;
|
||
|
const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\([^(]*\)/;
|
||
|
const BRACE_EXPANSION_SEPARATORS_RE = /,|\.\./;
|
||
|
function isStaticPattern(pattern, options = {}) {
|
||
|
return !isDynamicPattern(pattern, options);
|
||
|
}
|
||
|
pattern$1.isStaticPattern = isStaticPattern;
|
||
|
function isDynamicPattern(pattern, options = {}) {
|
||
|
/**
|
||
|
* A special case with an empty string is necessary for matching patterns that start with a forward slash.
|
||
|
* An empty string cannot be a dynamic pattern.
|
||
|
* For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'.
|
||
|
*/
|
||
|
if (pattern === '') {
|
||
|
return false;
|
||
|
}
|
||
|
/**
|
||
|
* When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check
|
||
|
* filepath directly (without read directory).
|
||
|
*/
|
||
|
if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) {
|
||
|
return true;
|
||
|
}
|
||
|
if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) {
|
||
|
return true;
|
||
|
}
|
||
|
if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) {
|
||
|
return true;
|
||
|
}
|
||
|
if (options.braceExpansion !== false && hasBraceExpansion(pattern)) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
pattern$1.isDynamicPattern = isDynamicPattern;
|
||
|
function hasBraceExpansion(pattern) {
|
||
|
const openingBraceIndex = pattern.indexOf('{');
|
||
|
if (openingBraceIndex === -1) {
|
||
|
return false;
|
||
|
}
|
||
|
const closingBraceIndex = pattern.indexOf('}', openingBraceIndex + 1);
|
||
|
if (closingBraceIndex === -1) {
|
||
|
return false;
|
||
|
}
|
||
|
const braceContent = pattern.slice(openingBraceIndex, closingBraceIndex);
|
||
|
return BRACE_EXPANSION_SEPARATORS_RE.test(braceContent);
|
||
|
}
|
||
|
function convertToPositivePattern(pattern) {
|
||
|
return isNegativePattern(pattern) ? pattern.slice(1) : pattern;
|
||
|
}
|
||
|
pattern$1.convertToPositivePattern = convertToPositivePattern;
|
||
|
function convertToNegativePattern(pattern) {
|
||
|
return '!' + pattern;
|
||
|
}
|
||
|
pattern$1.convertToNegativePattern = convertToNegativePattern;
|
||
|
function isNegativePattern(pattern) {
|
||
|
return pattern.startsWith('!') && pattern[1] !== '(';
|
||
|
}
|
||
|
pattern$1.isNegativePattern = isNegativePattern;
|
||
|
function isPositivePattern(pattern) {
|
||
|
return !isNegativePattern(pattern);
|
||
|
}
|
||
|
pattern$1.isPositivePattern = isPositivePattern;
|
||
|
function getNegativePatterns(patterns) {
|
||
|
return patterns.filter(isNegativePattern);
|
||
|
}
|
||
|
pattern$1.getNegativePatterns = getNegativePatterns;
|
||
|
function getPositivePatterns$1(patterns) {
|
||
|
return patterns.filter(isPositivePattern);
|
||
|
}
|
||
|
pattern$1.getPositivePatterns = getPositivePatterns$1;
|
||
|
/**
|
||
|
* Returns patterns that can be applied inside the current directory.
|
||
|
*
|
||
|
* @example
|
||
|
* // ['./*', '*', 'a/*']
|
||
|
* getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
|
||
|
*/
|
||
|
function getPatternsInsideCurrentDirectory(patterns) {
|
||
|
return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern));
|
||
|
}
|
||
|
pattern$1.getPatternsInsideCurrentDirectory = getPatternsInsideCurrentDirectory;
|
||
|
/**
|
||
|
* Returns patterns to be expanded relative to (outside) the current directory.
|
||
|
*
|
||
|
* @example
|
||
|
* // ['../*', './../*']
|
||
|
* getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*'])
|
||
|
*/
|
||
|
function getPatternsOutsideCurrentDirectory(patterns) {
|
||
|
return patterns.filter(isPatternRelatedToParentDirectory);
|
||
|
}
|
||
|
pattern$1.getPatternsOutsideCurrentDirectory = getPatternsOutsideCurrentDirectory;
|
||
|
function isPatternRelatedToParentDirectory(pattern) {
|
||
|
return pattern.startsWith('..') || pattern.startsWith('./..');
|
||
|
}
|
||
|
pattern$1.isPatternRelatedToParentDirectory = isPatternRelatedToParentDirectory;
|
||
|
function getBaseDirectory(pattern) {
|
||
|
return globParent(pattern, { flipBackslashes: false });
|
||
|
}
|
||
|
pattern$1.getBaseDirectory = getBaseDirectory;
|
||
|
function hasGlobStar(pattern) {
|
||
|
return pattern.includes(GLOBSTAR);
|
||
|
}
|
||
|
pattern$1.hasGlobStar = hasGlobStar;
|
||
|
function endsWithSlashGlobStar(pattern) {
|
||
|
return pattern.endsWith('/' + GLOBSTAR);
|
||
|
}
|
||
|
pattern$1.endsWithSlashGlobStar = endsWithSlashGlobStar;
|
||
|
function isAffectDepthOfReadingPattern(pattern) {
|
||
|
const basename = path$5.basename(pattern);
|
||
|
return endsWithSlashGlobStar(pattern) || isStaticPattern(basename);
|
||
|
}
|
||
|
pattern$1.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
|
||
|
function expandPatternsWithBraceExpansion(patterns) {
|
||
|
return patterns.reduce((collection, pattern) => {
|
||
|
return collection.concat(expandBraceExpansion(pattern));
|
||
|
}, []);
|
||
|
}
|
||
|
pattern$1.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion;
|
||
|
function expandBraceExpansion(pattern) {
|
||
|
return micromatch.braces(pattern, {
|
||
|
expand: true,
|
||
|
nodupes: true
|
||
|
});
|
||
|
}
|
||
|
pattern$1.expandBraceExpansion = expandBraceExpansion;
|
||
|
function getPatternParts(pattern, options) {
|
||
|
let { parts } = micromatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true }));
|
||
|
/**
|
||
|
* The scan method returns an empty array in some cases.
|
||
|
* See micromatch/picomatch#58 for more details.
|
||
|
*/
|
||
|
if (parts.length === 0) {
|
||
|
parts = [pattern];
|
||
|
}
|
||
|
/**
|
||
|
* The scan method does not return an empty part for the pattern with a forward slash.
|
||
|
* This is another part of micromatch/picomatch#58.
|
||
|
*/
|
||
|
if (parts[0].startsWith('/')) {
|
||
|
parts[0] = parts[0].slice(1);
|
||
|
parts.unshift('');
|
||
|
}
|
||
|
return parts;
|
||
|
}
|
||
|
pattern$1.getPatternParts = getPatternParts;
|
||
|
function makeRe(pattern, options) {
|
||
|
return micromatch.makeRe(pattern, options);
|
||
|
}
|
||
|
pattern$1.makeRe = makeRe;
|
||
|
function convertPatternsToRe(patterns, options) {
|
||
|
return patterns.map((pattern) => makeRe(pattern, options));
|
||
|
}
|
||
|
pattern$1.convertPatternsToRe = convertPatternsToRe;
|
||
|
function matchAny(entry, patternsRe) {
|
||
|
return patternsRe.some((patternRe) => patternRe.test(entry));
|
||
|
}
|
||
|
pattern$1.matchAny = matchAny;
|
||
|
|
||
|
var stream$4 = {};
|
||
|
|
||
|
/*
|
||
|
* merge2
|
||
|
* https://github.com/teambition/merge2
|
||
|
*
|
||
|
* Copyright (c) 2014-2020 Teambition
|
||
|
* Licensed under the MIT license.
|
||
|
*/
|
||
|
const Stream = require$$0$1;
|
||
|
const PassThrough = Stream.PassThrough;
|
||
|
const slice = Array.prototype.slice;
|
||
|
|
||
|
var merge2_1 = merge2$1;
|
||
|
|
||
|
function merge2$1 () {
|
||
|
const streamsQueue = [];
|
||
|
const args = slice.call(arguments);
|
||
|
let merging = false;
|
||
|
let options = args[args.length - 1];
|
||
|
|
||
|
if (options && !Array.isArray(options) && options.pipe == null) {
|
||
|
args.pop();
|
||
|
} else {
|
||
|
options = {};
|
||
|
}
|
||
|
|
||
|
const doEnd = options.end !== false;
|
||
|
const doPipeError = options.pipeError === true;
|
||
|
if (options.objectMode == null) {
|
||
|
options.objectMode = true;
|
||
|
}
|
||
|
if (options.highWaterMark == null) {
|
||
|
options.highWaterMark = 64 * 1024;
|
||
|
}
|
||
|
const mergedStream = PassThrough(options);
|
||
|
|
||
|
function addStream () {
|
||
|
for (let i = 0, len = arguments.length; i < len; i++) {
|
||
|
streamsQueue.push(pauseStreams(arguments[i], options));
|
||
|
}
|
||
|
mergeStream();
|
||
|
return this
|
||
|
}
|
||
|
|
||
|
function mergeStream () {
|
||
|
if (merging) {
|
||
|
return
|
||
|
}
|
||
|
merging = true;
|
||
|
|
||
|
let streams = streamsQueue.shift();
|
||
|
if (!streams) {
|
||
|
process.nextTick(endStream);
|
||
|
return
|
||
|
}
|
||
|
if (!Array.isArray(streams)) {
|
||
|
streams = [streams];
|
||
|
}
|
||
|
|
||
|
let pipesCount = streams.length + 1;
|
||
|
|
||
|
function next () {
|
||
|
if (--pipesCount > 0) {
|
||
|
return
|
||
|
}
|
||
|
merging = false;
|
||
|
mergeStream();
|
||
|
}
|
||
|
|
||
|
function pipe (stream) {
|
||
|
function onend () {
|
||
|
stream.removeListener('merge2UnpipeEnd', onend);
|
||
|
stream.removeListener('end', onend);
|
||
|
if (doPipeError) {
|
||
|
stream.removeListener('error', onerror);
|
||
|
}
|
||
|
next();
|
||
|
}
|
||
|
function onerror (err) {
|
||
|
mergedStream.emit('error', err);
|
||
|
}
|
||
|
// skip ended stream
|
||
|
if (stream._readableState.endEmitted) {
|
||
|
return next()
|
||
|
}
|
||
|
|
||
|
stream.on('merge2UnpipeEnd', onend);
|
||
|
stream.on('end', onend);
|
||
|
|
||
|
if (doPipeError) {
|
||
|
stream.on('error', onerror);
|
||
|
}
|
||
|
|
||
|
stream.pipe(mergedStream, { end: false });
|
||
|
// compatible for old stream
|
||
|
stream.resume();
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < streams.length; i++) {
|
||
|
pipe(streams[i]);
|
||
|
}
|
||
|
|
||
|
next();
|
||
|
}
|
||
|
|
||
|
function endStream () {
|
||
|
merging = false;
|
||
|
// emit 'queueDrain' when all streams merged.
|
||
|
mergedStream.emit('queueDrain');
|
||
|
if (doEnd) {
|
||
|
mergedStream.end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mergedStream.setMaxListeners(0);
|
||
|
mergedStream.add = addStream;
|
||
|
mergedStream.on('unpipe', function (stream) {
|
||
|
stream.emit('merge2UnpipeEnd');
|
||
|
});
|
||
|
|
||
|
if (args.length) {
|
||
|
addStream.apply(null, args);
|
||
|
}
|
||
|
return mergedStream
|
||
|
}
|
||
|
|
||
|
// check and pause streams for pipe.
|
||
|
function pauseStreams (streams, options) {
|
||
|
if (!Array.isArray(streams)) {
|
||
|
// Backwards-compat with old-style streams
|
||
|
if (!streams._readableState && streams.pipe) {
|
||
|
streams = streams.pipe(PassThrough(options));
|
||
|
}
|
||
|
if (!streams._readableState || !streams.pause || !streams.pipe) {
|
||
|
throw new Error('Only readable stream can be merged.')
|
||
|
}
|
||
|
streams.pause();
|
||
|
} else {
|
||
|
for (let i = 0, len = streams.length; i < len; i++) {
|
||
|
streams[i] = pauseStreams(streams[i], options);
|
||
|
}
|
||
|
}
|
||
|
return streams
|
||
|
}
|
||
|
|
||
|
Object.defineProperty(stream$4, "__esModule", { value: true });
|
||
|
stream$4.merge = void 0;
|
||
|
const merge2 = merge2_1;
|
||
|
function merge(streams) {
|
||
|
const mergedStream = merge2(streams);
|
||
|
streams.forEach((stream) => {
|
||
|
stream.once('error', (error) => mergedStream.emit('error', error));
|
||
|
});
|
||
|
mergedStream.once('close', () => propagateCloseEventToSources(streams));
|
||
|
mergedStream.once('end', () => propagateCloseEventToSources(streams));
|
||
|
return mergedStream;
|
||
|
}
|
||
|
stream$4.merge = merge;
|
||
|
function propagateCloseEventToSources(streams) {
|
||
|
streams.forEach((stream) => stream.emit('close'));
|
||
|
}
|
||
|
|
||
|
var string$1 = {};
|
||
|
|
||
|
Object.defineProperty(string$1, "__esModule", { value: true });
|
||
|
string$1.isEmpty = string$1.isString = void 0;
|
||
|
function isString(input) {
|
||
|
return typeof input === 'string';
|
||
|
}
|
||
|
string$1.isString = isString;
|
||
|
function isEmpty(input) {
|
||
|
return input === '';
|
||
|
}
|
||
|
string$1.isEmpty = isEmpty;
|
||
|
|
||
|
Object.defineProperty(utils$k, "__esModule", { value: true });
|
||
|
utils$k.string = utils$k.stream = utils$k.pattern = utils$k.path = utils$k.fs = utils$k.errno = utils$k.array = void 0;
|
||
|
const array = array$1;
|
||
|
utils$k.array = array;
|
||
|
const errno = errno$1;
|
||
|
utils$k.errno = errno;
|
||
|
const fs$6 = fs$7;
|
||
|
utils$k.fs = fs$6;
|
||
|
const path$4 = path$9;
|
||
|
utils$k.path = path$4;
|
||
|
const pattern = pattern$1;
|
||
|
utils$k.pattern = pattern;
|
||
|
const stream$3 = stream$4;
|
||
|
utils$k.stream = stream$3;
|
||
|
const string = string$1;
|
||
|
utils$k.string = string;
|
||
|
|
||
|
Object.defineProperty(tasks, "__esModule", { value: true });
|
||
|
tasks.convertPatternGroupToTask = tasks.convertPatternGroupsToTasks = tasks.groupPatternsByBaseDirectory = tasks.getNegativePatternsAsPositive = tasks.getPositivePatterns = tasks.convertPatternsToTasks = tasks.generate = void 0;
|
||
|
const utils$a = utils$k;
|
||
|
function generate(patterns, settings) {
|
||
|
const positivePatterns = getPositivePatterns(patterns);
|
||
|
const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore);
|
||
|
const staticPatterns = positivePatterns.filter((pattern) => utils$a.pattern.isStaticPattern(pattern, settings));
|
||
|
const dynamicPatterns = positivePatterns.filter((pattern) => utils$a.pattern.isDynamicPattern(pattern, settings));
|
||
|
const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false);
|
||
|
const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true);
|
||
|
return staticTasks.concat(dynamicTasks);
|
||
|
}
|
||
|
tasks.generate = generate;
|
||
|
/**
|
||
|
* Returns tasks grouped by basic pattern directories.
|
||
|
*
|
||
|
* Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately.
|
||
|
* This is necessary because directory traversal starts at the base directory and goes deeper.
|
||
|
*/
|
||
|
function convertPatternsToTasks(positive, negative, dynamic) {
|
||
|
const tasks = [];
|
||
|
const patternsOutsideCurrentDirectory = utils$a.pattern.getPatternsOutsideCurrentDirectory(positive);
|
||
|
const patternsInsideCurrentDirectory = utils$a.pattern.getPatternsInsideCurrentDirectory(positive);
|
||
|
const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory);
|
||
|
const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory);
|
||
|
tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic));
|
||
|
/*
|
||
|
* For the sake of reducing future accesses to the file system, we merge all tasks within the current directory
|
||
|
* into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest.
|
||
|
*/
|
||
|
if ('.' in insideCurrentDirectoryGroup) {
|
||
|
tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic));
|
||
|
}
|
||
|
else {
|
||
|
tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic));
|
||
|
}
|
||
|
return tasks;
|
||
|
}
|
||
|
tasks.convertPatternsToTasks = convertPatternsToTasks;
|
||
|
function getPositivePatterns(patterns) {
|
||
|
return utils$a.pattern.getPositivePatterns(patterns);
|
||
|
}
|
||
|
tasks.getPositivePatterns = getPositivePatterns;
|
||
|
function getNegativePatternsAsPositive(patterns, ignore) {
|
||
|
const negative = utils$a.pattern.getNegativePatterns(patterns).concat(ignore);
|
||
|
const positive = negative.map(utils$a.pattern.convertToPositivePattern);
|
||
|
return positive;
|
||
|
}
|
||
|
tasks.getNegativePatternsAsPositive = getNegativePatternsAsPositive;
|
||
|
function groupPatternsByBaseDirectory(patterns) {
|
||
|
const group = {};
|
||
|
return patterns.reduce((collection, pattern) => {
|
||
|
const base = utils$a.pattern.getBaseDirectory(pattern);
|
||
|
if (base in collection) {
|
||
|
collection[base].push(pattern);
|
||
|
}
|
||
|
else {
|
||
|
collection[base] = [pattern];
|
||
|
}
|
||
|
return collection;
|
||
|
}, group);
|
||
|
}
|
||
|
tasks.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory;
|
||
|
function convertPatternGroupsToTasks(positive, negative, dynamic) {
|
||
|
return Object.keys(positive).map((base) => {
|
||
|
return convertPatternGroupToTask(base, positive[base], negative, dynamic);
|
||
|
});
|
||
|
}
|
||
|
tasks.convertPatternGroupsToTasks = convertPatternGroupsToTasks;
|
||
|
function convertPatternGroupToTask(base, positive, negative, dynamic) {
|
||
|
return {
|
||
|
dynamic,
|
||
|
positive,
|
||
|
negative,
|
||
|
base,
|
||
|
patterns: [].concat(positive, negative.map(utils$a.pattern.convertToNegativePattern))
|
||
|
};
|
||
|
}
|
||
|
tasks.convertPatternGroupToTask = convertPatternGroupToTask;
|
||
|
|
||
|
var patterns = {};
|
||
|
|
||
|
Object.defineProperty(patterns, "__esModule", { value: true });
|
||
|
patterns.removeDuplicateSlashes = patterns.transform = void 0;
|
||
|
/**
|
||
|
* Matches a sequence of two or more consecutive slashes, excluding the first two slashes at the beginning of the string.
|
||
|
* The latter is due to the presence of the device path at the beginning of the UNC path.
|
||
|
* @todo rewrite to negative lookbehind with the next major release.
|
||
|
*/
|
||
|
const DOUBLE_SLASH_RE = /(?!^)\/{2,}/g;
|
||
|
function transform(patterns) {
|
||
|
return patterns.map((pattern) => removeDuplicateSlashes(pattern));
|
||
|
}
|
||
|
patterns.transform = transform;
|
||
|
/**
|
||
|
* This package only works with forward slashes as a path separator.
|
||
|
* Because of this, we cannot use the standard `path.normalize` method, because on Windows platform it will use of backslashes.
|
||
|
*/
|
||
|
function removeDuplicateSlashes(pattern) {
|
||
|
return pattern.replace(DOUBLE_SLASH_RE, '/');
|
||
|
}
|
||
|
patterns.removeDuplicateSlashes = removeDuplicateSlashes;
|
||
|
|
||
|
var async$7 = {};
|
||
|
|
||
|
var async$6 = {};
|
||
|
|
||
|
var out$3 = {};
|
||
|
|
||
|
var async$5 = {};
|
||
|
|
||
|
var async$4 = {};
|
||
|
|
||
|
var out$2 = {};
|
||
|
|
||
|
var async$3 = {};
|
||
|
|
||
|
var out$1 = {};
|
||
|
|
||
|
var async$2 = {};
|
||
|
|
||
|
Object.defineProperty(async$2, "__esModule", { value: true });
|
||
|
async$2.read = void 0;
|
||
|
function read$3(path, settings, callback) {
|
||
|
settings.fs.lstat(path, (lstatError, lstat) => {
|
||
|
if (lstatError !== null) {
|
||
|
callFailureCallback$2(callback, lstatError);
|
||
|
return;
|
||
|
}
|
||
|
if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) {
|
||
|
callSuccessCallback$2(callback, lstat);
|
||
|
return;
|
||
|
}
|
||
|
settings.fs.stat(path, (statError, stat) => {
|
||
|
if (statError !== null) {
|
||
|
if (settings.throwErrorOnBrokenSymbolicLink) {
|
||
|
callFailureCallback$2(callback, statError);
|
||
|
return;
|
||
|
}
|
||
|
callSuccessCallback$2(callback, lstat);
|
||
|
return;
|
||
|
}
|
||
|
if (settings.markSymbolicLink) {
|
||
|
stat.isSymbolicLink = () => true;
|
||
|
}
|
||
|
callSuccessCallback$2(callback, stat);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
async$2.read = read$3;
|
||
|
function callFailureCallback$2(callback, error) {
|
||
|
callback(error);
|
||
|
}
|
||
|
function callSuccessCallback$2(callback, result) {
|
||
|
callback(null, result);
|
||
|
}
|
||
|
|
||
|
var sync$7 = {};
|
||
|
|
||
|
Object.defineProperty(sync$7, "__esModule", { value: true });
|
||
|
sync$7.read = void 0;
|
||
|
function read$2(path, settings) {
|
||
|
const lstat = settings.fs.lstatSync(path);
|
||
|
if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) {
|
||
|
return lstat;
|
||
|
}
|
||
|
try {
|
||
|
const stat = settings.fs.statSync(path);
|
||
|
if (settings.markSymbolicLink) {
|
||
|
stat.isSymbolicLink = () => true;
|
||
|
}
|
||
|
return stat;
|
||
|
}
|
||
|
catch (error) {
|
||
|
if (!settings.throwErrorOnBrokenSymbolicLink) {
|
||
|
return lstat;
|
||
|
}
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
sync$7.read = read$2;
|
||
|
|
||
|
var settings$3 = {};
|
||
|
|
||
|
var fs$5 = {};
|
||
|
|
||
|
(function (exports) {
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.createFileSystemAdapter = exports.FILE_SYSTEM_ADAPTER = void 0;
|
||
|
const fs = require$$0$2;
|
||
|
exports.FILE_SYSTEM_ADAPTER = {
|
||
|
lstat: fs.lstat,
|
||
|
stat: fs.stat,
|
||
|
lstatSync: fs.lstatSync,
|
||
|
statSync: fs.statSync
|
||
|
};
|
||
|
function createFileSystemAdapter(fsMethods) {
|
||
|
if (fsMethods === undefined) {
|
||
|
return exports.FILE_SYSTEM_ADAPTER;
|
||
|
}
|
||
|
return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods);
|
||
|
}
|
||
|
exports.createFileSystemAdapter = createFileSystemAdapter;
|
||
|
} (fs$5));
|
||
|
|
||
|
Object.defineProperty(settings$3, "__esModule", { value: true });
|
||
|
const fs$4 = fs$5;
|
||
|
class Settings$2 {
|
||
|
constructor(_options = {}) {
|
||
|
this._options = _options;
|
||
|
this.followSymbolicLink = this._getValue(this._options.followSymbolicLink, true);
|
||
|
this.fs = fs$4.createFileSystemAdapter(this._options.fs);
|
||
|
this.markSymbolicLink = this._getValue(this._options.markSymbolicLink, false);
|
||
|
this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
|
||
|
}
|
||
|
_getValue(option, value) {
|
||
|
return option !== null && option !== void 0 ? option : value;
|
||
|
}
|
||
|
}
|
||
|
settings$3.default = Settings$2;
|
||
|
|
||
|
Object.defineProperty(out$1, "__esModule", { value: true });
|
||
|
out$1.statSync = out$1.stat = out$1.Settings = void 0;
|
||
|
const async$1 = async$2;
|
||
|
const sync$6 = sync$7;
|
||
|
const settings_1$3 = settings$3;
|
||
|
out$1.Settings = settings_1$3.default;
|
||
|
function stat(path, optionsOrSettingsOrCallback, callback) {
|
||
|
if (typeof optionsOrSettingsOrCallback === 'function') {
|
||
|
async$1.read(path, getSettings$2(), optionsOrSettingsOrCallback);
|
||
|
return;
|
||
|
}
|
||
|
async$1.read(path, getSettings$2(optionsOrSettingsOrCallback), callback);
|
||
|
}
|
||
|
out$1.stat = stat;
|
||
|
function statSync(path, optionsOrSettings) {
|
||
|
const settings = getSettings$2(optionsOrSettings);
|
||
|
return sync$6.read(path, settings);
|
||
|
}
|
||
|
out$1.statSync = statSync;
|
||
|
function getSettings$2(settingsOrOptions = {}) {
|
||
|
if (settingsOrOptions instanceof settings_1$3.default) {
|
||
|
return settingsOrOptions;
|
||
|
}
|
||
|
return new settings_1$3.default(settingsOrOptions);
|
||
|
}
|
||
|
|
||
|
/*! queue-microtask. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||
|
|
||
|
let promise;
|
||
|
|
||
|
var queueMicrotask_1 = typeof queueMicrotask === 'function'
|
||
|
? queueMicrotask.bind(typeof window !== 'undefined' ? window : commonjsGlobal)
|
||
|
// reuse resolved promise, and allocate it lazily
|
||
|
: cb => (promise || (promise = Promise.resolve()))
|
||
|
.then(cb)
|
||
|
.catch(err => setTimeout(() => { throw err }, 0));
|
||
|
|
||
|
/*! run-parallel. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
||
|
|
||
|
var runParallel_1 = runParallel;
|
||
|
|
||
|
const queueMicrotask$1 = queueMicrotask_1;
|
||
|
|
||
|
function runParallel (tasks, cb) {
|
||
|
let results, pending, keys;
|
||
|
let isSync = true;
|
||
|
|
||
|
if (Array.isArray(tasks)) {
|
||
|
results = [];
|
||
|
pending = tasks.length;
|
||
|
} else {
|
||
|
keys = Object.keys(tasks);
|
||
|
results = {};
|
||
|
pending = keys.length;
|
||
|
}
|
||
|
|
||
|
function done (err) {
|
||
|
function end () {
|
||
|
if (cb) cb(err, results);
|
||
|
cb = null;
|
||
|
}
|
||
|
if (isSync) queueMicrotask$1(end);
|
||
|
else end();
|
||
|
}
|
||
|
|
||
|
function each (i, err, result) {
|
||
|
results[i] = result;
|
||
|
if (--pending === 0 || err) {
|
||
|
done(err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!pending) {
|
||
|
// empty
|
||
|
done(null);
|
||
|
} else if (keys) {
|
||
|
// object
|
||
|
keys.forEach(function (key) {
|
||
|
tasks[key](function (err, result) { each(key, err, result); });
|
||
|
});
|
||
|
} else {
|
||
|
// array
|
||
|
tasks.forEach(function (task, i) {
|
||
|
task(function (err, result) { each(i, err, result); });
|
||
|
});
|
||
|
}
|
||
|
|
||
|
isSync = false;
|
||
|
}
|
||
|
|
||
|
var constants = {};
|
||
|
|
||
|
Object.defineProperty(constants, "__esModule", { value: true });
|
||
|
constants.IS_SUPPORT_READDIR_WITH_FILE_TYPES = void 0;
|
||
|
const NODE_PROCESS_VERSION_PARTS = process.versions.node.split('.');
|
||
|
if (NODE_PROCESS_VERSION_PARTS[0] === undefined || NODE_PROCESS_VERSION_PARTS[1] === undefined) {
|
||
|
throw new Error(`Unexpected behavior. The 'process.versions.node' variable has invalid value: ${process.versions.node}`);
|
||
|
}
|
||
|
const MAJOR_VERSION = Number.parseInt(NODE_PROCESS_VERSION_PARTS[0], 10);
|
||
|
const MINOR_VERSION = Number.parseInt(NODE_PROCESS_VERSION_PARTS[1], 10);
|
||
|
const SUPPORTED_MAJOR_VERSION = 10;
|
||
|
const SUPPORTED_MINOR_VERSION = 10;
|
||
|
const IS_MATCHED_BY_MAJOR = MAJOR_VERSION > SUPPORTED_MAJOR_VERSION;
|
||
|
const IS_MATCHED_BY_MAJOR_AND_MINOR = MAJOR_VERSION === SUPPORTED_MAJOR_VERSION && MINOR_VERSION >= SUPPORTED_MINOR_VERSION;
|
||
|
/**
|
||
|
* IS `true` for Node.js 10.10 and greater.
|
||
|
*/
|
||
|
constants.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_BY_MAJOR_AND_MINOR;
|
||
|
|
||
|
var utils$9 = {};
|
||
|
|
||
|
var fs$3 = {};
|
||
|
|
||
|
Object.defineProperty(fs$3, "__esModule", { value: true });
|
||
|
fs$3.createDirentFromStats = void 0;
|
||
|
class DirentFromStats {
|
||
|
constructor(name, stats) {
|
||
|
this.name = name;
|
||
|
this.isBlockDevice = stats.isBlockDevice.bind(stats);
|
||
|
this.isCharacterDevice = stats.isCharacterDevice.bind(stats);
|
||
|
this.isDirectory = stats.isDirectory.bind(stats);
|
||
|
this.isFIFO = stats.isFIFO.bind(stats);
|
||
|
this.isFile = stats.isFile.bind(stats);
|
||
|
this.isSocket = stats.isSocket.bind(stats);
|
||
|
this.isSymbolicLink = stats.isSymbolicLink.bind(stats);
|
||
|
}
|
||
|
}
|
||
|
function createDirentFromStats(name, stats) {
|
||
|
return new DirentFromStats(name, stats);
|
||
|
}
|
||
|
fs$3.createDirentFromStats = createDirentFromStats;
|
||
|
|
||
|
Object.defineProperty(utils$9, "__esModule", { value: true });
|
||
|
utils$9.fs = void 0;
|
||
|
const fs$2 = fs$3;
|
||
|
utils$9.fs = fs$2;
|
||
|
|
||
|
var common$6 = {};
|
||
|
|
||
|
Object.defineProperty(common$6, "__esModule", { value: true });
|
||
|
common$6.joinPathSegments = void 0;
|
||
|
function joinPathSegments$1(a, b, separator) {
|
||
|
/**
|
||
|
* The correct handling of cases when the first segment is a root (`/`, `C:/`) or UNC path (`//?/C:/`).
|
||
|
*/
|
||
|
if (a.endsWith(separator)) {
|
||
|
return a + b;
|
||
|
}
|
||
|
return a + separator + b;
|
||
|
}
|
||
|
common$6.joinPathSegments = joinPathSegments$1;
|
||
|
|
||
|
Object.defineProperty(async$3, "__esModule", { value: true });
|
||
|
async$3.readdir = async$3.readdirWithFileTypes = async$3.read = void 0;
|
||
|
const fsStat$5 = out$1;
|
||
|
const rpl = runParallel_1;
|
||
|
const constants_1$1 = constants;
|
||
|
const utils$8 = utils$9;
|
||
|
const common$5 = common$6;
|
||
|
function read$1(directory, settings, callback) {
|
||
|
if (!settings.stats && constants_1$1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) {
|
||
|
readdirWithFileTypes$1(directory, settings, callback);
|
||
|
return;
|
||
|
}
|
||
|
readdir$1(directory, settings, callback);
|
||
|
}
|
||
|
async$3.read = read$1;
|
||
|
function readdirWithFileTypes$1(directory, settings, callback) {
|
||
|
settings.fs.readdir(directory, { withFileTypes: true }, (readdirError, dirents) => {
|
||
|
if (readdirError !== null) {
|
||
|
callFailureCallback$1(callback, readdirError);
|
||
|
return;
|
||
|
}
|
||
|
const entries = dirents.map((dirent) => ({
|
||
|
dirent,
|
||
|
name: dirent.name,
|
||
|
path: common$5.joinPathSegments(directory, dirent.name, settings.pathSegmentSeparator)
|
||
|
}));
|
||
|
if (!settings.followSymbolicLinks) {
|
||
|
callSuccessCallback$1(callback, entries);
|
||
|
return;
|
||
|
}
|
||
|
const tasks = entries.map((entry) => makeRplTaskEntry(entry, settings));
|
||
|
rpl(tasks, (rplError, rplEntries) => {
|
||
|
if (rplError !== null) {
|
||
|
callFailureCallback$1(callback, rplError);
|
||
|
return;
|
||
|
}
|
||
|
callSuccessCallback$1(callback, rplEntries);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
async$3.readdirWithFileTypes = readdirWithFileTypes$1;
|
||
|
function makeRplTaskEntry(entry, settings) {
|
||
|
return (done) => {
|
||
|
if (!entry.dirent.isSymbolicLink()) {
|
||
|
done(null, entry);
|
||
|
return;
|
||
|
}
|
||
|
settings.fs.stat(entry.path, (statError, stats) => {
|
||
|
if (statError !== null) {
|
||
|
if (settings.throwErrorOnBrokenSymbolicLink) {
|
||
|
done(statError);
|
||
|
return;
|
||
|
}
|
||
|
done(null, entry);
|
||
|
return;
|
||
|
}
|
||
|
entry.dirent = utils$8.fs.createDirentFromStats(entry.name, stats);
|
||
|
done(null, entry);
|
||
|
});
|
||
|
};
|
||
|
}
|
||
|
function readdir$1(directory, settings, callback) {
|
||
|
settings.fs.readdir(directory, (readdirError, names) => {
|
||
|
if (readdirError !== null) {
|
||
|
callFailureCallback$1(callback, readdirError);
|
||
|
return;
|
||
|
}
|
||
|
const tasks = names.map((name) => {
|
||
|
const path = common$5.joinPathSegments(directory, name, settings.pathSegmentSeparator);
|
||
|
return (done) => {
|
||
|
fsStat$5.stat(path, settings.fsStatSettings, (error, stats) => {
|
||
|
if (error !== null) {
|
||
|
done(error);
|
||
|
return;
|
||
|
}
|
||
|
const entry = {
|
||
|
name,
|
||
|
path,
|
||
|
dirent: utils$8.fs.createDirentFromStats(name, stats)
|
||
|
};
|
||
|
if (settings.stats) {
|
||
|
entry.stats = stats;
|
||
|
}
|
||
|
done(null, entry);
|
||
|
});
|
||
|
};
|
||
|
});
|
||
|
rpl(tasks, (rplError, entries) => {
|
||
|
if (rplError !== null) {
|
||
|
callFailureCallback$1(callback, rplError);
|
||
|
return;
|
||
|
}
|
||
|
callSuccessCallback$1(callback, entries);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
async$3.readdir = readdir$1;
|
||
|
function callFailureCallback$1(callback, error) {
|
||
|
callback(error);
|
||
|
}
|
||
|
function callSuccessCallback$1(callback, result) {
|
||
|
callback(null, result);
|
||
|
}
|
||
|
|
||
|
var sync$5 = {};
|
||
|
|
||
|
Object.defineProperty(sync$5, "__esModule", { value: true });
|
||
|
sync$5.readdir = sync$5.readdirWithFileTypes = sync$5.read = void 0;
|
||
|
const fsStat$4 = out$1;
|
||
|
const constants_1 = constants;
|
||
|
const utils$7 = utils$9;
|
||
|
const common$4 = common$6;
|
||
|
function read(directory, settings) {
|
||
|
if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) {
|
||
|
return readdirWithFileTypes(directory, settings);
|
||
|
}
|
||
|
return readdir(directory, settings);
|
||
|
}
|
||
|
sync$5.read = read;
|
||
|
function readdirWithFileTypes(directory, settings) {
|
||
|
const dirents = settings.fs.readdirSync(directory, { withFileTypes: true });
|
||
|
return dirents.map((dirent) => {
|
||
|
const entry = {
|
||
|
dirent,
|
||
|
name: dirent.name,
|
||
|
path: common$4.joinPathSegments(directory, dirent.name, settings.pathSegmentSeparator)
|
||
|
};
|
||
|
if (entry.dirent.isSymbolicLink() && settings.followSymbolicLinks) {
|
||
|
try {
|
||
|
const stats = settings.fs.statSync(entry.path);
|
||
|
entry.dirent = utils$7.fs.createDirentFromStats(entry.name, stats);
|
||
|
}
|
||
|
catch (error) {
|
||
|
if (settings.throwErrorOnBrokenSymbolicLink) {
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return entry;
|
||
|
});
|
||
|
}
|
||
|
sync$5.readdirWithFileTypes = readdirWithFileTypes;
|
||
|
function readdir(directory, settings) {
|
||
|
const names = settings.fs.readdirSync(directory);
|
||
|
return names.map((name) => {
|
||
|
const entryPath = common$4.joinPathSegments(directory, name, settings.pathSegmentSeparator);
|
||
|
const stats = fsStat$4.statSync(entryPath, settings.fsStatSettings);
|
||
|
const entry = {
|
||
|
name,
|
||
|
path: entryPath,
|
||
|
dirent: utils$7.fs.createDirentFromStats(name, stats)
|
||
|
};
|
||
|
if (settings.stats) {
|
||
|
entry.stats = stats;
|
||
|
}
|
||
|
return entry;
|
||
|
});
|
||
|
}
|
||
|
sync$5.readdir = readdir;
|
||
|
|
||
|
var settings$2 = {};
|
||
|
|
||
|
var fs$1 = {};
|
||
|
|
||
|
(function (exports) {
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.createFileSystemAdapter = exports.FILE_SYSTEM_ADAPTER = void 0;
|
||
|
const fs = require$$0$2;
|
||
|
exports.FILE_SYSTEM_ADAPTER = {
|
||
|
lstat: fs.lstat,
|
||
|
stat: fs.stat,
|
||
|
lstatSync: fs.lstatSync,
|
||
|
statSync: fs.statSync,
|
||
|
readdir: fs.readdir,
|
||
|
readdirSync: fs.readdirSync
|
||
|
};
|
||
|
function createFileSystemAdapter(fsMethods) {
|
||
|
if (fsMethods === undefined) {
|
||
|
return exports.FILE_SYSTEM_ADAPTER;
|
||
|
}
|
||
|
return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods);
|
||
|
}
|
||
|
exports.createFileSystemAdapter = createFileSystemAdapter;
|
||
|
} (fs$1));
|
||
|
|
||
|
Object.defineProperty(settings$2, "__esModule", { value: true });
|
||
|
const path$3 = g;
|
||
|
const fsStat$3 = out$1;
|
||
|
const fs = fs$1;
|
||
|
class Settings$1 {
|
||
|
constructor(_options = {}) {
|
||
|
this._options = _options;
|
||
|
this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false);
|
||
|
this.fs = fs.createFileSystemAdapter(this._options.fs);
|
||
|
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$3.sep);
|
||
|
this.stats = this._getValue(this._options.stats, false);
|
||
|
this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
|
||
|
this.fsStatSettings = new fsStat$3.Settings({
|
||
|
followSymbolicLink: this.followSymbolicLinks,
|
||
|
fs: this.fs,
|
||
|
throwErrorOnBrokenSymbolicLink: this.throwErrorOnBrokenSymbolicLink
|
||
|
});
|
||
|
}
|
||
|
_getValue(option, value) {
|
||
|
return option !== null && option !== void 0 ? option : value;
|
||
|
}
|
||
|
}
|
||
|
settings$2.default = Settings$1;
|
||
|
|
||
|
Object.defineProperty(out$2, "__esModule", { value: true });
|
||
|
out$2.Settings = out$2.scandirSync = out$2.scandir = void 0;
|
||
|
const async = async$3;
|
||
|
const sync$4 = sync$5;
|
||
|
const settings_1$2 = settings$2;
|
||
|
out$2.Settings = settings_1$2.default;
|
||
|
function scandir(path, optionsOrSettingsOrCallback, callback) {
|
||
|
if (typeof optionsOrSettingsOrCallback === 'function') {
|
||
|
async.read(path, getSettings$1(), optionsOrSettingsOrCallback);
|
||
|
return;
|
||
|
}
|
||
|
async.read(path, getSettings$1(optionsOrSettingsOrCallback), callback);
|
||
|
}
|
||
|
out$2.scandir = scandir;
|
||
|
function scandirSync(path, optionsOrSettings) {
|
||
|
const settings = getSettings$1(optionsOrSettings);
|
||
|
return sync$4.read(path, settings);
|
||
|
}
|
||
|
out$2.scandirSync = scandirSync;
|
||
|
function getSettings$1(settingsOrOptions = {}) {
|
||
|
if (settingsOrOptions instanceof settings_1$2.default) {
|
||
|
return settingsOrOptions;
|
||
|
}
|
||
|
return new settings_1$2.default(settingsOrOptions);
|
||
|
}
|
||
|
|
||
|
var queue = {exports: {}};
|
||
|
|
||
|
function reusify$1 (Constructor) {
|
||
|
var head = new Constructor();
|
||
|
var tail = head;
|
||
|
|
||
|
function get () {
|
||
|
var current = head;
|
||
|
|
||
|
if (current.next) {
|
||
|
head = current.next;
|
||
|
} else {
|
||
|
head = new Constructor();
|
||
|
tail = head;
|
||
|
}
|
||
|
|
||
|
current.next = null;
|
||
|
|
||
|
return current
|
||
|
}
|
||
|
|
||
|
function release (obj) {
|
||
|
tail.next = obj;
|
||
|
tail = obj;
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
get: get,
|
||
|
release: release
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var reusify_1 = reusify$1;
|
||
|
|
||
|
/* eslint-disable no-var */
|
||
|
|
||
|
var reusify = reusify_1;
|
||
|
|
||
|
function fastqueue (context, worker, concurrency) {
|
||
|
if (typeof context === 'function') {
|
||
|
concurrency = worker;
|
||
|
worker = context;
|
||
|
context = null;
|
||
|
}
|
||
|
|
||
|
if (concurrency < 1) {
|
||
|
throw new Error('fastqueue concurrency must be greater than 1')
|
||
|
}
|
||
|
|
||
|
var cache = reusify(Task);
|
||
|
var queueHead = null;
|
||
|
var queueTail = null;
|
||
|
var _running = 0;
|
||
|
var errorHandler = null;
|
||
|
|
||
|
var self = {
|
||
|
push: push,
|
||
|
drain: noop,
|
||
|
saturated: noop,
|
||
|
pause: pause,
|
||
|
paused: false,
|
||
|
concurrency: concurrency,
|
||
|
running: running,
|
||
|
resume: resume,
|
||
|
idle: idle,
|
||
|
length: length,
|
||
|
getQueue: getQueue,
|
||
|
unshift: unshift,
|
||
|
empty: noop,
|
||
|
kill: kill,
|
||
|
killAndDrain: killAndDrain,
|
||
|
error: error
|
||
|
};
|
||
|
|
||
|
return self
|
||
|
|
||
|
function running () {
|
||
|
return _running
|
||
|
}
|
||
|
|
||
|
function pause () {
|
||
|
self.paused = true;
|
||
|
}
|
||
|
|
||
|
function length () {
|
||
|
var current = queueHead;
|
||
|
var counter = 0;
|
||
|
|
||
|
while (current) {
|
||
|
current = current.next;
|
||
|
counter++;
|
||
|
}
|
||
|
|
||
|
return counter
|
||
|
}
|
||
|
|
||
|
function getQueue () {
|
||
|
var current = queueHead;
|
||
|
var tasks = [];
|
||
|
|
||
|
while (current) {
|
||
|
tasks.push(current.value);
|
||
|
current = current.next;
|
||
|
}
|
||
|
|
||
|
return tasks
|
||
|
}
|
||
|
|
||
|
function resume () {
|
||
|
if (!self.paused) return
|
||
|
self.paused = false;
|
||
|
for (var i = 0; i < self.concurrency; i++) {
|
||
|
_running++;
|
||
|
release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function idle () {
|
||
|
return _running === 0 && self.length() === 0
|
||
|
}
|
||
|
|
||
|
function push (value, done) {
|
||
|
var current = cache.get();
|
||
|
|
||
|
current.context = context;
|
||
|
current.release = release;
|
||
|
current.value = value;
|
||
|
current.callback = done || noop;
|
||
|
current.errorHandler = errorHandler;
|
||
|
|
||
|
if (_running === self.concurrency || self.paused) {
|
||
|
if (queueTail) {
|
||
|
queueTail.next = current;
|
||
|
queueTail = current;
|
||
|
} else {
|
||
|
queueHead = current;
|
||
|
queueTail = current;
|
||
|
self.saturated();
|
||
|
}
|
||
|
} else {
|
||
|
_running++;
|
||
|
worker.call(context, current.value, current.worked);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function unshift (value, done) {
|
||
|
var current = cache.get();
|
||
|
|
||
|
current.context = context;
|
||
|
current.release = release;
|
||
|
current.value = value;
|
||
|
current.callback = done || noop;
|
||
|
|
||
|
if (_running === self.concurrency || self.paused) {
|
||
|
if (queueHead) {
|
||
|
current.next = queueHead;
|
||
|
queueHead = current;
|
||
|
} else {
|
||
|
queueHead = current;
|
||
|
queueTail = current;
|
||
|
self.saturated();
|
||
|
}
|
||
|
} else {
|
||
|
_running++;
|
||
|
worker.call(context, current.value, current.worked);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function release (holder) {
|
||
|
if (holder) {
|
||
|
cache.release(holder);
|
||
|
}
|
||
|
var next = queueHead;
|
||
|
if (next) {
|
||
|
if (!self.paused) {
|
||
|
if (queueTail === queueHead) {
|
||
|
queueTail = null;
|
||
|
}
|
||
|
queueHead = next.next;
|
||
|
next.next = null;
|
||
|
worker.call(context, next.value, next.worked);
|
||
|
if (queueTail === null) {
|
||
|
self.empty();
|
||
|
}
|
||
|
} else {
|
||
|
_running--;
|
||
|
}
|
||
|
} else if (--_running === 0) {
|
||
|
self.drain();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function kill () {
|
||
|
queueHead = null;
|
||
|
queueTail = null;
|
||
|
self.drain = noop;
|
||
|
}
|
||
|
|
||
|
function killAndDrain () {
|
||
|
queueHead = null;
|
||
|
queueTail = null;
|
||
|
self.drain();
|
||
|
self.drain = noop;
|
||
|
}
|
||
|
|
||
|
function error (handler) {
|
||
|
errorHandler = handler;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function noop () {}
|
||
|
|
||
|
function Task () {
|
||
|
this.value = null;
|
||
|
this.callback = noop;
|
||
|
this.next = null;
|
||
|
this.release = noop;
|
||
|
this.context = null;
|
||
|
this.errorHandler = null;
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
this.worked = function worked (err, result) {
|
||
|
var callback = self.callback;
|
||
|
var errorHandler = self.errorHandler;
|
||
|
var val = self.value;
|
||
|
self.value = null;
|
||
|
self.callback = noop;
|
||
|
if (self.errorHandler) {
|
||
|
errorHandler(err, val);
|
||
|
}
|
||
|
callback.call(self.context, err, result);
|
||
|
self.release(self);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function queueAsPromised (context, worker, concurrency) {
|
||
|
if (typeof context === 'function') {
|
||
|
concurrency = worker;
|
||
|
worker = context;
|
||
|
context = null;
|
||
|
}
|
||
|
|
||
|
function asyncWrapper (arg, cb) {
|
||
|
worker.call(this, arg)
|
||
|
.then(function (res) {
|
||
|
cb(null, res);
|
||
|
}, cb);
|
||
|
}
|
||
|
|
||
|
var queue = fastqueue(context, asyncWrapper, concurrency);
|
||
|
|
||
|
var pushCb = queue.push;
|
||
|
var unshiftCb = queue.unshift;
|
||
|
|
||
|
queue.push = push;
|
||
|
queue.unshift = unshift;
|
||
|
queue.drained = drained;
|
||
|
|
||
|
return queue
|
||
|
|
||
|
function push (value) {
|
||
|
var p = new Promise(function (resolve, reject) {
|
||
|
pushCb(value, function (err, result) {
|
||
|
if (err) {
|
||
|
reject(err);
|
||
|
return
|
||
|
}
|
||
|
resolve(result);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// Let's fork the promise chain to
|
||
|
// make the error bubble up to the user but
|
||
|
// not lead to a unhandledRejection
|
||
|
p.catch(noop);
|
||
|
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
function unshift (value) {
|
||
|
var p = new Promise(function (resolve, reject) {
|
||
|
unshiftCb(value, function (err, result) {
|
||
|
if (err) {
|
||
|
reject(err);
|
||
|
return
|
||
|
}
|
||
|
resolve(result);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// Let's fork the promise chain to
|
||
|
// make the error bubble up to the user but
|
||
|
// not lead to a unhandledRejection
|
||
|
p.catch(noop);
|
||
|
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
function drained () {
|
||
|
var previousDrain = queue.drain;
|
||
|
|
||
|
var p = new Promise(function (resolve) {
|
||
|
queue.drain = function () {
|
||
|
previousDrain();
|
||
|
resolve();
|
||
|
};
|
||
|
});
|
||
|
|
||
|
return p
|
||
|
}
|
||
|
}
|
||
|
|
||
|
queue.exports = fastqueue;
|
||
|
queue.exports.promise = queueAsPromised;
|
||
|
|
||
|
var common$3 = {};
|
||
|
|
||
|
Object.defineProperty(common$3, "__esModule", { value: true });
|
||
|
common$3.joinPathSegments = common$3.replacePathSegmentSeparator = common$3.isAppliedFilter = common$3.isFatalError = void 0;
|
||
|
function isFatalError(settings, error) {
|
||
|
if (settings.errorFilter === null) {
|
||
|
return true;
|
||
|
}
|
||
|
return !settings.errorFilter(error);
|
||
|
}
|
||
|
common$3.isFatalError = isFatalError;
|
||
|
function isAppliedFilter(filter, value) {
|
||
|
return filter === null || filter(value);
|
||
|
}
|
||
|
common$3.isAppliedFilter = isAppliedFilter;
|
||
|
function replacePathSegmentSeparator(filepath, separator) {
|
||
|
return filepath.split(/[/\\]/).join(separator);
|
||
|
}
|
||
|
common$3.replacePathSegmentSeparator = replacePathSegmentSeparator;
|
||
|
function joinPathSegments(a, b, separator) {
|
||
|
if (a === '') {
|
||
|
return b;
|
||
|
}
|
||
|
/**
|
||
|
* The correct handling of cases when the first segment is a root (`/`, `C:/`) or UNC path (`//?/C:/`).
|
||
|
*/
|
||
|
if (a.endsWith(separator)) {
|
||
|
return a + b;
|
||
|
}
|
||
|
return a + separator + b;
|
||
|
}
|
||
|
common$3.joinPathSegments = joinPathSegments;
|
||
|
|
||
|
var reader$1 = {};
|
||
|
|
||
|
Object.defineProperty(reader$1, "__esModule", { value: true });
|
||
|
const common$2 = common$3;
|
||
|
class Reader$1 {
|
||
|
constructor(_root, _settings) {
|
||
|
this._root = _root;
|
||
|
this._settings = _settings;
|
||
|
this._root = common$2.replacePathSegmentSeparator(_root, _settings.pathSegmentSeparator);
|
||
|
}
|
||
|
}
|
||
|
reader$1.default = Reader$1;
|
||
|
|
||
|
Object.defineProperty(async$4, "__esModule", { value: true });
|
||
|
const events_1 = require$$2;
|
||
|
const fsScandir$2 = out$2;
|
||
|
const fastq = queue.exports;
|
||
|
const common$1 = common$3;
|
||
|
const reader_1$4 = reader$1;
|
||
|
class AsyncReader extends reader_1$4.default {
|
||
|
constructor(_root, _settings) {
|
||
|
super(_root, _settings);
|
||
|
this._settings = _settings;
|
||
|
this._scandir = fsScandir$2.scandir;
|
||
|
this._emitter = new events_1.EventEmitter();
|
||
|
this._queue = fastq(this._worker.bind(this), this._settings.concurrency);
|
||
|
this._isFatalError = false;
|
||
|
this._isDestroyed = false;
|
||
|
this._queue.drain = () => {
|
||
|
if (!this._isFatalError) {
|
||
|
this._emitter.emit('end');
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
read() {
|
||
|
this._isFatalError = false;
|
||
|
this._isDestroyed = false;
|
||
|
setImmediate(() => {
|
||
|
this._pushToQueue(this._root, this._settings.basePath);
|
||
|
});
|
||
|
return this._emitter;
|
||
|
}
|
||
|
get isDestroyed() {
|
||
|
return this._isDestroyed;
|
||
|
}
|
||
|
destroy() {
|
||
|
if (this._isDestroyed) {
|
||
|
throw new Error('The reader is already destroyed');
|
||
|
}
|
||
|
this._isDestroyed = true;
|
||
|
this._queue.killAndDrain();
|
||
|
}
|
||
|
onEntry(callback) {
|
||
|
this._emitter.on('entry', callback);
|
||
|
}
|
||
|
onError(callback) {
|
||
|
this._emitter.once('error', callback);
|
||
|
}
|
||
|
onEnd(callback) {
|
||
|
this._emitter.once('end', callback);
|
||
|
}
|
||
|
_pushToQueue(directory, base) {
|
||
|
const queueItem = { directory, base };
|
||
|
this._queue.push(queueItem, (error) => {
|
||
|
if (error !== null) {
|
||
|
this._handleError(error);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
_worker(item, done) {
|
||
|
this._scandir(item.directory, this._settings.fsScandirSettings, (error, entries) => {
|
||
|
if (error !== null) {
|
||
|
done(error, undefined);
|
||
|
return;
|
||
|
}
|
||
|
for (const entry of entries) {
|
||
|
this._handleEntry(entry, item.base);
|
||
|
}
|
||
|
done(null, undefined);
|
||
|
});
|
||
|
}
|
||
|
_handleError(error) {
|
||
|
if (this._isDestroyed || !common$1.isFatalError(this._settings, error)) {
|
||
|
return;
|
||
|
}
|
||
|
this._isFatalError = true;
|
||
|
this._isDestroyed = true;
|
||
|
this._emitter.emit('error', error);
|
||
|
}
|
||
|
_handleEntry(entry, base) {
|
||
|
if (this._isDestroyed || this._isFatalError) {
|
||
|
return;
|
||
|
}
|
||
|
const fullpath = entry.path;
|
||
|
if (base !== undefined) {
|
||
|
entry.path = common$1.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator);
|
||
|
}
|
||
|
if (common$1.isAppliedFilter(this._settings.entryFilter, entry)) {
|
||
|
this._emitEntry(entry);
|
||
|
}
|
||
|
if (entry.dirent.isDirectory() && common$1.isAppliedFilter(this._settings.deepFilter, entry)) {
|
||
|
this._pushToQueue(fullpath, base === undefined ? undefined : entry.path);
|
||
|
}
|
||
|
}
|
||
|
_emitEntry(entry) {
|
||
|
this._emitter.emit('entry', entry);
|
||
|
}
|
||
|
}
|
||
|
async$4.default = AsyncReader;
|
||
|
|
||
|
Object.defineProperty(async$5, "__esModule", { value: true });
|
||
|
const async_1$4 = async$4;
|
||
|
class AsyncProvider {
|
||
|
constructor(_root, _settings) {
|
||
|
this._root = _root;
|
||
|
this._settings = _settings;
|
||
|
this._reader = new async_1$4.default(this._root, this._settings);
|
||
|
this._storage = [];
|
||
|
}
|
||
|
read(callback) {
|
||
|
this._reader.onError((error) => {
|
||
|
callFailureCallback(callback, error);
|
||
|
});
|
||
|
this._reader.onEntry((entry) => {
|
||
|
this._storage.push(entry);
|
||
|
});
|
||
|
this._reader.onEnd(() => {
|
||
|
callSuccessCallback(callback, this._storage);
|
||
|
});
|
||
|
this._reader.read();
|
||
|
}
|
||
|
}
|
||
|
async$5.default = AsyncProvider;
|
||
|
function callFailureCallback(callback, error) {
|
||
|
callback(error);
|
||
|
}
|
||
|
function callSuccessCallback(callback, entries) {
|
||
|
callback(null, entries);
|
||
|
}
|
||
|
|
||
|
var stream$2 = {};
|
||
|
|
||
|
Object.defineProperty(stream$2, "__esModule", { value: true });
|
||
|
const stream_1$5 = require$$0$1;
|
||
|
const async_1$3 = async$4;
|
||
|
class StreamProvider {
|
||
|
constructor(_root, _settings) {
|
||
|
this._root = _root;
|
||
|
this._settings = _settings;
|
||
|
this._reader = new async_1$3.default(this._root, this._settings);
|
||
|
this._stream = new stream_1$5.Readable({
|
||
|
objectMode: true,
|
||
|
read: () => { },
|
||
|
destroy: () => {
|
||
|
if (!this._reader.isDestroyed) {
|
||
|
this._reader.destroy();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
read() {
|
||
|
this._reader.onError((error) => {
|
||
|
this._stream.emit('error', error);
|
||
|
});
|
||
|
this._reader.onEntry((entry) => {
|
||
|
this._stream.push(entry);
|
||
|
});
|
||
|
this._reader.onEnd(() => {
|
||
|
this._stream.push(null);
|
||
|
});
|
||
|
this._reader.read();
|
||
|
return this._stream;
|
||
|
}
|
||
|
}
|
||
|
stream$2.default = StreamProvider;
|
||
|
|
||
|
var sync$3 = {};
|
||
|
|
||
|
var sync$2 = {};
|
||
|
|
||
|
Object.defineProperty(sync$2, "__esModule", { value: true });
|
||
|
const fsScandir$1 = out$2;
|
||
|
const common = common$3;
|
||
|
const reader_1$3 = reader$1;
|
||
|
class SyncReader extends reader_1$3.default {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this._scandir = fsScandir$1.scandirSync;
|
||
|
this._storage = [];
|
||
|
this._queue = new Set();
|
||
|
}
|
||
|
read() {
|
||
|
this._pushToQueue(this._root, this._settings.basePath);
|
||
|
this._handleQueue();
|
||
|
return this._storage;
|
||
|
}
|
||
|
_pushToQueue(directory, base) {
|
||
|
this._queue.add({ directory, base });
|
||
|
}
|
||
|
_handleQueue() {
|
||
|
for (const item of this._queue.values()) {
|
||
|
this._handleDirectory(item.directory, item.base);
|
||
|
}
|
||
|
}
|
||
|
_handleDirectory(directory, base) {
|
||
|
try {
|
||
|
const entries = this._scandir(directory, this._settings.fsScandirSettings);
|
||
|
for (const entry of entries) {
|
||
|
this._handleEntry(entry, base);
|
||
|
}
|
||
|
}
|
||
|
catch (error) {
|
||
|
this._handleError(error);
|
||
|
}
|
||
|
}
|
||
|
_handleError(error) {
|
||
|
if (!common.isFatalError(this._settings, error)) {
|
||
|
return;
|
||
|
}
|
||
|
throw error;
|
||
|
}
|
||
|
_handleEntry(entry, base) {
|
||
|
const fullpath = entry.path;
|
||
|
if (base !== undefined) {
|
||
|
entry.path = common.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator);
|
||
|
}
|
||
|
if (common.isAppliedFilter(this._settings.entryFilter, entry)) {
|
||
|
this._pushToStorage(entry);
|
||
|
}
|
||
|
if (entry.dirent.isDirectory() && common.isAppliedFilter(this._settings.deepFilter, entry)) {
|
||
|
this._pushToQueue(fullpath, base === undefined ? undefined : entry.path);
|
||
|
}
|
||
|
}
|
||
|
_pushToStorage(entry) {
|
||
|
this._storage.push(entry);
|
||
|
}
|
||
|
}
|
||
|
sync$2.default = SyncReader;
|
||
|
|
||
|
Object.defineProperty(sync$3, "__esModule", { value: true });
|
||
|
const sync_1$3 = sync$2;
|
||
|
class SyncProvider {
|
||
|
constructor(_root, _settings) {
|
||
|
this._root = _root;
|
||
|
this._settings = _settings;
|
||
|
this._reader = new sync_1$3.default(this._root, this._settings);
|
||
|
}
|
||
|
read() {
|
||
|
return this._reader.read();
|
||
|
}
|
||
|
}
|
||
|
sync$3.default = SyncProvider;
|
||
|
|
||
|
var settings$1 = {};
|
||
|
|
||
|
Object.defineProperty(settings$1, "__esModule", { value: true });
|
||
|
const path$2 = g;
|
||
|
const fsScandir = out$2;
|
||
|
class Settings {
|
||
|
constructor(_options = {}) {
|
||
|
this._options = _options;
|
||
|
this.basePath = this._getValue(this._options.basePath, undefined);
|
||
|
this.concurrency = this._getValue(this._options.concurrency, Number.POSITIVE_INFINITY);
|
||
|
this.deepFilter = this._getValue(this._options.deepFilter, null);
|
||
|
this.entryFilter = this._getValue(this._options.entryFilter, null);
|
||
|
this.errorFilter = this._getValue(this._options.errorFilter, null);
|
||
|
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$2.sep);
|
||
|
this.fsScandirSettings = new fsScandir.Settings({
|
||
|
followSymbolicLinks: this._options.followSymbolicLinks,
|
||
|
fs: this._options.fs,
|
||
|
pathSegmentSeparator: this._options.pathSegmentSeparator,
|
||
|
stats: this._options.stats,
|
||
|
throwErrorOnBrokenSymbolicLink: this._options.throwErrorOnBrokenSymbolicLink
|
||
|
});
|
||
|
}
|
||
|
_getValue(option, value) {
|
||
|
return option !== null && option !== void 0 ? option : value;
|
||
|
}
|
||
|
}
|
||
|
settings$1.default = Settings;
|
||
|
|
||
|
Object.defineProperty(out$3, "__esModule", { value: true });
|
||
|
out$3.Settings = out$3.walkStream = out$3.walkSync = out$3.walk = void 0;
|
||
|
const async_1$2 = async$5;
|
||
|
const stream_1$4 = stream$2;
|
||
|
const sync_1$2 = sync$3;
|
||
|
const settings_1$1 = settings$1;
|
||
|
out$3.Settings = settings_1$1.default;
|
||
|
function walk(directory, optionsOrSettingsOrCallback, callback) {
|
||
|
if (typeof optionsOrSettingsOrCallback === 'function') {
|
||
|
new async_1$2.default(directory, getSettings()).read(optionsOrSettingsOrCallback);
|
||
|
return;
|
||
|
}
|
||
|
new async_1$2.default(directory, getSettings(optionsOrSettingsOrCallback)).read(callback);
|
||
|
}
|
||
|
out$3.walk = walk;
|
||
|
function walkSync(directory, optionsOrSettings) {
|
||
|
const settings = getSettings(optionsOrSettings);
|
||
|
const provider = new sync_1$2.default(directory, settings);
|
||
|
return provider.read();
|
||
|
}
|
||
|
out$3.walkSync = walkSync;
|
||
|
function walkStream(directory, optionsOrSettings) {
|
||
|
const settings = getSettings(optionsOrSettings);
|
||
|
const provider = new stream_1$4.default(directory, settings);
|
||
|
return provider.read();
|
||
|
}
|
||
|
out$3.walkStream = walkStream;
|
||
|
function getSettings(settingsOrOptions = {}) {
|
||
|
if (settingsOrOptions instanceof settings_1$1.default) {
|
||
|
return settingsOrOptions;
|
||
|
}
|
||
|
return new settings_1$1.default(settingsOrOptions);
|
||
|
}
|
||
|
|
||
|
var reader = {};
|
||
|
|
||
|
Object.defineProperty(reader, "__esModule", { value: true });
|
||
|
const path$1 = g;
|
||
|
const fsStat$2 = out$1;
|
||
|
const utils$6 = utils$k;
|
||
|
class Reader {
|
||
|
constructor(_settings) {
|
||
|
this._settings = _settings;
|
||
|
this._fsStatSettings = new fsStat$2.Settings({
|
||
|
followSymbolicLink: this._settings.followSymbolicLinks,
|
||
|
fs: this._settings.fs,
|
||
|
throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks
|
||
|
});
|
||
|
}
|
||
|
_getFullEntryPath(filepath) {
|
||
|
return path$1.resolve(this._settings.cwd, filepath);
|
||
|
}
|
||
|
_makeEntry(stats, pattern) {
|
||
|
const entry = {
|
||
|
name: pattern,
|
||
|
path: pattern,
|
||
|
dirent: utils$6.fs.createDirentFromStats(pattern, stats)
|
||
|
};
|
||
|
if (this._settings.stats) {
|
||
|
entry.stats = stats;
|
||
|
}
|
||
|
return entry;
|
||
|
}
|
||
|
_isFatalError(error) {
|
||
|
return !utils$6.errno.isEnoentCodeError(error) && !this._settings.suppressErrors;
|
||
|
}
|
||
|
}
|
||
|
reader.default = Reader;
|
||
|
|
||
|
var stream$1 = {};
|
||
|
|
||
|
Object.defineProperty(stream$1, "__esModule", { value: true });
|
||
|
const stream_1$3 = require$$0$1;
|
||
|
const fsStat$1 = out$1;
|
||
|
const fsWalk$2 = out$3;
|
||
|
const reader_1$2 = reader;
|
||
|
class ReaderStream extends reader_1$2.default {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this._walkStream = fsWalk$2.walkStream;
|
||
|
this._stat = fsStat$1.stat;
|
||
|
}
|
||
|
dynamic(root, options) {
|
||
|
return this._walkStream(root, options);
|
||
|
}
|
||
|
static(patterns, options) {
|
||
|
const filepaths = patterns.map(this._getFullEntryPath, this);
|
||
|
const stream = new stream_1$3.PassThrough({ objectMode: true });
|
||
|
stream._write = (index, _enc, done) => {
|
||
|
return this._getEntry(filepaths[index], patterns[index], options)
|
||
|
.then((entry) => {
|
||
|
if (entry !== null && options.entryFilter(entry)) {
|
||
|
stream.push(entry);
|
||
|
}
|
||
|
if (index === filepaths.length - 1) {
|
||
|
stream.end();
|
||
|
}
|
||
|
done();
|
||
|
})
|
||
|
.catch(done);
|
||
|
};
|
||
|
for (let i = 0; i < filepaths.length; i++) {
|
||
|
stream.write(i);
|
||
|
}
|
||
|
return stream;
|
||
|
}
|
||
|
_getEntry(filepath, pattern, options) {
|
||
|
return this._getStat(filepath)
|
||
|
.then((stats) => this._makeEntry(stats, pattern))
|
||
|
.catch((error) => {
|
||
|
if (options.errorFilter(error)) {
|
||
|
return null;
|
||
|
}
|
||
|
throw error;
|
||
|
});
|
||
|
}
|
||
|
_getStat(filepath) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
this._stat(filepath, this._fsStatSettings, (error, stats) => {
|
||
|
return error === null ? resolve(stats) : reject(error);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
stream$1.default = ReaderStream;
|
||
|
|
||
|
Object.defineProperty(async$6, "__esModule", { value: true });
|
||
|
const fsWalk$1 = out$3;
|
||
|
const reader_1$1 = reader;
|
||
|
const stream_1$2 = stream$1;
|
||
|
class ReaderAsync extends reader_1$1.default {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this._walkAsync = fsWalk$1.walk;
|
||
|
this._readerStream = new stream_1$2.default(this._settings);
|
||
|
}
|
||
|
dynamic(root, options) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
this._walkAsync(root, options, (error, entries) => {
|
||
|
if (error === null) {
|
||
|
resolve(entries);
|
||
|
}
|
||
|
else {
|
||
|
reject(error);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
async static(patterns, options) {
|
||
|
const entries = [];
|
||
|
const stream = this._readerStream.static(patterns, options);
|
||
|
// After #235, replace it with an asynchronous iterator.
|
||
|
return new Promise((resolve, reject) => {
|
||
|
stream.once('error', reject);
|
||
|
stream.on('data', (entry) => entries.push(entry));
|
||
|
stream.once('end', () => resolve(entries));
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
async$6.default = ReaderAsync;
|
||
|
|
||
|
var provider = {};
|
||
|
|
||
|
var deep = {};
|
||
|
|
||
|
var partial = {};
|
||
|
|
||
|
var matcher = {};
|
||
|
|
||
|
Object.defineProperty(matcher, "__esModule", { value: true });
|
||
|
const utils$5 = utils$k;
|
||
|
class Matcher {
|
||
|
constructor(_patterns, _settings, _micromatchOptions) {
|
||
|
this._patterns = _patterns;
|
||
|
this._settings = _settings;
|
||
|
this._micromatchOptions = _micromatchOptions;
|
||
|
this._storage = [];
|
||
|
this._fillStorage();
|
||
|
}
|
||
|
_fillStorage() {
|
||
|
/**
|
||
|
* The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level).
|
||
|
* So, before expand patterns with brace expansion into separated patterns.
|
||
|
*/
|
||
|
const patterns = utils$5.pattern.expandPatternsWithBraceExpansion(this._patterns);
|
||
|
for (const pattern of patterns) {
|
||
|
const segments = this._getPatternSegments(pattern);
|
||
|
const sections = this._splitSegmentsIntoSections(segments);
|
||
|
this._storage.push({
|
||
|
complete: sections.length <= 1,
|
||
|
pattern,
|
||
|
segments,
|
||
|
sections
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
_getPatternSegments(pattern) {
|
||
|
const parts = utils$5.pattern.getPatternParts(pattern, this._micromatchOptions);
|
||
|
return parts.map((part) => {
|
||
|
const dynamic = utils$5.pattern.isDynamicPattern(part, this._settings);
|
||
|
if (!dynamic) {
|
||
|
return {
|
||
|
dynamic: false,
|
||
|
pattern: part
|
||
|
};
|
||
|
}
|
||
|
return {
|
||
|
dynamic: true,
|
||
|
pattern: part,
|
||
|
patternRe: utils$5.pattern.makeRe(part, this._micromatchOptions)
|
||
|
};
|
||
|
});
|
||
|
}
|
||
|
_splitSegmentsIntoSections(segments) {
|
||
|
return utils$5.array.splitWhen(segments, (segment) => segment.dynamic && utils$5.pattern.hasGlobStar(segment.pattern));
|
||
|
}
|
||
|
}
|
||
|
matcher.default = Matcher;
|
||
|
|
||
|
Object.defineProperty(partial, "__esModule", { value: true });
|
||
|
const matcher_1 = matcher;
|
||
|
class PartialMatcher extends matcher_1.default {
|
||
|
match(filepath) {
|
||
|
const parts = filepath.split('/');
|
||
|
const levels = parts.length;
|
||
|
const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels);
|
||
|
for (const pattern of patterns) {
|
||
|
const section = pattern.sections[0];
|
||
|
/**
|
||
|
* In this case, the pattern has a globstar and we must read all directories unconditionally,
|
||
|
* but only if the level has reached the end of the first group.
|
||
|
*
|
||
|
* fixtures/{a,b}/**
|
||
|
* ^ true/false ^ always true
|
||
|
*/
|
||
|
if (!pattern.complete && levels > section.length) {
|
||
|
return true;
|
||
|
}
|
||
|
const match = parts.every((part, index) => {
|
||
|
const segment = pattern.segments[index];
|
||
|
if (segment.dynamic && segment.patternRe.test(part)) {
|
||
|
return true;
|
||
|
}
|
||
|
if (!segment.dynamic && segment.pattern === part) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
});
|
||
|
if (match) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
partial.default = PartialMatcher;
|
||
|
|
||
|
Object.defineProperty(deep, "__esModule", { value: true });
|
||
|
const utils$4 = utils$k;
|
||
|
const partial_1 = partial;
|
||
|
class DeepFilter {
|
||
|
constructor(_settings, _micromatchOptions) {
|
||
|
this._settings = _settings;
|
||
|
this._micromatchOptions = _micromatchOptions;
|
||
|
}
|
||
|
getFilter(basePath, positive, negative) {
|
||
|
const matcher = this._getMatcher(positive);
|
||
|
const negativeRe = this._getNegativePatternsRe(negative);
|
||
|
return (entry) => this._filter(basePath, entry, matcher, negativeRe);
|
||
|
}
|
||
|
_getMatcher(patterns) {
|
||
|
return new partial_1.default(patterns, this._settings, this._micromatchOptions);
|
||
|
}
|
||
|
_getNegativePatternsRe(patterns) {
|
||
|
const affectDepthOfReadingPatterns = patterns.filter(utils$4.pattern.isAffectDepthOfReadingPattern);
|
||
|
return utils$4.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions);
|
||
|
}
|
||
|
_filter(basePath, entry, matcher, negativeRe) {
|
||
|
if (this._isSkippedByDeep(basePath, entry.path)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (this._isSkippedSymbolicLink(entry)) {
|
||
|
return false;
|
||
|
}
|
||
|
const filepath = utils$4.path.removeLeadingDotSegment(entry.path);
|
||
|
if (this._isSkippedByPositivePatterns(filepath, matcher)) {
|
||
|
return false;
|
||
|
}
|
||
|
return this._isSkippedByNegativePatterns(filepath, negativeRe);
|
||
|
}
|
||
|
_isSkippedByDeep(basePath, entryPath) {
|
||
|
/**
|
||
|
* Avoid unnecessary depth calculations when it doesn't matter.
|
||
|
*/
|
||
|
if (this._settings.deep === Infinity) {
|
||
|
return false;
|
||
|
}
|
||
|
return this._getEntryLevel(basePath, entryPath) >= this._settings.deep;
|
||
|
}
|
||
|
_getEntryLevel(basePath, entryPath) {
|
||
|
const entryPathDepth = entryPath.split('/').length;
|
||
|
if (basePath === '') {
|
||
|
return entryPathDepth;
|
||
|
}
|
||
|
const basePathDepth = basePath.split('/').length;
|
||
|
return entryPathDepth - basePathDepth;
|
||
|
}
|
||
|
_isSkippedSymbolicLink(entry) {
|
||
|
return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink();
|
||
|
}
|
||
|
_isSkippedByPositivePatterns(entryPath, matcher) {
|
||
|
return !this._settings.baseNameMatch && !matcher.match(entryPath);
|
||
|
}
|
||
|
_isSkippedByNegativePatterns(entryPath, patternsRe) {
|
||
|
return !utils$4.pattern.matchAny(entryPath, patternsRe);
|
||
|
}
|
||
|
}
|
||
|
deep.default = DeepFilter;
|
||
|
|
||
|
var entry$1 = {};
|
||
|
|
||
|
Object.defineProperty(entry$1, "__esModule", { value: true });
|
||
|
const utils$3 = utils$k;
|
||
|
class EntryFilter {
|
||
|
constructor(_settings, _micromatchOptions) {
|
||
|
this._settings = _settings;
|
||
|
this._micromatchOptions = _micromatchOptions;
|
||
|
this.index = new Map();
|
||
|
}
|
||
|
getFilter(positive, negative) {
|
||
|
const positiveRe = utils$3.pattern.convertPatternsToRe(positive, this._micromatchOptions);
|
||
|
const negativeRe = utils$3.pattern.convertPatternsToRe(negative, this._micromatchOptions);
|
||
|
return (entry) => this._filter(entry, positiveRe, negativeRe);
|
||
|
}
|
||
|
_filter(entry, positiveRe, negativeRe) {
|
||
|
if (this._settings.unique && this._isDuplicateEntry(entry)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) {
|
||
|
return false;
|
||
|
}
|
||
|
if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) {
|
||
|
return false;
|
||
|
}
|
||
|
const filepath = this._settings.baseNameMatch ? entry.name : entry.path;
|
||
|
const isDirectory = entry.dirent.isDirectory();
|
||
|
const isMatched = this._isMatchToPatterns(filepath, positiveRe, isDirectory) && !this._isMatchToPatterns(entry.path, negativeRe, isDirectory);
|
||
|
if (this._settings.unique && isMatched) {
|
||
|
this._createIndexRecord(entry);
|
||
|
}
|
||
|
return isMatched;
|
||
|
}
|
||
|
_isDuplicateEntry(entry) {
|
||
|
return this.index.has(entry.path);
|
||
|
}
|
||
|
_createIndexRecord(entry) {
|
||
|
this.index.set(entry.path, undefined);
|
||
|
}
|
||
|
_onlyFileFilter(entry) {
|
||
|
return this._settings.onlyFiles && !entry.dirent.isFile();
|
||
|
}
|
||
|
_onlyDirectoryFilter(entry) {
|
||
|
return this._settings.onlyDirectories && !entry.dirent.isDirectory();
|
||
|
}
|
||
|
_isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) {
|
||
|
if (!this._settings.absolute) {
|
||
|
return false;
|
||
|
}
|
||
|
const fullpath = utils$3.path.makeAbsolute(this._settings.cwd, entryPath);
|
||
|
return utils$3.pattern.matchAny(fullpath, patternsRe);
|
||
|
}
|
||
|
_isMatchToPatterns(entryPath, patternsRe, isDirectory) {
|
||
|
const filepath = utils$3.path.removeLeadingDotSegment(entryPath);
|
||
|
// Trying to match files and directories by patterns.
|
||
|
const isMatched = utils$3.pattern.matchAny(filepath, patternsRe);
|
||
|
// A pattern with a trailling slash can be used for directory matching.
|
||
|
// To apply such pattern, we need to add a tralling slash to the path.
|
||
|
if (!isMatched && isDirectory) {
|
||
|
return utils$3.pattern.matchAny(filepath + '/', patternsRe);
|
||
|
}
|
||
|
return isMatched;
|
||
|
}
|
||
|
}
|
||
|
entry$1.default = EntryFilter;
|
||
|
|
||
|
var error = {};
|
||
|
|
||
|
Object.defineProperty(error, "__esModule", { value: true });
|
||
|
const utils$2 = utils$k;
|
||
|
class ErrorFilter {
|
||
|
constructor(_settings) {
|
||
|
this._settings = _settings;
|
||
|
}
|
||
|
getFilter() {
|
||
|
return (error) => this._isNonFatalError(error);
|
||
|
}
|
||
|
_isNonFatalError(error) {
|
||
|
return utils$2.errno.isEnoentCodeError(error) || this._settings.suppressErrors;
|
||
|
}
|
||
|
}
|
||
|
error.default = ErrorFilter;
|
||
|
|
||
|
var entry = {};
|
||
|
|
||
|
Object.defineProperty(entry, "__esModule", { value: true });
|
||
|
const utils$1 = utils$k;
|
||
|
class EntryTransformer {
|
||
|
constructor(_settings) {
|
||
|
this._settings = _settings;
|
||
|
}
|
||
|
getTransformer() {
|
||
|
return (entry) => this._transform(entry);
|
||
|
}
|
||
|
_transform(entry) {
|
||
|
let filepath = entry.path;
|
||
|
if (this._settings.absolute) {
|
||
|
filepath = utils$1.path.makeAbsolute(this._settings.cwd, filepath);
|
||
|
filepath = utils$1.path.unixify(filepath);
|
||
|
}
|
||
|
if (this._settings.markDirectories && entry.dirent.isDirectory()) {
|
||
|
filepath += '/';
|
||
|
}
|
||
|
if (!this._settings.objectMode) {
|
||
|
return filepath;
|
||
|
}
|
||
|
return Object.assign(Object.assign({}, entry), { path: filepath });
|
||
|
}
|
||
|
}
|
||
|
entry.default = EntryTransformer;
|
||
|
|
||
|
Object.defineProperty(provider, "__esModule", { value: true });
|
||
|
const path = g;
|
||
|
const deep_1 = deep;
|
||
|
const entry_1 = entry$1;
|
||
|
const error_1 = error;
|
||
|
const entry_2 = entry;
|
||
|
class Provider {
|
||
|
constructor(_settings) {
|
||
|
this._settings = _settings;
|
||
|
this.errorFilter = new error_1.default(this._settings);
|
||
|
this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions());
|
||
|
this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions());
|
||
|
this.entryTransformer = new entry_2.default(this._settings);
|
||
|
}
|
||
|
_getRootDirectory(task) {
|
||
|
return path.resolve(this._settings.cwd, task.base);
|
||
|
}
|
||
|
_getReaderOptions(task) {
|
||
|
const basePath = task.base === '.' ? '' : task.base;
|
||
|
return {
|
||
|
basePath,
|
||
|
pathSegmentSeparator: '/',
|
||
|
concurrency: this._settings.concurrency,
|
||
|
deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative),
|
||
|
entryFilter: this.entryFilter.getFilter(task.positive, task.negative),
|
||
|
errorFilter: this.errorFilter.getFilter(),
|
||
|
followSymbolicLinks: this._settings.followSymbolicLinks,
|
||
|
fs: this._settings.fs,
|
||
|
stats: this._settings.stats,
|
||
|
throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink,
|
||
|
transform: this.entryTransformer.getTransformer()
|
||
|
};
|
||
|
}
|
||
|
_getMicromatchOptions() {
|
||
|
return {
|
||
|
dot: this._settings.dot,
|
||
|
matchBase: this._settings.baseNameMatch,
|
||
|
nobrace: !this._settings.braceExpansion,
|
||
|
nocase: !this._settings.caseSensitiveMatch,
|
||
|
noext: !this._settings.extglob,
|
||
|
noglobstar: !this._settings.globstar,
|
||
|
posix: true,
|
||
|
strictSlashes: false
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
provider.default = Provider;
|
||
|
|
||
|
Object.defineProperty(async$7, "__esModule", { value: true });
|
||
|
const async_1$1 = async$6;
|
||
|
const provider_1$2 = provider;
|
||
|
class ProviderAsync extends provider_1$2.default {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this._reader = new async_1$1.default(this._settings);
|
||
|
}
|
||
|
async read(task) {
|
||
|
const root = this._getRootDirectory(task);
|
||
|
const options = this._getReaderOptions(task);
|
||
|
const entries = await this.api(root, task, options);
|
||
|
return entries.map((entry) => options.transform(entry));
|
||
|
}
|
||
|
api(root, task, options) {
|
||
|
if (task.dynamic) {
|
||
|
return this._reader.dynamic(root, options);
|
||
|
}
|
||
|
return this._reader.static(task.patterns, options);
|
||
|
}
|
||
|
}
|
||
|
async$7.default = ProviderAsync;
|
||
|
|
||
|
var stream = {};
|
||
|
|
||
|
Object.defineProperty(stream, "__esModule", { value: true });
|
||
|
const stream_1$1 = require$$0$1;
|
||
|
const stream_2 = stream$1;
|
||
|
const provider_1$1 = provider;
|
||
|
class ProviderStream extends provider_1$1.default {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this._reader = new stream_2.default(this._settings);
|
||
|
}
|
||
|
read(task) {
|
||
|
const root = this._getRootDirectory(task);
|
||
|
const options = this._getReaderOptions(task);
|
||
|
const source = this.api(root, task, options);
|
||
|
const destination = new stream_1$1.Readable({ objectMode: true, read: () => { } });
|
||
|
source
|
||
|
.once('error', (error) => destination.emit('error', error))
|
||
|
.on('data', (entry) => destination.emit('data', options.transform(entry)))
|
||
|
.once('end', () => destination.emit('end'));
|
||
|
destination
|
||
|
.once('close', () => source.destroy());
|
||
|
return destination;
|
||
|
}
|
||
|
api(root, task, options) {
|
||
|
if (task.dynamic) {
|
||
|
return this._reader.dynamic(root, options);
|
||
|
}
|
||
|
return this._reader.static(task.patterns, options);
|
||
|
}
|
||
|
}
|
||
|
stream.default = ProviderStream;
|
||
|
|
||
|
var sync$1 = {};
|
||
|
|
||
|
var sync = {};
|
||
|
|
||
|
Object.defineProperty(sync, "__esModule", { value: true });
|
||
|
const fsStat = out$1;
|
||
|
const fsWalk = out$3;
|
||
|
const reader_1 = reader;
|
||
|
class ReaderSync extends reader_1.default {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this._walkSync = fsWalk.walkSync;
|
||
|
this._statSync = fsStat.statSync;
|
||
|
}
|
||
|
dynamic(root, options) {
|
||
|
return this._walkSync(root, options);
|
||
|
}
|
||
|
static(patterns, options) {
|
||
|
const entries = [];
|
||
|
for (const pattern of patterns) {
|
||
|
const filepath = this._getFullEntryPath(pattern);
|
||
|
const entry = this._getEntry(filepath, pattern, options);
|
||
|
if (entry === null || !options.entryFilter(entry)) {
|
||
|
continue;
|
||
|
}
|
||
|
entries.push(entry);
|
||
|
}
|
||
|
return entries;
|
||
|
}
|
||
|
_getEntry(filepath, pattern, options) {
|
||
|
try {
|
||
|
const stats = this._getStat(filepath);
|
||
|
return this._makeEntry(stats, pattern);
|
||
|
}
|
||
|
catch (error) {
|
||
|
if (options.errorFilter(error)) {
|
||
|
return null;
|
||
|
}
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
_getStat(filepath) {
|
||
|
return this._statSync(filepath, this._fsStatSettings);
|
||
|
}
|
||
|
}
|
||
|
sync.default = ReaderSync;
|
||
|
|
||
|
Object.defineProperty(sync$1, "__esModule", { value: true });
|
||
|
const sync_1$1 = sync;
|
||
|
const provider_1 = provider;
|
||
|
class ProviderSync extends provider_1.default {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this._reader = new sync_1$1.default(this._settings);
|
||
|
}
|
||
|
read(task) {
|
||
|
const root = this._getRootDirectory(task);
|
||
|
const options = this._getReaderOptions(task);
|
||
|
const entries = this.api(root, task, options);
|
||
|
return entries.map(options.transform);
|
||
|
}
|
||
|
api(root, task, options) {
|
||
|
if (task.dynamic) {
|
||
|
return this._reader.dynamic(root, options);
|
||
|
}
|
||
|
return this._reader.static(task.patterns, options);
|
||
|
}
|
||
|
}
|
||
|
sync$1.default = ProviderSync;
|
||
|
|
||
|
var settings = {};
|
||
|
|
||
|
(function (exports) {
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0;
|
||
|
const fs = require$$0$2;
|
||
|
const os = require$$0;
|
||
|
/**
|
||
|
* The `os.cpus` method can return zero. We expect the number of cores to be greater than zero.
|
||
|
* https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107
|
||
|
*/
|
||
|
const CPU_COUNT = Math.max(os.cpus().length, 1);
|
||
|
exports.DEFAULT_FILE_SYSTEM_ADAPTER = {
|
||
|
lstat: fs.lstat,
|
||
|
lstatSync: fs.lstatSync,
|
||
|
stat: fs.stat,
|
||
|
statSync: fs.statSync,
|
||
|
readdir: fs.readdir,
|
||
|
readdirSync: fs.readdirSync
|
||
|
};
|
||
|
class Settings {
|
||
|
constructor(_options = {}) {
|
||
|
this._options = _options;
|
||
|
this.absolute = this._getValue(this._options.absolute, false);
|
||
|
this.baseNameMatch = this._getValue(this._options.baseNameMatch, false);
|
||
|
this.braceExpansion = this._getValue(this._options.braceExpansion, true);
|
||
|
this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true);
|
||
|
this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT);
|
||
|
this.cwd = this._getValue(this._options.cwd, process.cwd());
|
||
|
this.deep = this._getValue(this._options.deep, Infinity);
|
||
|
this.dot = this._getValue(this._options.dot, false);
|
||
|
this.extglob = this._getValue(this._options.extglob, true);
|
||
|
this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true);
|
||
|
this.fs = this._getFileSystemMethods(this._options.fs);
|
||
|
this.globstar = this._getValue(this._options.globstar, true);
|
||
|
this.ignore = this._getValue(this._options.ignore, []);
|
||
|
this.markDirectories = this._getValue(this._options.markDirectories, false);
|
||
|
this.objectMode = this._getValue(this._options.objectMode, false);
|
||
|
this.onlyDirectories = this._getValue(this._options.onlyDirectories, false);
|
||
|
this.onlyFiles = this._getValue(this._options.onlyFiles, true);
|
||
|
this.stats = this._getValue(this._options.stats, false);
|
||
|
this.suppressErrors = this._getValue(this._options.suppressErrors, false);
|
||
|
this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false);
|
||
|
this.unique = this._getValue(this._options.unique, true);
|
||
|
if (this.onlyDirectories) {
|
||
|
this.onlyFiles = false;
|
||
|
}
|
||
|
if (this.stats) {
|
||
|
this.objectMode = true;
|
||
|
}
|
||
|
}
|
||
|
_getValue(option, value) {
|
||
|
return option === undefined ? value : option;
|
||
|
}
|
||
|
_getFileSystemMethods(methods = {}) {
|
||
|
return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods);
|
||
|
}
|
||
|
}
|
||
|
exports.default = Settings;
|
||
|
} (settings));
|
||
|
|
||
|
const taskManager = tasks;
|
||
|
const patternManager = patterns;
|
||
|
const async_1 = async$7;
|
||
|
const stream_1 = stream;
|
||
|
const sync_1 = sync$1;
|
||
|
const settings_1 = settings;
|
||
|
const utils = utils$k;
|
||
|
async function FastGlob(source, options) {
|
||
|
assertPatternsInput(source);
|
||
|
const works = getWorks(source, async_1.default, options);
|
||
|
const result = await Promise.all(works);
|
||
|
return utils.array.flatten(result);
|
||
|
}
|
||
|
// https://github.com/typescript-eslint/typescript-eslint/issues/60
|
||
|
// eslint-disable-next-line no-redeclare
|
||
|
(function (FastGlob) {
|
||
|
function sync(source, options) {
|
||
|
assertPatternsInput(source);
|
||
|
const works = getWorks(source, sync_1.default, options);
|
||
|
return utils.array.flatten(works);
|
||
|
}
|
||
|
FastGlob.sync = sync;
|
||
|
function stream(source, options) {
|
||
|
assertPatternsInput(source);
|
||
|
const works = getWorks(source, stream_1.default, options);
|
||
|
/**
|
||
|
* The stream returned by the provider cannot work with an asynchronous iterator.
|
||
|
* To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams.
|
||
|
* This affects performance (+25%). I don't see best solution right now.
|
||
|
*/
|
||
|
return utils.stream.merge(works);
|
||
|
}
|
||
|
FastGlob.stream = stream;
|
||
|
function generateTasks(source, options) {
|
||
|
assertPatternsInput(source);
|
||
|
const patterns = patternManager.transform([].concat(source));
|
||
|
const settings = new settings_1.default(options);
|
||
|
return taskManager.generate(patterns, settings);
|
||
|
}
|
||
|
FastGlob.generateTasks = generateTasks;
|
||
|
function isDynamicPattern(source, options) {
|
||
|
assertPatternsInput(source);
|
||
|
const settings = new settings_1.default(options);
|
||
|
return utils.pattern.isDynamicPattern(source, settings);
|
||
|
}
|
||
|
FastGlob.isDynamicPattern = isDynamicPattern;
|
||
|
function escapePath(source) {
|
||
|
assertPatternsInput(source);
|
||
|
return utils.path.escape(source);
|
||
|
}
|
||
|
FastGlob.escapePath = escapePath;
|
||
|
})(FastGlob || (FastGlob = {}));
|
||
|
function getWorks(source, _Provider, options) {
|
||
|
const patterns = patternManager.transform([].concat(source));
|
||
|
const settings = new settings_1.default(options);
|
||
|
const tasks = taskManager.generate(patterns, settings);
|
||
|
const provider = new _Provider(settings);
|
||
|
return tasks.map(provider.read, provider);
|
||
|
}
|
||
|
function assertPatternsInput(input) {
|
||
|
const source = [].concat(input);
|
||
|
const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item));
|
||
|
if (!isValidSource) {
|
||
|
throw new TypeError('Patterns must be a string (non empty) or an array of strings');
|
||
|
}
|
||
|
}
|
||
|
var out = FastGlob;
|
||
|
|
||
|
class SnapshotManager {
|
||
|
constructor(options) {
|
||
|
this.options = options;
|
||
|
this.summary = void 0;
|
||
|
this.extension = ".snap";
|
||
|
this.clear();
|
||
|
}
|
||
|
clear() {
|
||
|
this.summary = emptySummary(this.options);
|
||
|
}
|
||
|
add(result) {
|
||
|
addSnapshotResult(this.summary, result);
|
||
|
}
|
||
|
resolvePath(testPath) {
|
||
|
const resolver = this.options.resolveSnapshotPath || (() => {
|
||
|
return join(
|
||
|
join(
|
||
|
dirname(testPath),
|
||
|
"__snapshots__"
|
||
|
),
|
||
|
`${basename(testPath)}${this.extension}`
|
||
|
);
|
||
|
});
|
||
|
return resolver(testPath, this.extension);
|
||
|
}
|
||
|
}
|
||
|
function emptySummary(options) {
|
||
|
const summary = {
|
||
|
added: 0,
|
||
|
failure: false,
|
||
|
filesAdded: 0,
|
||
|
filesRemoved: 0,
|
||
|
filesRemovedList: [],
|
||
|
filesUnmatched: 0,
|
||
|
filesUpdated: 0,
|
||
|
matched: 0,
|
||
|
total: 0,
|
||
|
unchecked: 0,
|
||
|
uncheckedKeysByFile: [],
|
||
|
unmatched: 0,
|
||
|
updated: 0,
|
||
|
didUpdate: options.updateSnapshot === "all"
|
||
|
};
|
||
|
return summary;
|
||
|
}
|
||
|
function addSnapshotResult(summary, result) {
|
||
|
if (result.added)
|
||
|
summary.filesAdded++;
|
||
|
if (result.fileDeleted)
|
||
|
summary.filesRemoved++;
|
||
|
if (result.unmatched)
|
||
|
summary.filesUnmatched++;
|
||
|
if (result.updated)
|
||
|
summary.filesUpdated++;
|
||
|
summary.added += result.added;
|
||
|
summary.matched += result.matched;
|
||
|
summary.unchecked += result.unchecked;
|
||
|
if (result.uncheckedKeys && result.uncheckedKeys.length > 0) {
|
||
|
summary.uncheckedKeysByFile.push({
|
||
|
filePath: result.filepath,
|
||
|
keys: result.uncheckedKeys
|
||
|
});
|
||
|
}
|
||
|
summary.unmatched += result.unmatched;
|
||
|
summary.updated += result.updated;
|
||
|
summary.total += result.added + result.matched + result.unmatched + result.updated;
|
||
|
}
|
||
|
|
||
|
function T(r){const t=/^\\\\\?\\/.test(r),n=/[^\u0000-\u0080]+/.test(r);return t||n?r:r.replace(/\\/g,"/")}function B(r,t){for(;;){const n=g.join(r,t);if(require$$0$2.existsSync(n))return T(n);const e=g.dirname(r);if(e===r)return;r=e;}}function G(r,t){t===void 0&&(t=!1);var n=r.length,e=0,i="",a=0,o=16,u=0,f=0,d=0,C=0,c=0;function y(s,m){for(var v=0,b=0;v<s||!m;){var h=r.charCodeAt(e);if(h>=48&&h<=57)b=b*16+h-48;else if(h>=65&&h<=70)b=b*16+h-65+10;else if(h>=97&&h<=102)b=b*16+h-97+10;else break;e++,v++;}return v<s&&(b=-1),b}function V(s){e=s,i="",a=0,o=16,c=0;}function $(){var s=e;if(r.charCodeAt(e)===48)e++;else for(e++;e<r.length&&j(r.charCodeAt(e));)e++;if(e<r.length&&r.charCodeAt(e)===46)if(e++,e<r.length&&j(r.charCodeAt(e)))for(e++;e<r.length&&j(r.charCodeAt(e));)e++;else return c=3,r.substring(s,e);var m=e;if(e<r.length&&(r.charCodeAt(e)===69||r.charCodeAt(e)===101))if(e++,(e<r.length&&r.charCodeAt(e)===43||r.charCodeAt(e)===45)&&e++,e<r.length&&j(r.charCodeAt(e))){for(e++;e<r.length&&j(r.charCodeAt(e));)e++;m=e;}else c=3;return r.substring(s,m)}function _(){for(var s="",m=e;;){if(e>=n){s+=r.substring(m,e),c=2;break}var v=r.charCodeAt(e);if(v===34){s+=r.substring(m,e),e++;break}if(v===92){if(s+=r.substring(m,e),e++,e>=n){c=2;break}var b=r.charCodeAt(e++);switch(b){case 34:s+='"';break;case 92:s+="\\";break;case 47:s+="/";break;case 98:s+="\b";break;case 102:s+="\f";break;case 110:s+=`
|
||
|
`;break;case 114:s+="\r";break;case 116:s+=" ";break;case 117:var h=y(4,!0);h>=0?s+=String.fromCharCode(h):c=4;break;default:c=5;}m=e;continue}if(v>=0&&v<=31)if(P(v)){s+=r.substring(m,e),c=2;break}else c=6;e++;}return s}function S(){if(i="",c=0,a=e,f=u,C=d,e>=n)return a=n,o=17;var s=r.charCodeAt(e);if(U(s)){do e++,i+=String.fromCharCode(s),s=r.charCodeAt(e);while(U(s));return o=15}if(P(s))return e++,i+=String.fromCharCode(s),s===13&&r.charCodeAt(e)===10&&(e++,i+=`
|
||
|
`),u++,d=e,o=14;switch(s){case 123:return e++,o=1;case 125:return e++,o=2;case 91:return e++,o=3;case 93:return e++,o=4;case 58:return e++,o=6;case 44:return e++,o=5;case 34:return e++,i=_(),o=10;case 47:var m=e-1;if(r.charCodeAt(e+1)===47){for(e+=2;e<n&&!P(r.charCodeAt(e));)e++;return i=r.substring(m,e),o=12}if(r.charCodeAt(e+1)===42){e+=2;for(var v=n-1,b=!1;e<v;){var h=r.charCodeAt(e);if(h===42&&r.charCodeAt(e+1)===47){e+=2,b=!0;break}e++,P(h)&&(h===13&&r.charCodeAt(e)===10&&e++,u++,d=e);}return b||(e++,c=1),i=r.substring(m,e),o=13}return i+=String.fromCharCode(s),e++,o=16;case 45:if(i+=String.fromCharCode(s),e++,e===n||!j(r.charCodeAt(e)))return o=16;case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return i+=$(),o=11;default:for(;e<n&&k(s);)e++,s=r.charCodeAt(e);if(a!==e){switch(i=r.substring(a,e),i){case"true":return o=8;case"false":return o=9;case"null":return o=7}return o=16}return i+=String.fromCharCode(s),e++,o=16}}function k(s){if(U(s)||P(s))return !1;switch(s){case 125:case 93:case 123:case 91:case 34:case 58:case 44:case 47:return !1}return !0}function l(){var s;do s=S();while(s>=12&&s<=15);return s}return {setPosition:V,getPosition:function(){return e},scan:t?l:S,getToken:function(){return o},getTokenValue:function(){return i},getTokenOffset:function(){return a},getTokenLength:function(){return e-a},getTokenStartLine:function(){return f},getTokenStartCharacter:function(){return a-C},getTokenError:function(){return c}}}function U(r){return r===32||r===9||r===11||r===12||r===160||r===5760||r>=8192&&r<=8203||r===8239||r===8287||r===12288||r===65279}function P(r){return r===10||r===13||r===8232||r===8233}function j(r){return r>=48&&r<=57}var N;(function(r){r.DEFAULT={allowTrailingComma:!1};})(N||(N={}));function K(r,t,n){t===void 0&&(t=[]),n===void 0&&(n=N.DEFAULT);var e=null,i=[],a=[];function o(f){Array.isArray(i)?i.push(f):e!==null&&(i[e]=f);}var u={onObjectBegin:function(){var f={};o(f),a.push(i),i=f,e=null;},onObjectProperty:function(f){e=f;},onObjectEnd:function(){i=a.pop();},onArrayBegin:function(){var f=[];o(f),a.push(i),i=f,e=null;},onArrayEnd:function(){i=a.pop();},onLiteralValue:o,onError:function(f,d,C){t.push({error:f,offset:d,length:C});}};return Q(r,u,n),i[0]}function Q(r,t,n){n===void 0&&(n=N.DEFAULT);var e=G(r,!1);function i(p){return p?function(){return p(e.getTokenOffset(),e.getTokenLength(),e.getTokenStartLine(),e.getTokenStartCharacter())}:function(){return !0}}function a(p){return p?function(w){return p(w,e.getTokenOffset(),e.getTokenLength(),e.getTokenStartLine(),e.getTokenStartCharacter())}:function(){return !0}}var o=i(t.onObjectBegin),u=a(t.onObjectProperty),f=i(t.onObjectEnd),d=i(t.onArrayBegin),C=i(t.onArrayEnd),c=a(t.onLiteralValue),y=a(t.onSeparator),V=i(t.onComment),$=a(t.onError),_=n&&n.disallowComments,S=n&&n.allowTrailingComma;function k(){for(;;){var p=e.scan();switch(e.getTokenError()){case 4:l(14);break;case 5:l(15);break;case 3:l(13);break;case 1:_||l(11);break;case 2:l(12);break;case 6:l(16);break}switch(p){case 12:case 13:_?l(10):V();break;case 16:l(1);break;case 15:case 14:break;default:return p}}}function l(p,w,L){if(w===void 0&&(w=[]),L===void 0&&(L=[]),$(p),w.length+L.length>0)for(var D=e.getToken();D!==17;){if(w.indexOf(D)!==-1){k();break}else if(L.indexOf(D)!==-1)break;D=k();}}function s(p){var w=e.getTokenValue();return p?c(w):u(w),k(),!0}function m(){switch(e.getToken()){case 11:var p=e.getTokenValue(),w=Number(p);isNaN(w)&&(l(2),w=0),c(w);break;case 7:c(null);break;case 8:c(!0);break;case 9:c(!1);break;default:return !1}return k(),!0}function v(){return e.getToken()!==10?(l(3,[],[2,5]),!1):(s(!1),e.getToken()===6?(y(":"),k(),F()||l(4,[],[2,5])):l(5,[],[2,5]),!0)}function b(){o(),k();for(var p=!1;e.getToken()!==2&&e.getToken()!==17;){if(e.getToken()===5){if(p||l(4,[],[]),y(","),k(),e.getToken()===2&&S)break}else p&&l(6,[],[]);v()||l(4,[],[2,5]),p=!0;}return f(),e.getToken()!==2?l(7,[2],[]):k(),!0}function h(){d(),k();for(var p=!1;e.getToken()!==4&&e.getToken()!==17;){if(e.getToken()===5){if(p||l(4,[],[]),y(","),k(),e.getToken()===4&&S
|
||
|
|
||
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
||
|
const newLineRegExp = /\r?\n/;
|
||
|
const errCodeRegExp = /error TS(?<errCode>\d+)/;
|
||
|
async function makeTscErrorInfo(errInfo) {
|
||
|
var _a;
|
||
|
const [errFilePathPos = "", ...errMsgRawArr] = errInfo.split(":");
|
||
|
if (!errFilePathPos || errMsgRawArr.length === 0 || errMsgRawArr.join("").length === 0)
|
||
|
return ["unknown filepath", null];
|
||
|
const errMsgRaw = errMsgRawArr.join("").trim();
|
||
|
const [errFilePath, errPos] = errFilePathPos.slice(0, -1).split("(");
|
||
|
if (!errFilePath || !errPos)
|
||
|
return ["unknown filepath", null];
|
||
|
const [errLine, errCol] = errPos.split(",");
|
||
|
if (!errLine || !errCol)
|
||
|
return [errFilePath, null];
|
||
|
const execArr = errCodeRegExp.exec(errMsgRaw);
|
||
|
if (!execArr)
|
||
|
return [errFilePath, null];
|
||
|
const errCodeStr = ((_a = execArr.groups) == null ? void 0 : _a.errCode) ?? "";
|
||
|
if (!errCodeStr)
|
||
|
return [errFilePath, null];
|
||
|
const line = Number(errLine);
|
||
|
const col = Number(errCol);
|
||
|
const errCode = Number(errCodeStr);
|
||
|
return [
|
||
|
errFilePath,
|
||
|
{
|
||
|
filePath: errFilePath,
|
||
|
errCode,
|
||
|
line,
|
||
|
column: col,
|
||
|
errMsg: errMsgRaw.slice(`error TS${errCode} `.length)
|
||
|
}
|
||
|
];
|
||
|
}
|
||
|
async function getTsconfig(root, config) {
|
||
|
var _a;
|
||
|
const tempConfigPath = join(root, "tsconfig.temp.json");
|
||
|
const configName = ((_a = config.tsconfig) == null ? void 0 : _a.includes("jsconfig.json")) ? "jsconfig.json" : void 0;
|
||
|
const tsconfig = ie(config.tsconfig || root, configName);
|
||
|
if (!tsconfig)
|
||
|
throw new Error("no tsconfig.json found");
|
||
|
try {
|
||
|
const tmpTsConfig = { ...tsconfig.config };
|
||
|
tmpTsConfig.compilerOptions = tmpTsConfig.compilerOptions || {};
|
||
|
tmpTsConfig.compilerOptions.emitDeclarationOnly = false;
|
||
|
tmpTsConfig.compilerOptions.incremental = true;
|
||
|
tmpTsConfig.compilerOptions.tsBuildInfoFile = join(
|
||
|
__dirname,
|
||
|
"tsconfig.tmp.tsbuildinfo"
|
||
|
);
|
||
|
const tsconfigFinalContent = JSON.stringify(tmpTsConfig, null, 2);
|
||
|
await writeFile(tempConfigPath, tsconfigFinalContent);
|
||
|
return { path: tempConfigPath, config: tmpTsConfig };
|
||
|
} catch (err) {
|
||
|
throw new Error("failed to write tsconfig.temp.json", { cause: err });
|
||
|
}
|
||
|
}
|
||
|
async function getRawErrsMapFromTsCompile(tscErrorStdout) {
|
||
|
const rawErrsMap = /* @__PURE__ */ new Map();
|
||
|
const infos = await Promise.all(
|
||
|
tscErrorStdout.split(newLineRegExp).reduce((prev, next) => {
|
||
|
if (!next)
|
||
|
return prev;
|
||
|
else if (!next.startsWith(" "))
|
||
|
prev.push(next);
|
||
|
else
|
||
|
prev[prev.length - 1] += `
|
||
|
${next}`;
|
||
|
return prev;
|
||
|
}, []).map((errInfoLine) => makeTscErrorInfo(errInfoLine))
|
||
|
);
|
||
|
infos.forEach(([errFilePath, errInfo]) => {
|
||
|
var _a;
|
||
|
if (!errInfo)
|
||
|
return;
|
||
|
if (!rawErrsMap.has(errFilePath))
|
||
|
rawErrsMap.set(errFilePath, [errInfo]);
|
||
|
else
|
||
|
(_a = rawErrsMap.get(errFilePath)) == null ? void 0 : _a.push(errInfo);
|
||
|
});
|
||
|
return rawErrsMap;
|
||
|
}
|
||
|
|
||
|
const createIndexMap = (source) => {
|
||
|
const map = /* @__PURE__ */ new Map();
|
||
|
let index = 0;
|
||
|
let line = 1;
|
||
|
let column = 1;
|
||
|
for (const char of source) {
|
||
|
map.set(`${line}:${column}`, index++);
|
||
|
if (char === "\n" || char === "\r\n") {
|
||
|
line++;
|
||
|
column = 0;
|
||
|
} else {
|
||
|
column++;
|
||
|
}
|
||
|
}
|
||
|
return map;
|
||
|
};
|
||
|
|
||
|
async function collectTests(ctx, filepath) {
|
||
|
const request = await ctx.vitenode.transformRequest(filepath);
|
||
|
if (!request)
|
||
|
return null;
|
||
|
const ast = parse$4(request.code, {
|
||
|
ecmaVersion: "latest",
|
||
|
allowAwaitOutsideFunction: true
|
||
|
});
|
||
|
const testFilepath = relative(ctx.config.root, filepath);
|
||
|
const file = {
|
||
|
filepath,
|
||
|
type: "suite",
|
||
|
id: generateHash(testFilepath),
|
||
|
name: testFilepath,
|
||
|
mode: "run",
|
||
|
tasks: [],
|
||
|
start: ast.start,
|
||
|
end: ast.end
|
||
|
};
|
||
|
const definitions = [];
|
||
|
const getName = (callee) => {
|
||
|
var _a, _b, _c;
|
||
|
if (!callee)
|
||
|
return null;
|
||
|
if (callee.type === "Identifier")
|
||
|
return callee.name;
|
||
|
if (callee.type === "MemberExpression") {
|
||
|
if ((_b = (_a = callee.object) == null ? void 0 : _a.name) == null ? void 0 : _b.startsWith("__vite_ssr_"))
|
||
|
return getName(callee.property);
|
||
|
return getName((_c = callee.object) == null ? void 0 : _c.property);
|
||
|
}
|
||
|
return null;
|
||
|
};
|
||
|
ancestor(ast, {
|
||
|
CallExpression(node) {
|
||
|
var _a;
|
||
|
const { callee } = node;
|
||
|
const name = getName(callee);
|
||
|
if (!name)
|
||
|
return;
|
||
|
if (!["it", "test", "describe", "suite"].includes(name))
|
||
|
return;
|
||
|
const { arguments: [{ value: message }] } = node;
|
||
|
const property = (_a = callee == null ? void 0 : callee.property) == null ? void 0 : _a.name;
|
||
|
let mode = !property || property === name ? "run" : property;
|
||
|
if (!["run", "skip", "todo", "only", "skipIf", "runIf"].includes(mode))
|
||
|
throw new Error(`${name}.${mode} syntax is not supported when testing types`);
|
||
|
if (mode === "skipIf" || mode === "runIf")
|
||
|
mode = "skip";
|
||
|
definitions.push({
|
||
|
start: node.start,
|
||
|
end: node.end,
|
||
|
name: message,
|
||
|
type: name === "it" || name === "test" ? "test" : "suite",
|
||
|
mode
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
let lastSuite = file;
|
||
|
const updateLatestSuite = (index) => {
|
||
|
const suite = lastSuite;
|
||
|
while (lastSuite !== file && lastSuite.end < index)
|
||
|
lastSuite = suite.suite;
|
||
|
return lastSuite;
|
||
|
};
|
||
|
definitions.sort((a, b) => a.start - b.start).forEach((definition) => {
|
||
|
const latestSuite = updateLatestSuite(definition.start);
|
||
|
let mode = definition.mode;
|
||
|
if (latestSuite.mode !== "run")
|
||
|
mode = latestSuite.mode;
|
||
|
if (definition.type === "suite") {
|
||
|
const task2 = {
|
||
|
type: definition.type,
|
||
|
id: "",
|
||
|
suite: latestSuite,
|
||
|
file,
|
||
|
tasks: [],
|
||
|
mode,
|
||
|
name: definition.name,
|
||
|
end: definition.end,
|
||
|
start: definition.start,
|
||
|
meta: {
|
||
|
typecheck: true
|
||
|
}
|
||
|
};
|
||
|
definition.task = task2;
|
||
|
latestSuite.tasks.push(task2);
|
||
|
lastSuite = task2;
|
||
|
return;
|
||
|
}
|
||
|
const task = {
|
||
|
type: definition.type,
|
||
|
id: "",
|
||
|
suite: latestSuite,
|
||
|
file,
|
||
|
mode,
|
||
|
context: {},
|
||
|
name: definition.name,
|
||
|
end: definition.end,
|
||
|
start: definition.start,
|
||
|
meta: {
|
||
|
typecheck: true
|
||
|
}
|
||
|
};
|
||
|
definition.task = task;
|
||
|
latestSuite.tasks.push(task);
|
||
|
});
|
||
|
calculateSuiteHash(file);
|
||
|
const hasOnly = someTasksAreOnly(file);
|
||
|
interpretTaskModes(file, ctx.config.testNamePattern, hasOnly, false, ctx.config.allowOnly);
|
||
|
return {
|
||
|
file,
|
||
|
parsed: request.code,
|
||
|
filepath,
|
||
|
map: request.map,
|
||
|
definitions
|
||
|
};
|
||
|
}
|
||
|
|
||
|
class TypeCheckError extends Error {
|
||
|
constructor(message, stacks) {
|
||
|
super(message);
|
||
|
this.message = message;
|
||
|
this.stacks = stacks;
|
||
|
this.name = "TypeCheckError";
|
||
|
}
|
||
|
}
|
||
|
class Typechecker {
|
||
|
constructor(ctx, files) {
|
||
|
this.ctx = ctx;
|
||
|
this.files = files;
|
||
|
this._result = {
|
||
|
files: [],
|
||
|
sourceErrors: []
|
||
|
};
|
||
|
this._tests = {};
|
||
|
}
|
||
|
onParseStart(fn) {
|
||
|
this._onParseStart = fn;
|
||
|
}
|
||
|
onParseEnd(fn) {
|
||
|
this._onParseEnd = fn;
|
||
|
}
|
||
|
onWatcherRerun(fn) {
|
||
|
this._onWatcherRerun = fn;
|
||
|
}
|
||
|
async collectFileTests(filepath) {
|
||
|
return collectTests(this.ctx, filepath);
|
||
|
}
|
||
|
getFiles() {
|
||
|
return this.files.filter((filename) => {
|
||
|
const extension = extname(filename);
|
||
|
return extension !== ".js" || this.allowJs;
|
||
|
});
|
||
|
}
|
||
|
async collectTests() {
|
||
|
const tests = (await Promise.all(
|
||
|
this.getFiles().map((filepath) => this.collectFileTests(filepath))
|
||
|
)).reduce((acc, data) => {
|
||
|
if (!data)
|
||
|
return acc;
|
||
|
acc[data.filepath] = data;
|
||
|
return acc;
|
||
|
}, {});
|
||
|
this._tests = tests;
|
||
|
return tests;
|
||
|
}
|
||
|
async prepareResults(output) {
|
||
|
const typeErrors = await this.parseTscLikeOutput(output);
|
||
|
const testFiles = new Set(this.getFiles());
|
||
|
if (!this._tests)
|
||
|
this._tests = await this.collectTests();
|
||
|
const sourceErrors = [];
|
||
|
const files = [];
|
||
|
testFiles.forEach((path) => {
|
||
|
const { file, definitions, map, parsed } = this._tests[path];
|
||
|
const errors = typeErrors.get(path);
|
||
|
files.push(file);
|
||
|
if (!errors)
|
||
|
return;
|
||
|
const sortedDefinitions = [...definitions.sort((a, b) => b.start - a.start)];
|
||
|
const mapConsumer = map && new SourceMapConsumer(map);
|
||
|
const indexMap = createIndexMap(parsed);
|
||
|
const markFailed = (task) => {
|
||
|
task.result = {
|
||
|
state: task.mode === "run" || task.mode === "only" ? "fail" : task.mode
|
||
|
};
|
||
|
if (task.suite)
|
||
|
markFailed(task.suite);
|
||
|
};
|
||
|
errors.forEach(({ error, originalError }) => {
|
||
|
var _a;
|
||
|
const originalPos = (mapConsumer == null ? void 0 : mapConsumer.generatedPositionFor({
|
||
|
line: originalError.line,
|
||
|
column: originalError.column,
|
||
|
source: path
|
||
|
})) || originalError;
|
||
|
const index = indexMap.get(`${originalPos.line}:${originalPos.column}`);
|
||
|
const definition = index != null && sortedDefinitions.find((def) => def.start <= index && def.end >= index);
|
||
|
const suite = definition ? definition.task : file;
|
||
|
const state = suite.mode === "run" || suite.mode === "only" ? "fail" : suite.mode;
|
||
|
const errors2 = ((_a = suite.result) == null ? void 0 : _a.errors) || [];
|
||
|
suite.result = {
|
||
|
state,
|
||
|
errors: errors2
|
||
|
};
|
||
|
errors2.push(error);
|
||
|
if (state === "fail" && suite.suite)
|
||
|
markFailed(suite.suite);
|
||
|
});
|
||
|
});
|
||
|
typeErrors.forEach((errors, path) => {
|
||
|
if (!testFiles.has(path))
|
||
|
sourceErrors.push(...errors.map(({ error }) => error));
|
||
|
});
|
||
|
return {
|
||
|
files,
|
||
|
sourceErrors
|
||
|
};
|
||
|
}
|
||
|
async parseTscLikeOutput(output) {
|
||
|
const errorsMap = await getRawErrsMapFromTsCompile(output);
|
||
|
const typesErrors = /* @__PURE__ */ new Map();
|
||
|
errorsMap.forEach((errors, path) => {
|
||
|
const filepath = resolve(this.ctx.config.root, path);
|
||
|
const suiteErrors = errors.map((info) => {
|
||
|
const limit = Error.stackTraceLimit;
|
||
|
Error.stackTraceLimit = 0;
|
||
|
const error = new TypeCheckError(info.errMsg, [
|
||
|
{
|
||
|
file: filepath,
|
||
|
line: info.line,
|
||
|
column: info.column,
|
||
|
method: ""
|
||
|
}
|
||
|
]);
|
||
|
Error.stackTraceLimit = limit;
|
||
|
return {
|
||
|
originalError: info,
|
||
|
error
|
||
|
};
|
||
|
});
|
||
|
typesErrors.set(filepath, suiteErrors);
|
||
|
});
|
||
|
return typesErrors;
|
||
|
}
|
||
|
async clear() {
|
||
|
if (this.tempConfigPath)
|
||
|
await rm(this.tempConfigPath, { force: true });
|
||
|
}
|
||
|
async stop() {
|
||
|
var _a;
|
||
|
await this.clear();
|
||
|
(_a = this.process) == null ? void 0 : _a.kill();
|
||
|
}
|
||
|
async ensurePackageInstalled(root, checker) {
|
||
|
if (checker !== "tsc" && checker !== "vue-tsc")
|
||
|
return;
|
||
|
const packageName = checker === "tsc" ? "typescript" : "vue-tsc";
|
||
|
await ensurePackageInstalled(packageName, root);
|
||
|
}
|
||
|
async prepare() {
|
||
|
const { root, typecheck } = this.ctx.config;
|
||
|
await this.ensurePackageInstalled(root, typecheck.checker);
|
||
|
const { config, path } = await getTsconfig(root, typecheck);
|
||
|
this.tempConfigPath = path;
|
||
|
this.allowJs = typecheck.allowJs || config.allowJs || false;
|
||
|
}
|
||
|
async start() {
|
||
|
var _a, _b, _c;
|
||
|
if (!this.tempConfigPath)
|
||
|
throw new Error("tsconfig was not initialized");
|
||
|
const { root, watch, typecheck } = this.ctx.config;
|
||
|
const args = ["--noEmit", "--pretty", "false", "-p", this.tempConfigPath];
|
||
|
if (watch)
|
||
|
args.push("--watch");
|
||
|
if (typecheck.allowJs)
|
||
|
args.push("--allowJs", "--checkJs");
|
||
|
let output = "";
|
||
|
const child = execa(typecheck.checker, args, {
|
||
|
cwd: root,
|
||
|
stdout: "pipe",
|
||
|
reject: false
|
||
|
});
|
||
|
this.process = child;
|
||
|
await ((_a = this._onParseStart) == null ? void 0 : _a.call(this));
|
||
|
let rerunTriggered = false;
|
||
|
(_b = child.stdout) == null ? void 0 : _b.on("data", (chunk) => {
|
||
|
var _a2;
|
||
|
output += chunk;
|
||
|
if (!watch)
|
||
|
return;
|
||
|
if (output.includes("File change detected") && !rerunTriggered) {
|
||
|
(_a2 = this._onWatcherRerun) == null ? void 0 : _a2.call(this);
|
||
|
this._result.sourceErrors = [];
|
||
|
this._result.files = [];
|
||
|
this._tests = null;
|
||
|
rerunTriggered = true;
|
||
|
}
|
||
|
if (/Found \w+ errors*. Watching for/.test(output)) {
|
||
|
rerunTriggered = false;
|
||
|
this.prepareResults(output).then((result) => {
|
||
|
var _a3;
|
||
|
this._result = result;
|
||
|
(_a3 = this._onParseEnd) == null ? void 0 : _a3.call(this, result);
|
||
|
});
|
||
|
output = "";
|
||
|
}
|
||
|
});
|
||
|
if (!watch) {
|
||
|
await child;
|
||
|
this._result = await this.prepareResults(output);
|
||
|
await ((_c = this._onParseEnd) == null ? void 0 : _c.call(this, this._result));
|
||
|
}
|
||
|
}
|
||
|
getResult() {
|
||
|
return this._result;
|
||
|
}
|
||
|
getTestFiles() {
|
||
|
return Object.values(this._tests || {}).map((i) => i.file);
|
||
|
}
|
||
|
getTestPacks() {
|
||
|
return Object.values(this._tests || {}).map((i) => [i.file.id, void 0]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const workerPath = url.pathToFileURL(resolve(distDir, "./worker.js")).href;
|
||
|
const loaderPath = url.pathToFileURL(resolve(distDir, "./loader.js")).href;
|
||
|
const suppressLoaderWarningsPath = resolve(rootDir, "./suppress-warnings.cjs");
|
||
|
function createPool(ctx) {
|
||
|
var _a, _b, _c;
|
||
|
const threadsCount = ctx.config.watch ? Math.max(Math.floor(cpus().length / 2), 1) : Math.max(cpus().length - 1, 1);
|
||
|
const maxThreads = ctx.config.maxThreads ?? threadsCount;
|
||
|
const minThreads = ctx.config.minThreads ?? threadsCount;
|
||
|
const conditions = ((_a = ctx.server.config.resolve.conditions) == null ? void 0 : _a.flatMap((c) => ["--conditions", c])) || [];
|
||
|
const options = {
|
||
|
filename: workerPath,
|
||
|
useAtomics: false,
|
||
|
maxThreads,
|
||
|
minThreads,
|
||
|
execArgv: ctx.config.deps.registerNodeLoader ? [
|
||
|
"--require",
|
||
|
suppressLoaderWarningsPath,
|
||
|
"--experimental-loader",
|
||
|
loaderPath,
|
||
|
...conditions
|
||
|
] : conditions
|
||
|
};
|
||
|
if (ctx.config.isolate) {
|
||
|
options.isolateWorkers = true;
|
||
|
options.concurrentTasksPerWorker = 1;
|
||
|
}
|
||
|
if (!ctx.config.threads) {
|
||
|
options.concurrentTasksPerWorker = 1;
|
||
|
options.maxThreads = 1;
|
||
|
options.minThreads = 1;
|
||
|
}
|
||
|
(_c = (_b = ctx.coverageProvider) == null ? void 0 : _b.onBeforeFilesRun) == null ? void 0 : _c.call(_b);
|
||
|
options.env = {
|
||
|
TEST: "true",
|
||
|
VITEST: "true",
|
||
|
NODE_ENV: ctx.config.mode || "test",
|
||
|
VITEST_MODE: ctx.config.watch ? "WATCH" : "RUN",
|
||
|
...process.env,
|
||
|
...ctx.config.env
|
||
|
};
|
||
|
const pool = new Tinypool(options);
|
||
|
const runWithFiles = (name) => {
|
||
|
let id = 0;
|
||
|
async function runFiles(config, files, invalidates = []) {
|
||
|
ctx.state.clearFiles(files);
|
||
|
const { workerPort, port } = createChannel(ctx);
|
||
|
const workerId = ++id;
|
||
|
const data = {
|
||
|
port: workerPort,
|
||
|
config,
|
||
|
files,
|
||
|
invalidates,
|
||
|
workerId
|
||
|
};
|
||
|
try {
|
||
|
await pool.run(data, { transferList: [workerPort], name });
|
||
|
} finally {
|
||
|
port.close();
|
||
|
workerPort.close();
|
||
|
}
|
||
|
}
|
||
|
const Sequencer = ctx.config.sequence.sequencer;
|
||
|
const sequencer = new Sequencer(ctx);
|
||
|
return async (files, invalidates) => {
|
||
|
const config = ctx.getSerializableConfig();
|
||
|
if (config.shard)
|
||
|
files = await sequencer.shard(files);
|
||
|
files = await sequencer.sort(files);
|
||
|
if (!ctx.config.threads) {
|
||
|
await runFiles(config, files);
|
||
|
} else {
|
||
|
const results = await Promise.allSettled(files.map((file) => runFiles(config, [file], invalidates)));
|
||
|
const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
|
||
|
if (errors.length > 0)
|
||
|
throw new AggregateErrorPonyfill(errors, "Errors occurred while running tests. For more information, see serialized error.");
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
return {
|
||
|
runTests: runWithFiles("run"),
|
||
|
close: async () => {
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
function createChannel(ctx) {
|
||
|
const channel = new MessageChannel();
|
||
|
const port = channel.port2;
|
||
|
const workerPort = channel.port1;
|
||
|
createBirpc(
|
||
|
{
|
||
|
onWorkerExit(code) {
|
||
|
process.exit(code || 1);
|
||
|
},
|
||
|
snapshotSaved(snapshot) {
|
||
|
ctx.snapshot.add(snapshot);
|
||
|
},
|
||
|
resolveSnapshotPath(testPath) {
|
||
|
return ctx.snapshot.resolvePath(testPath);
|
||
|
},
|
||
|
async getSourceMap(id, force) {
|
||
|
if (force) {
|
||
|
const mod = ctx.server.moduleGraph.getModuleById(id);
|
||
|
if (mod)
|
||
|
ctx.server.moduleGraph.invalidateModule(mod);
|
||
|
}
|
||
|
const r = await ctx.vitenode.transformRequest(id);
|
||
|
return r == null ? void 0 : r.map;
|
||
|
},
|
||
|
fetch(id) {
|
||
|
return ctx.vitenode.fetchModule(id);
|
||
|
},
|
||
|
resolveId(id, importer) {
|
||
|
return ctx.vitenode.resolveId(id, importer);
|
||
|
},
|
||
|
onPathsCollected(paths) {
|
||
|
ctx.state.collectPaths(paths);
|
||
|
ctx.report("onPathsCollected", paths);
|
||
|
},
|
||
|
onCollected(files) {
|
||
|
ctx.state.collectFiles(files);
|
||
|
ctx.report("onCollected", files);
|
||
|
},
|
||
|
onAfterSuiteRun(meta) {
|
||
|
var _a;
|
||
|
(_a = ctx.coverageProvider) == null ? void 0 : _a.onAfterSuiteRun(meta);
|
||
|
},
|
||
|
onTaskUpdate(packs) {
|
||
|
ctx.state.updateTasks(packs);
|
||
|
ctx.report("onTaskUpdate", packs);
|
||
|
},
|
||
|
onUserConsoleLog(log) {
|
||
|
ctx.state.updateUserLog(log);
|
||
|
ctx.report("onUserConsoleLog", log);
|
||
|
},
|
||
|
onUnhandledRejection(err) {
|
||
|
ctx.state.catchError(err, "Unhandled Rejection");
|
||
|
},
|
||
|
onFinished(files) {
|
||
|
ctx.report("onFinished", files, ctx.state.getUnhandledErrors());
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
post(v) {
|
||
|
port.postMessage(v);
|
||
|
},
|
||
|
on(fn) {
|
||
|
port.on("message", fn);
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
return { workerPort, port };
|
||
|
}
|
||
|
|
||
|
const F_RIGHT = "\u2192";
|
||
|
const F_DOWN = "\u2193";
|
||
|
const F_DOWN_RIGHT = "\u21B3";
|
||
|
const F_POINTER = "\u276F";
|
||
|
const F_DOT = "\xB7";
|
||
|
const F_CHECK = "\u2713";
|
||
|
const F_CROSS = "\xD7";
|
||
|
const F_LONG_DASH = "\u23AF";
|
||
|
|
||
|
const spinnerMap = /* @__PURE__ */ new WeakMap();
|
||
|
const hookSpinnerMap = /* @__PURE__ */ new WeakMap();
|
||
|
const pointer = c.yellow(F_POINTER);
|
||
|
const skipped = c.dim(c.gray(F_DOWN));
|
||
|
function getCols(delta = 0) {
|
||
|
var _a;
|
||
|
let length = (_a = process.stdout) == null ? void 0 : _a.columns;
|
||
|
if (!length || isNaN(length))
|
||
|
length = 30;
|
||
|
return Math.max(length + delta, 0);
|
||
|
}
|
||
|
function divider(text, left, right) {
|
||
|
const cols = getCols();
|
||
|
if (text) {
|
||
|
const textLength = stripAnsi(text).length;
|
||
|
if (left == null && right != null) {
|
||
|
left = cols - textLength - right;
|
||
|
} else {
|
||
|
left = left ?? Math.floor((cols - textLength) / 2);
|
||
|
right = cols - textLength - left;
|
||
|
}
|
||
|
left = Math.max(0, left);
|
||
|
right = Math.max(0, right);
|
||
|
return `${F_LONG_DASH.repeat(left)}${text}${F_LONG_DASH.repeat(right)}`;
|
||
|
}
|
||
|
return F_LONG_DASH.repeat(cols);
|
||
|
}
|
||
|
function formatTestPath(root, path) {
|
||
|
var _a;
|
||
|
if (isAbsolute(path))
|
||
|
path = relative(root, path);
|
||
|
const dir = dirname(path);
|
||
|
const ext = ((_a = path.match(/(\.(spec|test)\.[cm]?[tj]sx?)$/)) == null ? void 0 : _a[0]) || "";
|
||
|
const base = basename(path, ext);
|
||
|
return slash$1(c.dim(`${dir}/`) + c.bold(base)) + c.dim(ext);
|
||
|
}
|
||
|
function renderSnapshotSummary(rootDir, snapshots) {
|
||
|
const summary = [];
|
||
|
if (snapshots.added)
|
||
|
summary.push(c.bold(c.green(`${snapshots.added} written`)));
|
||
|
if (snapshots.unmatched)
|
||
|
summary.push(c.bold(c.red(`${snapshots.unmatched} failed`)));
|
||
|
if (snapshots.updated)
|
||
|
summary.push(c.bold(c.green(`${snapshots.updated} updated `)));
|
||
|
if (snapshots.filesRemoved) {
|
||
|
if (snapshots.didUpdate)
|
||
|
summary.push(c.bold(c.green(`${snapshots.filesRemoved} files removed `)));
|
||
|
else
|
||
|
summary.push(c.bold(c.yellow(`${snapshots.filesRemoved} files obsolete `)));
|
||
|
}
|
||
|
if (snapshots.filesRemovedList && snapshots.filesRemovedList.length) {
|
||
|
const [head, ...tail] = snapshots.filesRemovedList;
|
||
|
summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, head)}`);
|
||
|
tail.forEach((key) => {
|
||
|
summary.push(` ${c.gray(F_DOT)} ${formatTestPath(rootDir, key)}`);
|
||
|
});
|
||
|
}
|
||
|
if (snapshots.unchecked) {
|
||
|
if (snapshots.didUpdate)
|
||
|
summary.push(c.bold(c.green(`${snapshots.unchecked} removed`)));
|
||
|
else
|
||
|
summary.push(c.bold(c.yellow(`${snapshots.unchecked} obsolete`)));
|
||
|
snapshots.uncheckedKeysByFile.forEach((uncheckedFile) => {
|
||
|
summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
|
||
|
uncheckedFile.keys.forEach((key) => summary.push(` ${c.gray(F_DOT)} ${key}`));
|
||
|
});
|
||
|
}
|
||
|
return summary;
|
||
|
}
|
||
|
function countTestErrors(tasks) {
|
||
|
return tasks.reduce((c2, i) => {
|
||
|
var _a, _b;
|
||
|
return c2 + (((_b = (_a = i.result) == null ? void 0 : _a.errors) == null ? void 0 : _b.length) || 0);
|
||
|
}, 0);
|
||
|
}
|
||
|
function getStateString(tasks, name = "tests", showTotal = true) {
|
||
|
if (tasks.length === 0)
|
||
|
return c.dim(`no ${name}`);
|
||
|
const passed = tasks.filter((i) => {
|
||
|
var _a;
|
||
|
return ((_a = i.result) == null ? void 0 : _a.state) === "pass";
|
||
|
});
|
||
|
const failed = tasks.filter((i) => {
|
||
|
var _a;
|
||
|
return ((_a = i.result) == null ? void 0 : _a.state) === "fail";
|
||
|
});
|
||
|
const skipped2 = tasks.filter((i) => i.mode === "skip");
|
||
|
const todo = tasks.filter((i) => i.mode === "todo");
|
||
|
return [
|
||
|
failed.length ? c.bold(c.red(`${failed.length} failed`)) : null,
|
||
|
passed.length ? c.bold(c.green(`${passed.length} passed`)) : null,
|
||
|
skipped2.length ? c.yellow(`${skipped2.length} skipped`) : null,
|
||
|
todo.length ? c.gray(`${todo.length} todo`) : null
|
||
|
].filter(Boolean).join(c.dim(" | ")) + (showTotal ? c.gray(` (${tasks.length})`) : "");
|
||
|
}
|
||
|
function getStateSymbol(task) {
|
||
|
if (task.mode === "skip" || task.mode === "todo")
|
||
|
return skipped;
|
||
|
if (!task.result)
|
||
|
return c.gray("\xB7");
|
||
|
if (task.result.state === "run") {
|
||
|
if (task.type === "suite")
|
||
|
return pointer;
|
||
|
let spinner = spinnerMap.get(task);
|
||
|
if (!spinner) {
|
||
|
spinner = elegantSpinner();
|
||
|
spinnerMap.set(task, spinner);
|
||
|
}
|
||
|
return c.yellow(spinner());
|
||
|
}
|
||
|
if (task.result.state === "pass") {
|
||
|
return task.type === "benchmark" ? c.green(F_DOT) : c.green(F_CHECK);
|
||
|
}
|
||
|
if (task.result.state === "fail") {
|
||
|
return task.type === "suite" ? pointer : c.red(F_CROSS);
|
||
|
}
|
||
|
return " ";
|
||
|
}
|
||
|
function getHookStateSymbol(task, hookName) {
|
||
|
var _a, _b;
|
||
|
const state = (_b = (_a = task.result) == null ? void 0 : _a.hooks) == null ? void 0 : _b[hookName];
|
||
|
if (state && state === "run") {
|
||
|
let spinnerMap2 = hookSpinnerMap.get(task);
|
||
|
if (!spinnerMap2) {
|
||
|
spinnerMap2 = /* @__PURE__ */ new Map();
|
||
|
hookSpinnerMap.set(task, spinnerMap2);
|
||
|
}
|
||
|
let spinner = spinnerMap2.get(hookName);
|
||
|
if (!spinner) {
|
||
|
spinner = elegantSpinner();
|
||
|
spinnerMap2.set(hookName, spinner);
|
||
|
}
|
||
|
return c.yellow(spinner());
|
||
|
}
|
||
|
}
|
||
|
const spinnerFrames = process.platform === "win32" ? ["-", "\\", "|", "/"] : ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
||
|
function elegantSpinner() {
|
||
|
let index = 0;
|
||
|
return () => {
|
||
|
index = ++index % spinnerFrames.length;
|
||
|
return spinnerFrames[index];
|
||
|
};
|
||
|
}
|
||
|
function formatTimeString(date) {
|
||
|
return date.toTimeString().split(" ")[0];
|
||
|
}
|
||
|
function formatProjectName(name, suffix = " ") {
|
||
|
if (!name)
|
||
|
return "";
|
||
|
const index = name.split("").reduce((acc, v, idx) => acc + v.charCodeAt(0) + idx, 0);
|
||
|
const colors = [
|
||
|
c.blue,
|
||
|
c.yellow,
|
||
|
c.cyan,
|
||
|
c.green,
|
||
|
c.magenta
|
||
|
];
|
||
|
return colors[index % colors.length](`|${name}|`) + suffix;
|
||
|
}
|
||
|
|
||
|
var _a;
|
||
|
const BADGE_PADDING = " ";
|
||
|
const HELP_HINT = `${c.dim("press ")}${c.bold("h")}${c.dim(" to show help")}`;
|
||
|
const HELP_UPDATE_SNAP = c.dim("press ") + c.bold(c.yellow("u")) + c.dim(" to update snapshot");
|
||
|
const HELP_QUITE = `${c.dim("press ")}${c.bold("q")}${c.dim(" to quit")}`;
|
||
|
const WAIT_FOR_CHANGE_PASS = `
|
||
|
${c.bold(c.inverse(c.green(" PASS ")))}${c.green(" Waiting for file changes...")}`;
|
||
|
const WAIT_FOR_CHANGE_FAIL = `
|
||
|
${c.bold(c.inverse(c.red(" FAIL ")))}${c.red(" Tests failed. Watching for file changes...")}`;
|
||
|
const LAST_RUN_LOG_TIMEOUT = 1500;
|
||
|
class BaseReporter {
|
||
|
constructor() {
|
||
|
this.start = 0;
|
||
|
this.end = 0;
|
||
|
this.isTTY = isNode && ((_a = process.stdout) == null ? void 0 : _a.isTTY) && !process.env.CI;
|
||
|
this.ctx = void 0;
|
||
|
this._filesInWatchMode = /* @__PURE__ */ new Map();
|
||
|
this._lastRunTimeout = 0;
|
||
|
this._lastRunCount = 0;
|
||
|
this._timeStart = new Date();
|
||
|
this.registerUnhandledRejection();
|
||
|
}
|
||
|
get mode() {
|
||
|
return this.ctx.config.mode;
|
||
|
}
|
||
|
onInit(ctx) {
|
||
|
this.ctx = ctx;
|
||
|
ctx.logger.printBanner();
|
||
|
this.start = performance.now();
|
||
|
}
|
||
|
relative(path) {
|
||
|
return relativePath(this.ctx.config.root, path);
|
||
|
}
|
||
|
async onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
||
|
this.end = performance.now();
|
||
|
await this.reportSummary(files);
|
||
|
if (errors.length) {
|
||
|
if (!this.ctx.config.dangerouslyIgnoreUnhandledErrors)
|
||
|
process.exitCode = 1;
|
||
|
await this.ctx.logger.printUnhandledErrors(errors);
|
||
|
}
|
||
|
}
|
||
|
onTaskUpdate(packs) {
|
||
|
var _a2, _b, _c, _d;
|
||
|
if (this.isTTY)
|
||
|
return;
|
||
|
const logger = this.ctx.logger;
|
||
|
for (const pack of packs) {
|
||
|
const task = this.ctx.state.idMap.get(pack[0]);
|
||
|
if (task && "filepath" in task && ((_a2 = task.result) == null ? void 0 : _a2.state) && ((_b = task.result) == null ? void 0 : _b.state) !== "run") {
|
||
|
const tests = getTests(task);
|
||
|
const failed = tests.filter((t) => {
|
||
|
var _a3;
|
||
|
return ((_a3 = t.result) == null ? void 0 : _a3.state) === "fail";
|
||
|
});
|
||
|
const skipped = tests.filter((t) => t.mode === "skip" || t.mode === "todo");
|
||
|
let state = c.dim(`${tests.length} test${tests.length > 1 ? "s" : ""}`);
|
||
|
if (failed.length)
|
||
|
state += ` ${c.dim("|")} ${c.red(`${failed.length} failed`)}`;
|
||
|
if (skipped.length)
|
||
|
state += ` ${c.dim("|")} ${c.yellow(`${skipped.length} skipped`)}`;
|
||
|
let suffix = c.dim(" (") + state + c.dim(")");
|
||
|
if (task.result.duration) {
|
||
|
const color = task.result.duration > this.ctx.config.slowTestThreshold ? c.yellow : c.gray;
|
||
|
suffix += color(` ${Math.round(task.result.duration)}${c.dim("ms")}`);
|
||
|
}
|
||
|
if (this.ctx.config.logHeapUsage && task.result.heap != null)
|
||
|
suffix += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`);
|
||
|
logger.log(` ${getStateSymbol(task)} ${task.name} ${suffix}`);
|
||
|
for (const test of failed) {
|
||
|
logger.log(c.red(` ${pointer} ${getFullName(test)}`));
|
||
|
(_d = (_c = test.result) == null ? void 0 : _c.errors) == null ? void 0 : _d.forEach((e) => {
|
||
|
logger.log(c.red(` ${F_RIGHT} ${e == null ? void 0 : e.message}`));
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
async onWatcherStart(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
||
|
this.resetLastRunLog();
|
||
|
const failed = errors.length > 0 || hasFailed(files);
|
||
|
const failedSnap = hasFailedSnapshot(files);
|
||
|
if (failed)
|
||
|
this.ctx.logger.log(WAIT_FOR_CHANGE_FAIL);
|
||
|
else
|
||
|
this.ctx.logger.log(WAIT_FOR_CHANGE_PASS);
|
||
|
const hints = [];
|
||
|
if (this.mode !== "typecheck")
|
||
|
hints.push(HELP_HINT);
|
||
|
if (failedSnap)
|
||
|
hints.unshift(HELP_UPDATE_SNAP);
|
||
|
else
|
||
|
hints.push(HELP_QUITE);
|
||
|
this.ctx.logger.log(BADGE_PADDING + hints.join(c.dim(", ")));
|
||
|
if (this._lastRunCount) {
|
||
|
const LAST_RUN_TEXT = `rerun x${this._lastRunCount}`;
|
||
|
const LAST_RUN_TEXTS = [
|
||
|
c.blue(LAST_RUN_TEXT),
|
||
|
c.gray(LAST_RUN_TEXT),
|
||
|
c.dim(c.gray(LAST_RUN_TEXT))
|
||
|
];
|
||
|
this.ctx.logger.logUpdate(BADGE_PADDING + LAST_RUN_TEXTS[0]);
|
||
|
this._lastRunTimeout = 0;
|
||
|
this._lastRunTimer = safeSetInterval(
|
||
|
() => {
|
||
|
this._lastRunTimeout += 1;
|
||
|
if (this._lastRunTimeout >= LAST_RUN_TEXTS.length)
|
||
|
this.resetLastRunLog();
|
||
|
else
|
||
|
this.ctx.logger.logUpdate(BADGE_PADDING + LAST_RUN_TEXTS[this._lastRunTimeout]);
|
||
|
},
|
||
|
LAST_RUN_LOG_TIMEOUT / LAST_RUN_TEXTS.length
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
resetLastRunLog() {
|
||
|
safeClearInterval(this._lastRunTimer);
|
||
|
this._lastRunTimer = void 0;
|
||
|
this.ctx.logger.logUpdate.clear();
|
||
|
}
|
||
|
async onWatcherRerun(files, trigger) {
|
||
|
this.resetLastRunLog();
|
||
|
this.watchFilters = files;
|
||
|
files.forEach((filepath) => {
|
||
|
let reruns = this._filesInWatchMode.get(filepath) ?? 0;
|
||
|
this._filesInWatchMode.set(filepath, ++reruns);
|
||
|
});
|
||
|
const BADGE = c.inverse(c.bold(c.blue(" RERUN ")));
|
||
|
const TRIGGER = trigger ? c.dim(` ${this.relative(trigger)}`) : "";
|
||
|
if (files.length > 1) {
|
||
|
this.ctx.logger.clearFullScreen(`
|
||
|
${BADGE}${TRIGGER}
|
||
|
`);
|
||
|
this._lastRunCount = 0;
|
||
|
} else if (files.length === 1) {
|
||
|
const rerun = this._filesInWatchMode.get(files[0]) ?? 1;
|
||
|
this._lastRunCount = rerun;
|
||
|
this.ctx.logger.clearFullScreen(`
|
||
|
${BADGE}${TRIGGER} ${c.blue(`x${rerun}`)}
|
||
|
`);
|
||
|
}
|
||
|
this._timeStart = new Date();
|
||
|
this.start = performance.now();
|
||
|
}
|
||
|
onUserConsoleLog(log) {
|
||
|
if (!this.shouldLog(log))
|
||
|
return;
|
||
|
const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : void 0;
|
||
|
this.ctx.logger.log(c.gray(log.type + c.dim(` | ${task ? getFullName(task) : "unknown test"}`)));
|
||
|
process[log.type].write(`${log.content}
|
||
|
`);
|
||
|
}
|
||
|
shouldLog(log) {
|
||
|
var _a2, _b;
|
||
|
if (this.ctx.config.silent)
|
||
|
return false;
|
||
|
const shouldLog = (_b = (_a2 = this.ctx.config).onConsoleLog) == null ? void 0 : _b.call(_a2, log.content, log.type);
|
||
|
if (shouldLog === false)
|
||
|
return shouldLog;
|
||
|
return true;
|
||
|
}
|
||
|
onServerRestart(reason) {
|
||
|
this.ctx.logger.log(c.bold(c.magenta(
|
||
|
reason === "config" ? "\nRestarting due to config changes..." : "\nRestarting Vitest..."
|
||
|
)));
|
||
|
}
|
||
|
async reportSummary(files) {
|
||
|
await this.printErrorsSummary(files);
|
||
|
if (this.mode === "benchmark")
|
||
|
await this.reportBenchmarkSummary(files);
|
||
|
else
|
||
|
await this.reportTestSummary(files);
|
||
|
}
|
||
|
async reportTestSummary(files) {
|
||
|
const tests = getTests(files);
|
||
|
const logger = this.ctx.logger;
|
||
|
const executionTime = this.end - this.start;
|
||
|
const collectTime = files.reduce((acc, test) => acc + Math.max(0, test.collectDuration || 0), 0);
|
||
|
const setupTime = files.reduce((acc, test) => acc + Math.max(0, test.setupDuration || 0), 0);
|
||
|
const testsTime = files.reduce((acc, test) => {
|
||
|
var _a2;
|
||
|
return acc + Math.max(0, ((_a2 = test.result) == null ? void 0 : _a2.duration) || 0);
|
||
|
}, 0);
|
||
|
const transformTime = Array.from(this.ctx.vitenode.fetchCache.values()).reduce((a, b) => a + ((b == null ? void 0 : b.duration) || 0), 0);
|
||
|
const threadTime = collectTime + testsTime + setupTime;
|
||
|
const padTitle = (str) => c.dim(`${str.padStart(11)} `);
|
||
|
const time = (time2) => {
|
||
|
if (time2 > 1e3)
|
||
|
return `${(time2 / 1e3).toFixed(2)}s`;
|
||
|
return `${Math.round(time2)}ms`;
|
||
|
};
|
||
|
const snapshotOutput = renderSnapshotSummary(this.ctx.config.root, this.ctx.snapshot.summary);
|
||
|
if (snapshotOutput.length) {
|
||
|
logger.log(snapshotOutput.map(
|
||
|
(t, i) => i === 0 ? `${padTitle("Snapshots")} ${t}` : `${padTitle("")} ${t}`
|
||
|
).join("\n"));
|
||
|
if (snapshotOutput.length > 1)
|
||
|
logger.log();
|
||
|
}
|
||
|
logger.log(padTitle("Test Files"), getStateString(files));
|
||
|
logger.log(padTitle("Tests"), getStateString(tests));
|
||
|
if (this.mode === "typecheck") {
|
||
|
const failed = tests.filter((t) => {
|
||
|
var _a2, _b, _c;
|
||
|
return ((_a2 = t.meta) == null ? void 0 : _a2.typecheck) && ((_c = (_b = t.result) == null ? void 0 : _b.errors) == null ? void 0 : _c.length);
|
||
|
});
|
||
|
logger.log(padTitle("Type Errors"), failed.length ? c.bold(c.red(`${failed} failed`)) : c.dim("no errors"));
|
||
|
}
|
||
|
logger.log(padTitle("Start at"), formatTimeString(this._timeStart));
|
||
|
if (this.watchFilters)
|
||
|
logger.log(padTitle("Duration"), time(threadTime));
|
||
|
else if (this.mode === "typecheck")
|
||
|
logger.log(padTitle("Duration"), time(executionTime));
|
||
|
else
|
||
|
logger.log(padTitle("Duration"), time(executionTime) + c.dim(` (transform ${time(transformTime)}, setup ${time(setupTime)}, collect ${time(collectTime)}, tests ${time(testsTime)})`));
|
||
|
logger.log();
|
||
|
}
|
||
|
async printErrorsSummary(files) {
|
||
|
const logger = this.ctx.logger;
|
||
|
const suites = getSuites(files);
|
||
|
const tests = getTests(files);
|
||
|
const failedSuites = suites.filter((i) => {
|
||
|
var _a2;
|
||
|
return (_a2 = i.result) == null ? void 0 : _a2.errors;
|
||
|
});
|
||
|
const failedTests = tests.filter((i) => {
|
||
|
var _a2;
|
||
|
return ((_a2 = i.result) == null ? void 0 : _a2.state) === "fail";
|
||
|
});
|
||
|
const failedTotal = countTestErrors(failedSuites) + countTestErrors(failedTests);
|
||
|
let current = 1;
|
||
|
const errorDivider = () => logger.error(`${c.red(c.dim(divider(`[${current++}/${failedTotal}]`, void 0, 1)))}
|
||
|
`);
|
||
|
if (failedSuites.length) {
|
||
|
logger.error(c.red(divider(c.bold(c.inverse(` Failed Suites ${failedSuites.length} `)))));
|
||
|
logger.error();
|
||
|
await this.printTaskErrors(failedSuites, errorDivider);
|
||
|
}
|
||
|
if (failedTests.length) {
|
||
|
logger.error(c.red(divider(c.bold(c.inverse(` Failed Tests ${failedTests.length} `)))));
|
||
|
logger.error();
|
||
|
await this.printTaskErrors(failedTests, errorDivider);
|
||
|
}
|
||
|
return tests;
|
||
|
}
|
||
|
async reportBenchmarkSummary(files) {
|
||
|
const logger = this.ctx.logger;
|
||
|
const benchs = getTests(files);
|
||
|
const topBenchs = benchs.filter((i) => {
|
||
|
var _a2, _b;
|
||
|
return ((_b = (_a2 = i.result) == null ? void 0 : _a2.benchmark) == null ? void 0 : _b.rank) === 1;
|
||
|
});
|
||
|
logger.log(`
|
||
|
${c.cyan(c.inverse(c.bold(" BENCH ")))} ${c.cyan("Summary")}
|
||
|
`);
|
||
|
for (const bench of topBenchs) {
|
||
|
const group = bench.suite;
|
||
|
if (!group)
|
||
|
continue;
|
||
|
const groupName = getFullName(group);
|
||
|
logger.log(` ${bench.name}${c.dim(` - ${groupName}`)}`);
|
||
|
const siblings = group.tasks.filter((i) => {
|
||
|
var _a2;
|
||
|
return ((_a2 = i.result) == null ? void 0 : _a2.benchmark) && i !== bench;
|
||
|
}).sort((a, b) => a.result.benchmark.rank - b.result.benchmark.rank);
|
||
|
for (const sibling of siblings) {
|
||
|
const number = `${(sibling.result.benchmark.mean / bench.result.benchmark.mean).toFixed(2)}x`;
|
||
|
logger.log(` ${c.green(number)} ${c.gray("faster than")} ${sibling.name}`);
|
||
|
}
|
||
|
logger.log("");
|
||
|
}
|
||
|
}
|
||
|
async printTaskErrors(tasks, errorDivider) {
|
||
|
var _a2, _b, _c;
|
||
|
const errorsQueue = [];
|
||
|
for (const task of tasks) {
|
||
|
(_b = (_a2 = task.result) == null ? void 0 : _a2.errors) == null ? void 0 : _b.forEach((error) => {
|
||
|
const errorItem = (error == null ? void 0 : error.stackStr) && errorsQueue.find((i) => {
|
||
|
var _a3;
|
||
|
return ((_a3 = i[0]) == null ? void 0 : _a3.stackStr) === error.stackStr;
|
||
|
});
|
||
|
if (errorItem)
|
||
|
errorItem[1].push(task);
|
||
|
else
|
||
|
errorsQueue.push([error, [task]]);
|
||
|
});
|
||
|
}
|
||
|
for (const [error, tasks2] of errorsQueue) {
|
||
|
for (const task of tasks2) {
|
||
|
const filepath = (task == null ? void 0 : task.filepath) || "";
|
||
|
const projectName = (task == null ? void 0 : task.projectName) || ((_c = task.file) == null ? void 0 : _c.projectName);
|
||
|
let name = getFullName(task);
|
||
|
if (filepath)
|
||
|
name = `${name} ${c.dim(`[ ${this.relative(filepath)} ]`)}`;
|
||
|
this.ctx.logger.error(`${c.red(c.bold(c.inverse(" FAIL ")))} ${formatProjectName(projectName)}${name}`);
|
||
|
}
|
||
|
await this.ctx.logger.printError(error);
|
||
|
errorDivider();
|
||
|
await Promise.resolve();
|
||
|
}
|
||
|
}
|
||
|
registerUnhandledRejection() {
|
||
|
process.on("unhandledRejection", async (err) => {
|
||
|
process.exitCode = 1;
|
||
|
await this.ctx.logger.printError(err, true, "Unhandled Rejection");
|
||
|
this.ctx.logger.error("\n\n");
|
||
|
process.exit(1);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const DURATION_LONG$1 = 300;
|
||
|
const outputMap$1 = /* @__PURE__ */ new WeakMap();
|
||
|
function formatFilepath$1(path) {
|
||
|
const lastSlash = Math.max(path.lastIndexOf("/") + 1, 0);
|
||
|
const basename = path.slice(lastSlash);
|
||
|
let firstDot = basename.indexOf(".");
|
||
|
if (firstDot < 0)
|
||
|
firstDot = basename.length;
|
||
|
firstDot += lastSlash;
|
||
|
return c.dim(path.slice(0, lastSlash)) + path.slice(lastSlash, firstDot) + c.dim(path.slice(firstDot));
|
||
|
}
|
||
|
function formatNumber$1(number) {
|
||
|
const res = String(number.toFixed(number < 100 ? 4 : 2)).split(".");
|
||
|
return res[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ",") + (res[1] ? `.${res[1]}` : "");
|
||
|
}
|
||
|
function renderHookState(task, hookName, level = 0) {
|
||
|
var _a, _b;
|
||
|
const state = (_b = (_a = task.result) == null ? void 0 : _a.hooks) == null ? void 0 : _b[hookName];
|
||
|
if (state && state === "run")
|
||
|
return `${" ".repeat(level)} ${getHookStateSymbol(task, hookName)} ${c.dim(`[ ${hookName} ]`)}`;
|
||
|
return "";
|
||
|
}
|
||
|
function renderBenchmarkItems$1(result) {
|
||
|
return [
|
||
|
result.name,
|
||
|
formatNumber$1(result.hz || 0),
|
||
|
formatNumber$1(result.p99 || 0),
|
||
|
`\xB1${result.rme.toFixed(2)}%`,
|
||
|
result.samples.length.toString()
|
||
|
];
|
||
|
}
|
||
|
function renderBenchmark$1(task, tasks) {
|
||
|
var _a;
|
||
|
const result = (_a = task.result) == null ? void 0 : _a.benchmark;
|
||
|
if (!result)
|
||
|
return task.name;
|
||
|
const benchs = tasks.map((i) => {
|
||
|
var _a2;
|
||
|
return i.type === "benchmark" ? (_a2 = i.result) == null ? void 0 : _a2.benchmark : void 0;
|
||
|
}).filter(notNullish);
|
||
|
const allItems = benchs.map(renderBenchmarkItems$1);
|
||
|
const items = renderBenchmarkItems$1(result);
|
||
|
const padded = items.map((i, idx) => {
|
||
|
const width = Math.max(...allItems.map((i2) => i2[idx].length));
|
||
|
return idx ? i.padStart(width, " ") : i.padEnd(width, " ");
|
||
|
});
|
||
|
return [
|
||
|
padded[0],
|
||
|
c.dim(" "),
|
||
|
c.blue(padded[1]),
|
||
|
c.dim(" ops/sec "),
|
||
|
c.cyan(padded[3]),
|
||
|
c.dim(` (${padded[4]} samples)`),
|
||
|
result.rank === 1 ? c.bold(c.green(" fastest")) : result.rank === benchs.length && benchs.length > 2 ? c.bold(c.gray(" slowest")) : ""
|
||
|
].join("");
|
||
|
}
|
||
|
function renderTree$1(tasks, options, level = 0) {
|
||
|
var _a, _b, _c, _d, _e, _f, _g;
|
||
|
let output = [];
|
||
|
for (const task of tasks) {
|
||
|
let suffix = "";
|
||
|
let prefix = ` ${getStateSymbol(task)} `;
|
||
|
if (level === 0 && task.type === "suite" && task.projectName)
|
||
|
prefix += formatProjectName(task.projectName);
|
||
|
if (task.type === "test" && ((_a = task.result) == null ? void 0 : _a.retryCount) && task.result.retryCount > 1)
|
||
|
suffix += c.yellow(` (retry x${task.result.retryCount})`);
|
||
|
if (task.type === "suite" && !((_b = task.meta) == null ? void 0 : _b.typecheck)) {
|
||
|
const tests = getTests(task);
|
||
|
suffix += c.dim(` (${tests.length})`);
|
||
|
}
|
||
|
if (task.mode === "skip" || task.mode === "todo")
|
||
|
suffix += ` ${c.dim(c.gray("[skipped]"))}`;
|
||
|
if (((_c = task.result) == null ? void 0 : _c.duration) != null) {
|
||
|
if (task.result.duration > DURATION_LONG$1)
|
||
|
suffix += c.yellow(` ${Math.round(task.result.duration)}${c.dim("ms")}`);
|
||
|
}
|
||
|
if (options.showHeap && ((_d = task.result) == null ? void 0 : _d.heap) != null)
|
||
|
suffix += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`);
|
||
|
let name = task.name;
|
||
|
if (level === 0)
|
||
|
name = formatFilepath$1(name);
|
||
|
const padding = " ".repeat(level);
|
||
|
const body = task.type === "benchmark" ? renderBenchmark$1(task, tasks) : name;
|
||
|
output.push(padding + prefix + body + suffix);
|
||
|
if (((_e = task.result) == null ? void 0 : _e.state) !== "pass" && outputMap$1.get(task) != null) {
|
||
|
let data = outputMap$1.get(task);
|
||
|
if (typeof data === "string") {
|
||
|
data = stripAnsi(data.trim().split("\n").filter(Boolean).pop());
|
||
|
if (data === "")
|
||
|
data = void 0;
|
||
|
}
|
||
|
if (data != null) {
|
||
|
const out = `${" ".repeat(level)}${F_RIGHT} ${data}`;
|
||
|
output.push(` ${c.gray(cliTruncate(out, getCols(-3)))}`);
|
||
|
}
|
||
|
}
|
||
|
output = output.concat(renderHookState(task, "beforeAll", level + 1));
|
||
|
output = output.concat(renderHookState(task, "beforeEach", level + 1));
|
||
|
if (task.type === "suite" && task.tasks.length > 0) {
|
||
|
if (((_f = task.result) == null ? void 0 : _f.state) === "fail" || ((_g = task.result) == null ? void 0 : _g.state) === "run" || options.renderSucceed)
|
||
|
output = output.concat(renderTree$1(task.tasks, options, level + 1));
|
||
|
}
|
||
|
output = output.concat(renderHookState(task, "afterAll", level + 1));
|
||
|
output = output.concat(renderHookState(task, "afterEach", level + 1));
|
||
|
}
|
||
|
return output.filter(Boolean).join("\n");
|
||
|
}
|
||
|
const createListRenderer = (_tasks, options) => {
|
||
|
let tasks = _tasks;
|
||
|
let timer;
|
||
|
const log = options.logger.logUpdate;
|
||
|
function update() {
|
||
|
log(renderTree$1(tasks, options));
|
||
|
}
|
||
|
return {
|
||
|
start() {
|
||
|
if (timer)
|
||
|
return this;
|
||
|
timer = safeSetInterval(update, 200);
|
||
|
return this;
|
||
|
},
|
||
|
update(_tasks2) {
|
||
|
tasks = _tasks2;
|
||
|
update();
|
||
|
return this;
|
||
|
},
|
||
|
async stop() {
|
||
|
if (timer) {
|
||
|
safeClearInterval(timer);
|
||
|
timer = void 0;
|
||
|
}
|
||
|
log.clear();
|
||
|
options.logger.log(renderTree$1(tasks, options));
|
||
|
return this;
|
||
|
},
|
||
|
clear() {
|
||
|
log.clear();
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
class DefaultReporter extends BaseReporter {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this.rendererOptions = {};
|
||
|
}
|
||
|
async onTestRemoved(trigger) {
|
||
|
await this.stopListRender();
|
||
|
this.ctx.logger.clearScreen(c.yellow("Test removed...") + (trigger ? c.dim(` [ ${this.relative(trigger)} ]
|
||
|
`) : ""), true);
|
||
|
const files = this.ctx.state.getFiles(this.watchFilters);
|
||
|
createListRenderer(files, this.rendererOptions).stop();
|
||
|
this.ctx.logger.log();
|
||
|
await super.reportSummary(files);
|
||
|
super.onWatcherStart();
|
||
|
}
|
||
|
onCollected() {
|
||
|
if (this.isTTY) {
|
||
|
this.rendererOptions.logger = this.ctx.logger;
|
||
|
this.rendererOptions.showHeap = this.ctx.config.logHeapUsage;
|
||
|
this.rendererOptions.mode = this.mode;
|
||
|
const files = this.ctx.state.getFiles(this.watchFilters);
|
||
|
if (!this.renderer)
|
||
|
this.renderer = createListRenderer(files, this.rendererOptions).start();
|
||
|
else
|
||
|
this.renderer.update(files);
|
||
|
}
|
||
|
}
|
||
|
async onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
||
|
await this.stopListRender();
|
||
|
this.ctx.logger.log();
|
||
|
await super.onFinished(files, errors);
|
||
|
}
|
||
|
async onWatcherStart(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
||
|
await this.stopListRender();
|
||
|
await super.onWatcherStart(files, errors);
|
||
|
}
|
||
|
async stopListRender() {
|
||
|
var _a;
|
||
|
await ((_a = this.renderer) == null ? void 0 : _a.stop());
|
||
|
this.renderer = void 0;
|
||
|
}
|
||
|
async onWatcherRerun(files, trigger) {
|
||
|
await this.stopListRender();
|
||
|
await super.onWatcherRerun(files, trigger);
|
||
|
}
|
||
|
onUserConsoleLog(log) {
|
||
|
var _a;
|
||
|
if (!this.shouldLog(log))
|
||
|
return;
|
||
|
(_a = this.renderer) == null ? void 0 : _a.clear();
|
||
|
super.onUserConsoleLog(log);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const check = c.green("\xB7");
|
||
|
const cross = c.red("x");
|
||
|
const pending = c.yellow("*");
|
||
|
const skip = c.dim(c.gray("-"));
|
||
|
function render(tasks) {
|
||
|
const all = getTests(tasks);
|
||
|
return all.map((i) => {
|
||
|
var _a;
|
||
|
if (i.mode === "skip" || i.mode === "todo")
|
||
|
return skip;
|
||
|
switch ((_a = i.result) == null ? void 0 : _a.state) {
|
||
|
case "pass":
|
||
|
return check;
|
||
|
case "fail":
|
||
|
return cross;
|
||
|
default:
|
||
|
return pending;
|
||
|
}
|
||
|
}).join("");
|
||
|
}
|
||
|
const createDotRenderer = (_tasks, options) => {
|
||
|
let tasks = _tasks;
|
||
|
let timer;
|
||
|
const log = options.logger.logUpdate;
|
||
|
function update() {
|
||
|
log(render(tasks));
|
||
|
}
|
||
|
return {
|
||
|
start() {
|
||
|
if (timer)
|
||
|
return this;
|
||
|
timer = safeSetInterval(update, 200);
|
||
|
return this;
|
||
|
},
|
||
|
update(_tasks2) {
|
||
|
tasks = _tasks2;
|
||
|
update();
|
||
|
return this;
|
||
|
},
|
||
|
async stop() {
|
||
|
if (timer) {
|
||
|
safeClearInterval(timer);
|
||
|
timer = void 0;
|
||
|
}
|
||
|
log.clear();
|
||
|
options.logger.log(render(tasks));
|
||
|
return this;
|
||
|
},
|
||
|
clear() {
|
||
|
log.clear();
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
class DotReporter extends BaseReporter {
|
||
|
onCollected() {
|
||
|
if (this.isTTY) {
|
||
|
const files = this.ctx.state.getFiles(this.watchFilters);
|
||
|
if (!this.renderer)
|
||
|
this.renderer = createDotRenderer(files, { logger: this.ctx.logger }).start();
|
||
|
else
|
||
|
this.renderer.update(files);
|
||
|
}
|
||
|
}
|
||
|
async onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
||
|
await this.stopListRender();
|
||
|
this.ctx.logger.log();
|
||
|
await super.onFinished(files, errors);
|
||
|
}
|
||
|
async onWatcherStart() {
|
||
|
await this.stopListRender();
|
||
|
super.onWatcherStart();
|
||
|
}
|
||
|
async stopListRender() {
|
||
|
var _a;
|
||
|
(_a = this.renderer) == null ? void 0 : _a.stop();
|
||
|
this.renderer = void 0;
|
||
|
await new Promise((resolve) => safeSetTimeout(resolve, 10));
|
||
|
}
|
||
|
async onWatcherRerun(files, trigger) {
|
||
|
await this.stopListRender();
|
||
|
await super.onWatcherRerun(files, trigger);
|
||
|
}
|
||
|
onUserConsoleLog(log) {
|
||
|
var _a;
|
||
|
(_a = this.renderer) == null ? void 0 : _a.clear();
|
||
|
super.onUserConsoleLog(log);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const getOutputFile = (config, reporter) => {
|
||
|
if (!(config == null ? void 0 : config.outputFile))
|
||
|
return;
|
||
|
if (typeof config.outputFile === "string")
|
||
|
return config.outputFile;
|
||
|
return config.outputFile[reporter];
|
||
|
};
|
||
|
|
||
|
const StatusMap = {
|
||
|
fail: "failed",
|
||
|
only: "pending",
|
||
|
pass: "passed",
|
||
|
run: "pending",
|
||
|
skip: "skipped",
|
||
|
todo: "todo"
|
||
|
};
|
||
|
class JsonReporter$1 {
|
||
|
constructor() {
|
||
|
this.start = 0;
|
||
|
}
|
||
|
onInit(ctx) {
|
||
|
this.ctx = ctx;
|
||
|
this.start = Date.now();
|
||
|
}
|
||
|
async logTasks(files) {
|
||
|
var _a, _b, _c;
|
||
|
const suites = getSuites(files);
|
||
|
const numTotalTestSuites = suites.length;
|
||
|
const tests = getTests(files);
|
||
|
const numTotalTests = tests.length;
|
||
|
const numFailedTestSuites = suites.filter((s) => {
|
||
|
var _a2;
|
||
|
return (_a2 = s.result) == null ? void 0 : _a2.errors;
|
||
|
}).length;
|
||
|
const numPassedTestSuites = numTotalTestSuites - numFailedTestSuites;
|
||
|
const numPendingTestSuites = suites.filter((s) => {
|
||
|
var _a2;
|
||
|
return ((_a2 = s.result) == null ? void 0 : _a2.state) === "run";
|
||
|
}).length;
|
||
|
const numFailedTests = tests.filter((t) => {
|
||
|
var _a2;
|
||
|
return ((_a2 = t.result) == null ? void 0 : _a2.state) === "fail";
|
||
|
}).length;
|
||
|
const numPassedTests = numTotalTests - numFailedTests;
|
||
|
const numPendingTests = tests.filter((t) => {
|
||
|
var _a2;
|
||
|
return ((_a2 = t.result) == null ? void 0 : _a2.state) === "run";
|
||
|
}).length;
|
||
|
const numTodoTests = tests.filter((t) => t.mode === "todo").length;
|
||
|
const testResults = [];
|
||
|
const success = numFailedTestSuites === 0 && numFailedTests === 0;
|
||
|
for (const file of files) {
|
||
|
const tests2 = getTests([file]);
|
||
|
let startTime = tests2.reduce((prev, next) => {
|
||
|
var _a2;
|
||
|
return Math.min(prev, ((_a2 = next.result) == null ? void 0 : _a2.startTime) ?? Infinity);
|
||
|
}, Infinity);
|
||
|
if (startTime === Infinity)
|
||
|
startTime = this.start;
|
||
|
const endTime = tests2.reduce((prev, next) => {
|
||
|
var _a2, _b2;
|
||
|
return Math.max(prev, (((_a2 = next.result) == null ? void 0 : _a2.startTime) ?? 0) + (((_b2 = next.result) == null ? void 0 : _b2.duration) ?? 0));
|
||
|
}, startTime);
|
||
|
const assertionResults = await Promise.all(tests2.map(async (t) => {
|
||
|
var _a2, _b2, _c2, _d;
|
||
|
const ancestorTitles = [];
|
||
|
let iter = t.suite;
|
||
|
while (iter) {
|
||
|
ancestorTitles.push(iter.name);
|
||
|
iter = iter.suite;
|
||
|
}
|
||
|
ancestorTitles.reverse();
|
||
|
return {
|
||
|
ancestorTitles,
|
||
|
fullName: ancestorTitles.length > 0 ? `${ancestorTitles.join(" ")} ${t.name}` : t.name,
|
||
|
status: StatusMap[((_a2 = t.result) == null ? void 0 : _a2.state) || t.mode] || "skipped",
|
||
|
title: t.name,
|
||
|
duration: (_b2 = t.result) == null ? void 0 : _b2.duration,
|
||
|
failureMessages: ((_d = (_c2 = t.result) == null ? void 0 : _c2.errors) == null ? void 0 : _d.map((e) => e.message)) || [],
|
||
|
location: await this.getFailureLocation(t)
|
||
|
};
|
||
|
}));
|
||
|
if (tests2.some((t) => {
|
||
|
var _a2;
|
||
|
return ((_a2 = t.result) == null ? void 0 : _a2.state) === "run";
|
||
|
})) {
|
||
|
this.ctx.logger.warn("WARNING: Some tests are still running when generating the JSON report.This is likely an internal bug in Vitest.Please report it to https://github.com/vitest-dev/vitest/issues");
|
||
|
}
|
||
|
testResults.push({
|
||
|
assertionResults,
|
||
|
startTime,
|
||
|
endTime,
|
||
|
status: tests2.some((t) => {
|
||
|
var _a2;
|
||
|
return ((_a2 = t.result) == null ? void 0 : _a2.state) === "fail";
|
||
|
}) ? "failed" : "passed",
|
||
|
message: ((_c = (_b = (_a = file.result) == null ? void 0 : _a.errors) == null ? void 0 : _b[0]) == null ? void 0 : _c.message) ?? "",
|
||
|
name: file.filepath
|
||
|
});
|
||
|
}
|
||
|
const result = {
|
||
|
numTotalTestSuites,
|
||
|
numPassedTestSuites,
|
||
|
numFailedTestSuites,
|
||
|
numPendingTestSuites,
|
||
|
numTotalTests,
|
||
|
numPassedTests,
|
||
|
numFailedTests,
|
||
|
numPendingTests,
|
||
|
numTodoTests,
|
||
|
startTime: this.start,
|
||
|
success,
|
||
|
testResults
|
||
|
};
|
||
|
await this.writeReport(JSON.stringify(result, null, 2));
|
||
|
}
|
||
|
async onFinished(files = this.ctx.state.getFiles()) {
|
||
|
await this.logTasks(files);
|
||
|
}
|
||
|
async writeReport(report) {
|
||
|
const outputFile = getOutputFile(this.ctx.config, "json");
|
||
|
if (outputFile) {
|
||
|
const reportFile = resolve(this.ctx.config.root, outputFile);
|
||
|
const outputDirectory = dirname(reportFile);
|
||
|
if (!existsSync(outputDirectory))
|
||
|
await promises.mkdir(outputDirectory, { recursive: true });
|
||
|
await promises.writeFile(reportFile, report, "utf-8");
|
||
|
this.ctx.logger.log(`JSON report written to ${reportFile}`);
|
||
|
} else {
|
||
|
this.ctx.logger.log(report);
|
||
|
}
|
||
|
}
|
||
|
async getFailureLocation(test) {
|
||
|
var _a, _b;
|
||
|
const error = (_b = (_a = test.result) == null ? void 0 : _a.errors) == null ? void 0 : _b[0];
|
||
|
if (!error)
|
||
|
return;
|
||
|
const stack = parseStacktrace(error);
|
||
|
const frame = stack[stack.length - 1];
|
||
|
if (!frame)
|
||
|
return;
|
||
|
return { line: frame.line, column: frame.column };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class VerboseReporter extends DefaultReporter {
|
||
|
constructor() {
|
||
|
super();
|
||
|
this.rendererOptions.renderSucceed = true;
|
||
|
}
|
||
|
onTaskUpdate(packs) {
|
||
|
var _a, _b, _c;
|
||
|
if (this.isTTY)
|
||
|
return;
|
||
|
for (const pack of packs) {
|
||
|
const task = this.ctx.state.idMap.get(pack[0]);
|
||
|
if (task && task.type === "test" && ((_a = task.result) == null ? void 0 : _a.state) && ((_b = task.result) == null ? void 0 : _b.state) !== "run") {
|
||
|
let title = ` ${getStateSymbol(task)} ${getFullName(task)}`;
|
||
|
if (this.ctx.config.logHeapUsage && task.result.heap != null)
|
||
|
title += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`);
|
||
|
this.ctx.logger.log(title);
|
||
|
if (task.result.state === "fail") {
|
||
|
(_c = task.result.errors) == null ? void 0 : _c.forEach((error) => {
|
||
|
this.ctx.logger.log(c.red(` ${F_RIGHT} ${error == null ? void 0 : error.message}`));
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class IndentedLogger {
|
||
|
constructor(baseLog) {
|
||
|
this.baseLog = baseLog;
|
||
|
this.currentIndent = "";
|
||
|
}
|
||
|
indent() {
|
||
|
this.currentIndent += " ";
|
||
|
}
|
||
|
unindent() {
|
||
|
this.currentIndent = this.currentIndent.substring(0, this.currentIndent.length - 4);
|
||
|
}
|
||
|
log(text) {
|
||
|
return this.baseLog(this.currentIndent + text);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function yamlString(str) {
|
||
|
return `"${str.replace(/"/g, '\\"')}"`;
|
||
|
}
|
||
|
function tapString(str) {
|
||
|
return str.replace(/\\/g, "\\\\").replace(/#/g, "\\#").replace(/\n/g, " ");
|
||
|
}
|
||
|
class TapReporter {
|
||
|
onInit(ctx) {
|
||
|
this.ctx = ctx;
|
||
|
this.logger = new IndentedLogger(ctx.logger.log.bind(ctx.logger));
|
||
|
}
|
||
|
static getComment(task) {
|
||
|
var _a;
|
||
|
if (task.mode === "skip")
|
||
|
return " # SKIP";
|
||
|
else if (task.mode === "todo")
|
||
|
return " # TODO";
|
||
|
else if (((_a = task.result) == null ? void 0 : _a.duration) != null)
|
||
|
return ` # time=${task.result.duration.toFixed(2)}ms`;
|
||
|
else
|
||
|
return "";
|
||
|
}
|
||
|
logErrorDetails(error, stack) {
|
||
|
this.logger.log(`name: ${yamlString(error.name)}`);
|
||
|
this.logger.log(`message: ${yamlString(error.message)}`);
|
||
|
if (stack) {
|
||
|
this.logger.log(`stack: ${yamlString(`${stack.file}:${stack.line}:${stack.column}`)}`);
|
||
|
}
|
||
|
}
|
||
|
logTasks(tasks) {
|
||
|
var _a, _b;
|
||
|
this.logger.log(`1..${tasks.length}`);
|
||
|
for (const [i, task] of tasks.entries()) {
|
||
|
const id = i + 1;
|
||
|
const ok = ((_a = task.result) == null ? void 0 : _a.state) === "pass" || task.mode === "skip" || task.mode === "todo" ? "ok" : "not ok";
|
||
|
const comment = TapReporter.getComment(task);
|
||
|
if (task.type === "suite" && task.tasks.length > 0) {
|
||
|
this.logger.log(`${ok} ${id} - ${tapString(task.name)}${comment} {`);
|
||
|
this.logger.indent();
|
||
|
this.logTasks(task.tasks);
|
||
|
this.logger.unindent();
|
||
|
this.logger.log("}");
|
||
|
} else {
|
||
|
this.logger.log(`${ok} ${id} - ${tapString(task.name)}${comment}`);
|
||
|
if (((_b = task.result) == null ? void 0 : _b.state) === "fail" && task.result.errors) {
|
||
|
this.logger.indent();
|
||
|
task.result.errors.forEach((error) => {
|
||
|
const stacks = parseStacktrace(error);
|
||
|
const stack = stacks[0];
|
||
|
this.logger.log("---");
|
||
|
this.logger.log("error:");
|
||
|
this.logger.indent();
|
||
|
this.logErrorDetails(error);
|
||
|
this.logger.unindent();
|
||
|
if (stack)
|
||
|
this.logger.log(`at: ${yamlString(`${stack.file}:${stack.line}:${stack.column}`)}`);
|
||
|
if (error.showDiff) {
|
||
|
this.logger.log(`actual: ${yamlString(error.actual)}`);
|
||
|
this.logger.log(`expected: ${yamlString(error.expected)}`);
|
||
|
}
|
||
|
});
|
||
|
this.logger.log("...");
|
||
|
this.logger.unindent();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
async onFinished(files = this.ctx.state.getFiles()) {
|
||
|
this.logger.log("TAP version 13");
|
||
|
this.logTasks(files);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function flattenTasks$1(task, baseName = "") {
|
||
|
const base = baseName ? `${baseName} > ` : "";
|
||
|
if (task.type === "suite") {
|
||
|
return task.tasks.flatMap((child) => flattenTasks$1(child, `${base}${task.name}`));
|
||
|
} else {
|
||
|
return [{
|
||
|
...task,
|
||
|
name: `${base}${task.name}`
|
||
|
}];
|
||
|
}
|
||
|
}
|
||
|
function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
|
||
|
let regex = /((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))/g;
|
||
|
value = String(value || "").replace(regex, "");
|
||
|
if (removeDiscouragedChars) {
|
||
|
regex = new RegExp(
|
||
|
"([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|(?:\\uD83F[\\uDFFE\\uDFFF])|(?:\\uD87F[\\uDFFE\\uDFFF])|(?:\\uD8BF[\\uDFFE\\uDFFF])|(?:\\uD8FF[\\uDFFE\\uDFFF])|(?:\\uD93F[\\uDFFE\\uDFFF])|(?:\\uD97F[\\uDFFE\\uDFFF])|(?:\\uD9BF[\\uDFFE\\uDFFF])|(?:\\uD9FF[\\uDFFE\\uDFFF])|(?:\\uDA3F[\\uDFFE\\uDFFF])|(?:\\uDA7F[\\uDFFE\\uDFFF])|(?:\\uDABF[\\uDFFE\\uDFFF])|(?:\\uDAFF[\\uDFFE\\uDFFF])|(?:\\uDB3F[\\uDFFE\\uDFFF])|(?:\\uDB7F[\\uDFFE\\uDFFF])|(?:\\uDBBF[\\uDFFE\\uDFFF])|(?:\\uDBFF[\\uDFFE\\uDFFF])(?:[\\0-\\t\\x0B\\f\\x0E-\\u2027\\u202A-\\uD7FF\\uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))",
|
||
|
"g"
|
||
|
);
|
||
|
value = value.replace(regex, "");
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
function escapeXML(value) {
|
||
|
return removeInvalidXMLCharacters(
|
||
|
String(value).replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">"),
|
||
|
true
|
||
|
);
|
||
|
}
|
||
|
function executionTime(durationMS) {
|
||
|
return (durationMS / 1e3).toLocaleString("en-US", { useGrouping: false, maximumFractionDigits: 10 });
|
||
|
}
|
||
|
function getDuration(task) {
|
||
|
var _a;
|
||
|
const duration = ((_a = task.result) == null ? void 0 : _a.duration) ?? 0;
|
||
|
return executionTime(duration);
|
||
|
}
|
||
|
class JUnitReporter {
|
||
|
constructor() {
|
||
|
this._timeStart = new Date();
|
||
|
}
|
||
|
async onInit(ctx) {
|
||
|
this.ctx = ctx;
|
||
|
const outputFile = getOutputFile(this.ctx.config, "junit");
|
||
|
if (outputFile) {
|
||
|
this.reportFile = resolve(this.ctx.config.root, outputFile);
|
||
|
const outputDirectory = dirname(this.reportFile);
|
||
|
if (!existsSync(outputDirectory))
|
||
|
await promises.mkdir(outputDirectory, { recursive: true });
|
||
|
const fileFd = await promises.open(this.reportFile, "w+");
|
||
|
this.baseLog = async (text) => await promises.writeFile(fileFd, `${text}
|
||
|
`);
|
||
|
} else {
|
||
|
this.baseLog = async (text) => this.ctx.logger.log(text);
|
||
|
}
|
||
|
this._timeStart = new Date();
|
||
|
this.logger = new IndentedLogger(this.baseLog);
|
||
|
}
|
||
|
async writeElement(name, attrs, children) {
|
||
|
const pairs = [];
|
||
|
for (const key in attrs) {
|
||
|
const attr = attrs[key];
|
||
|
if (attr === void 0)
|
||
|
continue;
|
||
|
pairs.push(`${key}="${escapeXML(attr)}"`);
|
||
|
}
|
||
|
await this.logger.log(`<${name}${pairs.length ? ` ${pairs.join(" ")}` : ""}>`);
|
||
|
this.logger.indent();
|
||
|
await children.call(this);
|
||
|
this.logger.unindent();
|
||
|
await this.logger.log(`</${name}>`);
|
||
|
}
|
||
|
async writeErrorDetails(error) {
|
||
|
const errorName = error.name ?? error.nameStr ?? "Unknown Error";
|
||
|
const errorDetails = `${errorName}: ${error.message}`;
|
||
|
await this.baseLog(escapeXML(errorDetails));
|
||
|
const stack = parseStacktrace(error);
|
||
|
for (const frame of stack) {
|
||
|
const path = relative(this.ctx.config.root, frame.file);
|
||
|
await this.baseLog(` ${F_POINTER} ${[frame.method, `${path}:${frame.line}:${frame.column}`].filter(Boolean).join(" ")}`);
|
||
|
if (frame.file in this.ctx.state.filesMap)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
async writeLogs(task, type) {
|
||
|
if (task.logs == null || task.logs.length === 0)
|
||
|
return;
|
||
|
const logType = type === "err" ? "stderr" : "stdout";
|
||
|
const logs = task.logs.filter((log) => log.type === logType);
|
||
|
if (logs.length === 0)
|
||
|
return;
|
||
|
await this.writeElement(`system-${type}`, {}, async () => {
|
||
|
for (const log of logs)
|
||
|
await this.baseLog(escapeXML(log.content));
|
||
|
});
|
||
|
}
|
||
|
async writeTasks(tasks, filename) {
|
||
|
for (const task of tasks) {
|
||
|
await this.writeElement("testcase", {
|
||
|
classname: filename,
|
||
|
name: task.name,
|
||
|
time: getDuration(task)
|
||
|
}, async () => {
|
||
|
var _a, _b;
|
||
|
await this.writeLogs(task, "out");
|
||
|
await this.writeLogs(task, "err");
|
||
|
if (task.mode === "skip" || task.mode === "todo")
|
||
|
await this.logger.log("<skipped/>");
|
||
|
if (((_a = task.result) == null ? void 0 : _a.state) === "fail") {
|
||
|
const promises = ((_b = task.result.errors) == null ? void 0 : _b.map(async (error) => {
|
||
|
await this.writeElement("failure", {
|
||
|
message: error == null ? void 0 : error.message,
|
||
|
type: (error == null ? void 0 : error.name) ?? (error == null ? void 0 : error.nameStr)
|
||
|
}, async () => {
|
||
|
if (!error)
|
||
|
return;
|
||
|
await this.writeErrorDetails(error);
|
||
|
});
|
||
|
})) || [];
|
||
|
await Promise.all(promises);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
async onFinished(files = this.ctx.state.getFiles()) {
|
||
|
await this.logger.log('<?xml version="1.0" encoding="UTF-8" ?>');
|
||
|
const transformed = files.map((file) => {
|
||
|
const tasks = file.tasks.flatMap((task) => flattenTasks$1(task));
|
||
|
const stats2 = tasks.reduce(
|
||
|
(stats3, task) => {
|
||
|
var _a, _b;
|
||
|
return {
|
||
|
passed: stats3.passed + Number(((_a = task.result) == null ? void 0 : _a.state) === "pass"),
|
||
|
failures: stats3.failures + Number(((_b = task.result) == null ? void 0 : _b.state) === "fail"),
|
||
|
skipped: stats3.skipped + Number(task.mode === "skip" || task.mode === "todo")
|
||
|
};
|
||
|
},
|
||
|
{
|
||
|
passed: 0,
|
||
|
failures: 0,
|
||
|
skipped: 0
|
||
|
}
|
||
|
);
|
||
|
return {
|
||
|
...file,
|
||
|
tasks,
|
||
|
stats: stats2
|
||
|
};
|
||
|
});
|
||
|
const stats = transformed.reduce((stats2, file) => {
|
||
|
stats2.tests += file.tasks.length;
|
||
|
stats2.failures += file.stats.failures;
|
||
|
return stats2;
|
||
|
}, {
|
||
|
name: process.env.VITEST_JUNIT_SUITE_NAME || "vitest tests",
|
||
|
tests: 0,
|
||
|
failures: 0,
|
||
|
errors: 0,
|
||
|
time: executionTime(new Date().getTime() - this._timeStart.getTime())
|
||
|
});
|
||
|
await this.writeElement("testsuites", stats, async () => {
|
||
|
for (const file of transformed) {
|
||
|
await this.writeElement("testsuite", {
|
||
|
name: file.name,
|
||
|
timestamp: new Date().toISOString(),
|
||
|
hostname: hostname(),
|
||
|
tests: file.tasks.length,
|
||
|
failures: file.stats.failures,
|
||
|
errors: 0,
|
||
|
skipped: file.stats.skipped,
|
||
|
time: getDuration(file)
|
||
|
}, async () => {
|
||
|
await this.writeTasks(file.tasks, file.name);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
if (this.reportFile)
|
||
|
this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function flattenTasks(task, baseName = "") {
|
||
|
const base = baseName ? `${baseName} > ` : "";
|
||
|
if (task.type === "suite" && task.tasks.length > 0) {
|
||
|
return task.tasks.flatMap((child) => flattenTasks(child, `${base}${task.name}`));
|
||
|
} else {
|
||
|
return [{
|
||
|
...task,
|
||
|
name: `${base}${task.name}`
|
||
|
}];
|
||
|
}
|
||
|
}
|
||
|
class TapFlatReporter extends TapReporter {
|
||
|
onInit(ctx) {
|
||
|
super.onInit(ctx);
|
||
|
}
|
||
|
async onFinished(files = this.ctx.state.getFiles()) {
|
||
|
this.ctx.logger.log("TAP version 13");
|
||
|
const flatTasks = files.flatMap((task) => flattenTasks(task));
|
||
|
this.logTasks(flatTasks);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class JsonReporter {
|
||
|
constructor() {
|
||
|
this.start = 0;
|
||
|
}
|
||
|
onInit(ctx) {
|
||
|
this.ctx = ctx;
|
||
|
}
|
||
|
async logTasks(files) {
|
||
|
var _a;
|
||
|
const suites = getSuites(files);
|
||
|
const numTotalTestSuites = suites.length;
|
||
|
const tests = getTests(files);
|
||
|
const numTotalTests = tests.length;
|
||
|
const testResults = {};
|
||
|
const outputFile = getOutputFile(this.ctx.config.benchmark, "json");
|
||
|
for (const file of files) {
|
||
|
const tests2 = getTests([file]);
|
||
|
for (const test of tests2) {
|
||
|
const res = (_a = test.result) == null ? void 0 : _a.benchmark;
|
||
|
if (!res || test.mode === "skip")
|
||
|
continue;
|
||
|
if (!outputFile)
|
||
|
res.samples = "ignore on terminal";
|
||
|
testResults[test.suite.name] = (testResults[test.suite.name] || []).concat(res);
|
||
|
}
|
||
|
if (tests2.some((t) => {
|
||
|
var _a2;
|
||
|
return ((_a2 = t.result) == null ? void 0 : _a2.state) === "run";
|
||
|
})) {
|
||
|
this.ctx.logger.warn("WARNING: Some tests are still running when generating the json report.This is likely an internal bug in Vitest.Please report it to https://github.com/vitest-dev/vitest/issues");
|
||
|
}
|
||
|
}
|
||
|
const result = {
|
||
|
numTotalTestSuites,
|
||
|
numTotalTests,
|
||
|
testResults
|
||
|
};
|
||
|
await this.writeReport(JSON.stringify(result, null, 2));
|
||
|
}
|
||
|
async onFinished(files = this.ctx.state.getFiles()) {
|
||
|
await this.logTasks(files);
|
||
|
}
|
||
|
async writeReport(report) {
|
||
|
const outputFile = getOutputFile(this.ctx.config.benchmark, "json");
|
||
|
if (outputFile) {
|
||
|
const reportFile = resolve(this.ctx.config.root, outputFile);
|
||
|
const outputDirectory = dirname(reportFile);
|
||
|
if (!existsSync(outputDirectory))
|
||
|
await promises.mkdir(outputDirectory, { recursive: true });
|
||
|
await promises.writeFile(reportFile, report, "utf-8");
|
||
|
this.ctx.logger.log(`json report written to ${reportFile}`);
|
||
|
} else {
|
||
|
this.ctx.logger.log(report);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const DURATION_LONG = 300;
|
||
|
const outputMap = /* @__PURE__ */ new WeakMap();
|
||
|
function formatFilepath(path) {
|
||
|
const lastSlash = Math.max(path.lastIndexOf("/") + 1, 0);
|
||
|
const basename = path.slice(lastSlash);
|
||
|
let firstDot = basename.indexOf(".");
|
||
|
if (firstDot < 0)
|
||
|
firstDot = basename.length;
|
||
|
firstDot += lastSlash;
|
||
|
return c.dim(path.slice(0, lastSlash)) + path.slice(lastSlash, firstDot) + c.dim(path.slice(firstDot));
|
||
|
}
|
||
|
function formatNumber(number) {
|
||
|
const res = String(number.toFixed(number < 100 ? 4 : 2)).split(".");
|
||
|
return res[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ",") + (res[1] ? `.${res[1]}` : "");
|
||
|
}
|
||
|
const tableHead = ["name", "hz", "min", "max", "mean", "p75", "p99", "p995", "p999", "rme", "samples"];
|
||
|
function renderTableHead(tasks) {
|
||
|
const benchs = tasks.map((i) => {
|
||
|
var _a;
|
||
|
return i.type === "benchmark" ? (_a = i.result) == null ? void 0 : _a.benchmark : void 0;
|
||
|
}).filter(notNullish);
|
||
|
const allItems = benchs.map(renderBenchmarkItems).concat([tableHead]);
|
||
|
return `${" ".repeat(3)}${tableHead.map((i, idx) => {
|
||
|
const width = Math.max(...allItems.map((i2) => i2[idx].length));
|
||
|
return idx ? i.padStart(width, " ") : i.padEnd(width, " ");
|
||
|
}).map(c.bold).join(" ")}`;
|
||
|
}
|
||
|
function renderBenchmarkItems(result) {
|
||
|
return [
|
||
|
result.name,
|
||
|
formatNumber(result.hz || 0),
|
||
|
formatNumber(result.min || 0),
|
||
|
formatNumber(result.max || 0),
|
||
|
formatNumber(result.mean || 0),
|
||
|
formatNumber(result.p75 || 0),
|
||
|
formatNumber(result.p99 || 0),
|
||
|
formatNumber(result.p995 || 0),
|
||
|
formatNumber(result.p999 || 0),
|
||
|
`\xB1${(result.rme || 0).toFixed(2)}%`,
|
||
|
result.samples.length.toString()
|
||
|
];
|
||
|
}
|
||
|
function renderBenchmark(task, tasks) {
|
||
|
var _a;
|
||
|
const result = (_a = task.result) == null ? void 0 : _a.benchmark;
|
||
|
if (!result)
|
||
|
return task.name;
|
||
|
const benchs = tasks.map((i) => {
|
||
|
var _a2;
|
||
|
return i.type === "benchmark" ? (_a2 = i.result) == null ? void 0 : _a2.benchmark : void 0;
|
||
|
}).filter(notNullish);
|
||
|
const allItems = benchs.map(renderBenchmarkItems).concat([tableHead]);
|
||
|
const items = renderBenchmarkItems(result);
|
||
|
const padded = items.map((i, idx) => {
|
||
|
const width = Math.max(...allItems.map((i2) => i2[idx].length));
|
||
|
return idx ? i.padStart(width, " ") : i.padEnd(width, " ");
|
||
|
});
|
||
|
return [
|
||
|
padded[0],
|
||
|
c.blue(padded[1]),
|
||
|
c.cyan(padded[2]),
|
||
|
c.cyan(padded[3]),
|
||
|
c.cyan(padded[4]),
|
||
|
c.cyan(padded[5]),
|
||
|
c.cyan(padded[6]),
|
||
|
c.cyan(padded[7]),
|
||
|
c.cyan(padded[8]),
|
||
|
c.dim(padded[9]),
|
||
|
c.dim(padded[10]),
|
||
|
result.rank === 1 ? c.bold(c.green(" fastest")) : result.rank === benchs.length && benchs.length > 2 ? c.bold(c.gray(" slowest")) : ""
|
||
|
].join(" ");
|
||
|
}
|
||
|
function renderTree(tasks, options, level = 0) {
|
||
|
var _a, _b, _c, _d;
|
||
|
let output = [];
|
||
|
let idx = 0;
|
||
|
for (const task of tasks) {
|
||
|
const padding = " ".repeat(level ? 1 : 0);
|
||
|
let prefix = "";
|
||
|
if (idx === 0 && task.type === "benchmark")
|
||
|
prefix += `${renderTableHead(tasks)}
|
||
|
${padding}`;
|
||
|
prefix += ` ${getStateSymbol(task)} `;
|
||
|
let suffix = "";
|
||
|
if (task.type === "suite")
|
||
|
suffix += c.dim(` (${getTests(task).length})`);
|
||
|
if (task.mode === "skip" || task.mode === "todo")
|
||
|
suffix += ` ${c.dim(c.gray("[skipped]"))}`;
|
||
|
if (((_a = task.result) == null ? void 0 : _a.duration) != null) {
|
||
|
if (task.result.duration > DURATION_LONG)
|
||
|
suffix += c.yellow(` ${Math.round(task.result.duration)}${c.dim("ms")}`);
|
||
|
}
|
||
|
if (options.showHeap && ((_b = task.result) == null ? void 0 : _b.heap) != null)
|
||
|
suffix += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`);
|
||
|
let name = task.name;
|
||
|
if (level === 0)
|
||
|
name = formatFilepath(name);
|
||
|
const body = task.type === "benchmark" ? renderBenchmark(task, tasks) : name;
|
||
|
output.push(padding + prefix + body + suffix);
|
||
|
if (((_c = task.result) == null ? void 0 : _c.state) !== "pass" && outputMap.get(task) != null) {
|
||
|
let data = outputMap.get(task);
|
||
|
if (typeof data === "string") {
|
||
|
data = stripAnsi(data.trim().split("\n").filter(Boolean).pop());
|
||
|
if (data === "")
|
||
|
data = void 0;
|
||
|
}
|
||
|
if (data != null) {
|
||
|
const out = `${" ".repeat(level)}${F_RIGHT} ${data}`;
|
||
|
output.push(` ${c.gray(cliTruncate(out, getCols(-3)))}`);
|
||
|
}
|
||
|
}
|
||
|
if (task.type === "suite" && task.tasks.length > 0) {
|
||
|
if ((_d = task.result) == null ? void 0 : _d.state)
|
||
|
output = output.concat(renderTree(task.tasks, options, level + 1));
|
||
|
}
|
||
|
idx++;
|
||
|
}
|
||
|
return output.filter(Boolean).join("\n");
|
||
|
}
|
||
|
const createTableRenderer = (_tasks, options) => {
|
||
|
let tasks = _tasks;
|
||
|
let timer;
|
||
|
const log = options.logger.logUpdate;
|
||
|
function update() {
|
||
|
log(renderTree(tasks, options));
|
||
|
}
|
||
|
return {
|
||
|
start() {
|
||
|
if (timer)
|
||
|
return this;
|
||
|
timer = safeSetInterval(update, 200);
|
||
|
return this;
|
||
|
},
|
||
|
update(_tasks2) {
|
||
|
tasks = _tasks2;
|
||
|
update();
|
||
|
return this;
|
||
|
},
|
||
|
async stop() {
|
||
|
if (timer) {
|
||
|
safeClearInterval(timer);
|
||
|
timer = void 0;
|
||
|
}
|
||
|
log.clear();
|
||
|
options.logger.log(renderTree(tasks, options));
|
||
|
return this;
|
||
|
},
|
||
|
clear() {
|
||
|
log.clear();
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
class TableReporter extends BaseReporter {
|
||
|
constructor() {
|
||
|
super(...arguments);
|
||
|
this.rendererOptions = {};
|
||
|
}
|
||
|
async onTestRemoved(trigger) {
|
||
|
await this.stopListRender();
|
||
|
this.ctx.logger.clearScreen(c.yellow("Test removed...") + (trigger ? c.dim(` [ ${this.relative(trigger)} ]
|
||
|
`) : ""), true);
|
||
|
const files = this.ctx.state.getFiles(this.watchFilters);
|
||
|
createTableRenderer(files, this.rendererOptions).stop();
|
||
|
this.ctx.logger.log();
|
||
|
await super.reportSummary(files);
|
||
|
super.onWatcherStart();
|
||
|
}
|
||
|
onCollected() {
|
||
|
if (this.isTTY) {
|
||
|
this.rendererOptions.logger = this.ctx.logger;
|
||
|
this.rendererOptions.showHeap = this.ctx.config.logHeapUsage;
|
||
|
const files = this.ctx.state.getFiles(this.watchFilters);
|
||
|
if (!this.renderer)
|
||
|
this.renderer = createTableRenderer(files, this.rendererOptions).start();
|
||
|
else
|
||
|
this.renderer.update(files);
|
||
|
}
|
||
|
}
|
||
|
async onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
||
|
await this.stopListRender();
|
||
|
this.ctx.logger.log();
|
||
|
await super.onFinished(files, errors);
|
||
|
}
|
||
|
async onWatcherStart() {
|
||
|
await this.stopListRender();
|
||
|
await super.onWatcherStart();
|
||
|
}
|
||
|
async stopListRender() {
|
||
|
var _a;
|
||
|
await ((_a = this.renderer) == null ? void 0 : _a.stop());
|
||
|
this.renderer = void 0;
|
||
|
}
|
||
|
async onWatcherRerun(files, trigger) {
|
||
|
await this.stopListRender();
|
||
|
await super.onWatcherRerun(files, trigger);
|
||
|
}
|
||
|
onUserConsoleLog(log) {
|
||
|
var _a;
|
||
|
if (!this.shouldLog(log))
|
||
|
return;
|
||
|
(_a = this.renderer) == null ? void 0 : _a.clear();
|
||
|
super.onUserConsoleLog(log);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const BenchmarkReportsMap = {
|
||
|
default: TableReporter,
|
||
|
verbose: VerboseReporter,
|
||
|
json: JsonReporter
|
||
|
};
|
||
|
|
||
|
const ReportersMap = {
|
||
|
"default": DefaultReporter,
|
||
|
"verbose": VerboseReporter,
|
||
|
"dot": DotReporter,
|
||
|
"json": JsonReporter$1,
|
||
|
"tap": TapReporter,
|
||
|
"tap-flat": TapFlatReporter,
|
||
|
"junit": JUnitReporter
|
||
|
};
|
||
|
|
||
|
async function loadCustomReporterModule(path, runner) {
|
||
|
let customReporterModule;
|
||
|
try {
|
||
|
customReporterModule = await runner.executeId(path);
|
||
|
} catch (customReporterModuleError) {
|
||
|
throw new Error(`Failed to load custom Reporter from ${path}`, { cause: customReporterModuleError });
|
||
|
}
|
||
|
if (customReporterModule.default === null || customReporterModule.default === void 0)
|
||
|
throw new Error(`Custom reporter loaded from ${path} was not the default export`);
|
||
|
return customReporterModule.default;
|
||
|
}
|
||
|
function createReporters(reporterReferences, runner) {
|
||
|
const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
|
||
|
if (typeof referenceOrInstance === "string") {
|
||
|
if (referenceOrInstance === "html") {
|
||
|
await ensurePackageInstalled("@vitest/ui", runner.root);
|
||
|
const CustomReporter = await loadCustomReporterModule("@vitest/ui/reporter", runner);
|
||
|
return new CustomReporter();
|
||
|
} else if (referenceOrInstance in ReportersMap) {
|
||
|
const BuiltinReporter = ReportersMap[referenceOrInstance];
|
||
|
return new BuiltinReporter();
|
||
|
} else {
|
||
|
const CustomReporter = await loadCustomReporterModule(referenceOrInstance, runner);
|
||
|
return new CustomReporter();
|
||
|
}
|
||
|
}
|
||
|
return referenceOrInstance;
|
||
|
});
|
||
|
return Promise.all(promisedReporters);
|
||
|
}
|
||
|
function createBenchmarkReporters(reporterReferences, runner) {
|
||
|
const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
|
||
|
if (typeof referenceOrInstance === "string") {
|
||
|
if (referenceOrInstance in BenchmarkReportsMap) {
|
||
|
const BuiltinReporter = BenchmarkReportsMap[referenceOrInstance];
|
||
|
return new BuiltinReporter();
|
||
|
} else {
|
||
|
const CustomReporter = await loadCustomReporterModule(referenceOrInstance, runner);
|
||
|
return new CustomReporter();
|
||
|
}
|
||
|
}
|
||
|
return referenceOrInstance;
|
||
|
});
|
||
|
return Promise.all(promisedReporters);
|
||
|
}
|
||
|
|
||
|
const isAggregateError = (err) => {
|
||
|
if (typeof AggregateError !== "undefined" && err instanceof AggregateError)
|
||
|
return true;
|
||
|
return err instanceof Error && "errors" in err;
|
||
|
};
|
||
|
class StateManager {
|
||
|
constructor() {
|
||
|
this.filesMap = /* @__PURE__ */ new Map();
|
||
|
this.pathsSet = /* @__PURE__ */ new Set();
|
||
|
this.collectingPromise = void 0;
|
||
|
this.idMap = /* @__PURE__ */ new Map();
|
||
|
this.taskFileMap = /* @__PURE__ */ new WeakMap();
|
||
|
this.errorsSet = /* @__PURE__ */ new Set();
|
||
|
}
|
||
|
catchError(err, type) {
|
||
|
if (isAggregateError(err))
|
||
|
return err.errors.forEach((error) => this.catchError(error, type));
|
||
|
err.type = type;
|
||
|
this.errorsSet.add(err);
|
||
|
}
|
||
|
clearErrors() {
|
||
|
this.errorsSet.clear();
|
||
|
}
|
||
|
getUnhandledErrors() {
|
||
|
return Array.from(this.errorsSet.values());
|
||
|
}
|
||
|
startCollectingPaths() {
|
||
|
let _resolve;
|
||
|
const promise = new Promise((resolve) => {
|
||
|
_resolve = resolve;
|
||
|
});
|
||
|
this.collectingPromise = { promise, resolve: _resolve };
|
||
|
}
|
||
|
finishCollectingPaths() {
|
||
|
var _a;
|
||
|
(_a = this.collectingPromise) == null ? void 0 : _a.resolve();
|
||
|
this.collectingPromise = void 0;
|
||
|
}
|
||
|
async getPaths() {
|
||
|
var _a;
|
||
|
await ((_a = this.collectingPromise) == null ? void 0 : _a.promise);
|
||
|
return Array.from(this.pathsSet);
|
||
|
}
|
||
|
getFiles(keys) {
|
||
|
if (keys)
|
||
|
return keys.map((key) => this.filesMap.get(key)).filter(Boolean);
|
||
|
return Array.from(this.filesMap.values());
|
||
|
}
|
||
|
getFilepaths() {
|
||
|
return Array.from(this.filesMap.keys());
|
||
|
}
|
||
|
getFailedFilepaths() {
|
||
|
return this.getFiles().filter((i) => {
|
||
|
var _a;
|
||
|
return ((_a = i.result) == null ? void 0 : _a.state) === "fail";
|
||
|
}).map((i) => i.filepath);
|
||
|
}
|
||
|
collectPaths(paths = []) {
|
||
|
paths.forEach((path) => {
|
||
|
this.pathsSet.add(path);
|
||
|
});
|
||
|
}
|
||
|
collectFiles(files = []) {
|
||
|
files.forEach((file) => {
|
||
|
this.filesMap.set(file.filepath, file);
|
||
|
this.updateId(file);
|
||
|
});
|
||
|
}
|
||
|
clearFiles(paths = []) {
|
||
|
paths.forEach((path) => {
|
||
|
this.filesMap.delete(path);
|
||
|
});
|
||
|
}
|
||
|
updateId(task) {
|
||
|
if (this.idMap.get(task.id) === task)
|
||
|
return;
|
||
|
this.idMap.set(task.id, task);
|
||
|
if (task.type === "suite") {
|
||
|
task.tasks.forEach((task2) => {
|
||
|
this.updateId(task2);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
updateTasks(packs) {
|
||
|
for (const [id, result] of packs) {
|
||
|
if (this.idMap.has(id))
|
||
|
this.idMap.get(id).result = result;
|
||
|
}
|
||
|
}
|
||
|
updateUserLog(log) {
|
||
|
const task = log.taskId && this.idMap.get(log.taskId);
|
||
|
if (task) {
|
||
|
if (!task.logs)
|
||
|
task.logs = [];
|
||
|
task.logs.push(log);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const defaultInclude = ["**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"];
|
||
|
const defaultExclude = ["**/node_modules/**", "**/dist/**", "**/cypress/**", "**/.{idea,git,cache,output,temp}/**", "**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress}.config.*"];
|
||
|
const benchmarkConfigDefaults = {
|
||
|
include: ["**/*.{bench,benchmark}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
||
|
exclude: defaultExclude,
|
||
|
includeSource: [],
|
||
|
reporters: ["default"]
|
||
|
};
|
||
|
const defaultCoverageExcludes = [
|
||
|
"coverage/**",
|
||
|
"dist/**",
|
||
|
"packages/*/test{,s}/**",
|
||
|
"**/*.d.ts",
|
||
|
"cypress/**",
|
||
|
"test{,s}/**",
|
||
|
"test{,-*}.{js,cjs,mjs,ts,tsx,jsx}",
|
||
|
"**/*{.,-}test.{js,cjs,mjs,ts,tsx,jsx}",
|
||
|
"**/*{.,-}spec.{js,cjs,mjs,ts,tsx,jsx}",
|
||
|
"**/__tests__/**",
|
||
|
"**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress}.config.*",
|
||
|
"**/.{eslint,mocha,prettier}rc.{js,cjs,yml}"
|
||
|
];
|
||
|
const coverageConfigDefaults = {
|
||
|
all: false,
|
||
|
provider: "c8",
|
||
|
enabled: false,
|
||
|
clean: true,
|
||
|
cleanOnRerun: true,
|
||
|
reportsDirectory: "./coverage",
|
||
|
excludeNodeModules: true,
|
||
|
exclude: defaultCoverageExcludes,
|
||
|
reporter: ["text", "html", "clover", "json"],
|
||
|
allowExternal: false,
|
||
|
extension: [".js", ".cjs", ".mjs", ".ts", ".mts", ".cts", ".tsx", ".jsx", ".vue", ".svelte"]
|
||
|
};
|
||
|
const fakeTimersDefaults = {
|
||
|
loopLimit: 1e4,
|
||
|
shouldClearNativeTimers: true,
|
||
|
toFake: [
|
||
|
"setTimeout",
|
||
|
"clearTimeout",
|
||
|
"setInterval",
|
||
|
"clearInterval",
|
||
|
"setImmediate",
|
||
|
"clearImmediate",
|
||
|
"Date"
|
||
|
]
|
||
|
};
|
||
|
const config = {
|
||
|
allowOnly: !process.env.CI,
|
||
|
watch: !process.env.CI,
|
||
|
globals: false,
|
||
|
environment: "node",
|
||
|
threads: true,
|
||
|
clearMocks: false,
|
||
|
restoreMocks: false,
|
||
|
mockReset: false,
|
||
|
include: defaultInclude,
|
||
|
exclude: defaultExclude,
|
||
|
testTimeout: 5e3,
|
||
|
hookTimeout: 1e4,
|
||
|
teardownTimeout: 1e3,
|
||
|
isolate: true,
|
||
|
watchExclude: ["**/node_modules/**", "**/dist/**"],
|
||
|
forceRerunTriggers: [
|
||
|
"**/package.json/**",
|
||
|
"**/{vitest,vite}.config.*/**"
|
||
|
],
|
||
|
update: false,
|
||
|
reporters: [],
|
||
|
silent: false,
|
||
|
api: false,
|
||
|
ui: false,
|
||
|
uiBase: "/__vitest__/",
|
||
|
open: true,
|
||
|
css: {
|
||
|
include: []
|
||
|
},
|
||
|
coverage: coverageConfigDefaults,
|
||
|
fakeTimers: fakeTimersDefaults,
|
||
|
maxConcurrency: 5,
|
||
|
dangerouslyIgnoreUnhandledErrors: false,
|
||
|
typecheck: {
|
||
|
checker: "tsc",
|
||
|
include: ["**/*.{test,spec}-d.{ts,js}"],
|
||
|
exclude: defaultExclude
|
||
|
},
|
||
|
slowTestThreshold: 300
|
||
|
};
|
||
|
const configDefaults = Object.freeze(config);
|
||
|
|
||
|
class FilesStatsCache {
|
||
|
constructor() {
|
||
|
this.cache = /* @__PURE__ */ new Map();
|
||
|
}
|
||
|
getStats(fsPath) {
|
||
|
return this.cache.get(fsPath);
|
||
|
}
|
||
|
async updateStats(fsPath) {
|
||
|
if (!fs$8.existsSync(fsPath))
|
||
|
return;
|
||
|
const stats = await fs$8.promises.stat(fsPath);
|
||
|
this.cache.set(fsPath, { size: stats.size });
|
||
|
}
|
||
|
removeStats(fsPath) {
|
||
|
this.cache.delete(fsPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class ResultsCache {
|
||
|
constructor() {
|
||
|
this.cache = /* @__PURE__ */ new Map();
|
||
|
this.cachePath = null;
|
||
|
this.version = version$1;
|
||
|
this.root = "/";
|
||
|
}
|
||
|
getCachePath() {
|
||
|
return this.cachePath;
|
||
|
}
|
||
|
setConfig(root, config) {
|
||
|
this.root = root;
|
||
|
if (config)
|
||
|
this.cachePath = resolve(config.dir, "results.json");
|
||
|
}
|
||
|
getResults(fsPath) {
|
||
|
return this.cache.get(fsPath == null ? void 0 : fsPath.slice(this.root.length));
|
||
|
}
|
||
|
async readFromCache() {
|
||
|
if (!this.cachePath)
|
||
|
return;
|
||
|
if (fs$8.existsSync(this.cachePath)) {
|
||
|
const resultsCache = await fs$8.promises.readFile(this.cachePath, "utf8");
|
||
|
const { results, version: version2 } = JSON.parse(resultsCache);
|
||
|
this.cache = new Map(results);
|
||
|
this.version = version2;
|
||
|
}
|
||
|
}
|
||
|
updateResults(files) {
|
||
|
files.forEach((file) => {
|
||
|
var _a;
|
||
|
const result = file.result;
|
||
|
if (!result)
|
||
|
return;
|
||
|
const duration = result.duration || 0;
|
||
|
const relativePath = (_a = file.filepath) == null ? void 0 : _a.slice(this.root.length);
|
||
|
this.cache.set(relativePath, {
|
||
|
duration: duration >= 0 ? duration : 0,
|
||
|
failed: result.state === "fail"
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
removeFromCache(filepath) {
|
||
|
this.cache.delete(filepath);
|
||
|
}
|
||
|
async writeToCache() {
|
||
|
if (!this.cachePath)
|
||
|
return;
|
||
|
const results = Array.from(this.cache.entries());
|
||
|
const cacheDirname = dirname(this.cachePath);
|
||
|
if (!fs$8.existsSync(cacheDirname))
|
||
|
await fs$8.promises.mkdir(cacheDirname, { recursive: true });
|
||
|
const cache = JSON.stringify({
|
||
|
version: this.version,
|
||
|
results
|
||
|
});
|
||
|
await fs$8.promises.writeFile(this.cachePath, cache);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class VitestCache {
|
||
|
constructor() {
|
||
|
this.results = new ResultsCache();
|
||
|
this.stats = new FilesStatsCache();
|
||
|
}
|
||
|
getFileTestResults(id) {
|
||
|
return this.results.getResults(id);
|
||
|
}
|
||
|
getFileStats(id) {
|
||
|
return this.stats.getStats(id);
|
||
|
}
|
||
|
static resolveCacheDir(root, dir) {
|
||
|
return resolve(root, slash$1(dir || "node_modules/.vitest"));
|
||
|
}
|
||
|
static async clearCache(options) {
|
||
|
var _a;
|
||
|
const root = resolve(options.root || process.cwd());
|
||
|
const configPath = options.config ? resolve(root, options.config) : await findUp(configFiles, { cwd: root });
|
||
|
const config = await loadConfigFromFile({ command: "serve", mode: "test" }, configPath);
|
||
|
const cache = (_a = config == null ? void 0 : config.config.test) == null ? void 0 : _a.cache;
|
||
|
if (cache === false)
|
||
|
throw new Error("Cache is disabled");
|
||
|
const cachePath = VitestCache.resolveCacheDir(root, cache == null ? void 0 : cache.dir);
|
||
|
let cleared = false;
|
||
|
if (fs$8.existsSync(cachePath)) {
|
||
|
fs$8.rmSync(cachePath, { recursive: true, force: true });
|
||
|
cleared = true;
|
||
|
}
|
||
|
return { dir: cachePath, cleared };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class BaseSequencer {
|
||
|
constructor(ctx) {
|
||
|
this.ctx = ctx;
|
||
|
}
|
||
|
async shard(files) {
|
||
|
const { config } = this.ctx;
|
||
|
const { index, count } = config.shard;
|
||
|
const shardSize = Math.ceil(files.length / count);
|
||
|
const shardStart = shardSize * (index - 1);
|
||
|
const shardEnd = shardSize * index;
|
||
|
return [...files].map((file) => {
|
||
|
const fullPath = resolve(slash$2(config.root), slash$2(file));
|
||
|
const specPath = fullPath == null ? void 0 : fullPath.slice(config.root.length);
|
||
|
return {
|
||
|
file,
|
||
|
hash: createHash("sha1").update(specPath).digest("hex")
|
||
|
};
|
||
|
}).sort((a, b) => a.hash < b.hash ? -1 : a.hash > b.hash ? 1 : 0).slice(shardStart, shardEnd).map(({ file }) => file);
|
||
|
}
|
||
|
async sort(files) {
|
||
|
const cache = this.ctx.cache;
|
||
|
return [...files].sort((a, b) => {
|
||
|
const aState = cache.getFileTestResults(a);
|
||
|
const bState = cache.getFileTestResults(b);
|
||
|
if (!aState || !bState) {
|
||
|
const statsA = cache.getFileStats(a);
|
||
|
const statsB = cache.getFileStats(b);
|
||
|
if (!statsA || !statsB)
|
||
|
return !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0;
|
||
|
return statsB.size - statsA.size;
|
||
|
}
|
||
|
if (aState.failed && !bState.failed)
|
||
|
return -1;
|
||
|
if (!aState.failed && bState.failed)
|
||
|
return 1;
|
||
|
return bState.duration - aState.duration;
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class RandomSequencer extends BaseSequencer {
|
||
|
async sort(files) {
|
||
|
const { sequence } = this.ctx.config;
|
||
|
const seed = (sequence == null ? void 0 : sequence.seed) ?? Date.now();
|
||
|
return shuffle(files, seed);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const extraInlineDeps = [
|
||
|
/^(?!.*(?:node_modules)).*\.mjs$/,
|
||
|
/^(?!.*(?:node_modules)).*\.cjs\.js$/,
|
||
|
/vite\w*\/dist\/client\/env.mjs/,
|
||
|
/\/vitest\/dist\//,
|
||
|
/vitest-virtual-\w+\/dist/,
|
||
|
/@vitest\/dist/,
|
||
|
"@nuxt/test-utils"
|
||
|
];
|
||
|
function resolveApiConfig(options) {
|
||
|
let api;
|
||
|
if ((options.ui || options.browser) && !options.api)
|
||
|
api = { port: defaultPort };
|
||
|
else if (options.api === true)
|
||
|
api = { port: defaultPort };
|
||
|
else if (typeof options.api === "number")
|
||
|
api = { port: options.api };
|
||
|
if (typeof options.api === "object") {
|
||
|
if (api) {
|
||
|
if (options.api.port)
|
||
|
api.port = options.api.port;
|
||
|
if (options.api.strictPort)
|
||
|
api.strictPort = options.api.strictPort;
|
||
|
if (options.api.host)
|
||
|
api.host = options.api.host;
|
||
|
} else {
|
||
|
api = { ...options.api };
|
||
|
}
|
||
|
}
|
||
|
if (api) {
|
||
|
if (!api.port)
|
||
|
api.port = defaultPort;
|
||
|
}
|
||
|
return api;
|
||
|
}
|
||
|
function resolveConfig(mode, options, viteConfig) {
|
||
|
var _a, _b, _c, _d, _e, _f, _g;
|
||
|
if (options.dom) {
|
||
|
if (((_a = viteConfig.test) == null ? void 0 : _a.environment) != null && viteConfig.test.environment !== "happy-dom") {
|
||
|
console.warn(
|
||
|
c.yellow(
|
||
|
`${c.inverse(c.yellow(" Vitest "))} Your config.test.environment ("${viteConfig.test.environment}") conflicts with --dom flag ("happy-dom"), ignoring "${viteConfig.test.environment}"`
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
options.environment = "happy-dom";
|
||
|
}
|
||
|
const resolved = {
|
||
|
...configDefaults,
|
||
|
...options,
|
||
|
root: viteConfig.root,
|
||
|
mode
|
||
|
};
|
||
|
if (viteConfig.base !== "/")
|
||
|
resolved.base = viteConfig.base;
|
||
|
if (options.shard) {
|
||
|
if (resolved.watch)
|
||
|
throw new Error("You cannot use --shard option with enabled watch");
|
||
|
const [indexString, countString] = options.shard.split("/");
|
||
|
const index = Math.abs(parseInt(indexString, 10));
|
||
|
const count = Math.abs(parseInt(countString, 10));
|
||
|
if (isNaN(count) || count <= 0)
|
||
|
throw new Error("--shard <count> must be a positive number");
|
||
|
if (isNaN(index) || index <= 0 || index > count)
|
||
|
throw new Error("--shard <index> must be a positive number less then <count>");
|
||
|
resolved.shard = { index, count };
|
||
|
}
|
||
|
resolved.deps = resolved.deps || {};
|
||
|
if (resolved.deps.inline !== true) {
|
||
|
const ssrOptions = viteConfig.ssr;
|
||
|
if ((ssrOptions == null ? void 0 : ssrOptions.noExternal) === true && resolved.deps.inline == null) {
|
||
|
resolved.deps.inline = true;
|
||
|
} else {
|
||
|
(_b = resolved.deps).inline ?? (_b.inline = []);
|
||
|
resolved.deps.inline.push(...extraInlineDeps);
|
||
|
}
|
||
|
}
|
||
|
(_c = resolved.deps).registerNodeLoader ?? (_c.registerNodeLoader = false);
|
||
|
resolved.testNamePattern = resolved.testNamePattern ? resolved.testNamePattern instanceof RegExp ? resolved.testNamePattern : new RegExp(resolved.testNamePattern) : void 0;
|
||
|
const CI = !!process.env.CI;
|
||
|
const UPDATE_SNAPSHOT = resolved.update || process.env.UPDATE_SNAPSHOT;
|
||
|
resolved.snapshotOptions = {
|
||
|
snapshotFormat: resolved.snapshotFormat || {},
|
||
|
updateSnapshot: CI && !UPDATE_SNAPSHOT ? "none" : UPDATE_SNAPSHOT ? "all" : "new",
|
||
|
resolveSnapshotPath: options.resolveSnapshotPath
|
||
|
};
|
||
|
if (options.resolveSnapshotPath)
|
||
|
delete resolved.resolveSnapshotPath;
|
||
|
if (process.env.VITEST_MAX_THREADS)
|
||
|
resolved.maxThreads = parseInt(process.env.VITEST_MAX_THREADS);
|
||
|
if (process.env.VITEST_MIN_THREADS)
|
||
|
resolved.minThreads = parseInt(process.env.VITEST_MIN_THREADS);
|
||
|
if (mode === "benchmark") {
|
||
|
resolved.benchmark = {
|
||
|
...benchmarkConfigDefaults,
|
||
|
...resolved.benchmark
|
||
|
};
|
||
|
resolved.coverage.enabled = false;
|
||
|
resolved.include = resolved.benchmark.include;
|
||
|
resolved.exclude = resolved.benchmark.exclude;
|
||
|
resolved.includeSource = resolved.benchmark.includeSource;
|
||
|
const reporters = Array.from(/* @__PURE__ */ new Set([
|
||
|
...toArray(resolved.benchmark.reporters),
|
||
|
...toArray(options.reporter)
|
||
|
])).filter(Boolean);
|
||
|
if (reporters.length)
|
||
|
resolved.benchmark.reporters = reporters;
|
||
|
else
|
||
|
resolved.benchmark.reporters = ["default"];
|
||
|
if (options.outputFile)
|
||
|
resolved.benchmark.outputFile = options.outputFile;
|
||
|
}
|
||
|
resolved.setupFiles = toArray(resolved.setupFiles || []).map(
|
||
|
(file) => normalize(
|
||
|
resolveModule(file, { paths: [resolved.root] }) ?? resolve(resolved.root, file)
|
||
|
)
|
||
|
);
|
||
|
resolved.api = resolveApiConfig(options);
|
||
|
if (options.related)
|
||
|
resolved.related = toArray(options.related).map((file) => resolve(resolved.root, file));
|
||
|
if (mode !== "benchmark") {
|
||
|
const reporters = resolved.reporter ?? resolved.reporters;
|
||
|
resolved.reporters = Array.from(new Set(toArray(reporters))).filter(Boolean);
|
||
|
}
|
||
|
if (!resolved.reporters.length)
|
||
|
resolved.reporters.push("default");
|
||
|
if (resolved.changed)
|
||
|
resolved.passWithNoTests ?? (resolved.passWithNoTests = true);
|
||
|
resolved.css ?? (resolved.css = {});
|
||
|
if (typeof resolved.css === "object") {
|
||
|
(_d = resolved.css).modules ?? (_d.modules = {});
|
||
|
(_e = resolved.css.modules).classNameStrategy ?? (_e.classNameStrategy = "stable");
|
||
|
}
|
||
|
resolved.cache ?? (resolved.cache = { dir: "" });
|
||
|
if (resolved.cache)
|
||
|
resolved.cache.dir = VitestCache.resolveCacheDir(resolved.root, resolved.cache.dir);
|
||
|
resolved.sequence ?? (resolved.sequence = {});
|
||
|
if (!((_f = resolved.sequence) == null ? void 0 : _f.sequencer)) {
|
||
|
resolved.sequence.sequencer = resolved.sequence.shuffle ? RandomSequencer : BaseSequencer;
|
||
|
}
|
||
|
(_g = resolved.sequence).hooks ?? (_g.hooks = "parallel");
|
||
|
resolved.typecheck = {
|
||
|
...configDefaults.typecheck,
|
||
|
...resolved.typecheck
|
||
|
};
|
||
|
if (mode === "typecheck") {
|
||
|
resolved.include = resolved.typecheck.include;
|
||
|
resolved.exclude = resolved.typecheck.exclude;
|
||
|
}
|
||
|
return resolved;
|
||
|
}
|
||
|
|
||
|
const ESC = '\u001B[';
|
||
|
const OSC = '\u001B]';
|
||
|
const BEL = '\u0007';
|
||
|
const SEP = ';';
|
||
|
const isTerminalApp = process.env.TERM_PROGRAM === 'Apple_Terminal';
|
||
|
|
||
|
const ansiEscapes = {};
|
||
|
|
||
|
ansiEscapes.cursorTo = (x, y) => {
|
||
|
if (typeof x !== 'number') {
|
||
|
throw new TypeError('The `x` argument is required');
|
||
|
}
|
||
|
|
||
|
if (typeof y !== 'number') {
|
||
|
return ESC + (x + 1) + 'G';
|
||
|
}
|
||
|
|
||
|
return ESC + (y + 1) + ';' + (x + 1) + 'H';
|
||
|
};
|
||
|
|
||
|
ansiEscapes.cursorMove = (x, y) => {
|
||
|
if (typeof x !== 'number') {
|
||
|
throw new TypeError('The `x` argument is required');
|
||
|
}
|
||
|
|
||
|
let returnValue = '';
|
||
|
|
||
|
if (x < 0) {
|
||
|
returnValue += ESC + (-x) + 'D';
|
||
|
} else if (x > 0) {
|
||
|
returnValue += ESC + x + 'C';
|
||
|
}
|
||
|
|
||
|
if (y < 0) {
|
||
|
returnValue += ESC + (-y) + 'A';
|
||
|
} else if (y > 0) {
|
||
|
returnValue += ESC + y + 'B';
|
||
|
}
|
||
|
|
||
|
return returnValue;
|
||
|
};
|
||
|
|
||
|
ansiEscapes.cursorUp = (count = 1) => ESC + count + 'A';
|
||
|
ansiEscapes.cursorDown = (count = 1) => ESC + count + 'B';
|
||
|
ansiEscapes.cursorForward = (count = 1) => ESC + count + 'C';
|
||
|
ansiEscapes.cursorBackward = (count = 1) => ESC + count + 'D';
|
||
|
|
||
|
ansiEscapes.cursorLeft = ESC + 'G';
|
||
|
ansiEscapes.cursorSavePosition = isTerminalApp ? '\u001B7' : ESC + 's';
|
||
|
ansiEscapes.cursorRestorePosition = isTerminalApp ? '\u001B8' : ESC + 'u';
|
||
|
ansiEscapes.cursorGetPosition = ESC + '6n';
|
||
|
ansiEscapes.cursorNextLine = ESC + 'E';
|
||
|
ansiEscapes.cursorPrevLine = ESC + 'F';
|
||
|
ansiEscapes.cursorHide = ESC + '?25l';
|
||
|
ansiEscapes.cursorShow = ESC + '?25h';
|
||
|
|
||
|
ansiEscapes.eraseLines = count => {
|
||
|
let clear = '';
|
||
|
|
||
|
for (let i = 0; i < count; i++) {
|
||
|
clear += ansiEscapes.eraseLine + (i < count - 1 ? ansiEscapes.cursorUp() : '');
|
||
|
}
|
||
|
|
||
|
if (count) {
|
||
|
clear += ansiEscapes.cursorLeft;
|
||
|
}
|
||
|
|
||
|
return clear;
|
||
|
};
|
||
|
|
||
|
ansiEscapes.eraseEndLine = ESC + 'K';
|
||
|
ansiEscapes.eraseStartLine = ESC + '1K';
|
||
|
ansiEscapes.eraseLine = ESC + '2K';
|
||
|
ansiEscapes.eraseDown = ESC + 'J';
|
||
|
ansiEscapes.eraseUp = ESC + '1J';
|
||
|
ansiEscapes.eraseScreen = ESC + '2J';
|
||
|
ansiEscapes.scrollUp = ESC + 'S';
|
||
|
ansiEscapes.scrollDown = ESC + 'T';
|
||
|
|
||
|
ansiEscapes.clearScreen = '\u001Bc';
|
||
|
|
||
|
ansiEscapes.clearTerminal = process.platform === 'win32' ?
|
||
|
`${ansiEscapes.eraseScreen}${ESC}0f` :
|
||
|
// 1. Erases the screen (Only done in case `2` is not supported)
|
||
|
// 2. Erases the whole screen including scrollback buffer
|
||
|
// 3. Moves cursor to the top-left position
|
||
|
// More info: https://www.real-world-systems.com/docs/ANSIcode.html
|
||
|
`${ansiEscapes.eraseScreen}${ESC}3J${ESC}H`;
|
||
|
|
||
|
ansiEscapes.beep = BEL;
|
||
|
|
||
|
ansiEscapes.link = (text, url) => {
|
||
|
return [
|
||
|
OSC,
|
||
|
'8',
|
||
|
SEP,
|
||
|
SEP,
|
||
|
url,
|
||
|
BEL,
|
||
|
text,
|
||
|
OSC,
|
||
|
'8',
|
||
|
SEP,
|
||
|
SEP,
|
||
|
BEL
|
||
|
].join('');
|
||
|
};
|
||
|
|
||
|
ansiEscapes.image = (buffer, options = {}) => {
|
||
|
let returnValue = `${OSC}1337;File=inline=1`;
|
||
|
|
||
|
if (options.width) {
|
||
|
returnValue += `;width=${options.width}`;
|
||
|
}
|
||
|
|
||
|
if (options.height) {
|
||
|
returnValue += `;height=${options.height}`;
|
||
|
}
|
||
|
|
||
|
if (options.preserveAspectRatio === false) {
|
||
|
returnValue += ';preserveAspectRatio=0';
|
||
|
}
|
||
|
|
||
|
return returnValue + ':' + buffer.toString('base64') + BEL;
|
||
|
};
|
||
|
|
||
|
ansiEscapes.iTerm = {
|
||
|
setCwd: (cwd = process.cwd()) => `${OSC}50;CurrentDir=${cwd}${BEL}`,
|
||
|
|
||
|
annotation: (message, options = {}) => {
|
||
|
let returnValue = `${OSC}1337;`;
|
||
|
|
||
|
const hasX = typeof options.x !== 'undefined';
|
||
|
const hasY = typeof options.y !== 'undefined';
|
||
|
if ((hasX || hasY) && !(hasX && hasY && typeof options.length !== 'undefined')) {
|
||
|
throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined');
|
||
|
}
|
||
|
|
||
|
message = message.replace(/\|/g, '');
|
||
|
|
||
|
returnValue += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation=';
|
||
|
|
||
|
if (options.length > 0) {
|
||
|
returnValue +=
|
||
|
(hasX ?
|
||
|
[message, options.length, options.x, options.y] :
|
||
|
[options.length, message]).join('|');
|
||
|
} else {
|
||
|
returnValue += message;
|
||
|
}
|
||
|
|
||
|
return returnValue + BEL;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const restoreCursor = onetime.exports(() => {
|
||
|
signalExit.exports(() => {
|
||
|
process$1.stderr.write('\u001B[?25h');
|
||
|
}, {alwaysLast: true});
|
||
|
});
|
||
|
|
||
|
let isHidden = false;
|
||
|
|
||
|
const cliCursor = {};
|
||
|
|
||
|
cliCursor.show = (writableStream = process$1.stderr) => {
|
||
|
if (!writableStream.isTTY) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
isHidden = false;
|
||
|
writableStream.write('\u001B[?25h');
|
||
|
};
|
||
|
|
||
|
cliCursor.hide = (writableStream = process$1.stderr) => {
|
||
|
if (!writableStream.isTTY) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
restoreCursor();
|
||
|
isHidden = true;
|
||
|
writableStream.write('\u001B[?25l');
|
||
|
};
|
||
|
|
||
|
cliCursor.toggle = (force, writableStream) => {
|
||
|
if (force !== undefined) {
|
||
|
isHidden = force;
|
||
|
}
|
||
|
|
||
|
if (isHidden) {
|
||
|
cliCursor.show(writableStream);
|
||
|
} else {
|
||
|
cliCursor.hide(writableStream);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const ESCAPES = new Set([
|
||
|
'\u001B',
|
||
|
'\u009B',
|
||
|
]);
|
||
|
|
||
|
const END_CODE = 39;
|
||
|
const ANSI_ESCAPE_BELL = '\u0007';
|
||
|
const ANSI_CSI = '[';
|
||
|
const ANSI_OSC = ']';
|
||
|
const ANSI_SGR_TERMINATOR = 'm';
|
||
|
const ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;
|
||
|
|
||
|
const wrapAnsiCode = code => `${ESCAPES.values().next().value}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
|
||
|
const wrapAnsiHyperlink = uri => `${ESCAPES.values().next().value}${ANSI_ESCAPE_LINK}${uri}${ANSI_ESCAPE_BELL}`;
|
||
|
|
||
|
// Calculate the length of words split on ' ', ignoring
|
||
|
// the extra characters added by ansi escape codes
|
||
|
const wordLengths = string => string.split(' ').map(character => stringWidth(character));
|
||
|
|
||
|
// Wrap a long word across multiple rows
|
||
|
// Ansi escape codes do not count towards length
|
||
|
const wrapWord = (rows, word, columns) => {
|
||
|
const characters = [...word];
|
||
|
|
||
|
let isInsideEscape = false;
|
||
|
let isInsideLinkEscape = false;
|
||
|
let visible = stringWidth(stripAnsi(rows[rows.length - 1]));
|
||
|
|
||
|
for (const [index, character] of characters.entries()) {
|
||
|
const characterLength = stringWidth(character);
|
||
|
|
||
|
if (visible + characterLength <= columns) {
|
||
|
rows[rows.length - 1] += character;
|
||
|
} else {
|
||
|
rows.push(character);
|
||
|
visible = 0;
|
||
|
}
|
||
|
|
||
|
if (ESCAPES.has(character)) {
|
||
|
isInsideEscape = true;
|
||
|
isInsideLinkEscape = characters.slice(index + 1).join('').startsWith(ANSI_ESCAPE_LINK);
|
||
|
}
|
||
|
|
||
|
if (isInsideEscape) {
|
||
|
if (isInsideLinkEscape) {
|
||
|
if (character === ANSI_ESCAPE_BELL) {
|
||
|
isInsideEscape = false;
|
||
|
isInsideLinkEscape = false;
|
||
|
}
|
||
|
} else if (character === ANSI_SGR_TERMINATOR) {
|
||
|
isInsideEscape = false;
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
visible += characterLength;
|
||
|
|
||
|
if (visible === columns && index < characters.length - 1) {
|
||
|
rows.push('');
|
||
|
visible = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// It's possible that the last row we copy over is only
|
||
|
// ansi escape characters, handle this edge-case
|
||
|
if (!visible && rows[rows.length - 1].length > 0 && rows.length > 1) {
|
||
|
rows[rows.length - 2] += rows.pop();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Trims spaces from a string ignoring invisible sequences
|
||
|
const stringVisibleTrimSpacesRight = string => {
|
||
|
const words = string.split(' ');
|
||
|
let last = words.length;
|
||
|
|
||
|
while (last > 0) {
|
||
|
if (stringWidth(words[last - 1]) > 0) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
last--;
|
||
|
}
|
||
|
|
||
|
if (last === words.length) {
|
||
|
return string;
|
||
|
}
|
||
|
|
||
|
return words.slice(0, last).join(' ') + words.slice(last).join('');
|
||
|
};
|
||
|
|
||
|
// The wrap-ansi module can be invoked in either 'hard' or 'soft' wrap mode
|
||
|
//
|
||
|
// 'hard' will never allow a string to take up more than columns characters
|
||
|
//
|
||
|
// 'soft' allows long words to expand past the column length
|
||
|
const exec = (string, columns, options = {}) => {
|
||
|
if (options.trim !== false && string.trim() === '') {
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
let returnValue = '';
|
||
|
let escapeCode;
|
||
|
let escapeUrl;
|
||
|
|
||
|
const lengths = wordLengths(string);
|
||
|
let rows = [''];
|
||
|
|
||
|
for (const [index, word] of string.split(' ').entries()) {
|
||
|
if (options.trim !== false) {
|
||
|
rows[rows.length - 1] = rows[rows.length - 1].trimStart();
|
||
|
}
|
||
|
|
||
|
let rowLength = stringWidth(rows[rows.length - 1]);
|
||
|
|
||
|
if (index !== 0) {
|
||
|
if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
|
||
|
// If we start with a new word but the current row length equals the length of the columns, add a new row
|
||
|
rows.push('');
|
||
|
rowLength = 0;
|
||
|
}
|
||
|
|
||
|
if (rowLength > 0 || options.trim === false) {
|
||
|
rows[rows.length - 1] += ' ';
|
||
|
rowLength++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// In 'hard' wrap mode, the length of a line is never allowed to extend past 'columns'
|
||
|
if (options.hard && lengths[index] > columns) {
|
||
|
const remainingColumns = (columns - rowLength);
|
||
|
const breaksStartingThisLine = 1 + Math.floor((lengths[index] - remainingColumns - 1) / columns);
|
||
|
const breaksStartingNextLine = Math.floor((lengths[index] - 1) / columns);
|
||
|
if (breaksStartingNextLine < breaksStartingThisLine) {
|
||
|
rows.push('');
|
||
|
}
|
||
|
|
||
|
wrapWord(rows, word, columns);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (rowLength + lengths[index] > columns && rowLength > 0 && lengths[index] > 0) {
|
||
|
if (options.wordWrap === false && rowLength < columns) {
|
||
|
wrapWord(rows, word, columns);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
rows.push('');
|
||
|
}
|
||
|
|
||
|
if (rowLength + lengths[index] > columns && options.wordWrap === false) {
|
||
|
wrapWord(rows, word, columns);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
rows[rows.length - 1] += word;
|
||
|
}
|
||
|
|
||
|
if (options.trim !== false) {
|
||
|
rows = rows.map(row => stringVisibleTrimSpacesRight(row));
|
||
|
}
|
||
|
|
||
|
const pre = [...rows.join('\n')];
|
||
|
|
||
|
for (const [index, character] of pre.entries()) {
|
||
|
returnValue += character;
|
||
|
|
||
|
if (ESCAPES.has(character)) {
|
||
|
const {groups} = new RegExp(`(?:\\${ANSI_CSI}(?<code>\\d+)m|\\${ANSI_ESCAPE_LINK}(?<uri>.*)${ANSI_ESCAPE_BELL})`).exec(pre.slice(index).join('')) || {groups: {}};
|
||
|
if (groups.code !== undefined) {
|
||
|
const code = Number.parseFloat(groups.code);
|
||
|
escapeCode = code === END_CODE ? undefined : code;
|
||
|
} else if (groups.uri !== undefined) {
|
||
|
escapeUrl = groups.uri.length === 0 ? undefined : groups.uri;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const code = ansiStyles.codes.get(Number(escapeCode));
|
||
|
|
||
|
if (pre[index + 1] === '\n') {
|
||
|
if (escapeUrl) {
|
||
|
returnValue += wrapAnsiHyperlink('');
|
||
|
}
|
||
|
|
||
|
if (escapeCode && code) {
|
||
|
returnValue += wrapAnsiCode(code);
|
||
|
}
|
||
|
} else if (character === '\n') {
|
||
|
if (escapeCode && code) {
|
||
|
returnValue += wrapAnsiCode(escapeCode);
|
||
|
}
|
||
|
|
||
|
if (escapeUrl) {
|
||
|
returnValue += wrapAnsiHyperlink(escapeUrl);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return returnValue;
|
||
|
};
|
||
|
|
||
|
// For each newline, invoke the method separately
|
||
|
function wrapAnsi(string, columns, options) {
|
||
|
return String(string)
|
||
|
.normalize()
|
||
|
.replace(/\r\n/g, '\n')
|
||
|
.split('\n')
|
||
|
.map(line => exec(line, columns, options))
|
||
|
.join('\n');
|
||
|
}
|
||
|
|
||
|
const defaultTerminalHeight = 24;
|
||
|
|
||
|
const getWidth = stream => {
|
||
|
const {columns} = stream;
|
||
|
|
||
|
if (!columns) {
|
||
|
return 80;
|
||
|
}
|
||
|
|
||
|
return columns;
|
||
|
};
|
||
|
|
||
|
const fitToTerminalHeight = (stream, text) => {
|
||
|
const terminalHeight = stream.rows || defaultTerminalHeight;
|
||
|
const lines = text.split('\n');
|
||
|
|
||
|
const toRemove = lines.length - terminalHeight;
|
||
|
if (toRemove <= 0) {
|
||
|
return text;
|
||
|
}
|
||
|
|
||
|
return sliceAnsi(
|
||
|
text,
|
||
|
stripAnsi(lines.slice(0, toRemove).join('\n')).length + 1,
|
||
|
);
|
||
|
};
|
||
|
|
||
|
function createLogUpdate(stream, {showCursor = false} = {}) {
|
||
|
let previousLineCount = 0;
|
||
|
let previousWidth = getWidth(stream);
|
||
|
let previousOutput = '';
|
||
|
|
||
|
const render = (...arguments_) => {
|
||
|
if (!showCursor) {
|
||
|
cliCursor.hide();
|
||
|
}
|
||
|
|
||
|
let output = arguments_.join(' ') + '\n';
|
||
|
output = fitToTerminalHeight(stream, output);
|
||
|
const width = getWidth(stream);
|
||
|
if (output === previousOutput && previousWidth === width) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
previousOutput = output;
|
||
|
previousWidth = width;
|
||
|
output = wrapAnsi(output, width, {
|
||
|
trim: false,
|
||
|
hard: true,
|
||
|
wordWrap: false,
|
||
|
});
|
||
|
stream.write(ansiEscapes.eraseLines(previousLineCount) + output);
|
||
|
previousLineCount = output.split('\n').length;
|
||
|
};
|
||
|
|
||
|
render.clear = () => {
|
||
|
stream.write(ansiEscapes.eraseLines(previousLineCount));
|
||
|
previousOutput = '';
|
||
|
previousWidth = getWidth(stream);
|
||
|
previousLineCount = 0;
|
||
|
};
|
||
|
|
||
|
render.done = () => {
|
||
|
previousOutput = '';
|
||
|
previousWidth = getWidth(stream);
|
||
|
previousLineCount = 0;
|
||
|
|
||
|
if (!showCursor) {
|
||
|
cliCursor.show();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
return render;
|
||
|
}
|
||
|
|
||
|
createLogUpdate(process$1.stdout);
|
||
|
|
||
|
createLogUpdate(process$1.stderr);
|
||
|
|
||
|
var version = "0.27.0";
|
||
|
|
||
|
function formatLine(line, outputTruncateLength) {
|
||
|
var _a;
|
||
|
return cliTruncate(line, (outputTruncateLength ?? (((_a = process.stdout) == null ? void 0 : _a.columns) || 80)) - 4);
|
||
|
}
|
||
|
function unifiedDiff(actual, expected, options = {}) {
|
||
|
if (actual === expected)
|
||
|
return "";
|
||
|
const { outputTruncateLength, outputDiffLines, outputDiffMaxLines, noColor, showLegend = true } = options;
|
||
|
const indent = " ";
|
||
|
const diffLimit = outputDiffLines || 15;
|
||
|
const diffMaxLines = outputDiffMaxLines || 50;
|
||
|
const counts = {
|
||
|
"+": 0,
|
||
|
"-": 0
|
||
|
};
|
||
|
let previousState = null;
|
||
|
let previousCount = 0;
|
||
|
const str = (str2) => str2;
|
||
|
const dim = noColor ? str : c.dim;
|
||
|
const green = noColor ? str : c.green;
|
||
|
const red = noColor ? str : c.red;
|
||
|
function preprocess(line) {
|
||
|
if (!line || line.match(/\\ No newline/))
|
||
|
return;
|
||
|
const char = line[0];
|
||
|
if ("-+".includes(char)) {
|
||
|
if (previousState !== char) {
|
||
|
previousState = char;
|
||
|
previousCount = 0;
|
||
|
}
|
||
|
previousCount++;
|
||
|
counts[char]++;
|
||
|
if (previousCount === diffLimit)
|
||
|
return dim(`${char} ...`);
|
||
|
else if (previousCount > diffLimit)
|
||
|
return;
|
||
|
}
|
||
|
return line;
|
||
|
}
|
||
|
const msg = createPatch("string", expected, actual);
|
||
|
let lines = msg.split("\n").slice(5).map(preprocess).filter(Boolean);
|
||
|
let moreLines = 0;
|
||
|
const isCompact = counts["+"] === 1 && counts["-"] === 1 && lines.length === 2;
|
||
|
if (lines.length > diffMaxLines) {
|
||
|
const firstDiff = lines.findIndex((line) => line[0] === "-" || line[0] === "+");
|
||
|
const displayLines = lines.slice(firstDiff - 2, diffMaxLines);
|
||
|
const lastDisplayedIndex = firstDiff - 2 + diffMaxLines;
|
||
|
if (lastDisplayedIndex < lines.length)
|
||
|
moreLines = lines.length - lastDisplayedIndex;
|
||
|
lines = displayLines;
|
||
|
}
|
||
|
let formatted = lines.map((line) => {
|
||
|
line = line.replace(/\\"/g, '"');
|
||
|
if (line[0] === "-") {
|
||
|
line = formatLine(line.slice(1), outputTruncateLength);
|
||
|
if (isCompact)
|
||
|
return green(line);
|
||
|
return green(`- ${formatLine(line, outputTruncateLength)}`);
|
||
|
}
|
||
|
if (line[0] === "+") {
|
||
|
line = formatLine(line.slice(1), outputTruncateLength);
|
||
|
if (isCompact)
|
||
|
return red(line);
|
||
|
return red(`+ ${formatLine(line, outputTruncateLength)}`);
|
||
|
}
|
||
|
if (line.match(/@@/))
|
||
|
return "--";
|
||
|
return ` ${line}`;
|
||
|
});
|
||
|
if (moreLines)
|
||
|
formatted.push(dim(`... ${moreLines} more lines`));
|
||
|
if (showLegend) {
|
||
|
if (isCompact) {
|
||
|
formatted = [
|
||
|
`${green("- Expected")} ${formatted[0]}`,
|
||
|
`${red("+ Received")} ${formatted[1]}`
|
||
|
];
|
||
|
} else {
|
||
|
if (formatted[0].includes('"'))
|
||
|
formatted[0] = formatted[0].replace('"', "");
|
||
|
const last = formatted.length - 1;
|
||
|
if (formatted[last].endsWith('"'))
|
||
|
formatted[last] = formatted[last].slice(0, formatted[last].length - 1);
|
||
|
formatted.unshift(
|
||
|
green(`- Expected - ${counts["-"]}`),
|
||
|
red(`+ Received + ${counts["+"]}`),
|
||
|
""
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
return formatted.map((i) => i ? indent + i : i).join("\n");
|
||
|
}
|
||
|
|
||
|
async function printError(error, ctx, options = {}) {
|
||
|
const { showCodeFrame = true, fullStack = false, type } = options;
|
||
|
let e = error;
|
||
|
if (typeof error === "string") {
|
||
|
e = {
|
||
|
message: error.split(/\n/g)[0],
|
||
|
stack: error
|
||
|
};
|
||
|
}
|
||
|
if (!e) {
|
||
|
const error2 = new Error("unknown error");
|
||
|
e = {
|
||
|
message: e ?? error2.message,
|
||
|
stack: error2.stack
|
||
|
};
|
||
|
}
|
||
|
const stacks = parseStacktrace(e, fullStack);
|
||
|
const nearest = error instanceof TypeCheckError ? error.stacks[0] : stacks.find(
|
||
|
(stack) => ctx.server.moduleGraph.getModuleById(stack.file) && existsSync$1(stack.file)
|
||
|
);
|
||
|
const errorProperties = getErrorProperties(e);
|
||
|
if (type)
|
||
|
printErrorType(type, ctx);
|
||
|
printErrorMessage(e, ctx.logger);
|
||
|
if (e.frame) {
|
||
|
ctx.logger.error(c.yellow(e.frame));
|
||
|
} else {
|
||
|
printStack(ctx, stacks, nearest, errorProperties, (s) => {
|
||
|
if (showCodeFrame && s === nearest && nearest) {
|
||
|
const sourceCode = readFileSync(nearest.file, "utf-8");
|
||
|
ctx.logger.error(c.yellow(generateCodeFrame(sourceCode, 4, s.line, s.column)));
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
if (typeof e.cause === "object" && e.cause && "name" in e.cause) {
|
||
|
e.cause.name = `Caused by: ${e.cause.name}`;
|
||
|
await printError(e.cause, ctx, { fullStack, showCodeFrame: false });
|
||
|
}
|
||
|
handleImportOutsideModuleError(e.stack || e.stackStr || "", ctx);
|
||
|
if (e.showDiff || e.showDiff === void 0 && e.actual && e.expected) {
|
||
|
displayDiff(stringify$5(e.actual), stringify$5(e.expected), ctx.logger.console, {
|
||
|
outputTruncateLength: ctx.config.outputTruncateLength,
|
||
|
outputDiffLines: ctx.config.outputDiffLines,
|
||
|
outputDiffMaxLines: ctx.config.outputDiffMaxLines
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
function printErrorType(type, ctx) {
|
||
|
ctx.logger.error(`
|
||
|
${c.red(divider(c.bold(c.inverse(` ${type} `))))}`);
|
||
|
}
|
||
|
const skipErrorProperties = /* @__PURE__ */ new Set([
|
||
|
"nameStr",
|
||
|
"stack",
|
||
|
"cause",
|
||
|
"stacks",
|
||
|
"stackStr",
|
||
|
"type",
|
||
|
"showDiff",
|
||
|
"actual",
|
||
|
"expected",
|
||
|
...Object.getOwnPropertyNames(Error.prototype),
|
||
|
...Object.getOwnPropertyNames(Object.prototype)
|
||
|
]);
|
||
|
function getErrorProperties(e) {
|
||
|
const errorObject = /* @__PURE__ */ Object.create(null);
|
||
|
if (e.name === "AssertionError")
|
||
|
return errorObject;
|
||
|
for (const key of Object.getOwnPropertyNames(e)) {
|
||
|
if (!skipErrorProperties.has(key))
|
||
|
errorObject[key] = e[key];
|
||
|
}
|
||
|
return errorObject;
|
||
|
}
|
||
|
const esmErrors = [
|
||
|
"Cannot use import statement outside a module",
|
||
|
"Unexpected token 'export'"
|
||
|
];
|
||
|
function handleImportOutsideModuleError(stack, ctx) {
|
||
|
if (!esmErrors.some((e) => stack.includes(e)))
|
||
|
return;
|
||
|
const path = normalize(stack.split("\n")[0].trim());
|
||
|
let name = path.split("/node_modules/").pop() || "";
|
||
|
if (name == null ? void 0 : name.startsWith("@"))
|
||
|
name = name.split("/").slice(0, 2).join("/");
|
||
|
else
|
||
|
name = name.split("/")[0];
|
||
|
ctx.logger.error(c.yellow(
|
||
|
`Module ${path} seems to be an ES Module but shipped in a CommonJS package. You might want to create an issue to the package ${c.bold(`"${name}"`)} asking them to ship the file in .mjs extension or add "type": "module" in their package.json.
|
||
|
|
||
|
As a temporary workaround you can try to inline the package by updating your config:
|
||
|
|
||
|
` + c.gray(c.dim("// vitest.config.js")) + "\n" + c.green(`export default {
|
||
|
test: {
|
||
|
deps: {
|
||
|
inline: [
|
||
|
${c.yellow(c.bold(`"${name}"`))}
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
`)
|
||
|
));
|
||
|
}
|
||
|
function displayDiff(actual, expected, console, options = {}) {
|
||
|
const diff = unifiedDiff(actual, expected, options);
|
||
|
const dim = options.noColor ? (s) => s : c.dim;
|
||
|
const black = options.noColor ? (s) => s : c.black;
|
||
|
if (diff)
|
||
|
console.error(diff + "\n");
|
||
|
else if (actual && expected && actual !== '"undefined"' && expected !== '"undefined"')
|
||
|
console.error(dim("Could not display diff. It's possible objects are too large to compare.\nTry increasing ") + black("--outputDiffMaxSize") + dim(" option.\n"));
|
||
|
}
|
||
|
function printErrorMessage(error, logger) {
|
||
|
const errorName = error.name || error.nameStr || "Unknown Error";
|
||
|
logger.error(c.red(`${c.bold(errorName)}: ${error.message}`));
|
||
|
}
|
||
|
function printStack(ctx, stack, highlight, errorProperties, onStack) {
|
||
|
if (!stack.length)
|
||
|
return;
|
||
|
const logger = ctx.logger;
|
||
|
for (const frame of stack) {
|
||
|
const color = frame === highlight ? c.yellow : c.gray;
|
||
|
const path = relative(ctx.config.root, frame.file);
|
||
|
logger.error(color(` ${c.dim(F_POINTER)} ${[frame.method, c.dim(`${path}:${frame.line}:${frame.column}`)].filter(Boolean).join(" ")}`));
|
||
|
onStack == null ? void 0 : onStack(frame);
|
||
|
if (frame.file in ctx.state.filesMap)
|
||
|
break;
|
||
|
}
|
||
|
logger.error();
|
||
|
const hasProperties = Object.keys(errorProperties).length > 0;
|
||
|
if (hasProperties) {
|
||
|
logger.error(c.red(c.dim(divider())));
|
||
|
const propertiesString = stringify$5(errorProperties, 10, { printBasicPrototype: false });
|
||
|
logger.error(c.red(c.bold("Serialized Error:")), c.gray(propertiesString));
|
||
|
}
|
||
|
}
|
||
|
function generateCodeFrame(source, indent = 0, lineNumber, columnNumber, range = 2) {
|
||
|
var _a;
|
||
|
const start = positionToOffset(source, lineNumber, columnNumber);
|
||
|
const end = start;
|
||
|
const lines = source.split(lineSplitRE);
|
||
|
let count = 0;
|
||
|
let res = [];
|
||
|
const columns = ((_a = process.stdout) == null ? void 0 : _a.columns) || 80;
|
||
|
function lineNo(no = "") {
|
||
|
return c.gray(`${String(no).padStart(3, " ")}| `);
|
||
|
}
|
||
|
for (let i = 0; i < lines.length; i++) {
|
||
|
count += lines[i].length + 1;
|
||
|
if (count >= start) {
|
||
|
for (let j = i - range; j <= i + range || end > count; j++) {
|
||
|
if (j < 0 || j >= lines.length)
|
||
|
continue;
|
||
|
const lineLength = lines[j].length;
|
||
|
if (lineLength > 200)
|
||
|
return "";
|
||
|
res.push(lineNo(j + 1) + cliTruncate(lines[j].replace(/\t/g, " "), columns - 5 - indent));
|
||
|
if (j === i) {
|
||
|
const pad = start - (count - lineLength);
|
||
|
const length = Math.max(1, end > count ? lineLength - pad : end - start);
|
||
|
res.push(lineNo() + " ".repeat(pad) + c.red("^".repeat(length)));
|
||
|
} else if (j > i) {
|
||
|
if (end > count) {
|
||
|
const length = Math.max(1, Math.min(end - count, lineLength));
|
||
|
res.push(lineNo() + c.red("^".repeat(length)));
|
||
|
}
|
||
|
count += lineLength + 1;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (indent)
|
||
|
res = res.map((line) => " ".repeat(indent) + line);
|
||
|
return res.join("\n");
|
||
|
}
|
||
|
|
||
|
class Logger {
|
||
|
constructor(ctx, console = globalThis.console) {
|
||
|
this.ctx = ctx;
|
||
|
this.console = console;
|
||
|
this.outputStream = process.stdout;
|
||
|
this.errorStream = process.stderr;
|
||
|
this.logUpdate = createLogUpdate(process.stdout);
|
||
|
}
|
||
|
log(...args) {
|
||
|
this._clearScreen();
|
||
|
this.console.log(...args);
|
||
|
}
|
||
|
error(...args) {
|
||
|
this._clearScreen();
|
||
|
this.console.error(...args);
|
||
|
}
|
||
|
warn(...args) {
|
||
|
this._clearScreen();
|
||
|
this.console.warn(...args);
|
||
|
}
|
||
|
clearFullScreen(message) {
|
||
|
if (this.ctx.server.config.clearScreen === false) {
|
||
|
this.console.log(message);
|
||
|
return;
|
||
|
}
|
||
|
this.console.log(`\x1Bc${message}`);
|
||
|
}
|
||
|
clearScreen(message, force = false) {
|
||
|
if (this.ctx.server.config.clearScreen === false) {
|
||
|
this.console.log(message);
|
||
|
return;
|
||
|
}
|
||
|
this._clearScreenPending = message;
|
||
|
if (force)
|
||
|
this._clearScreen();
|
||
|
}
|
||
|
_clearScreen() {
|
||
|
if (this._clearScreenPending == null)
|
||
|
return;
|
||
|
const log = this._clearScreenPending;
|
||
|
this._clearScreenPending = void 0;
|
||
|
this.console.log(`\x1B[1;1H\x1B[J${log}`);
|
||
|
}
|
||
|
printError(err, fullStack = false, type) {
|
||
|
return printError(err, this.ctx, {
|
||
|
fullStack,
|
||
|
type,
|
||
|
showCodeFrame: true
|
||
|
});
|
||
|
}
|
||
|
printNoTestFound(filters) {
|
||
|
const config = this.ctx.config;
|
||
|
const comma = c.dim(", ");
|
||
|
if (filters == null ? void 0 : filters.length)
|
||
|
this.console.error(c.dim("filter: ") + c.yellow(filters.join(comma)));
|
||
|
if (config.include)
|
||
|
this.console.error(c.dim("include: ") + c.yellow(config.include.join(comma)));
|
||
|
if (config.exclude)
|
||
|
this.console.error(c.dim("exclude: ") + c.yellow(config.exclude.join(comma)));
|
||
|
if (config.watchExclude)
|
||
|
this.console.error(c.dim("watch exclude: ") + c.yellow(config.watchExclude.join(comma)));
|
||
|
if (config.passWithNoTests)
|
||
|
this.log(`No ${config.mode} files found, exiting with code 0
|
||
|
`);
|
||
|
else
|
||
|
this.error(c.red(`
|
||
|
No ${config.mode} files found, exiting with code 1`));
|
||
|
}
|
||
|
printBanner() {
|
||
|
var _a, _b, _c;
|
||
|
this.log();
|
||
|
const versionTest = this.ctx.config.watch ? c.blue(`v${version}`) : c.cyan(`v${version}`);
|
||
|
const mode = this.ctx.config.watch ? c.blue(" DEV ") : c.cyan(" RUN ");
|
||
|
this.log(`${c.inverse(c.bold(mode))} ${versionTest} ${c.gray(this.ctx.config.root)}`);
|
||
|
if (this.ctx.config.browser)
|
||
|
this.log(c.dim(c.green(` Browser runner started at http://${((_a = this.ctx.config.api) == null ? void 0 : _a.host) || "localhost"}:${c.bold(`${this.ctx.server.config.server.port}`)}`)));
|
||
|
else if (this.ctx.config.ui)
|
||
|
this.log(c.dim(c.green(` UI started at http://${((_b = this.ctx.config.api) == null ? void 0 : _b.host) || "localhost"}:${c.bold(`${this.ctx.server.config.server.port}`)}${this.ctx.config.uiBase}`)));
|
||
|
else if (this.ctx.config.api)
|
||
|
this.log(c.dim(c.green(` API started at http://${((_c = this.ctx.config.api) == null ? void 0 : _c.host) || "localhost"}:${c.bold(`${this.ctx.config.api.port}`)}`)));
|
||
|
if (this.ctx.coverageProvider)
|
||
|
this.log(c.dim(" Coverage enabled with ") + c.yellow(this.ctx.coverageProvider.name));
|
||
|
this.log();
|
||
|
}
|
||
|
async printUnhandledErrors(errors) {
|
||
|
const errorMessage = c.red(c.bold(
|
||
|
`
|
||
|
Vitest caught ${errors.length} unhandled error${errors.length > 1 ? "s" : ""} during the test run.
|
||
|
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.`
|
||
|
));
|
||
|
this.log(c.red(divider(c.bold(c.inverse(" Unhandled Errors ")))));
|
||
|
this.log(errorMessage);
|
||
|
await Promise.all(errors.map(async (err) => {
|
||
|
await this.printError(err, true, err.type || "Unhandled Error");
|
||
|
}));
|
||
|
this.log(c.red(divider()));
|
||
|
}
|
||
|
async printSourceTypeErrors(errors) {
|
||
|
const errorMessage = c.red(c.bold(
|
||
|
`
|
||
|
Vitest found ${errors.length} error${errors.length > 1 ? "s" : ""} not related to your test files.`
|
||
|
));
|
||
|
this.log(c.red(divider(c.bold(c.inverse(" Source Errors ")))));
|
||
|
this.log(errorMessage);
|
||
|
await Promise.all(errors.map(async (err) => {
|
||
|
await this.printError(err, true);
|
||
|
}));
|
||
|
this.log(c.red(divider()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const WATCHER_DEBOUNCE = 100;
|
||
|
class Vitest {
|
||
|
constructor(mode) {
|
||
|
this.mode = mode;
|
||
|
this.config = void 0;
|
||
|
this.server = void 0;
|
||
|
this.state = void 0;
|
||
|
this.snapshot = void 0;
|
||
|
this.cache = void 0;
|
||
|
this.reporters = void 0;
|
||
|
this.vitenode = void 0;
|
||
|
this.invalidates = /* @__PURE__ */ new Set();
|
||
|
this.changedTests = /* @__PURE__ */ new Set();
|
||
|
this.isFirstRun = true;
|
||
|
this.restartsCount = 0;
|
||
|
this.runner = void 0;
|
||
|
this._onRestartListeners = [];
|
||
|
this._onSetServer = [];
|
||
|
this.unregisterWatcher = noop$1;
|
||
|
this.logger = new Logger(this);
|
||
|
}
|
||
|
async setServer(options, server) {
|
||
|
var _a, _b, _c;
|
||
|
(_a = this.unregisterWatcher) == null ? void 0 : _a.call(this);
|
||
|
safeClearTimeout(this._rerunTimer);
|
||
|
this.restartsCount += 1;
|
||
|
(_b = this.pool) == null ? void 0 : _b.close();
|
||
|
this.pool = void 0;
|
||
|
const resolved = resolveConfig(this.mode, options, server.config);
|
||
|
this.server = server;
|
||
|
this.config = resolved;
|
||
|
this.state = new StateManager();
|
||
|
this.cache = new VitestCache();
|
||
|
this.snapshot = new SnapshotManager({ ...resolved.snapshotOptions });
|
||
|
if (this.config.watch && this.mode !== "typecheck")
|
||
|
this.registerWatcher();
|
||
|
this.vitenode = new ViteNodeServer(server, this.config);
|
||
|
const node = this.vitenode;
|
||
|
this.runner = new ViteNodeRunner({
|
||
|
root: server.config.root,
|
||
|
base: server.config.base,
|
||
|
fetchModule(id) {
|
||
|
return node.fetchModule(id);
|
||
|
},
|
||
|
resolveId(id, importer) {
|
||
|
return node.resolveId(id, importer);
|
||
|
}
|
||
|
});
|
||
|
if (this.config.watch) {
|
||
|
const serverRestart = server.restart;
|
||
|
server.restart = async (...args) => {
|
||
|
await Promise.all(this._onRestartListeners.map((fn) => fn()));
|
||
|
return await serverRestart(...args);
|
||
|
};
|
||
|
server.watcher.on("change", async (file) => {
|
||
|
file = normalizePath(file);
|
||
|
const isConfig = file === server.config.configFile;
|
||
|
if (isConfig) {
|
||
|
await Promise.all(this._onRestartListeners.map((fn) => fn("config")));
|
||
|
await serverRestart();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
this.reporters = resolved.mode === "benchmark" ? await createBenchmarkReporters(toArray((_c = resolved.benchmark) == null ? void 0 : _c.reporters), this.runner) : await createReporters(resolved.reporters, this.runner);
|
||
|
this.runningPromise = void 0;
|
||
|
this.cache.results.setConfig(resolved.root, resolved.cache);
|
||
|
try {
|
||
|
await this.cache.results.readFromCache();
|
||
|
} catch {
|
||
|
}
|
||
|
await Promise.all(this._onSetServer.map((fn) => fn()));
|
||
|
}
|
||
|
async initCoverageProvider() {
|
||
|
if (this.coverageProvider !== void 0)
|
||
|
return;
|
||
|
this.coverageProvider = await getCoverageProvider(this.config.coverage);
|
||
|
if (this.coverageProvider) {
|
||
|
await this.coverageProvider.initialize(this);
|
||
|
this.config.coverage = this.coverageProvider.resolveOptions();
|
||
|
}
|
||
|
return this.coverageProvider;
|
||
|
}
|
||
|
getSerializableConfig() {
|
||
|
return deepMerge(
|
||
|
{
|
||
|
...this.config,
|
||
|
reporters: [],
|
||
|
snapshotOptions: {
|
||
|
...this.config.snapshotOptions,
|
||
|
resolveSnapshotPath: void 0
|
||
|
},
|
||
|
onConsoleLog: void 0,
|
||
|
sequence: {
|
||
|
...this.config.sequence,
|
||
|
sequencer: void 0
|
||
|
},
|
||
|
benchmark: {
|
||
|
...this.config.benchmark,
|
||
|
reporters: []
|
||
|
}
|
||
|
},
|
||
|
this.configOverride || {}
|
||
|
);
|
||
|
}
|
||
|
async typecheck(filters = []) {
|
||
|
const { include, exclude } = this.config.typecheck;
|
||
|
const testsFilesList = await this.globFiles(filters, include, exclude);
|
||
|
const checker = new Typechecker(this, testsFilesList);
|
||
|
this.typechecker = checker;
|
||
|
checker.onParseEnd(async ({ files, sourceErrors }) => {
|
||
|
this.state.collectFiles(checker.getTestFiles());
|
||
|
await this.report("onTaskUpdate", checker.getTestPacks());
|
||
|
await this.report("onCollected");
|
||
|
if (!files.length) {
|
||
|
this.logger.printNoTestFound();
|
||
|
} else {
|
||
|
if (hasFailed(files))
|
||
|
process.exitCode = 1;
|
||
|
await this.report("onFinished", files);
|
||
|
}
|
||
|
if (sourceErrors.length && !this.config.typecheck.ignoreSourceErrors) {
|
||
|
process.exitCode = 1;
|
||
|
await this.logger.printSourceTypeErrors(sourceErrors);
|
||
|
}
|
||
|
if (!files.length) {
|
||
|
const exitCode = this.config.passWithNoTests ? process.exitCode ?? 0 : 1;
|
||
|
process.exit(exitCode);
|
||
|
}
|
||
|
if (this.config.watch) {
|
||
|
await this.report("onWatcherStart", files, [
|
||
|
...sourceErrors,
|
||
|
...this.state.getUnhandledErrors()
|
||
|
]);
|
||
|
}
|
||
|
});
|
||
|
checker.onParseStart(async () => {
|
||
|
await this.report("onInit", this);
|
||
|
this.state.collectFiles(checker.getTestFiles());
|
||
|
await this.report("onCollected");
|
||
|
});
|
||
|
checker.onWatcherRerun(async () => {
|
||
|
await this.report("onWatcherRerun", testsFilesList, "File change detected. Triggering rerun.");
|
||
|
await checker.collectTests();
|
||
|
this.state.collectFiles(checker.getTestFiles());
|
||
|
await this.report("onTaskUpdate", checker.getTestPacks());
|
||
|
await this.report("onCollected");
|
||
|
});
|
||
|
await checker.prepare();
|
||
|
await checker.collectTests();
|
||
|
await checker.start();
|
||
|
}
|
||
|
async start(filters) {
|
||
|
var _a;
|
||
|
if (this.mode === "typecheck") {
|
||
|
await this.typecheck(filters);
|
||
|
return;
|
||
|
}
|
||
|
try {
|
||
|
await this.initCoverageProvider();
|
||
|
await ((_a = this.coverageProvider) == null ? void 0 : _a.clean(this.config.coverage.clean));
|
||
|
} catch (e) {
|
||
|
this.logger.error(e);
|
||
|
process.exit(1);
|
||
|
}
|
||
|
await this.report("onInit", this);
|
||
|
const files = await this.filterTestsBySource(
|
||
|
await this.globTestFiles(filters)
|
||
|
);
|
||
|
if (!files.length) {
|
||
|
const exitCode = this.config.passWithNoTests ? 0 : 1;
|
||
|
this.logger.printNoTestFound(filters);
|
||
|
process.exit(exitCode);
|
||
|
}
|
||
|
await Promise.all(files.map((file) => this.cache.stats.updateStats(file)));
|
||
|
await this.runFiles(files);
|
||
|
if (this.coverageProvider) {
|
||
|
this.logger.log(c.blue(" % ") + c.dim("Coverage report from ") + c.yellow(this.coverageProvider.name));
|
||
|
await this.coverageProvider.reportCoverage();
|
||
|
}
|
||
|
if (this.config.watch && !this.config.browser)
|
||
|
await this.report("onWatcherStart");
|
||
|
}
|
||
|
async getTestDependencies(filepath) {
|
||
|
const deps = /* @__PURE__ */ new Set();
|
||
|
const addImports = async (filepath2) => {
|
||
|
const transformed = await this.vitenode.transformRequest(filepath2);
|
||
|
if (!transformed)
|
||
|
return;
|
||
|
const dependencies = [...transformed.deps || [], ...transformed.dynamicDeps || []];
|
||
|
for (const dep of dependencies) {
|
||
|
const path = await this.server.pluginContainer.resolveId(dep, filepath2, { ssr: true });
|
||
|
const fsPath = path && !path.external && path.id.split("?")[0];
|
||
|
if (fsPath && !fsPath.includes("node_modules") && !deps.has(fsPath) && existsSync(fsPath)) {
|
||
|
deps.add(fsPath);
|
||
|
await addImports(fsPath);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
await addImports(filepath);
|
||
|
return deps;
|
||
|
}
|
||
|
async filterTestsBySource(tests) {
|
||
|
if (this.config.changed && !this.config.related) {
|
||
|
const { VitestGit } = await import('./chunk-node-git.543e964a.js');
|
||
|
const vitestGit = new VitestGit(this.config.root);
|
||
|
const related2 = await vitestGit.findChangedFiles({
|
||
|
changedSince: this.config.changed
|
||
|
});
|
||
|
if (!related2) {
|
||
|
this.logger.error(c.red("Could not find Git root. Have you initialized git with `git init`?\n"));
|
||
|
process.exit(1);
|
||
|
}
|
||
|
this.config.related = Array.from(new Set(related2));
|
||
|
}
|
||
|
const related = this.config.related;
|
||
|
if (!related)
|
||
|
return tests;
|
||
|
const forceRerunTriggers = this.config.forceRerunTriggers;
|
||
|
if (forceRerunTriggers.length && micromatch_1(related, forceRerunTriggers).length)
|
||
|
return tests;
|
||
|
if (!related.length)
|
||
|
return [];
|
||
|
const testGraphs = await Promise.all(
|
||
|
tests.map(async (filepath) => {
|
||
|
const deps = await this.getTestDependencies(filepath);
|
||
|
return [filepath, deps];
|
||
|
})
|
||
|
);
|
||
|
const runningTests = [];
|
||
|
for (const [filepath, deps] of testGraphs) {
|
||
|
if (related.some((path) => path === filepath || deps.has(path)))
|
||
|
runningTests.push(filepath);
|
||
|
}
|
||
|
return runningTests;
|
||
|
}
|
||
|
async runFiles(paths) {
|
||
|
paths = Array.from(new Set(paths));
|
||
|
await this.runningPromise;
|
||
|
this.state.startCollectingPaths();
|
||
|
this.runningPromise = (async () => {
|
||
|
if (!this.pool)
|
||
|
this.pool = createPool(this);
|
||
|
const invalidates = Array.from(this.invalidates);
|
||
|
this.invalidates.clear();
|
||
|
this.snapshot.clear();
|
||
|
this.state.clearErrors();
|
||
|
try {
|
||
|
await this.pool.runTests(paths, invalidates);
|
||
|
} catch (err) {
|
||
|
this.state.catchError(err, "Unhandled Error");
|
||
|
}
|
||
|
const files = this.state.getFiles();
|
||
|
if (hasFailed(files))
|
||
|
process.exitCode = 1;
|
||
|
this.cache.results.updateResults(files);
|
||
|
await this.cache.results.writeToCache();
|
||
|
})().finally(async () => {
|
||
|
this.state.finishCollectingPaths();
|
||
|
if (!this.config.browser)
|
||
|
await this.report("onFinished", this.state.getFiles(), this.state.getUnhandledErrors());
|
||
|
this.runningPromise = void 0;
|
||
|
});
|
||
|
return await this.runningPromise;
|
||
|
}
|
||
|
async rerunFiles(files = this.state.getFilepaths(), trigger) {
|
||
|
await this.report("onWatcherRerun", files, trigger);
|
||
|
await this.runFiles(files);
|
||
|
if (!this.config.browser)
|
||
|
await this.report("onWatcherStart");
|
||
|
}
|
||
|
async changeNamePattern(pattern, files = this.state.getFilepaths(), trigger) {
|
||
|
this.config.testNamePattern = pattern ? new RegExp(pattern) : void 0;
|
||
|
await this.rerunFiles(files, trigger);
|
||
|
}
|
||
|
async changeFilenamePattern(pattern) {
|
||
|
const files = this.state.getFilepaths();
|
||
|
if (!pattern)
|
||
|
return await this.rerunFiles(files, "reset filename pattern");
|
||
|
const filteredFiles = await this.globTestFiles([pattern]);
|
||
|
await this.rerunFiles(filteredFiles, "change filename pattern");
|
||
|
}
|
||
|
async rerunFailed() {
|
||
|
await this.rerunFiles(this.state.getFailedFilepaths(), "rerun failed");
|
||
|
}
|
||
|
async updateSnapshot(files) {
|
||
|
files = files || [
|
||
|
...this.state.getFailedFilepaths(),
|
||
|
...this.snapshot.summary.uncheckedKeysByFile.map((s) => s.filePath)
|
||
|
];
|
||
|
this.configOverride = {
|
||
|
snapshotOptions: {
|
||
|
updateSnapshot: "all"
|
||
|
}
|
||
|
};
|
||
|
try {
|
||
|
await this.rerunFiles(files, "update snapshot");
|
||
|
} finally {
|
||
|
this.configOverride = void 0;
|
||
|
}
|
||
|
}
|
||
|
async scheduleRerun(triggerId) {
|
||
|
const mod = this.server.moduleGraph.getModuleById(triggerId);
|
||
|
if (mod)
|
||
|
mod.lastHMRTimestamp = Date.now();
|
||
|
const currentCount = this.restartsCount;
|
||
|
safeClearTimeout(this._rerunTimer);
|
||
|
await this.runningPromise;
|
||
|
safeClearTimeout(this._rerunTimer);
|
||
|
if (this.restartsCount !== currentCount)
|
||
|
return;
|
||
|
this._rerunTimer = safeSetTimeout(async () => {
|
||
|
var _a;
|
||
|
if (this.changedTests.size === 0) {
|
||
|
this.invalidates.clear();
|
||
|
return;
|
||
|
}
|
||
|
if (this.restartsCount !== currentCount)
|
||
|
return;
|
||
|
this.isFirstRun = false;
|
||
|
this.snapshot.clear();
|
||
|
const files = Array.from(this.changedTests);
|
||
|
this.changedTests.clear();
|
||
|
if (this.coverageProvider && this.config.coverage.cleanOnRerun)
|
||
|
await this.coverageProvider.clean();
|
||
|
await this.report("onWatcherRerun", files, triggerId);
|
||
|
await this.runFiles(files);
|
||
|
await ((_a = this.coverageProvider) == null ? void 0 : _a.reportCoverage());
|
||
|
if (!this.config.browser)
|
||
|
await this.report("onWatcherStart");
|
||
|
}, WATCHER_DEBOUNCE);
|
||
|
}
|
||
|
registerWatcher() {
|
||
|
const onChange = (id) => {
|
||
|
id = slash$1(id);
|
||
|
const needsRerun = this.handleFileChanged(id);
|
||
|
if (needsRerun)
|
||
|
this.scheduleRerun(id);
|
||
|
};
|
||
|
const onUnlink = (id) => {
|
||
|
id = slash$1(id);
|
||
|
this.invalidates.add(id);
|
||
|
if (this.state.filesMap.has(id)) {
|
||
|
this.state.filesMap.delete(id);
|
||
|
this.cache.results.removeFromCache(id);
|
||
|
this.cache.stats.removeStats(id);
|
||
|
this.changedTests.delete(id);
|
||
|
this.report("onTestRemoved", id);
|
||
|
}
|
||
|
};
|
||
|
const onAdd = async (id) => {
|
||
|
id = slash$1(id);
|
||
|
if (await this.isTargetFile(id)) {
|
||
|
this.changedTests.add(id);
|
||
|
await this.cache.stats.updateStats(id);
|
||
|
this.scheduleRerun(id);
|
||
|
}
|
||
|
};
|
||
|
const watcher = this.server.watcher;
|
||
|
if (this.config.forceRerunTriggers.length)
|
||
|
watcher.add(this.config.forceRerunTriggers);
|
||
|
watcher.unwatch(this.config.watchExclude);
|
||
|
watcher.on("change", onChange);
|
||
|
watcher.on("unlink", onUnlink);
|
||
|
watcher.on("add", onAdd);
|
||
|
this.unregisterWatcher = () => {
|
||
|
watcher.off("change", onChange);
|
||
|
watcher.off("unlink", onUnlink);
|
||
|
watcher.off("add", onAdd);
|
||
|
this.unregisterWatcher = noop$1;
|
||
|
};
|
||
|
}
|
||
|
handleFileChanged(id) {
|
||
|
if (this.changedTests.has(id) || this.invalidates.has(id))
|
||
|
return false;
|
||
|
if (micromatch_1.isMatch(id, this.config.forceRerunTriggers)) {
|
||
|
this.state.getFilepaths().forEach((file) => this.changedTests.add(file));
|
||
|
return true;
|
||
|
}
|
||
|
const mod = this.server.moduleGraph.getModuleById(id);
|
||
|
if (!mod)
|
||
|
return false;
|
||
|
this.invalidates.add(id);
|
||
|
if (this.state.filesMap.has(id)) {
|
||
|
this.changedTests.add(id);
|
||
|
return true;
|
||
|
}
|
||
|
let rerun = false;
|
||
|
mod.importers.forEach((i) => {
|
||
|
if (!i.id)
|
||
|
return;
|
||
|
const heedsRerun = this.handleFileChanged(i.id);
|
||
|
if (heedsRerun)
|
||
|
rerun = true;
|
||
|
});
|
||
|
return rerun;
|
||
|
}
|
||
|
async close() {
|
||
|
var _a, _b;
|
||
|
if (!this.closingPromise) {
|
||
|
this.closingPromise = Promise.allSettled([
|
||
|
(_a = this.pool) == null ? void 0 : _a.close(),
|
||
|
this.server.close(),
|
||
|
(_b = this.typechecker) == null ? void 0 : _b.stop()
|
||
|
].filter(Boolean)).then((results) => {
|
||
|
results.filter((r) => r.status === "rejected").forEach((err) => {
|
||
|
this.logger.error("error during close", err.reason);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
return this.closingPromise;
|
||
|
}
|
||
|
async exit(force = false) {
|
||
|
safeSetTimeout(() => {
|
||
|
console.warn(`close timed out after ${this.config.teardownTimeout}ms`);
|
||
|
process.exit();
|
||
|
}, this.config.teardownTimeout).unref();
|
||
|
await this.close();
|
||
|
if (force)
|
||
|
process.exit();
|
||
|
}
|
||
|
async report(name, ...args) {
|
||
|
await Promise.all(this.reporters.map((r) => {
|
||
|
var _a;
|
||
|
return (_a = r[name]) == null ? void 0 : _a.call(
|
||
|
r,
|
||
|
...args
|
||
|
);
|
||
|
}));
|
||
|
}
|
||
|
async globFiles(filters, include, exclude) {
|
||
|
const globOptions = {
|
||
|
absolute: true,
|
||
|
dot: true,
|
||
|
cwd: this.config.dir || this.config.root,
|
||
|
ignore: exclude
|
||
|
};
|
||
|
let testFiles = await out(include, globOptions);
|
||
|
if (filters.length && process.platform === "win32")
|
||
|
filters = filters.map((f) => toNamespacedPath(f));
|
||
|
if (filters.length)
|
||
|
testFiles = testFiles.filter((i) => filters.some((f) => i.includes(f)));
|
||
|
return testFiles;
|
||
|
}
|
||
|
async globTestFiles(filters = []) {
|
||
|
const { include, exclude, includeSource } = this.config;
|
||
|
const testFiles = await this.globFiles(filters, include, exclude);
|
||
|
if (includeSource) {
|
||
|
const files = await this.globFiles(filters, includeSource, exclude);
|
||
|
await Promise.all(files.map(async (file) => {
|
||
|
try {
|
||
|
const code = await promises.readFile(file, "utf-8");
|
||
|
if (this.isInSourceTestFile(code))
|
||
|
testFiles.push(file);
|
||
|
} catch {
|
||
|
return null;
|
||
|
}
|
||
|
}));
|
||
|
}
|
||
|
return testFiles;
|
||
|
}
|
||
|
async isTargetFile(id, source) {
|
||
|
var _a;
|
||
|
const relativeId = relative(this.config.dir || this.config.root, id);
|
||
|
if (micromatch_1.isMatch(relativeId, this.config.exclude))
|
||
|
return false;
|
||
|
if (micromatch_1.isMatch(relativeId, this.config.include))
|
||
|
return true;
|
||
|
if (((_a = this.config.includeSource) == null ? void 0 : _a.length) && micromatch_1.isMatch(relativeId, this.config.includeSource)) {
|
||
|
source = source || await promises.readFile(id, "utf-8");
|
||
|
return this.isInSourceTestFile(source);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
isInSourceTestFile(code) {
|
||
|
return code.includes("import.meta.vitest");
|
||
|
}
|
||
|
onServerRestart(fn) {
|
||
|
this._onRestartListeners.push(fn);
|
||
|
}
|
||
|
onAfterSetServer(fn) {
|
||
|
this._onSetServer.push(fn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function generateCssFilenameHash(filepath) {
|
||
|
return createHash$1("md5").update(filepath).digest("hex").slice(0, 6);
|
||
|
}
|
||
|
function generateScopedClassName(strategy, name, filename) {
|
||
|
if (strategy === "scoped")
|
||
|
return null;
|
||
|
if (strategy === "non-scoped")
|
||
|
return name;
|
||
|
const hash = generateCssFilenameHash(filename);
|
||
|
return `_${name}_${hash}`;
|
||
|
}
|
||
|
|
||
|
const EnvReplacerPlugin = () => {
|
||
|
return {
|
||
|
name: "vitest:env-replacer",
|
||
|
enforce: "pre",
|
||
|
transform(code, id) {
|
||
|
if (!/\bimport\.meta\.env\b/g.test(code))
|
||
|
return null;
|
||
|
let s = null;
|
||
|
const envs = stripLiteral(code).matchAll(/\bimport\.meta\.env\b/g);
|
||
|
for (const env of envs) {
|
||
|
s || (s = new MagicString(code));
|
||
|
const startIndex = env.index;
|
||
|
const endIndex = startIndex + env[0].length;
|
||
|
s.overwrite(startIndex, endIndex, "process.env");
|
||
|
}
|
||
|
if (s) {
|
||
|
return {
|
||
|
code: s.toString(),
|
||
|
map: s.generateMap({
|
||
|
hires: true,
|
||
|
source: cleanUrl(id)
|
||
|
})
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
async function loadGlobalSetupFiles(ctx) {
|
||
|
var _a;
|
||
|
const server = ctx.server;
|
||
|
const runner = ctx.runner;
|
||
|
const globalSetupFiles = toArray((_a = server.config.test) == null ? void 0 : _a.globalSetup);
|
||
|
return Promise.all(globalSetupFiles.map((file) => loadGlobalSetupFile(file, runner)));
|
||
|
}
|
||
|
async function loadGlobalSetupFile(file, runner) {
|
||
|
const m = await runner.executeFile(file);
|
||
|
for (const exp of ["default", "setup", "teardown"]) {
|
||
|
if (m[exp] != null && typeof m[exp] !== "function")
|
||
|
throw new Error(`invalid export in globalSetup file ${file}: ${exp} must be a function`);
|
||
|
}
|
||
|
if (m.default) {
|
||
|
return {
|
||
|
file,
|
||
|
setup: m.default
|
||
|
};
|
||
|
} else if (m.setup || m.teardown) {
|
||
|
return {
|
||
|
file,
|
||
|
setup: m.setup,
|
||
|
teardown: m.teardown
|
||
|
};
|
||
|
} else {
|
||
|
throw new Error(`invalid globalSetup file ${file}. Must export setup, teardown or have a default export`);
|
||
|
}
|
||
|
}
|
||
|
const GlobalSetupPlugin = (ctx) => {
|
||
|
let globalSetupFiles;
|
||
|
return {
|
||
|
name: "vitest:global-setup-plugin",
|
||
|
enforce: "pre",
|
||
|
async buildStart() {
|
||
|
var _a, _b;
|
||
|
if (!((_a = ctx.server.config.test) == null ? void 0 : _a.globalSetup))
|
||
|
return;
|
||
|
globalSetupFiles = await loadGlobalSetupFiles(ctx);
|
||
|
try {
|
||
|
for (const globalSetupFile of globalSetupFiles) {
|
||
|
const teardown = await ((_b = globalSetupFile.setup) == null ? void 0 : _b.call(globalSetupFile));
|
||
|
if (teardown == null || !!globalSetupFile.teardown)
|
||
|
continue;
|
||
|
if (typeof teardown !== "function")
|
||
|
throw new Error(`invalid return value in globalSetup file ${globalSetupFile.file}. Must return a function`);
|
||
|
globalSetupFile.teardown = teardown;
|
||
|
}
|
||
|
} catch (e) {
|
||
|
ctx.logger.error(`
|
||
|
${c.red(divider(c.bold(c.inverse(" Error during global setup "))))}`);
|
||
|
await ctx.logger.printError(e);
|
||
|
process.exit(1);
|
||
|
}
|
||
|
},
|
||
|
async buildEnd() {
|
||
|
var _a;
|
||
|
if (globalSetupFiles == null ? void 0 : globalSetupFiles.length) {
|
||
|
for (const globalSetupFile of globalSetupFiles.reverse()) {
|
||
|
try {
|
||
|
await ((_a = globalSetupFile.teardown) == null ? void 0 : _a.call(globalSetupFile));
|
||
|
} catch (error) {
|
||
|
ctx.logger.error(`error during global teardown of ${globalSetupFile.file}`, error);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const hoistRegexp = /^[ \t]*\b((?:vitest|vi)\s*.\s*(mock|unmock)\(["`'\s]+(.*[@\w_-]+)["`'\s]+)[),]{1};?/gm;
|
||
|
const vitestRegexp = /import {[^}]*}.*(?=["'`]vitest["`']).*/gm;
|
||
|
function hoistMocks(code) {
|
||
|
let m;
|
||
|
const mocks = code.matchAll(hoistRegexp);
|
||
|
for (const mockResult of mocks) {
|
||
|
const lastIndex = getMockLastIndex(code.slice(mockResult.index));
|
||
|
if (lastIndex === null)
|
||
|
continue;
|
||
|
const startIndex = mockResult.index;
|
||
|
const { insideComment, insideString } = getIndexStatus(code, startIndex);
|
||
|
if (insideComment || insideString)
|
||
|
continue;
|
||
|
const endIndex = startIndex + lastIndex;
|
||
|
m ?? (m = new MagicString(code));
|
||
|
m.prepend(`${m.slice(startIndex, endIndex)}
|
||
|
`);
|
||
|
m.remove(startIndex, endIndex);
|
||
|
}
|
||
|
return m;
|
||
|
}
|
||
|
const API_NOT_FOUND_ERROR = `There are some problems in resolving the mocks API.
|
||
|
You may encounter this issue when importing the mocks API from another module other than 'vitest'.
|
||
|
|
||
|
To fix this issue you can either:
|
||
|
- import the mocks API directly from 'vitest'
|
||
|
- enable the 'globals' options`;
|
||
|
const MocksPlugin = () => {
|
||
|
return {
|
||
|
name: "vitest:mock-plugin",
|
||
|
enforce: "post",
|
||
|
async transform(code) {
|
||
|
const m = hoistMocks(code);
|
||
|
if (m) {
|
||
|
const vitestImports = code.matchAll(vitestRegexp);
|
||
|
let found = false;
|
||
|
for (const match of vitestImports) {
|
||
|
const indexStart = match.index;
|
||
|
const indexEnd = match[0].length + indexStart;
|
||
|
m.remove(indexStart, indexEnd);
|
||
|
m.prepend(`${match[0]}
|
||
|
`);
|
||
|
found = true;
|
||
|
}
|
||
|
if (!found) {
|
||
|
m.prepend(`try { vi } catch (_) { try { vitest } catch (__){ throw new Error(${JSON.stringify(API_NOT_FOUND_ERROR)}) } }
|
||
|
`);
|
||
|
}
|
||
|
return {
|
||
|
code: m.toString(),
|
||
|
map: m.generateMap({ hires: true })
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
function getMockLastIndex(code) {
|
||
|
const index = getCallLastIndex(code);
|
||
|
if (index === null)
|
||
|
return null;
|
||
|
return code[index + 1] === ";" ? index + 2 : index + 1;
|
||
|
}
|
||
|
function getIndexStatus(code, from) {
|
||
|
let index = 0;
|
||
|
let commentStarted = false;
|
||
|
let commentEnded = true;
|
||
|
let multilineCommentStarted = false;
|
||
|
let multilineCommentEnded = true;
|
||
|
let inString = null;
|
||
|
let beforeChar = null;
|
||
|
while (index <= from) {
|
||
|
const char = code[index];
|
||
|
const sub = code[index] + code[index + 1];
|
||
|
if (!inString) {
|
||
|
if (sub === "/*") {
|
||
|
multilineCommentStarted = true;
|
||
|
multilineCommentEnded = false;
|
||
|
}
|
||
|
if (sub === "*/" && multilineCommentStarted) {
|
||
|
multilineCommentStarted = false;
|
||
|
multilineCommentEnded = true;
|
||
|
}
|
||
|
if (sub === "//") {
|
||
|
commentStarted = true;
|
||
|
commentEnded = false;
|
||
|
}
|
||
|
if ((char === "\n" || sub === "\r\n") && commentStarted) {
|
||
|
commentStarted = false;
|
||
|
commentEnded = true;
|
||
|
}
|
||
|
}
|
||
|
if (!multilineCommentStarted && !commentStarted) {
|
||
|
const isCharString = char === '"' || char === "'" || char === "`";
|
||
|
if (isCharString && beforeChar !== "\\") {
|
||
|
if (inString === char)
|
||
|
inString = null;
|
||
|
else if (!inString)
|
||
|
inString = char;
|
||
|
}
|
||
|
}
|
||
|
beforeChar = char;
|
||
|
index++;
|
||
|
}
|
||
|
return {
|
||
|
insideComment: !multilineCommentEnded || !commentEnded,
|
||
|
insideString: inString !== null
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const cssLangs = "\\.(css|less|sass|scss|styl|stylus|pcss|postcss)($|\\?)";
|
||
|
const cssLangRE = new RegExp(cssLangs);
|
||
|
const cssModuleRE = new RegExp(`\\.module${cssLangs}`);
|
||
|
const isCSS = (id) => {
|
||
|
return cssLangRE.test(id);
|
||
|
};
|
||
|
const isCSSModule = (id) => {
|
||
|
return cssModuleRE.test(id);
|
||
|
};
|
||
|
const getCSSModuleProxyReturn = (strategy, filename) => {
|
||
|
if (strategy === "non-scoped")
|
||
|
return "style";
|
||
|
const hash = generateCssFilenameHash(filename);
|
||
|
return `\`_\${style}_${hash}\``;
|
||
|
};
|
||
|
function CSSEnablerPlugin(ctx) {
|
||
|
const shouldProcessCSS = (id) => {
|
||
|
const { css } = ctx.config;
|
||
|
if (typeof css === "boolean")
|
||
|
return css;
|
||
|
if (toArray(css.exclude).some((re) => re.test(id)))
|
||
|
return false;
|
||
|
if (toArray(css.include).some((re) => re.test(id)))
|
||
|
return true;
|
||
|
return false;
|
||
|
};
|
||
|
return [
|
||
|
{
|
||
|
name: "vitest:css-disable",
|
||
|
enforce: "pre",
|
||
|
transform(code, id) {
|
||
|
if (!isCSS(id))
|
||
|
return;
|
||
|
if (!shouldProcessCSS(id))
|
||
|
return { code: "" };
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
name: "vitest:css-empty-post",
|
||
|
enforce: "post",
|
||
|
transform(_, id) {
|
||
|
var _a;
|
||
|
if (!isCSS(id) || shouldProcessCSS(id))
|
||
|
return;
|
||
|
if (isCSSModule(id)) {
|
||
|
const scopeStrategy = typeof ctx.config.css !== "boolean" && ((_a = ctx.config.css.modules) == null ? void 0 : _a.classNameStrategy) || "stable";
|
||
|
const proxyReturn = getCSSModuleProxyReturn(scopeStrategy, relative(ctx.config.root, id));
|
||
|
const code = `export default new Proxy(Object.create(null), {
|
||
|
get(_, style) {
|
||
|
return ${proxyReturn};
|
||
|
},
|
||
|
})`;
|
||
|
return { code };
|
||
|
}
|
||
|
return { code: 'export default ""' };
|
||
|
}
|
||
|
}
|
||
|
];
|
||
|
}
|
||
|
|
||
|
function CoverageTransform(ctx) {
|
||
|
return {
|
||
|
name: "vitest:coverage-transform",
|
||
|
transform(srcCode, id) {
|
||
|
var _a, _b;
|
||
|
return (_b = (_a = ctx.coverageProvider) == null ? void 0 : _a.onFileTransform) == null ? void 0 : _b.call(_a, srcCode, id, this);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
|
||
|
const getRoot = () => {
|
||
|
var _a;
|
||
|
return ((_a = ctx.config) == null ? void 0 : _a.root) || options.root || process.cwd();
|
||
|
};
|
||
|
async function UIPlugin() {
|
||
|
await ensurePackageInstalled("@vitest/ui", getRoot());
|
||
|
return (await import('@vitest/ui')).default(options.uiBase);
|
||
|
}
|
||
|
async function BrowserPlugin() {
|
||
|
await ensurePackageInstalled("@vitest/browser", getRoot());
|
||
|
return (await import('@vitest/browser')).default("/");
|
||
|
}
|
||
|
return [
|
||
|
{
|
||
|
name: "vitest",
|
||
|
enforce: "pre",
|
||
|
options() {
|
||
|
this.meta.watchMode = false;
|
||
|
},
|
||
|
config(viteConfig) {
|
||
|
var _a, _b, _c;
|
||
|
const preOptions = deepMerge(
|
||
|
{},
|
||
|
configDefaults,
|
||
|
options,
|
||
|
removeUndefinedValues(viteConfig.test ?? {})
|
||
|
);
|
||
|
preOptions.api = resolveApiConfig(preOptions);
|
||
|
if (viteConfig.define) {
|
||
|
delete viteConfig.define["import.meta.vitest"];
|
||
|
delete viteConfig.define["process.env"];
|
||
|
}
|
||
|
const defines = {};
|
||
|
for (const key in viteConfig.define) {
|
||
|
const val = viteConfig.define[key];
|
||
|
let replacement;
|
||
|
try {
|
||
|
replacement = typeof val === "string" ? JSON.parse(val) : val;
|
||
|
} catch {
|
||
|
continue;
|
||
|
}
|
||
|
if (key.startsWith("import.meta.env.")) {
|
||
|
const envKey = key.slice("import.meta.env.".length);
|
||
|
process.env[envKey] = replacement;
|
||
|
delete viteConfig.define[key];
|
||
|
} else if (key.startsWith("process.env.")) {
|
||
|
const envKey = key.slice("process.env.".length);
|
||
|
process.env[envKey] = replacement;
|
||
|
delete viteConfig.define[key];
|
||
|
} else if (!key.includes(".")) {
|
||
|
defines[key] = replacement;
|
||
|
delete viteConfig.define[key];
|
||
|
}
|
||
|
}
|
||
|
options.defines = defines;
|
||
|
let open;
|
||
|
if (preOptions.ui && preOptions.open)
|
||
|
open = preOptions.uiBase ?? "/__vitest__/";
|
||
|
else if (preOptions.browser)
|
||
|
open = "/";
|
||
|
const config = {
|
||
|
esbuild: {
|
||
|
sourcemap: "external",
|
||
|
legalComments: "inline"
|
||
|
},
|
||
|
resolve: {
|
||
|
mainFields: [],
|
||
|
alias: preOptions.alias,
|
||
|
conditions: ["node"],
|
||
|
browserField: false
|
||
|
},
|
||
|
server: {
|
||
|
...preOptions.api,
|
||
|
watch: {
|
||
|
ignored: preOptions.watchExclude
|
||
|
},
|
||
|
open,
|
||
|
hmr: false,
|
||
|
preTransformRequests: false
|
||
|
}
|
||
|
};
|
||
|
const classNameStrategy = preOptions.css && ((_b = (_a = preOptions.css) == null ? void 0 : _a.modules) == null ? void 0 : _b.classNameStrategy);
|
||
|
if (classNameStrategy !== "scoped") {
|
||
|
config.css ?? (config.css = {});
|
||
|
(_c = config.css).modules ?? (_c.modules = {});
|
||
|
config.css.modules.generateScopedName = (name, filename) => {
|
||
|
const root = getRoot();
|
||
|
return generateScopedClassName(classNameStrategy, name, relative(root, filename));
|
||
|
};
|
||
|
}
|
||
|
if (!options.browser) {
|
||
|
Object.assign(config, {
|
||
|
cacheDir: void 0,
|
||
|
optimizeDeps: {
|
||
|
disabled: true,
|
||
|
entries: []
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
return config;
|
||
|
},
|
||
|
async configResolved(viteConfig) {
|
||
|
var _a, _b, _c, _d;
|
||
|
const viteConfigTest = viteConfig.test || {};
|
||
|
if (viteConfigTest.watch === false)
|
||
|
viteConfigTest.run = true;
|
||
|
if ("alias" in viteConfigTest)
|
||
|
delete viteConfigTest.alias;
|
||
|
options = deepMerge(
|
||
|
{},
|
||
|
configDefaults,
|
||
|
viteConfigTest,
|
||
|
options
|
||
|
);
|
||
|
options.api = resolveApiConfig(options);
|
||
|
const { PROD, DEV, ...envs } = viteConfig.env;
|
||
|
(_a = process.env).PROD ?? (_a.PROD = PROD ? "1" : "");
|
||
|
(_b = process.env).DEV ?? (_b.DEV = DEV ? "1" : "");
|
||
|
(_c = process.env).SSR ?? (_c.SSR = "1");
|
||
|
for (const name in envs)
|
||
|
(_d = process.env)[name] ?? (_d[name] = envs[name]);
|
||
|
},
|
||
|
async configureServer(server) {
|
||
|
try {
|
||
|
await ctx.setServer(options, server);
|
||
|
if (options.api && options.watch)
|
||
|
(await import('./chunk-api-setup.16ac28c0.js')).setup(ctx);
|
||
|
} catch (err) {
|
||
|
ctx.logger.printError(err, true);
|
||
|
process.exit(1);
|
||
|
}
|
||
|
if (!options.watch)
|
||
|
await server.watcher.close();
|
||
|
}
|
||
|
},
|
||
|
EnvReplacerPlugin(),
|
||
|
MocksPlugin(),
|
||
|
GlobalSetupPlugin(ctx),
|
||
|
...options.browser ? await BrowserPlugin() : [],
|
||
|
...CSSEnablerPlugin(ctx),
|
||
|
CoverageTransform(ctx),
|
||
|
options.ui ? await UIPlugin() : null
|
||
|
].filter(notNullish);
|
||
|
}
|
||
|
|
||
|
async function createVitest(mode, options, viteOverrides = {}) {
|
||
|
var _a;
|
||
|
const ctx = new Vitest(mode);
|
||
|
const root = resolve(options.root || process.cwd());
|
||
|
const configPath = options.config ? resolve(root, options.config) : await findUp(configFiles, { cwd: root });
|
||
|
const config = {
|
||
|
logLevel: "error",
|
||
|
configFile: configPath,
|
||
|
mode: options.mode || process.env.NODE_ENV || mode,
|
||
|
plugins: await VitestPlugin(options, ctx)
|
||
|
};
|
||
|
const server = await createServer(mergeConfig(config, mergeConfig(viteOverrides, { root: options.root })));
|
||
|
if ((_a = ctx.config.api) == null ? void 0 : _a.port)
|
||
|
await server.listen();
|
||
|
else
|
||
|
await server.pluginContainer.buildStart({});
|
||
|
return ctx;
|
||
|
}
|
||
|
|
||
|
const keys = [
|
||
|
["a", "rerun all tests"],
|
||
|
["f", "rerun only failed tests"],
|
||
|
["u", "update snapshot"],
|
||
|
["p", "filter by a filename"],
|
||
|
["t", "filter by a test name regex pattern"],
|
||
|
["q", "quit"]
|
||
|
];
|
||
|
function printShortcutsHelp() {
|
||
|
stdout().write(
|
||
|
`
|
||
|
${c.bold(" Watch Usage")}
|
||
|
${keys.map((i) => c.dim(" press ") + c.reset(c.bold(i[0])) + c.dim(` to ${i[1]}`)).join("\n")}
|
||
|
`
|
||
|
);
|
||
|
}
|
||
|
function registerConsoleShortcuts(ctx) {
|
||
|
let latestFilename = "";
|
||
|
async function _keypressHandler(str, key) {
|
||
|
if (str === "" || str === "\x1B" || key && key.ctrl && key.name === "c")
|
||
|
return ctx.exit(true);
|
||
|
if (!isWindows && key && key.ctrl && key.name === "z") {
|
||
|
process.kill(process.ppid, "SIGTSTP");
|
||
|
process.kill(process.pid, "SIGTSTP");
|
||
|
return;
|
||
|
}
|
||
|
if (ctx.runningPromise)
|
||
|
return;
|
||
|
const name = key == null ? void 0 : key.name;
|
||
|
if (name === "q")
|
||
|
return ctx.exit(true);
|
||
|
if (ctx.mode === "typecheck")
|
||
|
return;
|
||
|
if (name === "h")
|
||
|
return printShortcutsHelp();
|
||
|
if (name === "u")
|
||
|
return ctx.updateSnapshot();
|
||
|
if (name === "a" || name === "return")
|
||
|
return ctx.changeNamePattern("");
|
||
|
if (name === "f")
|
||
|
return ctx.rerunFailed();
|
||
|
if (name === "t")
|
||
|
return inputNamePattern();
|
||
|
if (name === "p")
|
||
|
return inputFilePattern();
|
||
|
}
|
||
|
async function keypressHandler(str, key) {
|
||
|
await _keypressHandler(str, key);
|
||
|
}
|
||
|
async function inputNamePattern() {
|
||
|
var _a;
|
||
|
off();
|
||
|
const { filter = "" } = await prompts([{
|
||
|
name: "filter",
|
||
|
type: "text",
|
||
|
message: "Input test name pattern (RegExp)",
|
||
|
initial: ((_a = ctx.config.testNamePattern) == null ? void 0 : _a.source) || ""
|
||
|
}]);
|
||
|
await ctx.changeNamePattern(filter, void 0, "change pattern");
|
||
|
on();
|
||
|
}
|
||
|
async function inputFilePattern() {
|
||
|
off();
|
||
|
const { filter = "" } = await prompts([{
|
||
|
name: "filter",
|
||
|
type: "text",
|
||
|
message: "Input filename pattern",
|
||
|
initial: latestFilename
|
||
|
}]);
|
||
|
latestFilename = filter;
|
||
|
await ctx.changeFilenamePattern(filter);
|
||
|
on();
|
||
|
}
|
||
|
let rl;
|
||
|
function on() {
|
||
|
off();
|
||
|
rl = require$$0$3.createInterface({ input: process.stdin, escapeCodeTimeout: 50 });
|
||
|
require$$0$3.emitKeypressEvents(process.stdin, rl);
|
||
|
if (process.stdin.isTTY)
|
||
|
process.stdin.setRawMode(true);
|
||
|
process.stdin.on("keypress", keypressHandler);
|
||
|
}
|
||
|
function off() {
|
||
|
rl == null ? void 0 : rl.close();
|
||
|
rl = void 0;
|
||
|
process.stdin.removeListener("keypress", keypressHandler);
|
||
|
if (process.stdin.isTTY)
|
||
|
process.stdin.setRawMode(false);
|
||
|
}
|
||
|
on();
|
||
|
}
|
||
|
|
||
|
async function startVitest(mode, cliFilters, options, viteOverrides) {
|
||
|
var _a;
|
||
|
process.env.TEST = "true";
|
||
|
process.env.VITEST = "true";
|
||
|
(_a = process.env).NODE_ENV ?? (_a.NODE_ENV = options.mode || "test");
|
||
|
if (options.run)
|
||
|
options.watch = false;
|
||
|
if (options.browser)
|
||
|
options.threads = false;
|
||
|
const root = resolve(options.root || process.cwd());
|
||
|
if (!await ensurePackageInstalled("vite", root)) {
|
||
|
process.exitCode = 1;
|
||
|
return;
|
||
|
}
|
||
|
if (typeof options.coverage === "boolean")
|
||
|
options.coverage = { enabled: options.coverage };
|
||
|
const ctx = await createVitest(mode, options, viteOverrides);
|
||
|
if (mode === "test" && ctx.config.coverage.enabled) {
|
||
|
const provider = ctx.config.coverage.provider || "c8";
|
||
|
if (typeof provider === "string") {
|
||
|
const requiredPackages = CoverageProviderMap[provider];
|
||
|
if (!await ensurePackageInstalled(requiredPackages, root)) {
|
||
|
process.exitCode = 1;
|
||
|
return ctx;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
const environmentPackage = getEnvPackageName(ctx.config.environment);
|
||
|
if (environmentPackage && !await ensurePackageInstalled(environmentPackage, root)) {
|
||
|
process.exitCode = 1;
|
||
|
return ctx;
|
||
|
}
|
||
|
if (process.stdin.isTTY && ctx.config.watch)
|
||
|
registerConsoleShortcuts(ctx);
|
||
|
ctx.onServerRestart((reason) => {
|
||
|
ctx.report("onServerRestart", reason);
|
||
|
if (process.env.VITEST_CLI_WRAPPER)
|
||
|
process.exit(EXIT_CODE_RESTART);
|
||
|
});
|
||
|
ctx.onAfterSetServer(() => {
|
||
|
ctx.start(cliFilters);
|
||
|
});
|
||
|
try {
|
||
|
await ctx.start(cliFilters);
|
||
|
} catch (e) {
|
||
|
process.exitCode = 1;
|
||
|
await ctx.logger.printError(e, true, "Unhandled Error");
|
||
|
ctx.logger.error("\n\n");
|
||
|
return ctx;
|
||
|
}
|
||
|
if (ctx.config.watch)
|
||
|
return ctx;
|
||
|
await ctx.close();
|
||
|
return ctx;
|
||
|
}
|
||
|
|
||
|
export { BaseSequencer as B, VitestPlugin as V, createVitest as c, divider as d, startVitest as s, version$1 as v };
|