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.
241 lines
5.9 KiB
241 lines
5.9 KiB
2 years ago
|
import { importModule } from 'local-pkg';
|
||
|
|
||
|
/*
|
||
|
How it works:
|
||
|
`this.#head` is an instance of `Node` which keeps track of its current value and nests another instance of `Node` that keeps the value that comes after it. When a value is provided to `.enqueue()`, the code needs to iterate through `this.#head`, going deeper and deeper to find the last value. However, iterating through every single item is slow. This problem is solved by saving a reference to the last value as `this.#tail` so that it can reference it to add a new value.
|
||
|
*/
|
||
|
|
||
|
class Node {
|
||
|
value;
|
||
|
next;
|
||
|
|
||
|
constructor(value) {
|
||
|
this.value = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class Queue {
|
||
|
#head;
|
||
|
#tail;
|
||
|
#size;
|
||
|
|
||
|
constructor() {
|
||
|
this.clear();
|
||
|
}
|
||
|
|
||
|
enqueue(value) {
|
||
|
const node = new Node(value);
|
||
|
|
||
|
if (this.#head) {
|
||
|
this.#tail.next = node;
|
||
|
this.#tail = node;
|
||
|
} else {
|
||
|
this.#head = node;
|
||
|
this.#tail = node;
|
||
|
}
|
||
|
|
||
|
this.#size++;
|
||
|
}
|
||
|
|
||
|
dequeue() {
|
||
|
const current = this.#head;
|
||
|
if (!current) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.#head = this.#head.next;
|
||
|
this.#size--;
|
||
|
return current.value;
|
||
|
}
|
||
|
|
||
|
clear() {
|
||
|
this.#head = undefined;
|
||
|
this.#tail = undefined;
|
||
|
this.#size = 0;
|
||
|
}
|
||
|
|
||
|
get size() {
|
||
|
return this.#size;
|
||
|
}
|
||
|
|
||
|
* [Symbol.iterator]() {
|
||
|
let current = this.#head;
|
||
|
|
||
|
while (current) {
|
||
|
yield current.value;
|
||
|
current = current.next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function pLimit(concurrency) {
|
||
|
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
|
||
|
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
|
||
|
}
|
||
|
|
||
|
const queue = new Queue();
|
||
|
let activeCount = 0;
|
||
|
|
||
|
const next = () => {
|
||
|
activeCount--;
|
||
|
|
||
|
if (queue.size > 0) {
|
||
|
queue.dequeue()();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const run = async (fn, resolve, args) => {
|
||
|
activeCount++;
|
||
|
|
||
|
const result = (async () => fn(...args))();
|
||
|
|
||
|
resolve(result);
|
||
|
|
||
|
try {
|
||
|
await result;
|
||
|
} catch {}
|
||
|
|
||
|
next();
|
||
|
};
|
||
|
|
||
|
const enqueue = (fn, resolve, args) => {
|
||
|
queue.enqueue(run.bind(undefined, fn, resolve, args));
|
||
|
|
||
|
(async () => {
|
||
|
// This function needs to wait until the next microtask before comparing
|
||
|
// `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
|
||
|
// when the run function is dequeued and called. The comparison in the if-statement
|
||
|
// needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
|
||
|
await Promise.resolve();
|
||
|
|
||
|
if (activeCount < concurrency && queue.size > 0) {
|
||
|
queue.dequeue()();
|
||
|
}
|
||
|
})();
|
||
|
};
|
||
|
|
||
|
const generator = (fn, ...args) => new Promise(resolve => {
|
||
|
enqueue(fn, resolve, args);
|
||
|
});
|
||
|
|
||
|
Object.defineProperties(generator, {
|
||
|
activeCount: {
|
||
|
get: () => activeCount,
|
||
|
},
|
||
|
pendingCount: {
|
||
|
get: () => queue.size,
|
||
|
},
|
||
|
clearQueue: {
|
||
|
value: () => {
|
||
|
queue.clear();
|
||
|
},
|
||
|
},
|
||
|
});
|
||
|
|
||
|
return generator;
|
||
|
}
|
||
|
|
||
|
const CoverageProviderMap = {
|
||
|
c8: "@vitest/coverage-c8",
|
||
|
istanbul: "@vitest/coverage-istanbul"
|
||
|
};
|
||
|
async function resolveCoverageProvider(provider) {
|
||
|
if (typeof provider === "string") {
|
||
|
const pkg = CoverageProviderMap[provider];
|
||
|
if (!pkg)
|
||
|
throw new Error(`Unknown coverage provider: ${provider}`);
|
||
|
return await importModule(pkg);
|
||
|
} else {
|
||
|
return provider;
|
||
|
}
|
||
|
}
|
||
|
async function getCoverageProvider(options) {
|
||
|
if ((options == null ? void 0 : options.enabled) && (options == null ? void 0 : options.provider)) {
|
||
|
const { getProvider } = await resolveCoverageProvider(options.provider);
|
||
|
return await getProvider();
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
async function takeCoverageInsideWorker(options) {
|
||
|
if (options.enabled && options.provider) {
|
||
|
const { takeCoverage } = await resolveCoverageProvider(options.provider);
|
||
|
return await (takeCoverage == null ? void 0 : takeCoverage());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function interpretTaskModes(suite, namePattern, onlyMode, parentIsOnly, allowOnly) {
|
||
|
const suiteIsOnly = parentIsOnly || suite.mode === "only";
|
||
|
suite.tasks.forEach((t) => {
|
||
|
const includeTask = suiteIsOnly || t.mode === "only";
|
||
|
if (onlyMode) {
|
||
|
if (t.type === "suite" && (includeTask || someTasksAreOnly(t))) {
|
||
|
if (t.mode === "only") {
|
||
|
checkAllowOnly(t, allowOnly);
|
||
|
t.mode = "run";
|
||
|
}
|
||
|
} else if (t.mode === "run" && !includeTask) {
|
||
|
t.mode = "skip";
|
||
|
} else if (t.mode === "only") {
|
||
|
checkAllowOnly(t, allowOnly);
|
||
|
t.mode = "run";
|
||
|
}
|
||
|
}
|
||
|
if (t.type === "test") {
|
||
|
if (namePattern && !getTaskFullName(t).match(namePattern))
|
||
|
t.mode = "skip";
|
||
|
} else if (t.type === "suite") {
|
||
|
if (t.mode === "skip")
|
||
|
skipAllTasks(t);
|
||
|
else
|
||
|
interpretTaskModes(t, namePattern, onlyMode, includeTask, allowOnly);
|
||
|
}
|
||
|
});
|
||
|
if (suite.mode === "run") {
|
||
|
if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run"))
|
||
|
suite.mode = "skip";
|
||
|
}
|
||
|
}
|
||
|
function getTaskFullName(task) {
|
||
|
return `${task.suite ? `${getTaskFullName(task.suite)} ` : ""}${task.name}`;
|
||
|
}
|
||
|
function someTasksAreOnly(suite) {
|
||
|
return suite.tasks.some((t) => t.mode === "only" || t.type === "suite" && someTasksAreOnly(t));
|
||
|
}
|
||
|
function skipAllTasks(suite) {
|
||
|
suite.tasks.forEach((t) => {
|
||
|
if (t.mode === "run") {
|
||
|
t.mode = "skip";
|
||
|
if (t.type === "suite")
|
||
|
skipAllTasks(t);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
function checkAllowOnly(task, allowOnly) {
|
||
|
if (allowOnly)
|
||
|
return;
|
||
|
task.result = {
|
||
|
state: "fail",
|
||
|
error: new Error("[Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error")
|
||
|
};
|
||
|
}
|
||
|
function generateHash(str) {
|
||
|
let hash = 0;
|
||
|
if (str.length === 0)
|
||
|
return `${hash}`;
|
||
|
for (let i = 0; i < str.length; i++) {
|
||
|
const char = str.charCodeAt(i);
|
||
|
hash = (hash << 5) - hash + char;
|
||
|
hash = hash & hash;
|
||
|
}
|
||
|
return `${hash}`;
|
||
|
}
|
||
|
function calculateSuiteHash(parent) {
|
||
|
parent.tasks.forEach((t, idx) => {
|
||
|
t.id = `${parent.id}_${idx}`;
|
||
|
if (t.type === "suite")
|
||
|
calculateSuiteHash(t);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
export { CoverageProviderMap as C, getCoverageProvider as a, calculateSuiteHash as c, generateHash as g, interpretTaskModes as i, pLimit as p, someTasksAreOnly as s, takeCoverageInsideWorker as t };
|