vitest 4.1.1 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/browser.d.ts +1 -1
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{base.C9_VThnT.js → base.C3wvLFNM.js} +7 -7
  4. package/dist/chunks/{benchmark.D0SlKNbZ.js → benchmark.CX_oY03V.js} +1 -1
  5. package/dist/chunks/{browser.d.X3SXoOCV.d.ts → browser.d.C0zGu1u9.d.ts} +1 -1
  6. package/dist/chunks/{cac.CHfKU_gf.js → cac.Bb7YBzMA.js} +24 -10
  7. package/dist/chunks/{cli-api.BUXBO6jS.js → cli-api.CaPRsymo.js} +47 -14
  8. package/dist/chunks/{config.d.EJLVE3es.d.ts → config.d.ChUh6-ad.d.ts} +2 -0
  9. package/dist/chunks/{coverage.CJ2HXVIG.js → coverage.CwUlQe0s.js} +13 -3
  10. package/dist/chunks/{defaults.CdU2lD-q.js → defaults.9aQKnqFk.js} +13 -5
  11. package/dist/chunks/{global.d.x-ILCfAE.d.ts → global.d.D74z04P1.d.ts} +2 -0
  12. package/dist/chunks/{globals.8mibwXRO.js → globals.7B-4LHAF.js} +4 -4
  13. package/dist/chunks/{index.Dkvtd-Ku.js → index.4L3g53iW.js} +3 -2
  14. package/dist/chunks/{index.6q6giCZO.js → index.Cj9kDiDi.js} +2 -2
  15. package/dist/chunks/{index.DXMFO5MJ.js → index.DICur-LY.js} +628 -586
  16. package/dist/chunks/{init-forks.B4YYSIj4.js → init-forks.UV3ZQGQH.js} +1 -1
  17. package/dist/chunks/{init-threads.Bd2Hsaex.js → init-threads.D3eCsY76.js} +1 -1
  18. package/dist/chunks/{init.BVxhC4nR.js → init.D98-gwRW.js} +3 -1
  19. package/dist/chunks/{nativeModuleMocker.Dd1Q1VIw.js → nativeModuleMocker.BRN2oBJd.js} +1 -1
  20. package/dist/chunks/{plugin.d.Dx0ozo6e.d.ts → plugin.d.BssAumYw.d.ts} +1 -1
  21. package/dist/chunks/{reporters.d.CZ5E0GCT.d.ts → reporters.d.yJ2fBir5.d.ts} +17 -8
  22. package/dist/chunks/{setup-common.CB31_KSV.js → setup-common.NdrZGMhw.js} +1 -1
  23. package/dist/chunks/{startVitestModuleRunner.C3FXk5Gv.js → startVitestModuleRunner.bRl2_oI_.js} +16 -2
  24. package/dist/chunks/{test.CBQUpOM3.js → test.BmQO5GaM.js} +402 -336
  25. package/dist/chunks/utils.DK8FXp4T.js +189 -0
  26. package/dist/chunks/{vm.DId8XBJa.js → vm.DVLYObm9.js} +7 -6
  27. package/dist/chunks/{worker.d.B84sVRy0.d.ts → worker.d.CckNUvI5.d.ts} +1 -1
  28. package/dist/cli.js +2 -20
  29. package/dist/config.cjs +13 -5
  30. package/dist/config.d.ts +8 -8
  31. package/dist/config.js +1 -1
  32. package/dist/coverage.d.ts +4 -4
  33. package/dist/coverage.js +2 -2
  34. package/dist/index.d.ts +79 -9
  35. package/dist/index.js +4 -4
  36. package/dist/node.d.ts +8 -8
  37. package/dist/node.js +8 -8
  38. package/dist/reporters.d.ts +4 -4
  39. package/dist/reporters.js +6 -6
  40. package/dist/runners.d.ts +1 -1
  41. package/dist/runners.js +3 -3
  42. package/dist/runtime.js +1 -1
  43. package/dist/suite.js +1 -1
  44. package/dist/worker.d.ts +2 -2
  45. package/dist/worker.js +9 -8
  46. package/dist/workers/forks.js +10 -9
  47. package/dist/workers/runVmTests.js +5 -5
  48. package/dist/workers/threads.js +10 -9
  49. package/dist/workers/vmForks.js +5 -5
  50. package/dist/workers/vmThreads.js +5 -5
  51. package/package.json +22 -14
  52. package/dist/chunks/index.CEzQDJGb.js +0 -231
@@ -1,21 +1,23 @@
1
- import { existsSync, readFileSync, writeFileSync, promises } from 'node:fs';
2
- import { performance as performance$1 } from 'node:perf_hooks';
1
+ import fs, { existsSync, readFileSync, writeFileSync, promises } from 'node:fs';
3
2
  import { getTests, generateHash, createTaskName, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, getTestName, hasFailed, getSuites, getTasks, getFullName } from '@vitest/runner/utils';
