vitest 1.5.2 → 1.6.0

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 (53) hide show
  1. package/dist/browser.d.ts +1 -1
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/environments-node.vcoXCoKs.js +19 -0
  4. package/dist/chunks/{integrations-globals.k0N5-dd1.js → integrations-globals.kw4co3rx.js} +7 -6
  5. package/dist/chunks/{runtime-console.kbFEN7E-.js → runtime-console.EO5ha7qv.js} +3 -3
  6. package/dist/chunks/{runtime-runBaseTests.-x-nNuJ_.js → runtime-runBaseTests.oAvMKtQC.js} +16 -16
  7. package/dist/cli.js +2 -2
  8. package/dist/config.cjs +3 -2
  9. package/dist/config.d.ts +1 -1
  10. package/dist/config.js +3 -2
  11. package/dist/coverage.d.ts +1 -1
  12. package/dist/environments.d.ts +1 -1
  13. package/dist/execute.d.ts +6 -4
  14. package/dist/execute.js +3 -3
  15. package/dist/index.d.ts +4 -3
  16. package/dist/index.js +8 -7
  17. package/dist/node.d.ts +2 -2
  18. package/dist/node.js +12 -11
  19. package/dist/{reporters-xEmem8D4.d.ts → reporters-yx5ZTtEV.d.ts} +101 -26
  20. package/dist/reporters.d.ts +1 -1
  21. package/dist/reporters.js +11 -10
  22. package/dist/runners.d.ts +1 -1
  23. package/dist/runners.js +8 -7
  24. package/dist/snapshot.d.ts +9 -0
  25. package/dist/snapshot.js +8 -0
  26. package/dist/{suite-HPAKvIxA.d.ts → suite-IbNSsUWN.d.ts} +1 -1
  27. package/dist/suite.d.ts +3 -3
  28. package/dist/suite.js +5 -4
  29. package/dist/vendor/{base.Xt0Omgh7.js → base.5NT-gWu5.js} +9 -1
  30. package/dist/vendor/{base.gAwDs8Jc.js → base.Ybri3C14.js} +2 -2
  31. package/dist/vendor/{benchmark.eeqk2rd8.js → benchmark.yGkUTKnC.js} +1 -1
  32. package/dist/vendor/{cac.8mXc9Oj6.js → cac.EdDItJD-.js} +38 -15
  33. package/dist/vendor/{cli-api._n4_Wp_j.js → cli-api.E07AF1Yq.js} +155 -46
  34. package/dist/vendor/env.AtSIuHFg.js +7 -0
  35. package/dist/vendor/{execute.2_yoIC01.js → execute.fL3szUAI.js} +8 -3
  36. package/dist/vendor/{index.gHZzsRJQ.js → index.DpVgvm2P.js} +17 -18
  37. package/dist/vendor/{index.GlXSU9xI.js → index.Q04MCqDO.js} +690 -645
  38. package/dist/vendor/{index.ir9i0ywP.js → index.SMVOaj7F.js} +2 -6
  39. package/dist/vendor/{index.0RrMQKD8.js → index.dI9lHwVn.js} +2 -2
  40. package/dist/vendor/{setup-common.7SXMSI--.js → setup-common.8nJLd4ay.js} +1 -1
  41. package/dist/vendor/{utils.VYmeMh-u.js → utils.dEtNIEgr.js} +1 -1
  42. package/dist/vendor/{vi.Y_w82WR8.js → vi.YFlodzP_.js} +1 -1
  43. package/dist/vendor/{vm.I_IsyNig.js → vm.QEE48c0T.js} +135 -43
  44. package/dist/worker.js +8 -2
  45. package/dist/workers/forks.js +4 -4
  46. package/dist/workers/runVmTests.js +13 -10
  47. package/dist/workers/threads.js +4 -4
  48. package/dist/workers/vmForks.js +7 -7
  49. package/dist/workers/vmThreads.js +7 -7
  50. package/dist/workers.d.ts +1 -1
  51. package/dist/workers.js +8 -8
  52. package/package.json +13 -9
  53. package/snapshot.d.ts +1 -0
@@ -1,18 +1,19 @@
1
- import { existsSync, promises, readFileSync } from 'node:fs';
2
- import { basename, dirname, resolve, join, relative, extname, normalize } from 'pathe';
3
- import { a as isNode, r as relativePath } from './index.ir9i0ywP.js';
4
- import { generateHash, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, getTasks, getTests, hasFailed, getSuites } from '@vitest/runner/utils';
1
+ import fs, { existsSync, promises, readFileSync } from 'node:fs';
5
2
  import c from 'picocolors';
6
- import { g as getStateSymbol, f as formatProjectName, p as pointer, F as F_RIGHT, r as renderSnapshotSummary, a as getStateString, b as formatTimeString, c as countTestErrors, d as divider, s as stripAnsi, e as getCols, h as getHookStateSymbol, i as F_POINTER } from './utils.VYmeMh-u.js';
7
- import { getSafeTimers, notNullish, inspect, positionToOffset, lineSplitRE, highlight, shuffle } from '@vitest/utils';
3
+ import * as pathe from 'pathe';
4
+ import { basename, dirname, resolve, join, relative, extname, normalize } from 'pathe';
8
5
  import { g as getFullName, h as hasFailedSnapshot } from './tasks.IknbGB2n.js';
6
+ import { getSafeTimers, notNullish, highlight, shuffle, inspect, positionToOffset, lineSplitRE } from '@vitest/utils';
7
+ import { i as isNode } from './env.AtSIuHFg.js';
8
+ import { g as getStateSymbol, f as formatProjectName, p as pointer, F as F_RIGHT, r as renderSnapshotSummary, a as getStateString, b as formatTimeString, c as countTestErrors, d as divider, s as stripAnsi, e as getCols, h as getHookStateSymbol, i as F_POINTER } from './utils.dEtNIEgr.js';
9
+ import { generateHash, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, getTasks, getTests, hasFailed, getSuites } from '@vitest/runner/utils';
9
10
  import { performance } from 'node:perf_hooks';
10
- import { UNKNOWN_TEST_ID } from '../chunks/runtime-console.kbFEN7E-.js';
11
- import { t as toArray, b as isPrimitive } from './base.Xt0Omgh7.js';
11
+ import { r as relativePath } from './index.SMVOaj7F.js';
12
+ import { UNKNOWN_TEST_ID } from '../chunks/runtime-console.EO5ha7qv.js';
13
+ import { t as toArray, b as isPrimitive } from './base.5NT-gWu5.js';
12
14
  import { isCI } from 'std-env';
13
15
  import { TraceMap, generatedPositionFor, parseErrorStacktrace } from '@vitest/utils/source-map';
14
16
  import nodeos__default, { hostname } from 'node:os';
15
- import { createRequire } from 'node:module';
16
17
  import { Writable } from 'node:stream';
17
18
  import { writeFile, rm } from 'node:fs/promises';
18
19
  import { execa } from 'execa';
@@ -29,6 +30,7 @@ import require$$0$2 from 'assert';
29
30
  import require$$0$1 from 'events';
30
31
  import { createHash } from 'node:crypto';
31
32
  import { slash } from 'vite-node/utils';
33
+ import { createRequire } from 'node:module';
32
34
 
33
35
  const REGEXP_WRAP_PREFIX = "$$vitest:";
34
36
  function getOutputFile(config, reporter) {
@@ -747,7 +749,7 @@ ${log.content}
747
749
  var _a2;
748
750
  return acc + Math.max(0, ((_a2 = test.result) == null ? void 0 : _a2.duration) || 0);
749
751
  }, 0);
750
- const transformTime = this.ctx.projects.flatMap((w) => Array.from(w.vitenode.fetchCache.values()).map((i) => i.duration || 0)).reduce((a, b) => a + b, 0);
752
+ const transformTime = this.ctx.projects.flatMap((w) => w.vitenode.getTotalDuration()).reduce((a, b) => a + b, 0);
751
753
  const environmentTime = files.reduce((acc, file) => acc + Math.max(0, file.environmentLoad || 0), 0);
752
754
  const prepareTime = files.reduce((acc, file) => acc + Math.max(0, file.prepareDuration || 0), 0);
753
755
  const threadTime = collectTime + testsTime + setupTime;
@@ -2185,7 +2187,7 @@ const StatusMap = {
2185
2187
  skip: "skipped",
2186
2188
  todo: "todo"
2187
2189
  };
2188
- let JsonReporter$1 = class JsonReporter {
2190
+ class JsonReporter {
2189
2191
  start = 0;
2190
2192
  ctx;
2191
2193
  options;
@@ -2325,7 +2327,7 @@ let JsonReporter$1 = class JsonReporter {
2325
2327
  return;
2326
2328
  return { line: frame.line, column: frame.column };
2327
2329
  }
2328
- };
2330
+ }
2329
2331
 