4
- import { slash, isPrimitive, toArray, deepMerge } from '@vitest/utils/helpers';
5
- import { defaultStackIgnorePatterns, parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
6
- import { relative, basename, resolve as resolve$1, join, isAbsolute, dirname, normalize } from 'pathe';
3
+ import * as pathe from 'pathe';
4
+ import { relative, basename, resolve as resolve$1, join, normalize, dirname } from 'pathe';
7
5
  import c from 'tinyrainbow';
6
+ import { t as truncateString, e as errorBanner, F as F_POINTER, d as divider, f as formatTimeString, a as taskFail, s as separator, b as F_CHECK, c as F_DOWN_RIGHT, g as formatProjectName, h as getStateSymbol, w as withLabel, r as renderSnapshotSummary, p as padSummaryTitle, i as getStateString$1, j as formatTime, k as countTestErrors, l as F_TREE_NODE_END, m as F_TREE_NODE_MIDDLE, n as noun, o as F_RIGHT } from './utils.DK8FXp4T.js';
7
+ import { stripVTControlCharacters } from 'node:util';
8
+ import { isPrimitive, toArray, deepMerge, notNullish } from '@vitest/utils/helpers';
9
+ import { performance as performance$1 } from 'node:perf_hooks';
10
+ import { defaultStackIgnorePatterns, parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
8
11
  import { i as isTTY } from './env.D4Lgay0q.js';
9
12
  import { Console } from 'node:console';
10
13
  import { Writable } from 'node:stream';
11
- import { stripVTControlCharacters } from 'node:util';
12
14
  import { inspect } from '@vitest/utils/display';
13
15
  import nodeos__default, { hostname } from 'node:os';
14
16
  import { x } from 'tinyexec';
15
17
  import { distDir } from '../path.js';
16
18
  import { parseAstAsync } from 'vite';
17
19
  import { positionToOffset, lineSplitRE } from '@vitest/utils/offset';
18
- import { mkdir, writeFile, readdir, stat, readFile } from 'node:fs/promises';
20
+ import { readdir, stat, readFile, mkdir, writeFile } from 'node:fs/promises';
19
21
  import { createRequire } from 'node:module';
20
22
 
21
23
  function groupBy(collection, iteratee) {
@@ -1063,6 +1065,120 @@ base.MethodDefinition = base.PropertyDefinition = base.Property = function (node
1063
1065
  if (node.value) { c(node.value, st, "Expression"); }
1064
1066
  };
1065
1067
 
1068
+ /// <reference types="../types/index.d.ts" />
1069
+
1070
+ // (c) 2020-present Andrea Giammarchi
1071
+
1072
+ const {parse: $parse, stringify: $stringify} = JSON;
1073
+ const {keys} = Object;
1074
+
1075
+ const Primitive = String; // it could be Number
1076
+ const primitive = 'string'; // it could be 'number'
1077
+
1078
+ const ignore = {};
1079
+ const object = 'object';
1080
+
1081
+ const noop = (_, value) => value;
1082
+
1083
+ const primitives = value => (
1084
+ value instanceof Primitive ? Primitive(value) : value
1085
+ );
1086
+
1087
+ const Primitives = (_, value) => (
1088
+ typeof value === primitive ? new Primitive(value) : value
1089
+ );
1090
+
1091
+ const resolver = (input, lazy, parsed, $) => output => {
1092
+ for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
1093
+ const k = ke[y];
1094
+ const value = output[k];
1095
+ if (value instanceof Primitive) {
1096
+ const tmp = input[+value];
1097
+ if (typeof tmp === object && !parsed.has(tmp)) {
1098
+ parsed.add(tmp);
1099
+ output[k] = ignore;
1100
+ lazy.push({ o: output, k, r: tmp });
1101
+ }
1102
+ else
1103
+ output[k] = $.call(output, k, tmp);
1104
+ }
1105
+ else if (output[k] !== ignore)
1106
+ output[k] = $.call(output, k, value);
1107
+ }
1108
+ return output;
1109
+ };
1110
+
1111
+ const set = (known, input, value) => {
1112
+ const index = Primitive(input.push(value) - 1);
1113
+ known.set(value, index);
1114
+ return index;
1115
+ };
1116
+
1117
+ /**
1118
+ * Converts a specialized flatted string into a JS value.
1119
+ * @param {string} text
1120
+ * @param {(this: any, key: string, value: any) => any} [reviver]
1121
+ * @returns {any}
1122
+ */
1123
+ const parse = (text, reviver) => {
1124
+ const input = $parse(text, Primitives).map(primitives);
1125
+ const $ = reviver || noop;
1126
+
1127
+ let value = input[0];
1128
+
1129
+ if (typeof value === object && value) {
1130
+ const lazy = [];
1131
+ const revive = resolver(input, lazy, new Set, $);
1132
+ value = revive(value);
1133
+
1134
+ let i = 0;
1135
+ while (i < lazy.length) {
1136
+ // it could be a lazy.shift() but that's costly
1137
+ const {o, k, r} = lazy[i++];
1138
+ o[k] = $.call(o, k, revive(r));
1139
+ }
1140
+ }
1141
+
1142
+ return $.call({'': value}, '', value);
1143
+ };
1144
+
1145
+ /**
1146
+ * Converts a JS value into a specialized flatted string.
1147
+ * @param {any} value
1148
+ * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
1149
+ * @param {string | number | undefined} [space]
1150
+ * @returns {string}
1151
+ */
1152
+ const stringify = (value, replacer, space) => {
1153
+ const $ = replacer && typeof replacer === object ?
1154
+ (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
1155
+ (replacer || noop);
1156
+ const known = new Map;
1157
+ const input = [];
1158
+ const output = [];
1159
+ let i = +set(known, input, $.call({'': value}, '', value));
1160
+ let firstRun = !i;
1161
+ while (i < input.length) {
1162
+ firstRun = true;
1163
+ output[i] = $stringify(input[i++], replace, space);
1164
+ }
1165
+ return '[' + output.join(',') + ']';
1166
+ function replace(key, value) {
1167
+ if (firstRun) {
1168
+ firstRun = !firstRun;
1169
+ return value;
1170
+ }
1171
+ const after = $.call(this, key, value);
1172
+ switch (typeof after) {
1173
+ case object:
1174
+ if (after === null) return after;
1175
+ case primitive:
1176
+ return known.get(after) || set(known, input, after);
1177
+ }
1178
+ return after;
1179
+ }
1180
+ };
1181
+
1066
1182
  async function collectTests(ctx, filepath) {
1067
1183
  const request = await ctx.vite.environments.ssr.transformRequest(filepath);
1068
1184
  if (!request) return null;
@@ -1572,189 +1688,6 @@ function findGeneratedPosition(traceMap, { line, column, source }) {
1572
1688
  };
1573
1689
  }
1574
1690
 
1575
- const F_RIGHT = "→";
1576
- const F_DOWN = "↓";
1577
- const F_DOWN_RIGHT = "↳";
1578
- const F_POINTER = "❯";
1579
- const F_DOT = "·";
1580
- const F_CHECK = "✓";
1581
- const F_CROSS = "×";
1582
- const F_LONG_DASH = "⎯";
1583
- const F_TODO = "□";
1584
- const F_TREE_NODE_MIDDLE = "├──";
1585
- const F_TREE_NODE_END = "└──";
1586
-
1587
- const pointer = c.yellow(F_POINTER);
1588
- const skipped = c.dim(c.gray(F_DOWN));
1589
- const todo = c.dim(c.gray(F_TODO));
1590
- const benchmarkPass = c.green(F_DOT);
1591
- const testPass = c.green(F_CHECK);
1592
- const taskFail = c.red(F_CROSS);
1593
- const suiteFail = c.red(F_POINTER);
1594
- const pending$1 = c.gray("·");
1595
- const separator = c.dim(" > ");
1596
- const labelDefaultColors = [
1597
- c.bgYellow,
1598
- c.bgCyan,
1599
- c.bgGreen,
1600
- c.bgMagenta
1601
- ];
1602
- function getCols(delta = 0) {
1603
- let length = process.stdout?.columns;
1604
- if (!length || Number.isNaN(length)) length = 30;
1605
- return Math.max(length + delta, 0);
1606
- }
1607
- function errorBanner(message) {
1608
- return divider(c.bold(c.bgRed(` ${message} `)), null, null, c.red);
1609
- }
1610
- function divider(text, left, right, color) {
1611
- const cols = getCols();
1612
- const c = color || ((text) => text);
1613
- if (text) {
1614
- const textLength = stripVTControlCharacters(text).length;
1615
- if (left == null && right != null) left = cols - textLength - right;
1616
- else {
1617
- left = left ?? Math.floor((cols - textLength) / 2);
1618
- right = cols - textLength - left;
1619
- }
1620
- left = Math.max(0, left);
1621
- right = Math.max(0, right);
1622
- return `${c(F_LONG_DASH.repeat(left))}${text}${c(F_LONG_DASH.repeat(right))}`;
1623
- }
1624
- return F_LONG_DASH.repeat(cols);
1625
- }
1626
- function formatTestPath(root, path) {
1627
- if (isAbsolute(path)) path = relative(root, path);
1628
- const dir = dirname(path);
1629
- const ext = path.match(/(\.(spec|test)\.[cm]?[tj]sx?)$/)?.[0] || "";
1630
- const base = basename(path, ext);
1631
- return slash(c.dim(`${dir}/`) + c.bold(base)) + c.dim(ext);
1632
- }
1633
- function renderSnapshotSummary(rootDir, snapshots) {
1634
- const summary = [];
1635
- if (snapshots.added) summary.push(c.bold(c.green(`${snapshots.added} written`)));
1636
- if (snapshots.unmatched) summary.push(c.bold(c.red(`${snapshots.unmatched} failed`)));
1637
- if (snapshots.updated) summary.push(c.bold(c.green(`${snapshots.updated} updated `)));
1638
- if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(c.bold(c.green(`${snapshots.filesRemoved} files removed `)));
1639
- else summary.push(c.bold(c.yellow(`${snapshots.filesRemoved} files obsolete `)));
1640
- if (snapshots.filesRemovedList && snapshots.filesRemovedList.length) {
1641
- const [head, ...tail] = snapshots.filesRemovedList;
1642
- summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, head)}`);
1643
- tail.forEach((key) => {
1644
- summary.push(` ${c.gray(F_DOT)} ${formatTestPath(rootDir, key)}`);
1645
- });
1646
- }
1647
- if (snapshots.unchecked) {
1648
- if (snapshots.didUpdate) summary.push(c.bold(c.green(`${snapshots.unchecked} removed`)));
1649
- else summary.push(c.bold(c.yellow(`${snapshots.unchecked} obsolete`)));
1650
- snapshots.uncheckedKeysByFile.forEach((uncheckedFile) => {
1651
- summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
1652
- uncheckedFile.keys.forEach((key) => summary.push(` ${c.gray(F_DOT)} ${key}`));
1653
- });
1654
- }
1655
- return summary;
1656
- }
1657
- function countTestErrors(tasks) {
1658
- return tasks.reduce((c, i) => c + (i.result?.errors?.length || 0), 0);
1659
- }
1660
- function getStateString$1(tasks, name = "tests", showTotal = true) {
1661
- if (tasks.length === 0) return c.dim(`no ${name}`);
1662
- const passed = tasks.reduce((acc, i) => {
1663
- // Exclude expected failures from passed count
1664
- if (i.result?.state === "pass" && i.type === "test" && i.fails) return acc;
1665
- return i.result?.state === "pass" ? acc + 1 : acc;
1666
- }, 0);
1667
- const failed = tasks.reduce((acc, i) => i.result?.state === "fail" ? acc + 1 : acc, 0);
1668
- const skipped = tasks.reduce((acc, i) => i.mode === "skip" ? acc + 1 : acc, 0);
1669
- const todo = tasks.reduce((acc, i) => i.mode === "todo" ? acc + 1 : acc, 0);
1670
- const expectedFail = tasks.reduce((acc, i) => {
1671
- // Count tests that are marked as .fails and passed (which means they failed as expected)
1672
- if (i.result?.state === "pass" && i.type === "test" && i.fails) return acc + 1;
1673
- return acc;
1674
- }, 0);
1675
- return [
1676
- failed ? c.bold(c.red(`${failed} failed`)) : null,
1677
- passed ? c.bold(c.green(`${passed} passed`)) : null,
1678
- expectedFail ? c.cyan(`${expectedFail} expected fail`) : null,
1679
- skipped ? c.yellow(`${skipped} skipped`) : null,
1680
- todo ? c.gray(`${todo} todo`) : null
1681
- ].filter(Boolean).join(c.dim(" | ")) + (showTotal ? c.gray(` (${tasks.length})`) : "");
1682
- }
1683
- function getStateSymbol(task) {
1684
- if (task.mode === "todo") return todo;
1685
- if (task.mode === "skip") return skipped;
1686
- if (!task.result) return pending$1;
1687
- if (task.result.state === "run" || task.result.state === "queued") {
1688
- if (task.type === "suite") return pointer;
1689
- }
1690
- if (task.result.state === "pass") return task.meta?.benchmark ? benchmarkPass : testPass;
1691
- if (task.result.state === "fail") return task.type === "suite" ? suiteFail : taskFail;
1692
- return " ";
1693
- }
1694
- function formatTimeString(date) {
1695
- return date.toTimeString().split(" ")[0];
1696
- }
1697
- function formatTime(time) {
1698
- if (time > 1e3) return `${(time / 1e3).toFixed(2)}s`;
1699
- return `${Math.round(time)}ms`;
1700
- }
1701
- function formatProjectName(project, suffix = " ") {
1702
- if (!project?.name) return "";
1703
- if (!c.isColorSupported) return `|${project.name}|${suffix}`;
1704
- let background = project.color && c[`bg${capitalize(project.color)}`];
1705
- if (!background) background = labelDefaultColors[project.name.split("").reduce((acc, v, idx) => acc + v.charCodeAt(0) + idx, 0) % labelDefaultColors.length];
1706
- return c.black(background(` ${project.name} `)) + suffix;
1707
- }
1708
- function withLabel(color, label, message) {
1709
- const bgColor = `bg${color.charAt(0).toUpperCase()}${color.slice(1)}`;
1710
- return `${c.bold(c[bgColor](` ${label} `))} ${message ? c[color](message) : ""}`;
1711
- }
1712
- function padSummaryTitle(str) {
1713
- return c.dim(`${str.padStart(11)} `);
1714
- }
1715
- function truncateString(text, maxLength) {
1716
- const plainText = stripVTControlCharacters(text);
1717
- if (plainText.length <= maxLength) return text;
1718
- return `${plainText.slice(0, maxLength - 1)}…`;
1719
- }
1720
- function capitalize(text) {
1721
- return `${text[0].toUpperCase()}${text.slice(1)}`;
1722
- }
1723
- /**
1724
- * Returns the singular or plural form of a word based on the count.
1725
- */
1726
- function noun(count, singular, plural) {
1727
- if (count === 1) return singular;
1728
- return plural;
1729
- }
1730
-
1731
- var utils = /*#__PURE__*/Object.freeze({
1732
- __proto__: null,
1733
- benchmarkPass: benchmarkPass,
1734
- countTestErrors: countTestErrors,
1735
- divider: divider,
1736
- errorBanner: errorBanner,
1737
- formatProjectName: formatProjectName,
1738
- formatTestPath: formatTestPath,
1739
- formatTime: formatTime,
1740
- formatTimeString: formatTimeString,
1741
- getStateString: getStateString$1,
1742
- getStateSymbol: getStateSymbol,
1743
- noun: noun,
1744
- padSummaryTitle: padSummaryTitle,
1745
- pending: pending$1,
1746
- pointer: pointer,
1747
- renderSnapshotSummary: renderSnapshotSummary,
1748
- separator: separator,
1749
- skipped: skipped,
1750
- suiteFail: suiteFail,
1751
- taskFail: taskFail,
1752
- testPass: testPass,
1753
- todo: todo,
1754
- truncateString: truncateString,
1755
- withLabel: withLabel
1756
- });
1757
-
1758
1691
  // use Logger with custom Console to capture entire error printing
1759
1692
  function capturePrintError(error, ctx, options) {
1760
1693
  let output = "";
@@ -1903,6 +1836,7 @@ const skipErrorProperties = new Set([
1903
1836
  "VITEST_TEST_NAME",
1904
1837
  "VITEST_TEST_PATH",
1905
1838
  "__vitest_rollup_error__",
1839
+ "__vitest_error_context__",
1906
1840
  ...Object.getOwnPropertyNames(Error.prototype),
1907
1841
  ...Object.getOwnPropertyNames(Object.prototype)
1908
1842
  ]);
@@ -2018,64 +1952,295 @@ function lineNo(no = "") {
2018
1952
  return c.gray(`${String(no).padStart(3, " ")}|`);
2019
1953
  }
2020
1954
 
2021
- const BADGE_PADDING = " ";
2022
- class BaseReporter {
1955
+ function getOutputFile(config, reporter) {
1956
+ if (!config?.outputFile) return;
1957
+ if (typeof config.outputFile === "string") return config.outputFile;
1958
+ return config.outputFile[reporter];
1959
+ }
1960
+ function createDefinesScript(define) {
1961
+ if (!define) return "";
1962
+ if (serializeDefine(define) === "{}") return "";
1963
+ return `
1964
+ const defines = ${serializeDefine(define)}
1965
+ Object.keys(defines).forEach((key) => {
1966
+ const segments = key.split('.')
1967
+ let target = globalThis
1968
+ for (let i = 0; i < segments.length; i++) {
1969
+ const segment = segments[i]
1970
+ if (i === segments.length - 1) {
1971
+ target[segment] = defines[key]
1972
+ } else {
1973
+ target = target[segment] || (target[segment] = {})
1974
+ }
1975
+ }
1976
+ })
1977
+ `;
1978
+ }
1979
+ /**
1980
+ * Like `JSON.stringify` but keeps raw string values as a literal
1981
+ * in the generated code. For example: `"window"` would refer to
1982
+ * the global `window` object directly.
1983
+ */
1984
+ function serializeDefine(define) {
1985
+ const userDefine = {};
1986
+ for (const key in define) {
1987
+ // vitest sets this to avoid vite:client-inject plugin
1988
+ if (key === "process.env.NODE_ENV" && define[key] === "process.env.NODE_ENV") continue;
1989
+ // import.meta.env.* is handled in `importAnalysis` plugin
1990
+ if (!key.startsWith("import.meta.env.")) userDefine[key] = define[key];
1991
+ }
1992
+ let res = `{`;
1993
+ const keys = Object.keys(userDefine).sort();
1994
+ for (let i = 0; i < keys.length; i++) {
1995
+ const key = keys[i];
1996
+ const val = userDefine[key];
1997
+ res += `${JSON.stringify(key)}: ${handleDefineValue(val)}`;
1998
+ if (i !== keys.length - 1) res += `, `;
1999
+ }
2000
+ return `${res}}`;
2001
+ }
2002
+ function handleDefineValue(value) {
2003
+ if (typeof value === "undefined") return "undefined";
2004
+ if (typeof value === "string") return value;
2005
+ return JSON.stringify(value);
2006
+ }
2007
+
2008
+ class BlobReporter {
2023
2009
  start = 0;
2024
- end = 0;
2025
- watchFilters;
2026
- failedUnwatchedFiles = [];
2027
- isTTY;
2028
- ctx = void 0;
2029
- renderSucceed = false;
2030
- verbose = false;
2031
- silent;
2032
- _filesInWatchMode = /* @__PURE__ */ new Map();
2033
- _timeStart = formatTimeString(/* @__PURE__ */ new Date());
2034
- constructor(options = {}) {
2035
- this.isTTY = options.isTTY ?? isTTY;
2036
- this.silent = options.silent;
2010
+ ctx;
2011
+ options;
2012
+ coverage;
2013
+ constructor(options) {
2014
+ this.options = options;
2037
2015
  }
2038
2016
  onInit(ctx) {
2017
+ if (ctx.config.watch) throw new Error("Blob reporter is not supported in watch mode");
2039
2018
  this.ctx = ctx;
2040
- this.silent ??= this.ctx.config.silent;
2041
- this.ctx.logger.printBanner();
2042
- }
2043
- log(...messages) {
2044
- this.ctx.logger.log(...messages);
2045
- }
2046
- error(...messages) {
2047
- this.ctx.logger.error(...messages);
2048
- }
2049
- relative(path) {
2050
- return relative(this.ctx.config.root, path);
2019
+ this.start = performance.now();
2020
+ this.coverage = void 0;
2051
2021
  }
2052
- onTestRunStart(_specifications) {
2053
- this.start = performance$1.now();
2054
- this._timeStart = formatTimeString(/* @__PURE__ */ new Date());
2022
+ onCoverage(coverage) {
2023
+ this.coverage = coverage;
2055
2024
  }
2056
- onTestRunEnd(testModules, unhandledErrors, _reason) {
2025
+ async onTestRunEnd(testModules, unhandledErrors) {
2026
+ const executionTime = performance.now() - this.start;
2057
2027
  const files = testModules.map((testModule) => testModule.task);
2058
2028
  const errors = [...unhandledErrors];
2059
- this.end = performance$1.now();
2060
- if (!files.length && !errors.length) this.ctx.logger.printNoTestFound(this.ctx.filenamePattern);
2061
- else this.reportSummary(files, errors);
2062
- }
2063
- onTestCaseResult(testCase) {
2064
- if (testCase.result().state === "failed") this.logFailedTask(testCase.task);
2065
- }
2066
- onTestSuiteResult(testSuite) {
2067
- if (testSuite.state() === "failed") this.logFailedTask(testSuite.task);
2068
- }
2069
- onTestModuleEnd(testModule) {
2070
- if (testModule.state() === "failed") this.logFailedTask(testModule.task);
2071
- this.printTestModule(testModule);
2072
- }
2073
- logFailedTask(task) {
2074
- if (this.silent === "passed-only") for (const log of task.logs || []) this.onUserConsoleLog(log, "failed");
2075
- }
2076
- printTestModule(testModule) {
2077
- const moduleState = testModule.state();
2078
- if (moduleState === "queued" || moduleState === "pending") return;
2029
+ const coverage = this.coverage;
2030
+ let outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "blob");
2031
+ if (!outputFile) {
2032
+ const shard = this.ctx.config.shard;
2033
+ outputFile = shard ? `.vitest-reports/blob-${shard.index}-${shard.count}.json` : ".vitest-reports/blob.json";
2034
+ }
2035
+ const environmentModules = {};
2036
+ this.ctx.projects.forEach((project) => {
2037
+ const serializedProject = {
2038
+ environments: {},
2039
+ external: []
2040
+ };
2041
+ Object.entries(project.vite.environments).forEach(([environmentName, environment]) => {
2042
+ serializedProject.environments[environmentName] = serializeEnvironmentModuleGraph(environment);
2043
+ });
2044
+ if (project.browser?.vite.environments.client) serializedProject.browser = serializeEnvironmentModuleGraph(project.browser.vite.environments.client);
2045
+ for (const [id, value] of project._resolver.externalizeCache.entries()) if (typeof value === "string") serializedProject.external.push([id, value]);
2046
+ environmentModules[project.name] = serializedProject;
2047
+ });
2048
+ const report = [
2049
+ this.ctx.version,
2050
+ files,
2051
+ errors,
2052
+ coverage,
2053
+ executionTime,
2054
+ environmentModules
2055
+ ];
2056
+ const reportFile = resolve$1(this.ctx.config.root, outputFile);
2057
+ await writeBlob(report, reportFile);
2058
+ this.ctx.logger.log("blob report written to", reportFile);
2059
+ }
2060
+ }
2061
+ async function writeBlob(content, filename) {
2062
+ const report = stringify(content);
2063
+ const dir = dirname(filename);
2064
+ if (!existsSync(dir)) await mkdir(dir, { recursive: true });
2065
+ await writeFile(filename, report, "utf-8");
2066
+ }
2067
+ async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
2068
+ // using process.cwd() because --merge-reports can only be used in CLI
2069
+ const resolvedDir = resolve$1(process.cwd(), blobsDirectory);
2070
+ const promises = (await readdir(resolvedDir)).map(async (filename) => {
2071
+ const fullPath = resolve$1(resolvedDir, filename);
2072
+ if (!(await stat(fullPath)).isFile()) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`);
2073
+ const [version, files, errors, coverage, executionTime, environmentModules] = parse(await readFile(fullPath, "utf-8"));
2074
+ if (!version) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a valid blob file`);
2075
+ return {
2076
+ version,
2077
+ files,
2078
+ errors,
2079
+ coverage,
2080
+ file: filename,
2081
+ executionTime,
2082
+ environmentModules
2083
+ };
2084
+ });
2085
+ const blobs = await Promise.all(promises);
2086
+ if (!blobs.length) throw new Error(`vitest.mergeReports() requires at least one blob file in "${blobsDirectory}" directory, but none were found`);
2087
+ const versions = new Set(blobs.map((blob) => blob.version));
2088
+ if (versions.size > 1) throw new Error(`vitest.mergeReports() requires all blob files to be generated by the same Vitest version, received\n\n${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`);
2089
+ if (!versions.has(currentVersion)) throw new Error(`the blobs in "${blobsDirectory}" were generated by a different version of Vitest. Expected v${currentVersion}, but received v${blobs[0].version}`);
2090
+ // Restore module graph
2091
+ const projects = Object.fromEntries(projectsArray.map((p) => [p.name, p]));
2092
+ for (const project of projectsArray) if (project.isBrowserEnabled()) await project._initBrowserServer();
2093
+ blobs.forEach((blob) => {
2094
+ Object.entries(blob.environmentModules).forEach(([projectName, modulesByProject]) => {
2095
+ const project = projects[projectName];
2096
+ if (!project) return;
2097
+ modulesByProject.external.forEach(([id, externalized]) => {
2098
+ project._resolver.externalizeCache.set(id, externalized);
2099
+ });
2100
+ Object.entries(modulesByProject.environments).forEach(([environmentName, moduleGraph]) => {
2101
+ const environment = project.vite.environments[environmentName];
2102
+ deserializeEnvironmentModuleGraph(environment, moduleGraph);
2103
+ });
2104
+ const browserModuleGraph = modulesByProject.browser;
2105
+ if (browserModuleGraph) {
2106
+ const browserEnvironment = project.browser.vite.environments.client;
2107
+ deserializeEnvironmentModuleGraph(browserEnvironment, browserModuleGraph);
2108
+ }
2109
+ });
2110
+ });
2111
+ return {
2112
+ files: blobs.flatMap((blob) => blob.files).sort((f1, f2) => {
2113
+ return (f1.result?.startTime || 0) - (f2.result?.startTime || 0);
2114
+ }),
2115
+ errors: blobs.flatMap((blob) => blob.errors),
2116
+ coverages: blobs.map((blob) => blob.coverage),
2117
+ executionTimes: blobs.map((blob) => blob.executionTime)
2118
+ };
2119
+ }
2120
+ function serializeEnvironmentModuleGraph(environment) {
2121
+ const idTable = [];
2122
+ const idMap = /* @__PURE__ */ new Map();
2123
+ const getIdIndex = (id) => {
2124
+ const existing = idMap.get(id);
2125
+ if (existing != null) return existing;
2126
+ const next = idTable.length;
2127
+ idMap.set(id, next);
2128
+ idTable.push(id);
2129
+ return next;
2130
+ };
2131
+ const modules = [];
2132
+ for (const [id, mod] of environment.moduleGraph.idToModuleMap.entries()) {
2133
+ if (!mod.file) continue;
2134
+ const importedIds = [];
2135
+ for (const importedNode of mod.importedModules) if (importedNode.id) importedIds.push(getIdIndex(importedNode.id));
2136
+ modules.push([
2137
+ getIdIndex(id),
2138
+ getIdIndex(mod.file),
2139
+ getIdIndex(mod.url),
2140
+ importedIds
2141
+ ]);
2142
+ }
2143
+ return {
2144
+ idTable,
2145
+ modules
2146
+ };
2147
+ }
2148
+ function deserializeEnvironmentModuleGraph(environment, serialized) {
2149
+ const nodesById = /* @__PURE__ */ new Map();
2150
+ serialized.modules.forEach(([id, file, url]) => {
2151
+ const moduleId = serialized.idTable[id];
2152
+ const filePath = serialized.idTable[file];
2153
+ const urlPath = serialized.idTable[url];
2154
+ const moduleNode = environment.moduleGraph.createFileOnlyEntry(filePath);
2155
+ moduleNode.url = urlPath;
2156
+ moduleNode.id = moduleId;
2157
+ moduleNode.transformResult = {
2158
+ code: " ",
2159
+ map: null
2160
+ };
2161
+ environment.moduleGraph.idToModuleMap.set(moduleId, moduleNode);
2162
+ nodesById.set(moduleId, moduleNode);
2163
+ });
2164
+ serialized.modules.forEach(([id, _file, _url, importedIds]) => {
2165
+ const moduleId = serialized.idTable[id];
2166
+ const moduleNode = nodesById.get(moduleId);
2167
+ importedIds.forEach((importedIdIndex) => {
2168
+ const importedId = serialized.idTable[importedIdIndex];
2169
+ const importedNode = nodesById.get(importedId);
2170
+ moduleNode.importedModules.add(importedNode);
2171
+ importedNode.importers.add(moduleNode);
2172
+ });
2173
+ });
2174
+ }
2175
+
2176
+ class HangingProcessReporter {
2177
+ whyRunning;
2178
+ onInit() {
2179
+ this.whyRunning = createRequire(import.meta.url)("why-is-node-running");
2180
+ }
2181
+ onProcessTimeout() {
2182
+ this.whyRunning?.();
2183
+ }
2184
+ }
2185
+
2186
+ const BADGE_PADDING = " ";
2187
+ class BaseReporter {
2188
+ start = 0;
2189
+ end = 0;
2190
+ watchFilters;
2191
+ failedUnwatchedFiles = [];
2192
+ isTTY;
2193
+ ctx = void 0;
2194
+ renderSucceed = false;
2195
+ verbose = false;
2196
+ silent;
2197
+ _filesInWatchMode = /* @__PURE__ */ new Map();
2198
+ _timeStart = formatTimeString(/* @__PURE__ */ new Date());
2199
+ constructor(options = {}) {
2200
+ this.isTTY = options.isTTY ?? isTTY;
2201
+ this.silent = options.silent;
2202
+ }
2203
+ onInit(ctx) {
2204
+ this.ctx = ctx;
2205
+ this.silent ??= this.ctx.config.silent;
2206
+ this.ctx.logger.printBanner();
2207
+ }
2208
+ log(...messages) {
2209
+ this.ctx.logger.log(...messages);
2210
+ }
2211
+ error(...messages) {
2212
+ this.ctx.logger.error(...messages);
2213
+ }
2214
+ relative(path) {
2215
+ return relative(this.ctx.config.root, path);
2216
+ }
2217
+ onTestRunStart(_specifications) {
2218
+ this.start = performance$1.now();
2219
+ this._timeStart = formatTimeString(/* @__PURE__ */ new Date());
2220
+ }
2221
+ onTestRunEnd(testModules, unhandledErrors, _reason) {
2222
+ const files = testModules.map((testModule) => testModule.task);
2223
+ const errors = [...unhandledErrors];
2224
+ this.end = performance$1.now();
2225
+ if (!files.length && !errors.length) this.ctx.logger.printNoTestFound(this.ctx.filenamePattern);
2226
+ else this.reportSummary(files, errors);
2227
+ }
2228
+ onTestCaseResult(testCase) {
2229
+ if (testCase.result().state === "failed") this.logFailedTask(testCase.task);
2230
+ }
2231
+ onTestSuiteResult(testSuite) {
2232
+ if (testSuite.state() === "failed") this.logFailedTask(testSuite.task);
2233
+ }
2234
+ onTestModuleEnd(testModule) {
2235
+ if (testModule.state() === "failed") this.logFailedTask(testModule.task);
2236
+ this.printTestModule(testModule);
2237
+ }
2238
+ logFailedTask(task) {
2239
+ if (this.silent === "passed-only") for (const log of task.logs || []) this.onUserConsoleLog(log, "failed");
2240
+ }
2241
+ printTestModule(testModule) {
2242
+ const moduleState = testModule.state();
2243
+ if (moduleState === "queued" || moduleState === "pending") return;
2079
2244
  let testsCount = 0;
2080
2245
  let failedCount = 0;
2081
2246
  let skippedCount = 0;
@@ -2992,341 +3157,6 @@ class AgentReporter extends DefaultReporter {
2992
3157
  }
2993
3158
  }
2994
3159
 
2995
- /// <reference types="../types/index.d.ts" />
2996
-
2997
- // (c) 2020-present Andrea Giammarchi
2998
-
2999
- const {parse: $parse, stringify: $stringify} = JSON;
3000
- const {keys} = Object;
3001
-
3002
- const Primitive = String; // it could be Number
3003
- const primitive = 'string'; // it could be 'number'
3004
-
3005
- const ignore = {};
3006
- const object = 'object';
3007
-
3008
- const noop = (_, value) => value;
3009
-
3010
- const primitives = value => (
3011
- value instanceof Primitive ? Primitive(value) : value
3012
- );
3013
-
3014
- const Primitives = (_, value) => (
3015
- typeof value === primitive ? new Primitive(value) : value
3016
- );
3017
-
3018
- const resolver = (input, lazy, parsed, $) => output => {
3019
- for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
3020
- const k = ke[y];
3021
- const value = output[k];
3022
- if (value instanceof Primitive) {
3023
- const tmp = input[value];
3024
- if (typeof tmp === object && !parsed.has(tmp)) {
3025
- parsed.add(tmp);
3026
- output[k] = ignore;
3027
- lazy.push({ o: output, k, r: tmp });
3028
- }
3029
- else
3030
- output[k] = $.call(output, k, tmp);
3031
- }
3032
- else if (output[k] !== ignore)
3033
- output[k] = $.call(output, k, value);
3034
- }
3035
- return output;
3036
- };
3037
-
3038
- const set = (known, input, value) => {
3039
- const index = Primitive(input.push(value) - 1);
3040
- known.set(value, index);
3041
- return index;
3042
- };
3043
-
3044
- /**
3045
- * Converts a specialized flatted string into a JS value.
3046
- * @param {string} text
3047
- * @param {(this: any, key: string, value: any) => any} [reviver]
3048
- * @returns {any}
3049
- */
3050
- const parse = (text, reviver) => {
3051
- const input = $parse(text, Primitives).map(primitives);
3052
- const $ = reviver || noop;
3053
-
3054
- let value = input[0];
3055
-
3056
- if (typeof value === object && value) {
3057
- const lazy = [];
3058
- const revive = resolver(input, lazy, new Set, $);
3059
- value = revive(value);
3060
-
3061
- let i = 0;
3062
- while (i < lazy.length) {
3063
- // it could be a lazy.shift() but that's costly
3064
- const {o, k, r} = lazy[i++];
3065
- o[k] = $.call(o, k, revive(r));
3066
- }
3067
- }
3068
-
3069
- return $.call({'': value}, '', value);
3070
- };
3071
-
3072
- /**
3073
- * Converts a JS value into a specialized flatted string.
3074
- * @param {any} value
3075
- * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
3076
- * @param {string | number | undefined} [space]
3077
- * @returns {string}
3078
- */
3079
- const stringify = (value, replacer, space) => {
3080
- const $ = replacer && typeof replacer === object ?
3081
- (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
3082
- (replacer || noop);
3083
- const known = new Map;
3084
- const input = [];
3085
- const output = [];
3086
- let i = +set(known, input, $.call({'': value}, '', value));
3087
- let firstRun = !i;
3088
- while (i < input.length) {
3089
- firstRun = true;
3090
- output[i] = $stringify(input[i++], replace, space);
3091
- }
3092
- return '[' + output.join(',') + ']';
3093
- function replace(key, value) {
3094
- if (firstRun) {
3095
- firstRun = !firstRun;
3096
- return value;
3097
- }
3098
- const after = $.call(this, key, value);
3099
- switch (typeof after) {
3100
- case object:
3101
- if (after === null) return after;
3102
- case primitive:
3103
- return known.get(after) || set(known, input, after);
3104
- }
3105
- return after;
3106
- }
3107
- };
3108
-
3109
- function getOutputFile(config, reporter) {
3110
- if (!config?.outputFile) return;
3111
- if (typeof config.outputFile === "string") return config.outputFile;
3112
- return config.outputFile[reporter];
3113
- }
3114
- function createDefinesScript(define) {
3115
- if (!define) return "";
3116
- if (serializeDefine(define) === "{}") return "";
3117
- return `
3118
- const defines = ${serializeDefine(define)}
3119
- Object.keys(defines).forEach((key) => {
3120
- const segments = key.split('.')
3121
- let target = globalThis
3122
- for (let i = 0; i < segments.length; i++) {
3123
- const segment = segments[i]
3124
- if (i === segments.length - 1) {
3125
- target[segment] = defines[key]
3126
- } else {
3127
- target = target[segment] || (target[segment] = {})
3128
- }
3129
- }
3130
- })
3131
- `;
3132
- }
3133
- /**
3134
- * Like `JSON.stringify` but keeps raw string values as a literal
3135
- * in the generated code. For example: `"window"` would refer to
3136
- * the global `window` object directly.
3137
- */
3138
- function serializeDefine(define) {
3139
- const userDefine = {};
3140
- for (const key in define) {
3141
- // vitest sets this to avoid vite:client-inject plugin
3142
- if (key === "process.env.NODE_ENV" && define[key] === "process.env.NODE_ENV") continue;
3143
- // import.meta.env.* is handled in `importAnalysis` plugin
3144
- if (!key.startsWith("import.meta.env.")) userDefine[key] = define[key];
3145
- }
3146
- let res = `{`;
3147
- const keys = Object.keys(userDefine).sort();
3148
- for (let i = 0; i < keys.length; i++) {
3149
- const key = keys[i];
3150
- const val = userDefine[key];
3151
- res += `${JSON.stringify(key)}: ${handleDefineValue(val)}`;
3152
- if (i !== keys.length - 1) res += `, `;
3153
- }
3154
- return `${res}}`;
3155
- }
3156
- function handleDefineValue(value) {
3157
- if (typeof value === "undefined") return "undefined";
3158
- if (typeof value === "string") return value;
3159
- return JSON.stringify(value);
3160
- }
3161
-
3162
- class BlobReporter {
3163
- start = 0;
3164
- ctx;
3165
- options;
3166
- coverage;
3167
- constructor(options) {
3168
- this.options = options;
3169
- }
3170
- onInit(ctx) {
3171
- if (ctx.config.watch) throw new Error("Blob reporter is not supported in watch mode");
3172
- this.ctx = ctx;
3173
- this.start = performance.now();
3174
- this.coverage = void 0;
3175
- }
3176
- onCoverage(coverage) {
3177
- this.coverage = coverage;
3178
- }
3179
- async onTestRunEnd(testModules, unhandledErrors) {
3180
- const executionTime = performance.now() - this.start;
3181
- const files = testModules.map((testModule) => testModule.task);
3182
- const errors = [...unhandledErrors];
3183
- const coverage = this.coverage;
3184
- let outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "blob");
3185
- if (!outputFile) {
3186
- const shard = this.ctx.config.shard;
3187
- outputFile = shard ? `.vitest-reports/blob-${shard.index}-${shard.count}.json` : ".vitest-reports/blob.json";
3188
- }
3189
- const environmentModules = {};
3190
- this.ctx.projects.forEach((project) => {
3191
- const serializedProject = {
3192
- environments: {},
3193
- external: []
3194
- };
3195
- Object.entries(project.vite.environments).forEach(([environmentName, environment]) => {
3196
- serializedProject.environments[environmentName] = serializeEnvironmentModuleGraph(environment);
3197
- });
3198
- if (project.browser?.vite.environments.client) serializedProject.browser = serializeEnvironmentModuleGraph(project.browser.vite.environments.client);
3199
- for (const [id, value] of project._resolver.externalizeCache.entries()) if (typeof value === "string") serializedProject.external.push([id, value]);
3200
- environmentModules[project.name] = serializedProject;
3201
- });
3202
- const report = [
3203
- this.ctx.version,
3204
- files,
3205
- errors,
3206
- coverage,
3207
- executionTime,
3208
- environmentModules
3209
- ];
3210
- const reportFile = resolve$1(this.ctx.config.root, outputFile);
3211
- await writeBlob(report, reportFile);
3212
- this.ctx.logger.log("blob report written to", reportFile);
3213
- }
3214
- }
3215
- async function writeBlob(content, filename) {
3216
- const report = stringify(content);
3217
- const dir = dirname(filename);
3218
- if (!existsSync(dir)) await mkdir(dir, { recursive: true });
3219
- await writeFile(filename, report, "utf-8");
3220
- }
3221
- async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
3222
- // using process.cwd() because --merge-reports can only be used in CLI
3223
- const resolvedDir = resolve$1(process.cwd(), blobsDirectory);
3224
- const promises = (await readdir(resolvedDir)).map(async (filename) => {
3225
- const fullPath = resolve$1(resolvedDir, filename);
3226
- if (!(await stat(fullPath)).isFile()) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`);
3227
- const [version, files, errors, coverage, executionTime, environmentModules] = parse(await readFile(fullPath, "utf-8"));
3228
- if (!version) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a valid blob file`);
3229
- return {
3230
- version,
3231
- files,
3232
- errors,
3233
- coverage,
3234
- file: filename,
3235
- executionTime,
3236
- environmentModules
3237
- };
3238
- });
3239
- const blobs = await Promise.all(promises);
3240
- if (!blobs.length) throw new Error(`vitest.mergeReports() requires at least one blob file in "${blobsDirectory}" directory, but none were found`);
3241
- const versions = new Set(blobs.map((blob) => blob.version));
3242
- if (versions.size > 1) throw new Error(`vitest.mergeReports() requires all blob files to be generated by the same Vitest version, received\n\n${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`);
3243
- if (!versions.has(currentVersion)) throw new Error(`the blobs in "${blobsDirectory}" were generated by a different version of Vitest. Expected v${currentVersion}, but received v${blobs[0].version}`);
3244
- // Restore module graph
3245
- const projects = Object.fromEntries(projectsArray.map((p) => [p.name, p]));
3246
- for (const project of projectsArray) if (project.isBrowserEnabled()) await project._initBrowserServer();
3247
- blobs.forEach((blob) => {
3248
- Object.entries(blob.environmentModules).forEach(([projectName, modulesByProject]) => {
3249
- const project = projects[projectName];
3250
- if (!project) return;
3251
- modulesByProject.external.forEach(([id, externalized]) => {
3252
- project._resolver.externalizeCache.set(id, externalized);
3253
- });
3254
- Object.entries(modulesByProject.environments).forEach(([environmentName, moduleGraph]) => {
3255
- const environment = project.vite.environments[environmentName];
3256
- deserializeEnvironmentModuleGraph(environment, moduleGraph);
3257
- });
3258
- const browserModuleGraph = modulesByProject.browser;
3259
- if (browserModuleGraph) {
3260
- const browserEnvironment = project.browser.vite.environments.client;
3261
- deserializeEnvironmentModuleGraph(browserEnvironment, browserModuleGraph);
3262
- }
3263
- });
3264
- });
3265
- return {
3266
- files: blobs.flatMap((blob) => blob.files).sort((f1, f2) => {
3267
- return (f1.result?.startTime || 0) - (f2.result?.startTime || 0);
3268
- }),
3269
- errors: blobs.flatMap((blob) => blob.errors),
3270
- coverages: blobs.map((blob) => blob.coverage),
3271
- executionTimes: blobs.map((blob) => blob.executionTime)
3272
- };
3273
- }
3274
- function serializeEnvironmentModuleGraph(environment) {
3275
- const idTable = [];
3276
- const idMap = /* @__PURE__ */ new Map();
3277
- const getIdIndex = (id) => {
3278
- const existing = idMap.get(id);
3279
- if (existing != null) return existing;
3280
- const next = idTable.length;
3281
- idMap.set(id, next);
3282
- idTable.push(id);
3283
- return next;
3284
- };
3285
- const modules = [];
3286
- for (const [id, mod] of environment.moduleGraph.idToModuleMap.entries()) {
3287
- if (!mod.file) continue;
3288
- const importedIds = [];
3289
- for (const importedNode of mod.importedModules) if (importedNode.id) importedIds.push(getIdIndex(importedNode.id));
3290
- modules.push([
3291
- getIdIndex(id),
3292
- getIdIndex(mod.file),
3293
- getIdIndex(mod.url),
3294
- importedIds
3295
- ]);
3296
- }
3297
- return {
3298
- idTable,
3299
- modules
3300
- };
3301
- }
3302
- function deserializeEnvironmentModuleGraph(environment, serialized) {
3303
- const nodesById = /* @__PURE__ */ new Map();
3304
- serialized.modules.forEach(([id, file, url]) => {
3305
- const moduleId = serialized.idTable[id];
3306
- const filePath = serialized.idTable[file];
3307
- const urlPath = serialized.idTable[url];
3308
- const moduleNode = environment.moduleGraph.createFileOnlyEntry(filePath);
3309
- moduleNode.url = urlPath;
3310
- moduleNode.id = moduleId;
3311
- moduleNode.transformResult = {
3312
- code: " ",
3313
- map: null
3314
- };
3315
- environment.moduleGraph.idToModuleMap.set(moduleId, moduleNode);
3316
- nodesById.set(moduleId, moduleNode);
3317
- });
3318
- serialized.modules.forEach(([id, _file, _url, importedIds]) => {
3319
- const moduleId = serialized.idTable[id];
3320
- const moduleNode = nodesById.get(moduleId);
3321
- importedIds.forEach((importedIdIndex) => {
3322
- const importedId = serialized.idTable[importedIdIndex];
3323
- const importedNode = nodesById.get(importedId);
3324
- moduleNode.importedModules.add(importedNode);
3325
- importedNode.importers.add(moduleNode);
3326
- });
3327
- });
3328
- }
3329
-
3330
3160
  class DotReporter extends BaseReporter {
3331
3161
  renderer;
3332
3162
  tests = /* @__PURE__ */ new Map();
@@ -3702,16 +3532,6 @@ function renderSummary(summaryData, fileLinks) {
3702
3532
  return summary;
3703
3533
  }
3704
3534
 
3705
- class HangingProcessReporter {
3706
- whyRunning;
3707
- onInit() {
3708
- this.whyRunning = createRequire(import.meta.url)("why-is-node-running");
3709
- }
3710
- onProcessTimeout() {
3711
- this.whyRunning?.();
3712
- }
3713
- }
3714
-
3715
3535
  const StatusMap = {
3716
3536
  fail: "failed",
3717
3537
  only: "pending",
@@ -4186,6 +4006,228 @@ class VerboseReporter extends DefaultReporter {
4186
4006
  }
4187
4007
  }
4188
4008
 
4009
+ function createBenchmarkJsonReport(files) {
4010
+ const report = { files: [] };
4011
+ for (const file of files) {
4012
+ const groups = [];
4013
+ for (const task of getTasks(file)) if (task?.type === "suite") {
4014
+ const benchmarks = [];
4015
+ for (const t of task.tasks) {
4016
+ const benchmark = t.meta.benchmark && t.result?.benchmark;
4017
+ if (benchmark) benchmarks.push({
4018
+ id: t.id,
4019
+ ...benchmark,
4020
+ samples: []
4021
+ });
4022
+ }
4023
+ if (benchmarks.length) groups.push({
4024
+ fullName: getFullName(task, " > "),
4025
+ benchmarks
4026
+ });
4027
+ }
4028
+ report.files.push({
4029
+ filepath: file.filepath,
4030
+ groups
4031
+ });
4032
+ }
4033
+ return report;
4034
+ }
4035
+ function flattenFormattedBenchmarkReport(report) {
4036
+ const flat = {};
4037
+ for (const file of report.files) for (const group of file.groups) for (const t of group.benchmarks) flat[t.id] = t;
4038
+ return flat;
4039
+ }
4040
+
4041
+ const outputMap = /* @__PURE__ */ new WeakMap();
4042
+ function formatNumber(number) {
4043
+ const res = String(number.toFixed(number < 100 ? 4 : 2)).split(".");
4044
+ return res[0].replace(/(?=(?:\d{3})+$)\B/g, ",") + (res[1] ? `.${res[1]}` : "");
4045
+ }
4046
+ const tableHead = [
4047
+ "name",
4048
+ "hz",
4049
+ "min",
4050
+ "max",
4051
+ "mean",
4052
+ "p75",
4053
+ "p99",
4054
+ "p995",
4055
+ "p999",
4056
+ "rme",
4057
+ "samples"
4058
+ ];
4059
+ function renderBenchmarkItems(result) {
4060
+ return [
4061
+ result.name,
4062
+ formatNumber(result.hz || 0),
4063
+ formatNumber(result.min || 0),
4064
+ formatNumber(result.max || 0),
4065
+ formatNumber(result.mean || 0),
4066
+ formatNumber(result.p75 || 0),
4067
+ formatNumber(result.p99 || 0),
4068
+ formatNumber(result.p995 || 0),
4069
+ formatNumber(result.p999 || 0),
4070
+ `±${(result.rme || 0).toFixed(2)}%`,
4071
+ (result.sampleCount || 0).toString()
4072
+ ];
4073
+ }
4074
+ function computeColumnWidths(results) {
4075
+ const rows = [tableHead, ...results.map((v) => renderBenchmarkItems(v))];
4076
+ return Array.from(tableHead, (_, i) => Math.max(...rows.map((row) => stripVTControlCharacters(row[i]).length)));
4077
+ }
4078
+ function padRow(row, widths) {
4079
+ return row.map((v, i) => i ? v.padStart(widths[i], " ") : v.padEnd(widths[i], " "));
4080
+ }
4081
+ function renderTableHead(widths) {
4082
+ return " ".repeat(3) + padRow(tableHead, widths).map(c.bold).join(" ");
4083
+ }
4084
+ function renderBenchmark(result, widths) {
4085
+ const padded = padRow(renderBenchmarkItems(result), widths);
4086
+ return [
4087
+ padded[0],
4088
+ c.blue(padded[1]),
4089
+ c.cyan(padded[2]),
4090
+ c.cyan(padded[3]),
4091
+ c.cyan(padded[4]),
4092
+ c.cyan(padded[5]),
4093
+ c.cyan(padded[6]),
4094
+ c.cyan(padded[7]),
4095
+ c.cyan(padded[8]),
4096
+ c.dim(padded[9]),
4097
+ c.dim(padded[10])
4098
+ ].join(" ");
4099
+ }
4100
+ function renderTable(options) {
4101
+ const output = [];
4102
+ const benchMap = {};
4103
+ for (const task of options.tasks) if (task.meta.benchmark && task.result?.benchmark) benchMap[task.id] = {
4104
+ current: task.result.benchmark,
4105
+ baseline: options.compare?.[task.id]
4106
+ };
4107
+ const benchCount = Object.entries(benchMap).length;
4108
+ const columnWidths = computeColumnWidths(Object.values(benchMap).flatMap((v) => [v.current, v.baseline]).filter(notNullish));
4109
+ let idx = 0;
4110
+ const padding = " ".repeat(1 );
4111
+ for (const task of options.tasks) {
4112
+ const duration = task.result?.duration;
4113
+ const bench = benchMap[task.id];
4114
+ let prefix = "";
4115
+ if (idx === 0 && task.meta?.benchmark) prefix += `${renderTableHead(columnWidths)}\n${padding}`;
4116
+ prefix += ` ${getStateSymbol(task)} `;
4117
+ let suffix = "";
4118
+ if (task.type === "suite") suffix += c.dim(` (${getTests(task).length})`);
4119
+ if (task.mode === "skip" || task.mode === "todo") suffix += c.dim(c.gray(" [skipped]"));
4120
+ if (duration != null) {
4121
+ const color = duration > options.slowTestThreshold ? c.yellow : c.green;
4122
+ suffix += color(` ${Math.round(duration)}${c.dim("ms")}`);
4123
+ }
4124
+ if (options.showHeap && task.result?.heap != null) suffix += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`);
4125
+ if (bench) {
4126
+ let body = renderBenchmark(bench.current, columnWidths);
4127
+ if (options.compare && bench.baseline) {
4128
+ if (bench.current.hz) {
4129
+ const diff = bench.current.hz / bench.baseline.hz;
4130
+ const diffFixed = diff.toFixed(2);
4131
+ if (diffFixed === "1.0.0") body += c.gray(` [${diffFixed}x]`);
4132
+ if (diff > 1) body += c.blue(` [${diffFixed}x] ⇑`);
4133
+ else body += c.red(` [${diffFixed}x] ⇓`);
4134
+ }
4135
+ output.push(padding + prefix + body + suffix);
4136
+ const bodyBaseline = renderBenchmark(bench.baseline, columnWidths);
4137
+ output.push(`${padding} ${bodyBaseline} ${c.dim("(baseline)")}`);
4138
+ } else {
4139
+ if (bench.current.rank === 1 && benchCount > 1) body += c.bold(c.green(" fastest"));
4140
+ if (bench.current.rank === benchCount && benchCount > 2) body += c.bold(c.gray(" slowest"));
4141
+ output.push(padding + prefix + body + suffix);
4142
+ }
4143
+ } else output.push(padding + prefix + task.name + suffix);
4144
+ if (task.result?.state !== "pass" && outputMap.get(task) != null) {
4145
+ let data = outputMap.get(task);
4146
+ if (typeof data === "string") {
4147
+ data = stripVTControlCharacters(data.trim().split("\n").filter(Boolean).pop());
4148
+ if (data === "") data = void 0;
4149
+ }
4150
+ if (data != null) {
4151
+ const out = ` ${" ".repeat(options.level)}${F_RIGHT} ${data}`;
4152
+ output.push(c.gray(truncateString(out, options.columns)));
4153
+ }
4154
+ }
4155
+ idx++;
4156
+ }
4157
+ return output.filter(Boolean).join("\n");
4158
+ }
4159
+
4160
+ class BenchmarkReporter extends DefaultReporter {
4161
+ compare;
4162
+ async onInit(ctx) {
4163
+ super.onInit(ctx);
4164
+ if (this.ctx.config.benchmark?.compare) {
4165
+ const compareFile = pathe.resolve(this.ctx.config.root, this.ctx.config.benchmark?.compare);
4166
+ try {
4167
+ this.compare = flattenFormattedBenchmarkReport(JSON.parse(await fs.promises.readFile(compareFile, "utf-8")));
4168
+ } catch (e) {
4169
+ this.error(`Failed to read '${compareFile}'`, e);
4170
+ }
4171
+ }
4172
+ }
4173
+ onTaskUpdate(packs) {
4174
+ for (const pack of packs) {
4175
+ const task = this.ctx.state.idMap.get(pack[0]);
4176
+ if (task?.type === "suite" && task.result?.state !== "run") task.tasks.filter((task) => task.result?.benchmark).sort((benchA, benchB) => benchA.result.benchmark.mean - benchB.result.benchmark.mean).forEach((bench, idx) => {
4177
+ bench.result.benchmark.rank = Number(idx) + 1;
4178
+ });
4179
+ }
4180
+ }
4181
+ onTestSuiteResult(testSuite) {
4182
+ super.onTestSuiteResult(testSuite);
4183
+ this.printSuiteTable(testSuite);
4184
+ }
4185
+ printTestModule(testModule) {
4186
+ this.printSuiteTable(testModule);
4187
+ }
4188
+ printSuiteTable(testTask) {
4189
+ const state = testTask.state();
4190
+ if (state === "pending" || state === "queued") return;
4191
+ const benches = testTask.task.tasks.filter((t) => t.meta.benchmark);
4192
+ const duration = testTask.task.result?.duration || 0;
4193
+ if (benches.length > 0 && benches.every((t) => t.result?.state !== "run" && t.result?.state !== "queued")) {
4194
+ let title = `\n ${getStateSymbol(testTask.task)} ${formatProjectName(testTask.project)}${getFullName(testTask.task, separator)}`;
4195
+ if (duration != null && duration > this.ctx.config.slowTestThreshold) title += c.yellow(` ${Math.round(duration)}${c.dim("ms")}`);
4196
+ this.log(title);
4197
+ this.log(renderTable({
4198
+ tasks: benches,
4199
+ level: 1,
4200
+ columns: this.ctx.logger.getColumns(),
4201
+ compare: this.compare,
4202
+ showHeap: this.ctx.config.logHeapUsage,
4203
+ slowTestThreshold: this.ctx.config.slowTestThreshold
4204
+ }));
4205
+ }
4206
+ }
4207
+ async onTestRunEnd(testModules, unhandledErrors, reason) {
4208
+ super.onTestRunEnd(testModules, unhandledErrors, reason);
4209
+ // write output for future comparison
4210
+ let outputFile = this.ctx.config.benchmark?.outputJson;
4211
+ if (outputFile) {
4212
+ outputFile = pathe.resolve(this.ctx.config.root, outputFile);
4213
+ const outputDirectory = pathe.dirname(outputFile);
4214
+ if (!fs.existsSync(outputDirectory)) await fs.promises.mkdir(outputDirectory, { recursive: true });
4215
+ const output = createBenchmarkJsonReport(testModules.map((t) => t.task.file));
4216
+ await fs.promises.writeFile(outputFile, JSON.stringify(output, null, 2));
4217
+ this.log(`Benchmark report written to ${outputFile}`);
4218
+ }
4219
+ }
4220
+ }
4221
+
4222
+ class VerboseBenchmarkReporter extends BenchmarkReporter {
4223
+ verbose = true;
4224
+ }
4225
+
4226
+ const BenchmarkReportsMap = {
4227
+ default: BenchmarkReporter,
4228
+ verbose: VerboseBenchmarkReporter
4229
+ };
4230
+
4189
4231
  const ReportersMap = {
4190
4232
  "default": DefaultReporter,
4191
4233
  "agent": AgentReporter,
@@ -4201,4 +4243,4 @@ const ReportersMap = {
4201
4243
  "github-actions": GithubActionsReporter
4202
4244
  };
4203
4245
 
4204
- export { AgentReporter as A, BlobReporter as B, utils as C, DefaultReporter as D, F_RIGHT as F, GithubActionsReporter as G, HangingProcessReporter as H, JUnitReporter as J, ReportersMap as R, TapFlatReporter as T, VerboseReporter as V, DotReporter as a, JsonReporter as b, TapReporter as c, createIndexLocationsMap as d, TraceMap as e, formatProjectName as f, getStateSymbol as g, ancestor as h, stringify as i, printError as j, errorBanner as k, divider as l, Typechecker as m, generateCodeFrame as n, originalPositionFor as o, parse as p, escapeRegExp as q, createDefinesScript as r, separator as s, truncateString as t, groupBy as u, readBlobs as v, withLabel as w, convertTasksToEvents as x, wildcardPatternToRegExp as y, stdout as z };
4246
+ export { AgentReporter as A, BenchmarkReporter as B, DefaultReporter as D, GithubActionsReporter as G, HangingProcessReporter as H, JUnitReporter as J, ReportersMap as R, TapFlatReporter as T, VerboseBenchmarkReporter as V, BenchmarkReportsMap as a, DotReporter as b, JsonReporter as c, TapReporter as d, VerboseReporter as e, createIndexLocationsMap as f, TraceMap as g, ancestor as h, printError as i, Typechecker as j, generateCodeFrame as k, escapeRegExp as l, createDefinesScript as m, groupBy as n, originalPositionFor as o, parse as p, BlobReporter as q, readBlobs as r, stringify as s, convertTasksToEvents as t, stdout as u, wildcardPatternToRegExp as w };