2330
2332
  class VerboseReporter extends DefaultReporter {
2331
2333
  constructor() {
@@ -2454,499 +2456,13 @@ class TapReporter {
2454
2456
  }
2455
2457
  }
2456
2458
 
2457
- function flattenTasks$1(task, baseName = "") {
2458
- const base = baseName ? `${baseName} > ` : "";
2459
- if (task.type === "suite") {
2460
- return task.tasks.flatMap((child) => flattenTasks$1(child, `${base}${task.name}`));
2461
- } else {
2462
- return [{
2463
- ...task,
2464
- name: `${base}${task.name}`
2465
- }];
2466
- }
2467
- }
2468
- function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
2469
- let regex = /((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))/g;
2470
- value = String(value || "").replace(regex, "");
2471
- if (removeDiscouragedChars) {
2472
- regex = new RegExp(
2473
- "([\\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]))",
2474
- "g"
2475
- );
2476
- value = value.replace(regex, "");
2477
- }
2478
- return value;
2479
- }
2480
- function escapeXML(value) {
2481
- return removeInvalidXMLCharacters(
2482
- String(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;"),
2483
- true
2484
- );
2485
- }
2486
- function executionTime(durationMS) {
2487
- return (durationMS / 1e3).toLocaleString("en-US", { useGrouping: false, maximumFractionDigits: 10 });
2488
- }
2489
- function getDuration(task) {
2490
- var _a;
2491
- const duration = ((_a = task.result) == null ? void 0 : _a.duration) ?? 0;
2492
- return executionTime(duration);
2493
- }
2494
- class JUnitReporter {
2495
- ctx;
2496
- reportFile;
2497
- baseLog;
2498
- logger;
2499
- _timeStart = /* @__PURE__ */ new Date();
2500
- fileFd;
2501
- options;
2502
- constructor(options) {
2503
- this.options = options;
2504
- }
2505
- async onInit(ctx) {
2506
- this.ctx = ctx;
2507
- const outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "junit");
2508
- if (outputFile) {
2509
- this.reportFile = resolve(this.ctx.config.root, outputFile);
2510
- const outputDirectory = dirname(this.reportFile);
2511
- if (!existsSync(outputDirectory))
2512
- await promises.mkdir(outputDirectory, { recursive: true });
2513
- const fileFd = await promises.open(this.reportFile, "w+");
2514
- this.fileFd = fileFd;
2515
- this.baseLog = async (text) => {
2516
- if (!this.fileFd)
2517
- this.fileFd = await promises.open(this.reportFile, "w+");
2518
- await promises.writeFile(this.fileFd, `${text}
2519
- `);
2520
- };
2521
- } else {
2522
- this.baseLog = async (text) => this.ctx.logger.log(text);
2523
- }
2524
- this._timeStart = /* @__PURE__ */ new Date();
2525
- this.logger = new IndentedLogger(this.baseLog);
2526
- }
2527
- async writeElement(name, attrs, children) {
2528
- const pairs = [];
2529
- for (const key in attrs) {
2530
- const attr = attrs[key];
2531
- if (attr === void 0)
2532
- continue;
2533
- pairs.push(`${key}="${escapeXML(attr)}"`);
2534
- }
2535
- await this.logger.log(`<${name}${pairs.length ? ` ${pairs.join(" ")}` : ""}>`);
2536
- this.logger.indent();
2537
- await children.call(this);
2538
- this.logger.unindent();
2539
- await this.logger.log(`</${name}>`);
2540
- }
2541
- async writeErrorDetails(task, error) {
2542
- const errorName = error.name ?? error.nameStr ?? "Unknown Error";
2543
- const errorDetails = `${errorName}: ${error.message}`;
2544
- await this.baseLog(escapeXML(errorDetails));
2545
- const project = this.ctx.getProjectByTaskId(task.id);
2546
- const stack = parseErrorStacktrace(error, {
2547
- getSourceMap: (file) => project.getBrowserSourceMapModuleById(file),
2548
- frameFilter: this.ctx.config.onStackTrace
2549
- });
2550
- for (const frame of stack) {
2551
- const path = relative(this.ctx.config.root, frame.file);
2552
- await this.baseLog(escapeXML(` ${F_POINTER} ${[frame.method, `${path}:${frame.line}:${frame.column}`].filter(Boolean).join(" ")}`));
2553
- if (frame.file in this.ctx.state.filesMap)
2554
- break;
2555
- }
2556
- }
2557
- async writeLogs(task, type) {
2558
- if (task.logs == null || task.logs.length === 0)
2559
- return;
2560
- const logType = type === "err" ? "stderr" : "stdout";
2561
- const logs = task.logs.filter((log) => log.type === logType);
2562
- if (logs.length === 0)
2563
- return;
2564
- await this.writeElement(`system-${type}`, {}, async () => {
2565
- for (const log of logs)
2566
- await this.baseLog(escapeXML(log.content));
2567
- });
2568
- }
2569
- async writeTasks(tasks, filename) {
2570
- for (const task of tasks) {
2571
- await this.writeElement("testcase", {
2572
- // TODO: v2.0.0 Remove env variable in favor of custom reporter options, e.g. "reporters: [['json', { classname: 'something' }]]"
2573
- classname: this.options.classname ?? process.env.VITEST_JUNIT_CLASSNAME ?? filename,
2574
- name: task.name,
2575
- time: getDuration(task)
2576
- }, async () => {
2577
- var _a;
2578
- await this.writeLogs(task, "out");
2579
- await this.writeLogs(task, "err");
2580
- if (task.mode === "skip" || task.mode === "todo")
2581
- await this.logger.log("<skipped/>");
2582
- if (((_a = task.result) == null ? void 0 : _a.state) === "fail") {
2583
- const errors = task.result.errors || [];
2584
- for (const error of errors) {
2585
- await this.writeElement("failure", {
2586
- message: error == null ? void 0 : error.message,
2587
- type: (error == null ? void 0 : error.name) ?? (error == null ? void 0 : error.nameStr)
2588
- }, async () => {
2589
- if (!error)
2590
- return;
2591
- await this.writeErrorDetails(task, error);
2592
- });
2593
- }
2594
- }
2595
- });
2596
- }
2597
- }
2598
- async onFinished(files = this.ctx.state.getFiles()) {
2599
- var _a;
2600
- await this.logger.log('<?xml version="1.0" encoding="UTF-8" ?>');
2601
- const transformed = files.map((file) => {
2602
- var _a2, _b;
2603
- const tasks = file.tasks.flatMap((task) => flattenTasks$1(task));
2604
- const stats2 = tasks.reduce((stats3, task) => {
2605
- var _a3, _b2;
2606
- return {
2607
- passed: stats3.passed + Number(((_a3 = task.result) == null ? void 0 : _a3.state) === "pass"),
2608
- failures: stats3.failures + Number(((_b2 = task.result) == null ? void 0 : _b2.state) === "fail"),
2609
- skipped: stats3.skipped + Number(task.mode === "skip" || task.mode === "todo")
2610
- };
2611
- }, {
2612
- passed: 0,
2613
- failures: 0,
2614
- skipped: 0
2615
- });
2616
- const suites = getSuites(file);
2617
- for (const suite of suites) {
2618
- if ((_a2 = suite.result) == null ? void 0 : _a2.errors) {
2619
- tasks.push(suite);
2620
- stats2.failures += 1;
2621
- }
2622
- }
2623
- if (tasks.length === 0 && ((_b = file.result) == null ? void 0 : _b.state) === "fail") {
2624
- stats2.failures = 1;
2625
- tasks.push({
2626
- id: file.id,
2627
- type: "test",
2628
- name: file.name,
2629
- mode: "run",
2630
- result: file.result,
2631
- meta: {},
2632
- // NOTE: not used in JUnitReporter
2633
- context: null,
2634
- suite: null
2635
- });
2636
- }
2637
- return {
2638
- ...file,
2639
- tasks,
2640
- stats: stats2
2641
- };
2642
- });
2643
- const stats = transformed.reduce((stats2, file) => {
2644
- stats2.tests += file.tasks.length;
2645
- stats2.failures += file.stats.failures;
2646
- return stats2;
2647
- }, {
2648
- // TODO: v2.0.0 Remove env variable in favor of custom reporter options, e.g. "reporters: [['json', { suiteName: 'something' }]]"
2649
- name: this.options.suiteName || process.env.VITEST_JUNIT_SUITE_NAME || "vitest tests",
2650
- tests: 0,
2651
- failures: 0,
2652
- errors: 0,
2653
- // we cannot detect those
2654
- time: executionTime((/* @__PURE__ */ new Date()).getTime() - this._timeStart.getTime())
2655
- });
2656
- await this.writeElement("testsuites", stats, async () => {
2657
- for (const file of transformed) {
2658
- await this.writeElement("testsuite", {
2659
- name: relative(this.ctx.config.root, file.filepath),
2660
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2661
- hostname: hostname(),
2662
- tests: file.tasks.length,
2663
- failures: file.stats.failures,
2664
- errors: 0,
2665
- // An errored test is one that had an unanticipated problem. We cannot detect those.
2666
- skipped: file.stats.skipped,
2667
- time: getDuration(file)
2668
- }, async () => {
2669
- await this.writeTasks(file.tasks, file.name);
2670
- });
2671
- }
2672
- });
2673
- if (this.reportFile)
2674
- this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
2675
- await ((_a = this.fileFd) == null ? void 0 : _a.close());
2676
- this.fileFd = void 0;
2677
- }
2678
- }
2679
-
2680
- function flattenTasks(task, baseName = "") {
2681
- const base = baseName ? `${baseName} > ` : "";
2682
- if (task.type === "suite" && task.tasks.length > 0) {
2683
- return task.tasks.flatMap((child) => flattenTasks(child, `${base}${task.name}`));
2684
- } else {
2685
- return [{
2686
- ...task,
2687
- name: `${base}${task.name}`
2688
- }];
2689
- }
2690
- }
2691
- class TapFlatReporter extends TapReporter {
2692
- onInit(ctx) {
2693
- super.onInit(ctx);
2694
- }
2695
- async onFinished(files = this.ctx.state.getFiles()) {
2696
- this.ctx.logger.log("TAP version 13");
2697
- const flatTasks = files.flatMap((task) => flattenTasks(task));
2698
- this.logTasks(flatTasks);
2699
- }
2700
- }
2459
+ const ESC$1 = '\u001B[';
2460
+ const OSC = '\u001B]';
2461
+ const BEL = '\u0007';
2462
+ const SEP = ';';
2463
+ const isTerminalApp = process.env.TERM_PROGRAM === 'Apple_Terminal';
2701
2464
 
2702
- class HangingProcessReporter {
2703
- whyRunning;
2704
- onInit() {
2705
- const _require = createRequire(import.meta.url);
2706
- this.whyRunning = _require("why-is-node-running");
2707
- }
2708
- onProcessTimeout() {
2709
- var _a;
2710
- (_a = this.whyRunning) == null ? void 0 : _a.call(this);
2711
- }
2712
- }
2713
-
2714
- async function printError(error, project, options) {
2715
- const { showCodeFrame = true, fullStack = false, type } = options;
2716
- const logger = options.logger;
2717
- let e = error;
2718
- if (isPrimitive(e)) {
2719
- e = {
2720
- message: String(error).split(/\n/g)[0],
2721
- stack: String(error)
2722
- };
2723
- }
2724
- if (!e) {
2725
- const error2 = new Error("unknown error");
2726
- e = {
2727
- message: e ?? error2.message,
2728
- stack: error2.stack
2729
- };
2730
- }
2731
- if (!project) {
2732
- printErrorMessage(e, logger);
2733
- return;
2734
- }
2735
- const parserOptions = {
2736
- // only browser stack traces require remapping
2737
- getSourceMap: (file) => project.getBrowserSourceMapModuleById(file),
2738
- frameFilter: project.config.onStackTrace
2739
- };
2740
- if (fullStack)
2741
- parserOptions.ignoreStackEntries = [];
2742
- const stacks = parseErrorStacktrace(e, parserOptions);
2743
- const nearest = error instanceof TypeCheckError ? error.stacks[0] : stacks.find(
2744
- (stack) => {
2745
- try {
2746
- return project.server && project.getModuleById(stack.file) && existsSync(stack.file);
2747
- } catch {
2748
- return false;
2749
- }
2750
- }
2751
- );
2752
- const errorProperties = getErrorProperties(e);
2753
- if (type)
2754
- printErrorType(type, project.ctx);
2755
- printErrorMessage(e, logger);
2756
- if (e.codeFrame)
2757
- logger.error(`${e.codeFrame}
2758
- `);
2759
- if (e.diff)
2760
- displayDiff(e.diff, logger.console);
2761
- if (e.frame) {
2762
- logger.error(c.yellow(e.frame));
2763
- } else {
2764
- printStack(logger, project, stacks, nearest, errorProperties, (s) => {
2765
- if (showCodeFrame && s === nearest && nearest) {
2766
- const sourceCode = readFileSync(nearest.file, "utf-8");
2767
- logger.error(generateCodeFrame(sourceCode.length > 1e5 ? sourceCode : logger.highlight(nearest.file, sourceCode), 4, s));
2768
- }
2769
- });
2770
- }
2771
- const testPath = e.VITEST_TEST_PATH;
2772
- const testName = e.VITEST_TEST_NAME;
2773
- const afterEnvTeardown = e.VITEST_AFTER_ENV_TEARDOWN;
2774
- if (testPath)
2775
- logger.error(c.red(`This error originated in "${c.bold(testPath)}" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.`));
2776
- if (testName) {
2777
- logger.error(c.red(`The latest test that might've caused the error is "${c.bold(testName)}". It might mean one of the following:
2778
- - The error was thrown, while Vitest was running this test.
2779
- - If the error occurred after the test had been completed, this was the last documented test before it was thrown.`));
2780
- }
2781
- if (afterEnvTeardown) {
2782
- logger.error(c.red("This error was caught after test environment was torn down. Make sure to cancel any running tasks before test finishes:\n- cancel timeouts using clearTimeout and clearInterval\n- wait for promises to resolve using the await keyword"));
2783
- }
2784
- if (typeof e.cause === "object" && e.cause && "name" in e.cause) {
2785
- e.cause.name = `Caused by: ${e.cause.name}`;
2786
- await printError(e.cause, project, { fullStack, showCodeFrame: false, logger: options.logger });
2787
- }
2788
- handleImportOutsideModuleError(e.stack || e.stackStr || "", logger);
2789
- return { nearest };
2790
- }
2791
- function printErrorType(type, ctx) {
2792
- ctx.logger.error(`
2793
- ${c.red(divider(c.bold(c.inverse(` ${type} `))))}`);
2794
- }
2795
- const skipErrorProperties = /* @__PURE__ */ new Set([
2796
- "nameStr",
2797
- "stack",
2798
- "cause",
2799
- "stacks",
2800
- "stackStr",
2801
- "type",
2802
- "showDiff",
2803
- "diff",
2804
- "codeFrame",
2805
- "actual",
2806
- "expected",
2807
- "diffOptions",
2808
- "VITEST_TEST_NAME",
2809
- "VITEST_TEST_PATH",
2810
- "VITEST_AFTER_ENV_TEARDOWN",
2811
- ...Object.getOwnPropertyNames(Error.prototype),
2812
- ...Object.getOwnPropertyNames(Object.prototype)
2813
- ]);
2814
- function getErrorProperties(e) {
2815
- const errorObject = /* @__PURE__ */ Object.create(null);
2816
- if (e.name === "AssertionError")
2817
- return errorObject;
2818
- for (const key of Object.getOwnPropertyNames(e)) {
2819
- if (!skipErrorProperties.has(key))
2820
- errorObject[key] = e[key];
2821
- }
2822
- return errorObject;
2823
- }
2824
- const esmErrors = [
2825
- "Cannot use import statement outside a module",
2826
- "Unexpected token 'export'"
2827
- ];
2828
- function handleImportOutsideModuleError(stack, logger) {
2829
- if (!esmErrors.some((e) => stack.includes(e)))
2830
- return;
2831
- const path = normalize(stack.split("\n")[0].trim());
2832
- let name = path.split("/node_modules/").pop() || "";
2833
- if (name == null ? void 0 : name.startsWith("@"))
2834
- name = name.split("/").slice(0, 2).join("/");
2835
- else
2836
- name = name.split("/")[0];
2837
- if (name)
2838
- printModuleWarningForPackage(logger, path, name);
2839
- else
2840
- printModuleWarningForSourceCode(logger, path);
2841
- }
2842
- function printModuleWarningForPackage(logger, path, name) {
2843
- logger.error(c.yellow(
2844
- `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.
2845
-
2846
- As a temporary workaround you can try to inline the package by updating your config:
2847
-
2848
- ` + c.gray(c.dim("// vitest.config.js")) + "\n" + c.green(`export default {
2849
- test: {
2850
- server: {
2851
- deps: {
2852
- inline: [
2853
- ${c.yellow(c.bold(`"${name}"`))}
2854
- ]
2855
- }
2856
- }
2857
- }
2858
- }
2859
- `)
2860
- ));
2861
- }
2862
- function printModuleWarningForSourceCode(logger, path) {
2863
- logger.error(c.yellow(
2864
- `Module ${path} seems to be an ES Module but shipped in a CommonJS package. To fix this issue, change the file extension to .mjs or add "type": "module" in your package.json.`
2865
- ));
2866
- }
2867
- function displayDiff(diff, console) {
2868
- if (diff)
2869
- console.error(`
2870
- ${diff}
2871
- `);
2872
- }
2873
- function printErrorMessage(error, logger) {
2874
- const errorName = error.name || error.nameStr || "Unknown Error";
2875
- if (!error.message) {
2876
- logger.error(error);
2877
- return;
2878
- }
2879
- if (error.message.length > 5e3) {
2880
- logger.error(`${c.red(c.bold(errorName))}: ${error.message}`);
2881
- } else {
2882
- logger.error(c.red(`${c.bold(errorName)}: ${error.message}`));
2883
- }
2884
- }
2885
- function printStack(logger, project, stack, highlight, errorProperties, onStack) {
2886
- for (const frame of stack) {
2887
- const color = frame === highlight ? c.cyan : c.gray;
2888
- const path = relative(project.config.root, frame.file);
2889
- logger.error(color(` ${c.dim(F_POINTER)} ${[frame.method, `${path}:${c.dim(`${frame.line}:${frame.column}`)}`].filter(Boolean).join(" ")}`));
2890
- onStack == null ? void 0 : onStack(frame);
2891
- }
2892
- if (stack.length)
2893
- logger.error();
2894
- const hasProperties = Object.keys(errorProperties).length > 0;
2895
- if (hasProperties) {
2896
- logger.error(c.red(c.dim(divider())));
2897
- const propertiesString = inspect(errorProperties);
2898
- logger.error(c.red(c.bold("Serialized Error:")), c.gray(propertiesString));
2899
- }
2900
- }
2901
- function generateCodeFrame(source, indent = 0, loc, range = 2) {
2902
- var _a;
2903
- const start = typeof loc === "object" ? positionToOffset(source, loc.line, loc.column) : loc;
2904
- const end = start;
2905
- const lines = source.split(lineSplitRE);
2906
- const nl = /\r\n/.test(source) ? 2 : 1;
2907
- let count = 0;
2908
- let res = [];
2909
- const columns = ((_a = process.stdout) == null ? void 0 : _a.columns) || 80;
2910
- function lineNo(no = "") {
2911
- return c.gray(`${String(no).padStart(3, " ")}| `);
2912
- }
2913
- for (let i = 0; i < lines.length; i++) {
2914
- count += lines[i].length + nl;
2915
- if (count >= start) {
2916
- for (let j = i - range; j <= i + range || end > count; j++) {
2917
- if (j < 0 || j >= lines.length)
2918
- continue;
2919
- const lineLength = lines[j].length;
2920
- if (stripAnsi(lines[j]).length > 200)
2921
- return "";
2922
- res.push(lineNo(j + 1) + cliTruncate(lines[j].replace(/\t/g, " "), columns - 5 - indent));
2923
- if (j === i) {
2924
- const pad = start - (count - lineLength) + (nl - 1);
2925
- const length = Math.max(1, end > count ? lineLength - pad : end - start);
2926
- res.push(lineNo() + " ".repeat(pad) + c.red("^".repeat(length)));
2927
- } else if (j > i) {
2928
- if (end > count) {
2929
- const length = Math.max(1, Math.min(end - count, lineLength));
2930
- res.push(lineNo() + c.red("^".repeat(length)));
2931
- }
2932
- count += lineLength + 1;
2933
- }
2934
- }
2935
- break;
2936
- }
2937
- }
2938
- if (indent)
2939
- res = res.map((line) => " ".repeat(indent) + line);
2940
- return res.join("\n");
2941
- }
2942
-
2943
- const ESC$1 = '\u001B[';
2944
- const OSC = '\u001B]';
2945
- const BEL = '\u0007';
2946
- const SEP = ';';
2947
- const isTerminalApp = process.env.TERM_PROGRAM === 'Apple_Terminal';
2948
-
2949
- const ansiEscapes = {};
2465
+ const ansiEscapes = {};
2950
2466
 
2951
2467
  ansiEscapes.cursorTo = (x, y) => {
2952
2468
  if (typeof x !== 'number') {
@@ -4139,7 +3655,7 @@ createLogUpdate(process$2.stdout);
4139
3655
 
4140
3656
  createLogUpdate(process$2.stderr);
4141
3657
 
4142
- var version = "1.5.2";
3658
+ var version = "1.6.0";
4143
3659
 
4144
3660
  const HIGHLIGHT_SUPPORTED_EXTS = new Set(["js", "ts"].flatMap((lang) => [
4145
3661
  `.${lang}`,
@@ -4350,7 +3866,11 @@ No ${config.mode} files found, exiting with code 1`));
4350
3866
  }
4351
3867
  if (this.ctx.coverageProvider)
4352
3868
  this.log(c.dim(" Coverage enabled with ") + c.yellow(this.ctx.coverageProvider.name));
4353
- this.log();
3869
+ if (this.ctx.config.standalone)
3870
+ this.log(c.yellow(`
3871
+ Vitest is running in standalone mode. Edit a test file to rerun tests.`));
3872
+ else
3873
+ this.log();
4354
3874
  }
4355
3875
  async printUnhandledErrors(errors) {
4356
3876
  const errorMessage = c.red(c.bold(
@@ -4379,24 +3899,519 @@ Vitest found ${errors.length} error${errors.length > 1 ? "s" : ""} not related t
4379
3899
  }
4380
3900
  }
4381
3901
 
4382
- class GithubActionsReporter {
4383
- ctx = void 0;
4384
- onInit(ctx) {
4385
- this.ctx = ctx;
4386
- }
4387
- async onFinished(files = [], errors = []) {
4388
- var _a, _b;
4389
- const projectErrors = new Array();
4390
- for (const error of errors) {
4391
- projectErrors.push({
4392
- project: this.ctx.getCoreWorkspaceProject(),
4393
- title: "Unhandled error",
4394
- error
4395
- });
4396
- }
4397
- for (const file of files) {
4398
- const tasks = getTasks(file);
4399
- const project = this.ctx.getProjectByTaskId(file.id);
3902
+ async function captuerPrintError(error, ctx, project) {
3903
+ let output = "";
3904
+ const writable = new Writable({
3905
+ write(chunk, _encoding, callback) {
3906
+ output += String(chunk);
3907
+ callback();
3908
+ }
3909
+ });
3910
+ const result = await printError(error, project, {
3911
+ showCodeFrame: false,
3912
+ logger: new Logger(ctx, writable, writable)
3913
+ });
3914
+ return { nearest: result == null ? void 0 : result.nearest, output };
3915
+ }
3916
+ async function printError(error, project, options) {
3917
+ const { showCodeFrame = true, fullStack = false, type } = options;
3918
+ const logger = options.logger;
3919
+ let e = error;
3920
+ if (isPrimitive(e)) {
3921
+ e = {
3922
+ message: String(error).split(/\n/g)[0],
3923
+ stack: String(error)
3924
+ };
3925
+ }
3926
+ if (!e) {
3927
+ const error2 = new Error("unknown error");
3928
+ e = {
3929
+ message: e ?? error2.message,
3930
+ stack: error2.stack
3931
+ };
3932
+ }
3933
+ if (!project) {
3934
+ printErrorMessage(e, logger);
3935
+ return;
3936
+ }
3937
+ const parserOptions = {
3938
+ // only browser stack traces require remapping
3939
+ getSourceMap: (file) => project.getBrowserSourceMapModuleById(file),
3940
+ frameFilter: project.config.onStackTrace
3941
+ };
3942
+ if (fullStack)
3943
+ parserOptions.ignoreStackEntries = [];
3944
+ const stacks = parseErrorStacktrace(e, parserOptions);
3945
+ const nearest = error instanceof TypeCheckError ? error.stacks[0] : stacks.find(
3946
+ (stack) => {
3947
+ try {
3948
+ return project.server && project.getModuleById(stack.file) && existsSync(stack.file);
3949
+ } catch {
3950
+ return false;
3951
+ }
3952
+ }
3953
+ );
3954
+ const errorProperties = getErrorProperties(e);
3955
+ if (type)
3956
+ printErrorType(type, project.ctx);
3957
+ printErrorMessage(e, logger);
3958
+ if (e.codeFrame)
3959
+ logger.error(`${e.codeFrame}
3960
+ `);
3961
+ if (e.diff)
3962
+ displayDiff(e.diff, logger.console);
3963
+ if (e.frame) {
3964
+ logger.error(c.yellow(e.frame));
3965
+ } else {
3966
+ printStack(logger, project, stacks, nearest, errorProperties, (s) => {
3967
+ if (showCodeFrame && s === nearest && nearest) {
3968
+ const sourceCode = readFileSync(nearest.file, "utf-8");
3969
+ logger.error(generateCodeFrame(sourceCode.length > 1e5 ? sourceCode : logger.highlight(nearest.file, sourceCode), 4, s));
3970
+ }
3971
+ });
3972
+ }
3973
+ const testPath = e.VITEST_TEST_PATH;
3974
+ const testName = e.VITEST_TEST_NAME;
3975
+ const afterEnvTeardown = e.VITEST_AFTER_ENV_TEARDOWN;
3976
+ if (testPath)
3977
+ logger.error(c.red(`This error originated in "${c.bold(testPath)}" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.`));
3978
+ if (testName) {
3979
+ logger.error(c.red(`The latest test that might've caused the error is "${c.bold(testName)}". It might mean one of the following:
3980
+ - The error was thrown, while Vitest was running this test.
3981
+ - If the error occurred after the test had been completed, this was the last documented test before it was thrown.`));
3982
+ }
3983
+ if (afterEnvTeardown) {
3984
+ logger.error(c.red("This error was caught after test environment was torn down. Make sure to cancel any running tasks before test finishes:\n- cancel timeouts using clearTimeout and clearInterval\n- wait for promises to resolve using the await keyword"));
3985
+ }
3986
+ if (typeof e.cause === "object" && e.cause && "name" in e.cause) {
3987
+ e.cause.name = `Caused by: ${e.cause.name}`;
3988
+ await printError(e.cause, project, { fullStack, showCodeFrame: false, logger: options.logger });
3989
+ }
3990
+ handleImportOutsideModuleError(e.stack || e.stackStr || "", logger);
3991
+ return { nearest };
3992
+ }
3993
+ function printErrorType(type, ctx) {
3994
+ ctx.logger.error(`
3995
+ ${c.red(divider(c.bold(c.inverse(` ${type} `))))}`);
3996
+ }
3997
+ const skipErrorProperties = /* @__PURE__ */ new Set([
3998
+ "nameStr",
3999
+ "stack",
4000
+ "cause",
4001
+ "stacks",
4002
+ "stackStr",
4003
+ "type",
4004
+ "showDiff",
4005
+ "diff",
4006
+ "codeFrame",
4007
+ "actual",
4008
+ "expected",
4009
+ "diffOptions",
4010
+ "VITEST_TEST_NAME",
4011
+ "VITEST_TEST_PATH",
4012
+ "VITEST_AFTER_ENV_TEARDOWN",
4013
+ ...Object.getOwnPropertyNames(Error.prototype),
4014
+ ...Object.getOwnPropertyNames(Object.prototype)
4015
+ ]);
4016
+ function getErrorProperties(e) {
4017
+ const errorObject = /* @__PURE__ */ Object.create(null);
4018
+ if (e.name === "AssertionError")
4019
+ return errorObject;
4020
+ for (const key of Object.getOwnPropertyNames(e)) {
4021
+ if (!skipErrorProperties.has(key))
4022
+ errorObject[key] = e[key];
4023
+ }
4024
+ return errorObject;
4025
+ }
4026
+ const esmErrors = [
4027
+ "Cannot use import statement outside a module",
4028
+ "Unexpected token 'export'"
4029
+ ];
4030
+ function handleImportOutsideModuleError(stack, logger) {
4031
+ if (!esmErrors.some((e) => stack.includes(e)))
4032
+ return;
4033
+ const path = normalize(stack.split("\n")[0].trim());
4034
+ let name = path.split("/node_modules/").pop() || "";
4035
+ if (name == null ? void 0 : name.startsWith("@"))
4036
+ name = name.split("/").slice(0, 2).join("/");
4037
+ else
4038
+ name = name.split("/")[0];
4039
+ if (name)
4040
+ printModuleWarningForPackage(logger, path, name);
4041
+ else
4042
+ printModuleWarningForSourceCode(logger, path);
4043
+ }
4044
+ function printModuleWarningForPackage(logger, path, name) {
4045
+ logger.error(c.yellow(
4046
+ `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.
4047
+
4048
+ As a temporary workaround you can try to inline the package by updating your config:
4049
+
4050
+ ` + c.gray(c.dim("// vitest.config.js")) + "\n" + c.green(`export default {
4051
+ test: {
4052
+ server: {
4053
+ deps: {
4054
+ inline: [
4055
+ ${c.yellow(c.bold(`"${name}"`))}
4056
+ ]
4057
+ }
4058
+ }
4059
+ }
4060
+ }
4061
+ `)
4062
+ ));
4063
+ }
4064
+ function printModuleWarningForSourceCode(logger, path) {
4065
+ logger.error(c.yellow(
4066
+ `Module ${path} seems to be an ES Module but shipped in a CommonJS package. To fix this issue, change the file extension to .mjs or add "type": "module" in your package.json.`
4067
+ ));
4068
+ }
4069
+ function displayDiff(diff, console) {
4070
+ if (diff)
4071
+ console.error(`
4072
+ ${diff}
4073
+ `);
4074
+ }
4075
+ function printErrorMessage(error, logger) {
4076
+ const errorName = error.name || error.nameStr || "Unknown Error";
4077
+ if (!error.message) {
4078
+ logger.error(error);
4079
+ return;
4080
+ }
4081
+ if (error.message.length > 5e3) {
4082
+ logger.error(`${c.red(c.bold(errorName))}: ${error.message}`);
4083
+ } else {
4084
+ logger.error(c.red(`${c.bold(errorName)}: ${error.message}`));
4085
+ }
4086
+ }
4087
+ function printStack(logger, project, stack, highlight, errorProperties, onStack) {
4088
+ for (const frame of stack) {
4089
+ const color = frame === highlight ? c.cyan : c.gray;
4090
+ const path = relative(project.config.root, frame.file);
4091
+ logger.error(color(` ${c.dim(F_POINTER)} ${[frame.method, `${path}:${c.dim(`${frame.line}:${frame.column}`)}`].filter(Boolean).join(" ")}`));
4092
+ onStack == null ? void 0 : onStack(frame);
4093
+ }
4094
+ if (stack.length)
4095
+ logger.error();
4096
+ const hasProperties = Object.keys(errorProperties).length > 0;
4097
+ if (hasProperties) {
4098
+ logger.error(c.red(c.dim(divider())));
4099
+ const propertiesString = inspect(errorProperties);
4100
+ logger.error(c.red(c.bold("Serialized Error:")), c.gray(propertiesString));
4101
+ }
4102
+ }
4103
+ function generateCodeFrame(source, indent = 0, loc, range = 2) {
4104
+ var _a;
4105
+ const start = typeof loc === "object" ? positionToOffset(source, loc.line, loc.column) : loc;
4106
+ const end = start;
4107
+ const lines = source.split(lineSplitRE);
4108
+ const nl = /\r\n/.test(source) ? 2 : 1;
4109
+ let count = 0;
4110
+ let res = [];
4111
+ const columns = ((_a = process.stdout) == null ? void 0 : _a.columns) || 80;
4112
+ function lineNo(no = "") {
4113
+ return c.gray(`${String(no).padStart(3, " ")}| `);
4114
+ }
4115
+ for (let i = 0; i < lines.length; i++) {
4116
+ count += lines[i].length + nl;
4117
+ if (count >= start) {
4118
+ for (let j = i - range; j <= i + range || end > count; j++) {
4119
+ if (j < 0 || j >= lines.length)
4120
+ continue;
4121
+ const lineLength = lines[j].length;
4122
+ if (stripAnsi(lines[j]).length > 200)
4123
+ return "";
4124
+ res.push(lineNo(j + 1) + cliTruncate(lines[j].replace(/\t/g, " "), columns - 5 - indent));
4125
+ if (j === i) {
4126
+ const pad = start - (count - lineLength) + (nl - 1);
4127
+ const length = Math.max(1, end > count ? lineLength - pad : end - start);
4128
+ res.push(lineNo() + " ".repeat(pad) + c.red("^".repeat(length)));
4129
+ } else if (j > i) {
4130
+ if (end > count) {
4131
+ const length = Math.max(1, Math.min(end - count, lineLength));
4132
+ res.push(lineNo() + c.red("^".repeat(length)));
4133
+ }
4134
+ count += lineLength + 1;
4135
+ }
4136
+ }
4137
+ break;
4138
+ }
4139
+ }
4140
+ if (indent)
4141
+ res = res.map((line) => " ".repeat(indent) + line);
4142
+ return res.join("\n");
4143
+ }
4144
+
4145
+ function flattenTasks$1(task, baseName = "") {
4146
+ const base = baseName ? `${baseName} > ` : "";
4147
+ if (task.type === "suite") {
4148
+ return task.tasks.flatMap((child) => flattenTasks$1(child, `${base}${task.name}`));
4149
+ } else {
4150
+ return [{
4151
+ ...task,
4152
+ name: `${base}${task.name}`
4153
+ }];
4154
+ }
4155
+ }
4156
+ function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
4157
+ let regex = /((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))/g;
4158
+ value = String(value || "").replace(regex, "");
4159
+ if (removeDiscouragedChars) {
4160
+ regex = new RegExp(
4161
+ "([\\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]))",
4162
+ "g"
4163
+ );
4164
+ value = value.replace(regex, "");
4165
+ }
4166
+ return value;
4167
+ }
4168
+ function escapeXML(value) {
4169
+ return removeInvalidXMLCharacters(
4170
+ String(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;"),
4171
+ true
4172
+ );
4173
+ }
4174
+ function executionTime(durationMS) {
4175
+ return (durationMS / 1e3).toLocaleString("en-US", { useGrouping: false, maximumFractionDigits: 10 });
4176
+ }
4177
+ function getDuration(task) {
4178
+ var _a;
4179
+ const duration = ((_a = task.result) == null ? void 0 : _a.duration) ?? 0;
4180
+ return executionTime(duration);
4181
+ }
4182
+ class JUnitReporter {
4183
+ ctx;
4184
+ reportFile;
4185
+ baseLog;
4186
+ logger;
4187
+ _timeStart = /* @__PURE__ */ new Date();
4188
+ fileFd;
4189
+ options;
4190
+ constructor(options) {
4191
+ var _a;
4192
+ this.options = { ...options };
4193
+ (_a = this.options).includeConsoleOutput ?? (_a.includeConsoleOutput = true);
4194
+ }
4195
+ async onInit(ctx) {
4196
+ this.ctx = ctx;
4197
+ const outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "junit");
4198
+ if (outputFile) {
4199
+ this.reportFile = resolve(this.ctx.config.root, outputFile);
4200
+ const outputDirectory = dirname(this.reportFile);
4201
+ if (!existsSync(outputDirectory))
4202
+ await promises.mkdir(outputDirectory, { recursive: true });
4203
+ const fileFd = await promises.open(this.reportFile, "w+");
4204
+ this.fileFd = fileFd;
4205
+ this.baseLog = async (text) => {
4206
+ if (!this.fileFd)
4207
+ this.fileFd = await promises.open(this.reportFile, "w+");
4208
+ await promises.writeFile(this.fileFd, `${text}
4209
+ `);
4210
+ };
4211
+ } else {
4212
+ this.baseLog = async (text) => this.ctx.logger.log(text);
4213
+ }
4214
+ this._timeStart = /* @__PURE__ */ new Date();
4215
+ this.logger = new IndentedLogger(this.baseLog);
4216
+ }
4217
+ async writeElement(name, attrs, children) {
4218
+ const pairs = [];
4219
+ for (const key in attrs) {
4220
+ const attr = attrs[key];
4221
+ if (attr === void 0)
4222
+ continue;
4223
+ pairs.push(`${key}="${escapeXML(attr)}"`);
4224
+ }
4225
+ await this.logger.log(`<${name}${pairs.length ? ` ${pairs.join(" ")}` : ""}>`);
4226
+ this.logger.indent();
4227
+ await children.call(this);
4228
+ this.logger.unindent();
4229
+ await this.logger.log(`</${name}>`);
4230
+ }
4231
+ async writeLogs(task, type) {
4232
+ if (task.logs == null || task.logs.length === 0)
4233
+ return;
4234
+ const logType = type === "err" ? "stderr" : "stdout";
4235
+ const logs = task.logs.filter((log) => log.type === logType);
4236
+ if (logs.length === 0)
4237
+ return;
4238
+ await this.writeElement(`system-${type}`, {}, async () => {
4239
+ for (const log of logs)
4240
+ await this.baseLog(escapeXML(log.content));
4241
+ });
4242
+ }
4243
+ async writeTasks(tasks, filename) {
4244
+ for (const task of tasks) {
4245
+ await this.writeElement("testcase", {
4246
+ // TODO: v2.0.0 Remove env variable in favor of custom reporter options, e.g. "reporters: [['json', { classname: 'something' }]]"
4247
+ classname: this.options.classname ?? process.env.VITEST_JUNIT_CLASSNAME ?? filename,
4248
+ file: this.options.addFileAttribute ? filename : void 0,
4249
+ name: task.name,
4250
+ time: getDuration(task)
4251
+ }, async () => {
4252
+ var _a;
4253
+ if (this.options.includeConsoleOutput) {
4254
+ await this.writeLogs(task, "out");
4255
+ await this.writeLogs(task, "err");
4256
+ }
4257
+ if (task.mode === "skip" || task.mode === "todo")
4258
+ await this.logger.log("<skipped/>");
4259
+ if (((_a = task.result) == null ? void 0 : _a.state) === "fail") {
4260
+ const errors = task.result.errors || [];
4261
+ for (const error of errors) {
4262
+ await this.writeElement("failure", {
4263
+ message: error == null ? void 0 : error.message,
4264
+ type: (error == null ? void 0 : error.name) ?? (error == null ? void 0 : error.nameStr)
4265
+ }, async () => {
4266
+ if (!error)
4267
+ return;
4268
+ const result = await captuerPrintError(
4269
+ error,
4270
+ this.ctx,
4271
+ this.ctx.getProjectByTaskId(task.id)
4272
+ );
4273
+ await this.baseLog(escapeXML(stripAnsi(result.output.trim())));
4274
+ });
4275
+ }
4276
+ }
4277
+ });
4278
+ }
4279
+ }
4280
+ async onFinished(files = this.ctx.state.getFiles()) {
4281
+ var _a;
4282
+ await this.logger.log('<?xml version="1.0" encoding="UTF-8" ?>');
4283
+ const transformed = files.map((file) => {
4284
+ var _a2, _b;
4285
+ const tasks = file.tasks.flatMap((task) => flattenTasks$1(task));
4286
+ const stats2 = tasks.reduce((stats3, task) => {
4287
+ var _a3, _b2;
4288
+ return {
4289
+ passed: stats3.passed + Number(((_a3 = task.result) == null ? void 0 : _a3.state) === "pass"),
4290
+ failures: stats3.failures + Number(((_b2 = task.result) == null ? void 0 : _b2.state) === "fail"),
4291
+ skipped: stats3.skipped + Number(task.mode === "skip" || task.mode === "todo")
4292
+ };
4293
+ }, {
4294
+ passed: 0,
4295
+ failures: 0,
4296
+ skipped: 0
4297
+ });
4298
+ const suites = getSuites(file);
4299
+ for (const suite of suites) {
4300
+ if ((_a2 = suite.result) == null ? void 0 : _a2.errors) {
4301
+ tasks.push(suite);
4302
+ stats2.failures += 1;
4303
+ }
4304
+ }
4305
+ if (tasks.length === 0 && ((_b = file.result) == null ? void 0 : _b.state) === "fail") {
4306
+ stats2.failures = 1;
4307
+ tasks.push({
4308
+ id: file.id,
4309
+ type: "test",
4310
+ name: file.name,
4311
+ mode: "run",
4312
+ result: file.result,
4313
+ meta: {},
4314
+ // NOTE: not used in JUnitReporter
4315
+ context: null,
4316
+ suite: null
4317
+ });
4318
+ }
4319
+ return {
4320
+ ...file,
4321
+ tasks,
4322
+ stats: stats2
4323
+ };
4324
+ });
4325
+ const stats = transformed.reduce((stats2, file) => {
4326
+ stats2.tests += file.tasks.length;
4327
+ stats2.failures += file.stats.failures;
4328
+ return stats2;
4329
+ }, {
4330
+ // TODO: v2.0.0 Remove env variable in favor of custom reporter options, e.g. "reporters: [['json', { suiteName: 'something' }]]"
4331
+ name: this.options.suiteName || process.env.VITEST_JUNIT_SUITE_NAME || "vitest tests",
4332
+ tests: 0,
4333
+ failures: 0,
4334
+ errors: 0,
4335
+ // we cannot detect those
4336
+ time: executionTime((/* @__PURE__ */ new Date()).getTime() - this._timeStart.getTime())
4337
+ });
4338
+ await this.writeElement("testsuites", stats, async () => {
4339
+ for (const file of transformed) {
4340
+ const filename = relative(this.ctx.config.root, file.filepath);
4341
+ await this.writeElement("testsuite", {
4342
+ name: filename,
4343
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4344
+ hostname: hostname(),
4345
+ tests: file.tasks.length,
4346
+ failures: file.stats.failures,
4347
+ errors: 0,
4348
+ // An errored test is one that had an unanticipated problem. We cannot detect those.
4349
+ skipped: file.stats.skipped,
4350
+ time: getDuration(file)
4351
+ }, async () => {
4352
+ await this.writeTasks(file.tasks, filename);
4353
+ });
4354
+ }
4355
+ });
4356
+ if (this.reportFile)
4357
+ this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
4358
+ await ((_a = this.fileFd) == null ? void 0 : _a.close());
4359
+ this.fileFd = void 0;
4360
+ }
4361
+ }
4362
+
4363
+ function flattenTasks(task, baseName = "") {
4364
+ const base = baseName ? `${baseName} > ` : "";
4365
+ if (task.type === "suite" && task.tasks.length > 0) {
4366
+ return task.tasks.flatMap((child) => flattenTasks(child, `${base}${task.name}`));
4367
+ } else {
4368
+ return [{
4369
+ ...task,
4370
+ name: `${base}${task.name}`
4371
+ }];
4372
+ }
4373
+ }
4374
+ class TapFlatReporter extends TapReporter {
4375
+ onInit(ctx) {
4376
+ super.onInit(ctx);
4377
+ }
4378
+ async onFinished(files = this.ctx.state.getFiles()) {
4379
+ this.ctx.logger.log("TAP version 13");
4380
+ const flatTasks = files.flatMap((task) => flattenTasks(task));
4381
+ this.logTasks(flatTasks);
4382
+ }
4383
+ }
4384
+
4385
+ class HangingProcessReporter {
4386
+ whyRunning;
4387
+ onInit() {
4388
+ const _require = createRequire(import.meta.url);
4389
+ this.whyRunning = _require("why-is-node-running");
4390
+ }
4391
+ onProcessTimeout() {
4392
+ var _a;
4393
+ (_a = this.whyRunning) == null ? void 0 : _a.call(this);
4394
+ }
4395
+ }
4396
+
4397
+ class GithubActionsReporter {
4398
+ ctx = void 0;
4399
+ onInit(ctx) {
4400
+ this.ctx = ctx;
4401
+ }
4402
+ async onFinished(files = [], errors = []) {
4403
+ var _a, _b;
4404
+ const projectErrors = new Array();
4405
+ for (const error of errors) {
4406
+ projectErrors.push({
4407
+ project: this.ctx.getCoreWorkspaceProject(),
4408
+ title: "Unhandled error",
4409
+ error
4410
+ });
4411
+ }
4412
+ for (const file of files) {
4413
+ const tasks = getTasks(file);
4414
+ const project = this.ctx.getProjectByTaskId(file.id);
4400
4415
  for (const task of tasks) {
4401
4416
  if (((_a = task.result) == null ? void 0 : _a.state) !== "fail")
4402
4417
  continue;
@@ -4411,7 +4426,7 @@ class GithubActionsReporter {
4411
4426
  }
4412
4427
  }
4413
4428
  for (const { project, title, error } of projectErrors) {
4414
- const result = await printErrorWrapper(error, this.ctx, project);
4429
+ const result = await captuerPrintError(error, this.ctx, project);
4415
4430
  const stack = result == null ? void 0 : result.nearest;
4416
4431
  if (!stack)
4417
4432
  continue;
@@ -4430,20 +4445,6 @@ ${formatted}`);
4430
4445
  }
4431
4446
  }
4432
4447
  }
4433
- async function printErrorWrapper(error, ctx, project) {
4434
- let output = "";
4435
- const writable = new Writable({
4436
- write(chunk, _encoding, callback) {
4437
- output += String(chunk);
4438
- callback();
4439
- }
4440
- });
4441
- const result = await printError(error, project, {
4442
- showCodeFrame: false,
4443
- logger: new Logger(ctx, writable, writable)
4444
- });
4445
- return { nearest: result == null ? void 0 : result.nearest, output };
4446
- }
4447
4448
  function formatMessage({
4448
4449
  command,
4449
4450
  properties,
@@ -4464,67 +4465,6 @@ function escapeProperty(s) {
4464
4465
  return s.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A").replace(/:/g, "%3A").replace(/,/g, "%2C");
4465
4466
  }
4466
4467
 
4467
- class JsonReporter {
4468
- start = 0;
4469
- ctx;
4470
- onInit(ctx) {
4471
- this.ctx = ctx;
4472
- }
4473
- async logTasks(files) {
4474
- var _a;
4475
- const suites = getSuites(files);
4476
- const numTotalTestSuites = suites.length;
4477
- const tests = getTests(files);
4478
- const numTotalTests = tests.length;
4479
- const testResults = {};
4480
- const outputFile = getOutputFile(this.ctx.config.benchmark, "json");
4481
- for (const file of files) {
4482
- const tests2 = getTests([file]);
4483
- for (const test of tests2) {
4484
- const res = (_a = test.result) == null ? void 0 : _a.benchmark;
4485
- if (!res || test.mode === "skip")
4486
- continue;
4487
- if (!outputFile)
4488
- res.samples = "ignore on terminal";
4489
- testResults[test.suite.name] = (testResults[test.suite.name] || []).concat(res);
4490
- }
4491
- if (tests2.some((t) => {
4492
- var _a2;
4493
- return ((_a2 = t.result) == null ? void 0 : _a2.state) === "run";
4494
- })) {
4495
- 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");
4496
- }
4497
- }
4498
- const result = {
4499
- numTotalTestSuites,
4500
- numTotalTests,
4501
- testResults
4502
- };
4503
- await this.writeReport(JSON.stringify(result, null, 2));
4504
- }
4505
- async onFinished(files = this.ctx.state.getFiles()) {
4506
- await this.logTasks(files);
4507
- }
4508
- /**
4509
- * Writes the report to an output file if specified in the config,
4510
- * or logs it to the console otherwise.
4511
- * @param report
4512
- */
4513
- async writeReport(report) {
4514
- const outputFile = getOutputFile(this.ctx.config.benchmark, "json");
4515
- if (outputFile) {
4516
- const reportFile = resolve(this.ctx.config.root, outputFile);
4517
- const outputDirectory = dirname(reportFile);
4518
- if (!existsSync(outputDirectory))
4519
- await promises.mkdir(outputDirectory, { recursive: true });
4520
- await promises.writeFile(reportFile, report, "utf-8");
4521
- this.ctx.logger.log(`json report written to ${reportFile}`);
4522
- } else {
4523
- this.ctx.logger.log(report);
4524
- }
4525
- }
4526
- }
4527
-
4528
4468
  const outputMap = /* @__PURE__ */ new WeakMap();
4529
4469
  function formatFilepath(path) {
4530
4470
  const lastSlash = Math.max(path.lastIndexOf("/") + 1, 0);
@@ -4540,17 +4480,6 @@ function formatNumber(number) {
4540
4480
  return res[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ",") + (res[1] ? `.${res[1]}` : "");
4541
4481
  }
4542
4482
  const tableHead = ["name", "hz", "min", "max", "mean", "p75", "p99", "p995", "p999", "rme", "samples"];
4543
- function renderTableHead(tasks) {
4544
- const benches = tasks.map((i) => {
4545
- var _a, _b;
4546
- return ((_a = i.meta) == null ? void 0 : _a.benchmark) ? (_b = i.result) == null ? void 0 : _b.benchmark : void 0;
4547
- }).filter(notNullish);
4548
- const allItems = benches.map(renderBenchmarkItems).concat([tableHead]);
4549
- return `${" ".repeat(3)}${tableHead.map((i, idx) => {
4550
- const width = Math.max(...allItems.map((i2) => i2[idx].length));
4551
- return idx ? i.padStart(width, " ") : i.padEnd(width, " ");
4552
- }).map(c.bold).join(" ")}`;
4553
- }
4554
4483
  function renderBenchmarkItems(result) {
4555
4484
  return [
4556
4485
  result.name,
@@ -4566,21 +4495,27 @@ function renderBenchmarkItems(result) {
4566
4495
  result.samples.length.toString()
4567
4496
  ];
4568
4497
  }
4569
- function renderBenchmark(task, tasks) {
4570
- var _a;
4571
- const result = (_a = task.result) == null ? void 0 : _a.benchmark;
4572
- if (!result)
4573
- return task.name;
4574
- const benches = tasks.map((i) => {
4575
- var _a2, _b;
4576
- return ((_a2 = i.meta) == null ? void 0 : _a2.benchmark) ? (_b = i.result) == null ? void 0 : _b.benchmark : void 0;
4577
- }).filter(notNullish);
4578
- const allItems = benches.map(renderBenchmarkItems).concat([tableHead]);
4579
- const items = renderBenchmarkItems(result);
4580
- const padded = items.map((i, idx) => {
4581
- const width = Math.max(...allItems.map((i2) => i2[idx].length));
4582
- return idx ? i.padStart(width, " ") : i.padEnd(width, " ");
4583
- });
4498
+ function computeColumnWidths(results) {
4499
+ const rows = [
4500
+ tableHead,
4501
+ ...results.map((v) => renderBenchmarkItems(v))
4502
+ ];
4503
+ return Array.from(
4504
+ tableHead,
4505
+ (_, i) => Math.max(...rows.map((row) => stripAnsi(row[i]).length))
4506
+ );
4507
+ }
4508
+ function padRow(row, widths) {
4509
+ return row.map(
4510
+ (v, i) => i ? v.padStart(widths[i], " ") : v.padEnd(widths[i], " ")
4511
+ // name
4512
+ );
4513
+ }
4514
+ function renderTableHead(widths) {
4515
+ return " ".repeat(3) + padRow(tableHead, widths).map(c.bold).join(" ");
4516
+ }
4517
+ function renderBenchmark(result, widths) {
4518
+ const padded = padRow(renderBenchmarkItems(result), widths);
4584
4519
  return [
4585
4520
  padded[0],
4586
4521
  // name
@@ -4602,20 +4537,38 @@ function renderBenchmark(task, tasks) {
4602
4537
  // p999
4603
4538
  c.dim(padded[9]),
4604
4539
  // rem
4605
- c.dim(padded[10]),
4540
+ c.dim(padded[10])
4606
4541
  // sample
4607
- result.rank === 1 ? c.bold(c.green(" fastest")) : result.rank === benches.length && benches.length > 2 ? c.bold(c.gray(" slowest")) : ""
4608
4542
  ].join(" ");
4609
4543
  }
4610
4544
  function renderTree(tasks, options, level = 0, shallow = false) {
4611
- var _a, _b, _c, _d, _e, _f;
4545
+ var _a, _b, _c, _d, _e, _f, _g;
4612
4546
  const output = [];
4547
+ const benchMap = {};
4548
+ for (const t of tasks) {
4549
+ if (t.meta.benchmark && ((_a = t.result) == null ? void 0 : _a.benchmark)) {
4550
+ benchMap[t.id] = {
4551
+ current: t.result.benchmark
4552
+ };
4553
+ const baseline = (_b = options.compare) == null ? void 0 : _b[t.id];
4554
+ if (baseline) {
4555
+ benchMap[t.id].baseline = {
4556
+ ...baseline,
4557
+ samples: Array(baseline.sampleCount)
4558
+ };
4559
+ }
4560
+ }
4561
+ }
4562
+ const benchCount = Object.entries(benchMap).length;
4563
+ const columnWidths = computeColumnWidths(
4564
+ Object.values(benchMap).flatMap((v) => [v.current, v.baseline]).filter(notNullish)
4565
+ );
4613
4566
  let idx = 0;
4614
4567
  for (const task of tasks) {
4615
4568
  const padding = " ".repeat(level ? 1 : 0);
4616
4569
  let prefix = "";
4617
- if (idx === 0 && ((_a = task.meta) == null ? void 0 : _a.benchmark))
4618
- prefix += `${renderTableHead(tasks)}
4570
+ if (idx === 0 && ((_c = task.meta) == null ? void 0 : _c.benchmark))
4571
+ prefix += `${renderTableHead(columnWidths)}
4619
4572
  ${padding}`;
4620
4573
  prefix += ` ${getStateSymbol(task)} `;
4621
4574
  let suffix = "";
@@ -4623,18 +4576,43 @@ ${padding}`;
4623
4576
  suffix += c.dim(` (${getTests(task).length})`);
4624
4577
  if (task.mode === "skip" || task.mode === "todo")
4625
4578
  suffix += ` ${c.dim(c.gray("[skipped]"))}`;
4626
- if (((_b = task.result) == null ? void 0 : _b.duration) != null) {
4579
+ if (((_d = task.result) == null ? void 0 : _d.duration) != null) {
4627
4580
  if (task.result.duration > options.slowTestThreshold)
4628
4581
  suffix += c.yellow(` ${Math.round(task.result.duration)}${c.dim("ms")}`);
4629
4582
  }
4630
- if (options.showHeap && ((_c = task.result) == null ? void 0 : _c.heap) != null)
4583
+ if (options.showHeap && ((_e = task.result) == null ? void 0 : _e.heap) != null)
4631
4584
  suffix += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`);
4632
4585
  let name = task.name;
4633
4586
  if (level === 0)
4634
4587
  name = formatFilepath(name);
4635
- const body = ((_d = task.meta) == null ? void 0 : _d.benchmark) ? renderBenchmark(task, tasks) : name;
4636
- output.push(padding + prefix + body + suffix);
4637
- if (((_e = task.result) == null ? void 0 : _e.state) !== "pass" && outputMap.get(task) != null) {
4588
+ const bench = benchMap[task.id];
4589
+ if (bench) {
4590
+ let body = renderBenchmark(bench.current, columnWidths);
4591
+ if (options.compare && bench.baseline) {
4592
+ if (bench.current.hz) {
4593
+ const diff = bench.current.hz / bench.baseline.hz;
4594
+ const diffFixed = diff.toFixed(2);
4595
+ if (diffFixed === "1.0.0")
4596
+ body += ` ${c.gray(`[${diffFixed}x]`)}`;
4597
+ if (diff > 1)
4598
+ body += ` ${c.blue(`[${diffFixed}x] \u21D1`)}`;
4599
+ else
4600
+ body += ` ${c.red(`[${diffFixed}x] \u21D3`)}`;
4601
+ }
4602
+ output.push(padding + prefix + body + suffix);
4603
+ const bodyBaseline = renderBenchmark(bench.baseline, columnWidths);
4604
+ output.push(`${padding} ${bodyBaseline} ${c.dim("(baseline)")}`);
4605
+ } else {
4606
+ if (bench.current.rank === 1 && benchCount > 1)
4607
+ body += ` ${c.bold(c.green(" fastest"))}`;
4608
+ if (bench.current.rank === benchCount && benchCount > 2)
4609
+ body += ` ${c.bold(c.gray(" slowest"))}`;
4610
+ output.push(padding + prefix + body + suffix);
4611
+ }
4612
+ } else {
4613
+ output.push(padding + prefix + name + suffix);
4614
+ }
4615
+ if (((_f = task.result) == null ? void 0 : _f.state) !== "pass" && outputMap.get(task) != null) {
4638
4616
  let data = outputMap.get(task);
4639
4617
  if (typeof data === "string") {
4640
4618
  data = stripAnsi(data.trim().split("\n").filter(Boolean).pop());
@@ -4647,7 +4625,7 @@ ${padding}`;
4647
4625
  }
4648
4626
  }
4649
4627
  if (!shallow && task.type === "suite" && task.tasks.length > 0) {
4650
- if ((_f = task.result) == null ? void 0 : _f.state)
4628
+ if ((_g = task.result) == null ? void 0 : _g.state)
4651
4629
  output.push(renderTree(task.tasks, options, level + 1));
4652
4630
  }
4653
4631
  idx++;
@@ -4701,11 +4679,24 @@ class TableReporter extends BaseReporter {
4701
4679
  await super.reportSummary(files, this.ctx.state.getUnhandledErrors());
4702
4680
  super.onWatcherStart();
4703
4681
  }
4704
- onCollected() {
4682
+ async onCollected() {
4683
+ var _a, _b;
4684
+ this.rendererOptions.logger = this.ctx.logger;
4685
+ this.rendererOptions.showHeap = this.ctx.config.logHeapUsage;
4686
+ this.rendererOptions.slowTestThreshold = this.ctx.config.slowTestThreshold;
4687
+ if ((_a = this.ctx.config.benchmark) == null ? void 0 : _a.compare) {
4688
+ const compareFile = pathe.resolve(this.ctx.config.root, (_b = this.ctx.config.benchmark) == null ? void 0 : _b.compare);
4689
+ try {
4690
+ this.rendererOptions.compare = flattenFormattedBenchamrkReport(
4691
+ JSON.parse(
4692
+ await fs.promises.readFile(compareFile, "utf-8")
4693
+ )
4694
+ );
4695
+ } catch (e) {
4696
+ this.ctx.logger.error(`Failed to read '${compareFile}'`, e);
4697
+ }
4698
+ }
4705
4699
  if (this.isTTY) {
4706
- this.rendererOptions.logger = this.ctx.logger;
4707
- this.rendererOptions.showHeap = this.ctx.config.logHeapUsage;
4708
- this.rendererOptions.slowTestThreshold = this.ctx.config.slowTestThreshold;
4709
4700
  const files = this.ctx.state.getFiles(this.watchFilters);
4710
4701
  if (!this.renderer)
4711
4702
  this.renderer = createTableRenderer(files, this.rendererOptions).start();
@@ -4735,9 +4726,20 @@ class TableReporter extends BaseReporter {
4735
4726
  }
4736
4727
  }
4737
4728
  async onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
4729
+ var _a;
4738
4730
  await this.stopListRender();
4739
4731
  this.ctx.logger.log();
4740
4732
  await super.onFinished(files, errors);
4733
+ let outputFile = (_a = this.ctx.config.benchmark) == null ? void 0 : _a.outputJson;
4734
+ if (outputFile) {
4735
+ outputFile = pathe.resolve(this.ctx.config.root, outputFile);
4736
+ const outputDirectory = pathe.dirname(outputFile);
4737
+ if (!fs.existsSync(outputDirectory))
4738
+ await fs.promises.mkdir(outputDirectory, { recursive: true });
4739
+ const output = createFormattedBenchamrkReport(files);
4740
+ await fs.promises.writeFile(outputFile, JSON.stringify(output, null, 2));
4741
+ this.ctx.logger.log(`Benchmark report written to ${outputFile}`);
4742
+ }
4741
4743
  }
4742
4744
  async onWatcherStart() {
4743
4745
  await this.stopListRender();
@@ -4760,11 +4762,54 @@ class TableReporter extends BaseReporter {
4760
4762
  super.onUserConsoleLog(log);
4761
4763
  }
4762
4764
  }
4765
+ function createFormattedBenchamrkReport(files) {
4766
+ var _a;
4767
+ const report = { files: [] };
4768
+ for (const file of files) {
4769
+ const groups = [];
4770
+ for (const task of getTasks(file)) {
4771
+ if (task && task.type === "suite") {
4772
+ const benchmarks = [];
4773
+ for (const t of task.tasks) {
4774
+ const benchmark = t.meta.benchmark && ((_a = t.result) == null ? void 0 : _a.benchmark);
4775
+ if (benchmark) {
4776
+ const { samples, ...rest } = benchmark;
4777
+ benchmarks.push({
4778
+ id: t.id,
4779
+ sampleCount: samples.length,
4780
+ ...rest
4781
+ });
4782
+ }
4783
+ }
4784
+ if (benchmarks.length) {
4785
+ groups.push({
4786
+ fullName: getFullName(task, " > "),
4787
+ benchmarks
4788
+ });
4789
+ }
4790
+ }
4791
+ }
4792
+ report.files.push({
4793
+ filepath: file.filepath,
4794
+ groups
4795
+ });
4796
+ }
4797
+ return report;
4798
+ }
4799
+ function flattenFormattedBenchamrkReport(report) {
4800
+ const flat = {};
4801
+ for (const file of report.files) {
4802
+ for (const group of file.groups) {
4803
+ for (const t of group.benchmarks)
4804
+ flat[t.id] = t;
4805
+ }
4806
+ }
4807
+ return flat;
4808
+ }
4763
4809
 
4764
4810
  const BenchmarkReportsMap = {
4765
4811
  default: TableReporter,
4766
- verbose: VerboseReporter,
4767
- json: JsonReporter
4812
+ verbose: VerboseReporter
4768
4813
  };
4769
4814
 
4770
4815
  const ReportersMap = {
@@ -4772,7 +4817,7 @@ const ReportersMap = {
4772
4817
  "basic": BasicReporter,
4773
4818
  "verbose": VerboseReporter,
4774
4819
  "dot": DotReporter,
4775
- "json": JsonReporter$1,
4820
+ "json": JsonReporter,
4776
4821
  "tap": TapReporter,
4777
4822
  "tap-flat": TapFlatReporter,
4778
4823
  "junit": JUnitReporter,
@@ -4780,4 +4825,4 @@ const ReportersMap = {
4780
4825
  "github-actions": GithubActionsReporter
4781
4826
  };
4782
4827
 
4783
- export { BaseSequencer as B, DefaultReporter as D, GithubActionsReporter as G, HangingProcessReporter as H, JsonReporter$1 as J, Logger as L, ReportersMap as R, TapReporter as T, VerboseReporter as V, BasicReporter as a, DotReporter as b, JUnitReporter as c, TapFlatReporter as d, BenchmarkReportsMap as e, Typechecker as f, RandomSequencer as g, generateCodeFrame as h, highlightCode as i, wrapSerializableConfig as w };
4828
+ export { BaseSequencer as B, DefaultReporter as D, GithubActionsReporter as G, HangingProcessReporter as H, JsonReporter as J, Logger as L, ReportersMap as R, TapReporter as T, VerboseReporter as V, BasicReporter as a, DotReporter as b, JUnitReporter as c, TapFlatReporter as d, BenchmarkReportsMap as e, Typechecker as f, RandomSequencer as g, generateCodeFrame as h, highlightCode as i, wrapSerializableConfig as w };