vitest 4.0.0-beta.9 → 4.0.1

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 (83) hide show
  1. package/LICENSE.md +86 -102
  2. package/browser/context.d.ts +7 -0
  3. package/browser/context.js +20 -0
  4. package/dist/browser.d.ts +24 -7
  5. package/dist/browser.js +15 -5
  6. package/dist/chunks/{base.CA5N8Af0.js → base.BYPMk0VN.js} +36 -36
  7. package/dist/chunks/{benchmark.CJUa-Hsa.js → benchmark.DHKMYAts.js} +2 -2
  8. package/dist/chunks/{browser.d.DtfyY9yS.d.ts → browser.d.B9iJzZyn.d.ts} +3 -3
  9. package/dist/chunks/{cac.Dt7e1TIu.js → cac.DrF4Gm0S.js} +47 -73
  10. package/dist/chunks/{cli-api.eAzsLIxz.js → cli-api.W2Q-JQoO.js} +1524 -296
  11. package/dist/chunks/{config.d.DacWrqWe.d.ts → config.d.u2CUDWwS.d.ts} +5 -19
  12. package/dist/chunks/{console.7h5kHUIf.js → console.CTJL2nuH.js} +4 -6
  13. package/dist/chunks/{coverage.CDRAMTt7.js → coverage.FU3w4IrQ.js} +125 -1108
  14. package/dist/chunks/{creator.KEg6n5IC.js → creator.DucAaYBz.js} +10 -37
  15. package/dist/chunks/{defaults.CXFFjsi8.js → defaults.BOqNVLsY.js} +0 -1
  16. package/dist/chunks/environment.d.CrsxCzP1.d.ts +29 -0
  17. package/dist/chunks/evaluatedModules.Dg1zASAC.js +17 -0
  18. package/dist/chunks/{global.d.K6uBQHzY.d.ts → global.d.BgJSTpgQ.d.ts} +2 -17
  19. package/dist/chunks/{globals.CJrTTbxC.js → globals.BGT_RUsD.js} +11 -7
  20. package/dist/chunks/{index.BjKEiSn0.js → index.BdSLhLDZ.js} +3 -3
  21. package/dist/chunks/{index.DfviD7lX.js → index.CbWINfS7.js} +49 -21
  22. package/dist/chunks/{index.BIP7prJq.js → index.CcRZ6fUh.js} +1493 -114
  23. package/dist/chunks/{index.X0nbfr6-.js → index.Dc3xnDvT.js} +48 -289
  24. package/dist/chunks/{index.C832ioot.js → index.RwjEGCQ0.js} +4 -4
  25. package/dist/chunks/init-forks.WglB-sfY.js +54 -0
  26. package/dist/chunks/init-threads.Czek6eA5.js +17 -0
  27. package/dist/chunks/init.94FWN9pW.js +213 -0
  28. package/dist/chunks/{inspector.CvQD-Nie.js → inspector.DLZxSeU3.js} +2 -6
  29. package/dist/chunks/{moduleRunner.d.DxTLreRD.d.ts → moduleRunner.d.YtNsMIoJ.d.ts} +9 -14
  30. package/dist/chunks/{node.CyipiPvJ.js → node.BwAWWjHZ.js} +3 -4
  31. package/dist/chunks/{plugin.d.CIk0YiKb.d.ts → plugin.d.DQU1R5px.d.ts} +1 -1
  32. package/dist/chunks/{reporters.d.DmP-iHLr.d.ts → reporters.d.BMKt7f6I.d.ts} +1064 -1021
  33. package/dist/chunks/{resolveSnapshotEnvironment.Bvv2zr69.js → resolveSnapshotEnvironment.DJJKMKxb.js} +7 -8
  34. package/dist/chunks/{rpc.BKr6mtxz.js → rpc.cD77ENhU.js} +13 -14
  35. package/dist/chunks/{setup-common.B7I37Tji.js → setup-common.DR1sucx6.js} +6 -6
  36. package/dist/chunks/{startModuleRunner.BDRvKSdz.js → startModuleRunner.iF1E9Bt4.js} +126 -110
  37. package/dist/chunks/{test.BAlBebnP.js → test.C3RPt8JR.js} +7 -7
  38. package/dist/chunks/{utils.D2R2NiOH.js → utils.CG9h5ccR.js} +2 -5
  39. package/dist/chunks/{vi.BB37KeLx.js → vi.BZvkKVkM.js} +61 -164
  40. package/dist/chunks/{vm.CjLTDaST.js → vm.CuMWYx_F.js} +20 -29
  41. package/dist/chunks/{worker.d.B2r4Ln6p.d.ts → worker.d.BFk-vvBU.d.ts} +42 -6
  42. package/dist/cli.js +12 -11
  43. package/dist/config.cjs +0 -1
  44. package/dist/config.d.ts +11 -13
  45. package/dist/config.js +1 -1
  46. package/dist/coverage.d.ts +7 -6
  47. package/dist/coverage.js +3 -14
  48. package/dist/environments.d.ts +3 -6
  49. package/dist/environments.js +1 -1
  50. package/dist/index.d.ts +20 -25
  51. package/dist/index.js +11 -7
  52. package/dist/module-evaluator.d.ts +5 -4
  53. package/dist/module-evaluator.js +11 -13
  54. package/dist/module-runner.js +5 -5
  55. package/dist/node.d.ts +82 -25
  56. package/dist/node.js +23 -20
  57. package/dist/reporters.d.ts +10 -9
  58. package/dist/reporters.js +12 -11
  59. package/dist/runners.d.ts +1 -1
  60. package/dist/runners.js +9 -7
  61. package/dist/snapshot.js +3 -3
  62. package/dist/suite.js +4 -3
  63. package/dist/worker.d.ts +26 -0
  64. package/dist/worker.js +45 -165
  65. package/dist/workers/forks.js +26 -43
  66. package/dist/workers/runVmTests.js +16 -12
  67. package/dist/workers/threads.js +26 -31
  68. package/dist/workers/vmForks.js +26 -39
  69. package/dist/workers/vmThreads.js +26 -29
  70. package/package.json +48 -32
  71. package/worker.d.ts +1 -0
  72. package/browser.d.ts +0 -1
  73. package/dist/chunks/environment.d.2fYMoz3o.d.ts +0 -66
  74. package/dist/chunks/moduleTransport.I-bgQy0S.js +0 -19
  75. package/dist/chunks/resolver.Bx6lE0iq.js +0 -119
  76. package/dist/chunks/typechecker.DB-fIMaH.js +0 -805
  77. package/dist/chunks/utils.C2YI6McM.js +0 -52
  78. package/dist/chunks/worker.d.DJ6qxO2w.d.ts +0 -8
  79. package/dist/workers.d.ts +0 -38
  80. package/dist/workers.js +0 -49
  81. package/execute.d.ts +0 -1
  82. package/utils.d.ts +0 -1
  83. package/workers.d.ts +0 -1
@@ -1,805 +0,0 @@
1
- import nodeos__default from 'node:os';
2
- import { performance } from 'node:perf_hooks';
3
- import { TraceMap, generatedPositionFor, eachMapping } from '@vitest/utils/source-map';
4
- import { relative, basename, resolve, join } from 'pathe';
5
- import { x } from 'tinyexec';
6
- import { distDir } from '../path.js';
7
- import { getTests, generateHash, calculateSuiteHash, someTasksAreOnly, interpretTaskModes } from '@vitest/runner/utils';
8
- import '@vitest/utils';
9
- import { parseAstAsync } from 'vite';
10
-
11
- const REGEXP_WRAP_PREFIX = "$$vitest:";
12
- function getOutputFile(config, reporter) {
13
- if (config?.outputFile) return typeof config.outputFile === "string" ? config.outputFile : config.outputFile[reporter];
14
- }
15
- /**
16
- * Prepares `SerializedConfig` for serialization, e.g. `node:v8.serialize`
17
- */
18
- function wrapSerializableConfig(config) {
19
- let testNamePattern = config.testNamePattern, defines = config.defines;
20
- // v8 serialize does not support regex
21
- if (testNamePattern && typeof testNamePattern !== "string") testNamePattern = `${REGEXP_WRAP_PREFIX}${testNamePattern.toString()}`;
22
- // v8 serialize drops properties with undefined value
23
- if (defines) defines = {
24
- keys: Object.keys(defines),
25
- original: defines
26
- };
27
- return {
28
- ...config,
29
- testNamePattern,
30
- defines
31
- };
32
- }
33
- function createDefinesScript(define) {
34
- if (!define) return "";
35
- const serializedDefine = serializeDefine(define);
36
- return serializedDefine === "{}" ? "" : `
37
- const defines = ${serializeDefine(define)}
38
- Object.keys(defines).forEach((key) => {
39
- const segments = key.split('.')
40
- let target = globalThis
41
- for (let i = 0; i < segments.length; i++) {
42
- const segment = segments[i]
43
- if (i === segments.length - 1) {
44
- target[segment] = defines[key]
45
- } else {
46
- target = target[segment] || (target[segment] = {})
47
- }
48
- }
49
- })
50
- `;
51
- }
52
- /**
53
- * Like `JSON.stringify` but keeps raw string values as a literal
54
- * in the generated code. For example: `"window"` would refer to
55
- * the global `window` object directly.
56
- */
57
- function serializeDefine(define) {
58
- const userDefine = {};
59
- for (const key in define) {
60
- // vitest sets this to avoid vite:client-inject plugin
61
- if (key === "process.env.NODE_ENV" && define[key] === "process.env.NODE_ENV") continue;
62
- // import.meta.env.* is handled in `importAnalysis` plugin
63
- if (!key.startsWith("import.meta.env.")) userDefine[key] = define[key];
64
- }
65
- let res = `{`;
66
- const keys = Object.keys(userDefine).sort();
67
- for (let i = 0; i < keys.length; i++) {
68
- const key = keys[i], val = userDefine[key];
69
- if (res += `${JSON.stringify(key)}: ${handleDefineValue(val)}`, i !== keys.length - 1) res += `, `;
70
- }
71
- return `${res}}`;
72
- }
73
- function handleDefineValue(value) {
74
- return typeof value === "undefined" ? "undefined" : typeof value === "string" ? value : JSON.stringify(value);
75
- }
76
-
77
- function hasFailedSnapshot(suite) {
78
- return getTests(suite).some((s) => {
79
- return s.result?.errors?.some((e) => typeof e?.message === "string" && e.message.match(/Snapshot .* mismatched/));
80
- });
81
- }
82
- function convertTasksToEvents(file, onTask) {
83
- const packs = [], events = [];
84
- function visit(suite) {
85
- onTask?.(suite), packs.push([
86
- suite.id,
87
- suite.result,
88
- suite.meta
89
- ]), events.push([
90
- suite.id,
91
- "suite-prepare",
92
- void 0
93
- ]), suite.tasks.forEach((task) => {
94
- if (task.type === "suite") visit(task);
95
- else if (onTask?.(task), suite.mode !== "skip" && suite.mode !== "todo") packs.push([
96
- task.id,
97
- task.result,
98
- task.meta
99
- ]), events.push([
100
- task.id,
101
- "test-prepare",
102
- void 0
103
- ]), task.annotations.forEach((annotation) => {
104
- events.push([
105
- task.id,
106
- "test-annotation",
107
- { annotation }
108
- ]);
109
- }), events.push([
110
- task.id,
111
- "test-finished",
112
- void 0
113
- ]);
114
- }), events.push([
115
- suite.id,
116
- "suite-finished",
117
- void 0
118
- ]);
119
- }
120
- return visit(file), {
121
- packs,
122
- events
123
- };
124
- }
125
-
126
- // AST walker module for ESTree compatible trees
127
-
128
-
129
- // An ancestor walk keeps an array of ancestor nodes (including the
130
- // current node) and passes them to the callback as third parameter
131
- // (and also as state parameter when no other state is present).
132
- function ancestor(node, visitors, baseVisitor, state, override) {
133
- var ancestors = [];
134
- if (!baseVisitor) { baseVisitor = base
135
- ; }(function c(node, st, override) {
136
- var type = override || node.type;
137
- var isNew = node !== ancestors[ancestors.length - 1];
138
- if (isNew) { ancestors.push(node); }
139
- baseVisitor[type](node, st, c);
140
- if (visitors[type]) { visitors[type](node, st || ancestors, ancestors); }
141
- if (isNew) { ancestors.pop(); }
142
- })(node, state, override);
143
- }
144
-
145
- function skipThrough(node, st, c) { c(node, st); }
146
- function ignore(_node, _st, _c) {}
147
-
148
- // Node walkers.
149
-
150
- var base = {};
151
-
152
- base.Program = base.BlockStatement = base.StaticBlock = function (node, st, c) {
153
- for (var i = 0, list = node.body; i < list.length; i += 1)
154
- {
155
- var stmt = list[i];
156
-
157
- c(stmt, st, "Statement");
158
- }
159
- };
160
- base.Statement = skipThrough;
161
- base.EmptyStatement = ignore;
162
- base.ExpressionStatement = base.ParenthesizedExpression = base.ChainExpression =
163
- function (node, st, c) { return c(node.expression, st, "Expression"); };
164
- base.IfStatement = function (node, st, c) {
165
- c(node.test, st, "Expression");
166
- c(node.consequent, st, "Statement");
167
- if (node.alternate) { c(node.alternate, st, "Statement"); }
168
- };
169
- base.LabeledStatement = function (node, st, c) { return c(node.body, st, "Statement"); };
170
- base.BreakStatement = base.ContinueStatement = ignore;
171
- base.WithStatement = function (node, st, c) {
172
- c(node.object, st, "Expression");
173
- c(node.body, st, "Statement");
174
- };
175
- base.SwitchStatement = function (node, st, c) {
176
- c(node.discriminant, st, "Expression");
177
- for (var i = 0, list = node.cases; i < list.length; i += 1) {
178
- var cs = list[i];
179
-
180
- c(cs, st);
181
- }
182
- };
183
- base.SwitchCase = function (node, st, c) {
184
- if (node.test) { c(node.test, st, "Expression"); }
185
- for (var i = 0, list = node.consequent; i < list.length; i += 1)
186
- {
187
- var cons = list[i];
188
-
189
- c(cons, st, "Statement");
190
- }
191
- };
192
- base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {
193
- if (node.argument) { c(node.argument, st, "Expression"); }
194
- };
195
- base.ThrowStatement = base.SpreadElement =
196
- function (node, st, c) { return c(node.argument, st, "Expression"); };
197
- base.TryStatement = function (node, st, c) {
198
- c(node.block, st, "Statement");
199
- if (node.handler) { c(node.handler, st); }
200
- if (node.finalizer) { c(node.finalizer, st, "Statement"); }
201
- };
202
- base.CatchClause = function (node, st, c) {
203
- if (node.param) { c(node.param, st, "Pattern"); }
204
- c(node.body, st, "Statement");
205
- };
206
- base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
207
- c(node.test, st, "Expression");
208
- c(node.body, st, "Statement");
209
- };
210
- base.ForStatement = function (node, st, c) {
211
- if (node.init) { c(node.init, st, "ForInit"); }
212
- if (node.test) { c(node.test, st, "Expression"); }
213
- if (node.update) { c(node.update, st, "Expression"); }
214
- c(node.body, st, "Statement");
215
- };
216
- base.ForInStatement = base.ForOfStatement = function (node, st, c) {
217
- c(node.left, st, "ForInit");
218
- c(node.right, st, "Expression");
219
- c(node.body, st, "Statement");
220
- };
221
- base.ForInit = function (node, st, c) {
222
- if (node.type === "VariableDeclaration") { c(node, st); }
223
- else { c(node, st, "Expression"); }
224
- };
225
- base.DebuggerStatement = ignore;
226
-
227
- base.FunctionDeclaration = function (node, st, c) { return c(node, st, "Function"); };
228
- base.VariableDeclaration = function (node, st, c) {
229
- for (var i = 0, list = node.declarations; i < list.length; i += 1)
230
- {
231
- var decl = list[i];
232
-
233
- c(decl, st);
234
- }
235
- };
236
- base.VariableDeclarator = function (node, st, c) {
237
- c(node.id, st, "Pattern");
238
- if (node.init) { c(node.init, st, "Expression"); }
239
- };
240
-
241
- base.Function = function (node, st, c) {
242
- if (node.id) { c(node.id, st, "Pattern"); }
243
- for (var i = 0, list = node.params; i < list.length; i += 1)
244
- {
245
- var param = list[i];
246
-
247
- c(param, st, "Pattern");
248
- }
249
- c(node.body, st, node.expression ? "Expression" : "Statement");
250
- };
251
-
252
- base.Pattern = function (node, st, c) {
253
- if (node.type === "Identifier")
254
- { c(node, st, "VariablePattern"); }
255
- else if (node.type === "MemberExpression")
256
- { c(node, st, "MemberPattern"); }
257
- else
258
- { c(node, st); }
259
- };
260
- base.VariablePattern = ignore;
261
- base.MemberPattern = skipThrough;
262
- base.RestElement = function (node, st, c) { return c(node.argument, st, "Pattern"); };
263
- base.ArrayPattern = function (node, st, c) {
264
- for (var i = 0, list = node.elements; i < list.length; i += 1) {
265
- var elt = list[i];
266
-
267
- if (elt) { c(elt, st, "Pattern"); }
268
- }
269
- };
270
- base.ObjectPattern = function (node, st, c) {
271
- for (var i = 0, list = node.properties; i < list.length; i += 1) {
272
- var prop = list[i];
273
-
274
- if (prop.type === "Property") {
275
- if (prop.computed) { c(prop.key, st, "Expression"); }
276
- c(prop.value, st, "Pattern");
277
- } else if (prop.type === "RestElement") {
278
- c(prop.argument, st, "Pattern");
279
- }
280
- }
281
- };
282
-
283
- base.Expression = skipThrough;
284
- base.ThisExpression = base.Super = base.MetaProperty = ignore;
285
- base.ArrayExpression = function (node, st, c) {
286
- for (var i = 0, list = node.elements; i < list.length; i += 1) {
287
- var elt = list[i];
288
-
289
- if (elt) { c(elt, st, "Expression"); }
290
- }
291
- };
292
- base.ObjectExpression = function (node, st, c) {
293
- for (var i = 0, list = node.properties; i < list.length; i += 1)
294
- {
295
- var prop = list[i];
296
-
297
- c(prop, st);
298
- }
299
- };
300
- base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
301
- base.SequenceExpression = function (node, st, c) {
302
- for (var i = 0, list = node.expressions; i < list.length; i += 1)
303
- {
304
- var expr = list[i];
305
-
306
- c(expr, st, "Expression");
307
- }
308
- };
309
- base.TemplateLiteral = function (node, st, c) {
310
- for (var i = 0, list = node.quasis; i < list.length; i += 1)
311
- {
312
- var quasi = list[i];
313
-
314
- c(quasi, st);
315
- }
316
-
317
- for (var i$1 = 0, list$1 = node.expressions; i$1 < list$1.length; i$1 += 1)
318
- {
319
- var expr = list$1[i$1];
320
-
321
- c(expr, st, "Expression");
322
- }
323
- };
324
- base.TemplateElement = ignore;
325
- base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
326
- c(node.argument, st, "Expression");
327
- };
328
- base.BinaryExpression = base.LogicalExpression = function (node, st, c) {
329
- c(node.left, st, "Expression");
330
- c(node.right, st, "Expression");
331
- };
332
- base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {
333
- c(node.left, st, "Pattern");
334
- c(node.right, st, "Expression");
335
- };
336
- base.ConditionalExpression = function (node, st, c) {
337
- c(node.test, st, "Expression");
338
- c(node.consequent, st, "Expression");
339
- c(node.alternate, st, "Expression");
340
- };
341
- base.NewExpression = base.CallExpression = function (node, st, c) {
342
- c(node.callee, st, "Expression");
343
- if (node.arguments)
344
- { for (var i = 0, list = node.arguments; i < list.length; i += 1)
345
- {
346
- var arg = list[i];
347
-
348
- c(arg, st, "Expression");
349
- } }
350
- };
351
- base.MemberExpression = function (node, st, c) {
352
- c(node.object, st, "Expression");
353
- if (node.computed) { c(node.property, st, "Expression"); }
354
- };
355
- base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
356
- if (node.declaration)
357
- { c(node.declaration, st, node.type === "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression"); }
358
- if (node.source) { c(node.source, st, "Expression"); }
359
- };
360
- base.ExportAllDeclaration = function (node, st, c) {
361
- if (node.exported)
362
- { c(node.exported, st); }
363
- c(node.source, st, "Expression");
364
- };
365
- base.ImportDeclaration = function (node, st, c) {
366
- for (var i = 0, list = node.specifiers; i < list.length; i += 1)
367
- {
368
- var spec = list[i];
369
-
370
- c(spec, st);
371
- }
372
- c(node.source, st, "Expression");
373
- };
374
- base.ImportExpression = function (node, st, c) {
375
- c(node.source, st, "Expression");
376
- };
377
- base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.PrivateIdentifier = base.Literal = ignore;
378
-
379
- base.TaggedTemplateExpression = function (node, st, c) {
380
- c(node.tag, st, "Expression");
381
- c(node.quasi, st, "Expression");
382
- };
383
- base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, "Class"); };
384
- base.Class = function (node, st, c) {
385
- if (node.id) { c(node.id, st, "Pattern"); }
386
- if (node.superClass) { c(node.superClass, st, "Expression"); }
387
- c(node.body, st);
388
- };
389
- base.ClassBody = function (node, st, c) {
390
- for (var i = 0, list = node.body; i < list.length; i += 1)
391
- {
392
- var elt = list[i];
393
-
394
- c(elt, st);
395
- }
396
- };
397
- base.MethodDefinition = base.PropertyDefinition = base.Property = function (node, st, c) {
398
- if (node.computed) { c(node.key, st, "Expression"); }
399
- if (node.value) { c(node.value, st, "Expression"); }
400
- };
401
-
402
- async function collectTests(ctx, filepath) {
403
- const request = await ctx.vite.environments.ssr.transformRequest(filepath);
404
- if (!request) return null;
405
- const ast = await parseAstAsync(request.code), testFilepath = relative(ctx.config.root, filepath), projectName = ctx.name, typecheckSubprojectName = projectName ? `${projectName}:__typecheck__` : "__typecheck__", file = {
406
- filepath,
407
- type: "suite",
408
- id: generateHash(`${testFilepath}${typecheckSubprojectName}`),
409
- name: testFilepath,
410
- mode: "run",
411
- tasks: [],
412
- start: ast.start,
413
- end: ast.end,
414
- projectName,
415
- meta: { typecheck: true },
416
- file: null
417
- };
418
- file.file = file;
419
- const definitions = [], getName = (callee) => {
420
- if (!callee) return null;
421
- if (callee.type === "Identifier") return callee.name;
422
- if (callee.type === "CallExpression") return getName(callee.callee);
423
- if (callee.type === "TaggedTemplateExpression") return getName(callee.tag);
424
- if (callee.type === "MemberExpression")
425
- // call as `__vite_ssr__.test.skip()`
426
- return callee.object?.type === "Identifier" && [
427
- "it",
428
- "test",
429
- "describe",
430
- "suite"
431
- ].includes(callee.object.name) ? callee.object?.name : callee.object?.name?.startsWith("__vite_ssr_") ? getName(callee.property) : getName(callee.object?.property);
432
- // unwrap (0, ...)
433
- if (callee.type === "SequenceExpression" && callee.expressions.length === 2) {
434
- const [e0, e1] = callee.expressions;
435
- if (e0.type === "Literal" && e0.value === 0) return getName(e1);
436
- }
437
- return null;
438
- };
439
- ancestor(ast, { CallExpression(node) {
440
- const { callee } = node, name = getName(callee);
441
- if (!name || ![
442
- "it",
443
- "test",
444
- "describe",
445
- "suite"
446
- ].includes(name)) return;
447
- const property = callee?.property?.name;
448
- let mode = !property || property === name ? "run" : property;
449
- // they will be picked up in the next iteration
450
- if ([
451
- "each",
452
- "for",
453
- "skipIf",
454
- "runIf"
455
- ].includes(mode)) return;
456
- let start;
457
- const end = node.end;
458
- // .each
459
- if (callee.type === "CallExpression") start = callee.end;
460
- else if (callee.type === "TaggedTemplateExpression") start = callee.end + 1;
461
- else start = node.start;
462
- const { arguments: [messageNode] } = node, isQuoted = messageNode?.type === "Literal" || messageNode?.type === "TemplateLiteral", message = isQuoted ? request.code.slice(messageNode.start + 1, messageNode.end - 1) : request.code.slice(messageNode.start, messageNode.end);
463
- // cannot statically analyze, so we always skip it
464
- if (mode === "skipIf" || mode === "runIf") mode = "skip";
465
- definitions.push({
466
- start,
467
- end,
468
- name: message,
469
- type: name === "it" || name === "test" ? "test" : "suite",
470
- mode,
471
- task: null
472
- });
473
- } });
474
- let lastSuite = file;
475
- const updateLatestSuite = (index) => {
476
- while (lastSuite.suite && lastSuite.end < index) lastSuite = lastSuite.suite;
477
- return lastSuite;
478
- };
479
- definitions.sort((a, b) => a.start - b.start).forEach((definition) => {
480
- const latestSuite = updateLatestSuite(definition.start);
481
- let mode = definition.mode;
482
- if (latestSuite.mode !== "run")
483
- // inherit suite mode, if it's set
484
- mode = latestSuite.mode;
485
- if (definition.type === "suite") {
486
- const task = {
487
- type: definition.type,
488
- id: "",
489
- suite: latestSuite,
490
- file,
491
- tasks: [],
492
- mode,
493
- name: definition.name,
494
- end: definition.end,
495
- start: definition.start,
496
- meta: { typecheck: true }
497
- };
498
- definition.task = task, latestSuite.tasks.push(task), lastSuite = task;
499
- return;
500
- }
501
- const task = {
502
- type: definition.type,
503
- id: "",
504
- suite: latestSuite,
505
- file,
506
- mode,
507
- timeout: 0,
508
- context: {},
509
- name: definition.name,
510
- end: definition.end,
511
- start: definition.start,
512
- annotations: [],
513
- meta: { typecheck: true }
514
- };
515
- definition.task = task, latestSuite.tasks.push(task);
516
- }), calculateSuiteHash(file);
517
- const hasOnly = someTasksAreOnly(file);
518
- return interpretTaskModes(file, ctx.config.testNamePattern, void 0, hasOnly, false, ctx.config.allowOnly), {
519
- file,
520
- parsed: request.code,
521
- filepath,
522
- map: request.map,
523
- definitions
524
- };
525
- }
526
-
527
- const newLineRegExp = /\r?\n/, errCodeRegExp = /error TS(?<errCode>\d+)/;
528
- async function makeTscErrorInfo(errInfo) {
529
- const [errFilePathPos = "", ...errMsgRawArr] = errInfo.split(":");
530
- if (!errFilePathPos || errMsgRawArr.length === 0 || errMsgRawArr.join("").length === 0) return ["unknown filepath", null];
531
- const errMsgRaw = errMsgRawArr.join("").trim(), [errFilePath, errPos] = errFilePathPos.slice(0, -1).split("(");
532
- if (!errFilePath || !errPos) return ["unknown filepath", null];
533
- const [errLine, errCol] = errPos.split(",");
534
- if (!errLine || !errCol) return [errFilePath, null];
535
- // get errCode, errMsg
536
- const execArr = errCodeRegExp.exec(errMsgRaw);
537
- if (!execArr) return [errFilePath, null];
538
- const errCodeStr = execArr.groups?.errCode ?? "";
539
- if (!errCodeStr) return [errFilePath, null];
540
- const line = Number(errLine), col = Number(errCol), errCode = Number(errCodeStr);
541
- return [errFilePath, {
542
- filePath: errFilePath,
543
- errCode,
544
- line,
545
- column: col,
546
- errMsg: errMsgRaw.slice(`error TS${errCode} `.length)
547
- }];
548
- }
549
- async function getRawErrsMapFromTsCompile(tscErrorStdout) {
550
- const rawErrsMap = /* @__PURE__ */ new Map(), infos = await Promise.all(tscErrorStdout.split(newLineRegExp).reduce((prev, next) => {
551
- if (!next) return prev;
552
- if (!next.startsWith(" ")) prev.push(next);
553
- else prev[prev.length - 1] += `\n${next}`;
554
- return prev;
555
- }, []).map((errInfoLine) => makeTscErrorInfo(errInfoLine)));
556
- return infos.forEach(([errFilePath, errInfo]) => {
557
- if (errInfo) if (!rawErrsMap.has(errFilePath)) rawErrsMap.set(errFilePath, [errInfo]);
558
- else rawErrsMap.get(errFilePath)?.push(errInfo);
559
- }), rawErrsMap;
560
- }
561
-
562
- function createIndexMap(source) {
563
- const map = /* @__PURE__ */ new Map();
564
- let index = 0, line = 1, column = 1;
565
- for (const char of source) if (map.set(`${line}:${column}`, index++), char === "\n" || char === "\r\n") line++, column = 0;
566
- else column++;
567
- return map;
568
- }
569
-
570
- class TypeCheckError extends Error {
571
- name = "TypeCheckError";
572
- constructor(message, stacks) {
573
- super(message), this.message = message, this.stacks = stacks;
574
- }
575
- }
576
- class Typechecker {
577
- _onParseStart;
578
- _onParseEnd;
579
- _onWatcherRerun;
580
- _result = {
581
- files: [],
582
- sourceErrors: [],
583
- time: 0
584
- };
585
- _startTime = 0;
586
- _output = "";
587
- _tests = {};
588
- process;
589
- files = [];
590
- constructor(project) {
591
- this.project = project;
592
- }
593
- setFiles(files) {
594
- this.files = files;
595
- }
596
- onParseStart(fn) {
597
- this._onParseStart = fn;
598
- }
599
- onParseEnd(fn) {
600
- this._onParseEnd = fn;
601
- }
602
- onWatcherRerun(fn) {
603
- this._onWatcherRerun = fn;
604
- }
605
- async collectFileTests(filepath) {
606
- return collectTests(this.project, filepath);
607
- }
608
- getFiles() {
609
- return this.files;
610
- }
611
- async collectTests() {
612
- const tests = (await Promise.all(this.getFiles().map((filepath) => this.collectFileTests(filepath)))).reduce((acc, data) => {
613
- return data && (acc[data.filepath] = data), acc;
614
- }, {});
615
- return this._tests = tests, tests;
616
- }
617
- markPassed(file) {
618
- if (!file.result?.state) file.result = { state: "pass" };
619
- const markTasks = (tasks) => {
620
- for (const task of tasks) {
621
- if ("tasks" in task) markTasks(task.tasks);
622
- if (!task.result?.state && (task.mode === "run" || task.mode === "queued")) task.result = { state: "pass" };
623
- }
624
- };
625
- markTasks(file.tasks);
626
- }
627
- async prepareResults(output) {
628
- const typeErrors = await this.parseTscLikeOutput(output), testFiles = new Set(this.getFiles());
629
- if (!this._tests) this._tests = await this.collectTests();
630
- const sourceErrors = [], files = [];
631
- return testFiles.forEach((path) => {
632
- const { file, definitions, map, parsed } = this._tests[path], errors = typeErrors.get(path);
633
- if (files.push(file), !errors) {
634
- this.markPassed(file);
635
- return;
636
- }
637
- const sortedDefinitions = [...definitions.sort((a, b) => b.start - a.start)], traceMap = map && new TraceMap(map), indexMap = createIndexMap(parsed), markState = (task, state) => {
638
- if (task.result = { state: task.mode === "run" || task.mode === "only" ? state : task.mode }, task.suite) markState(task.suite, state);
639
- else if (task.file && task !== task.file) markState(task.file, state);
640
- };
641
- errors.forEach(({ error, originalError }) => {
642
- const processedPos = traceMap ? findGeneratedPosition(traceMap, {
643
- line: originalError.line,
644
- column: originalError.column,
645
- source: basename(path)
646
- }) : originalError, line = processedPos.line ?? originalError.line, column = processedPos.column ?? originalError.column, index = indexMap.get(`${line}:${column}`), definition = index != null && sortedDefinitions.find((def) => def.start <= index && def.end >= index), suite = definition ? definition.task : file, state = suite.mode === "run" || suite.mode === "only" ? "fail" : suite.mode, errors = suite.result?.errors || [];
647
- if (suite.result = {
648
- state,
649
- errors
650
- }, errors.push(error), state === "fail") {
651
- if (suite.suite) markState(suite.suite, "fail");
652
- else if (suite.file && suite !== suite.file) markState(suite.file, "fail");
653
- }
654
- }), this.markPassed(file);
655
- }), typeErrors.forEach((errors, path) => {
656
- if (!testFiles.has(path)) sourceErrors.push(...errors.map(({ error }) => error));
657
- }), {
658
- files,
659
- sourceErrors,
660
- time: performance.now() - this._startTime
661
- };
662
- }
663
- async parseTscLikeOutput(output) {
664
- const errorsMap = await getRawErrsMapFromTsCompile(output), typesErrors = /* @__PURE__ */ new Map();
665
- return errorsMap.forEach((errors, path) => {
666
- const filepath = resolve(this.project.config.root, path), suiteErrors = errors.map((info) => {
667
- const limit = Error.stackTraceLimit;
668
- Error.stackTraceLimit = 0;
669
- // Some expect-type errors have the most useful information on the second line e.g. `This expression is not callable.\n Type 'ExpectString<number>' has no call signatures.`
670
- const errMsg = info.errMsg.replace(/\r?\n\s*(Type .* has no call signatures)/g, " $1"), error = new TypeCheckError(errMsg, [{
671
- file: filepath,
672
- line: info.line,
673
- column: info.column,
674
- method: ""
675
- }]);
676
- return Error.stackTraceLimit = limit, {
677
- originalError: info,
678
- error: {
679
- name: error.name,
680
- message: errMsg,
681
- stacks: error.stacks,
682
- stack: ""
683
- }
684
- };
685
- });
686
- typesErrors.set(filepath, suiteErrors);
687
- }), typesErrors;
688
- }
689
- async stop() {
690
- this.process?.kill(), this.process = void 0;
691
- }
692
- async ensurePackageInstalled(ctx, checker) {
693
- if (checker !== "tsc" && checker !== "vue-tsc") return;
694
- const packageName = checker === "tsc" ? "typescript" : "vue-tsc";
695
- await ctx.packageInstaller.ensureInstalled(packageName, ctx.config.root);
696
- }
697
- getExitCode() {
698
- return this.process?.exitCode != null && this.process.exitCode;
699
- }
700
- getOutput() {
701
- return this._output;
702
- }
703
- async spawn() {
704
- const { root, watch, typecheck } = this.project.config, args = [
705
- "--noEmit",
706
- "--pretty",
707
- "false",
708
- "--incremental",
709
- "--tsBuildInfoFile",
710
- join(process.versions.pnp ? join(nodeos__default.tmpdir(), this.project.hash) : distDir, "tsconfig.tmp.tsbuildinfo")
711
- ];
712
- // use builtin watcher because it's faster
713
- if (watch) args.push("--watch");
714
- if (typecheck.allowJs) args.push("--allowJs", "--checkJs");
715
- if (typecheck.tsconfig) args.push("-p", resolve(root, typecheck.tsconfig));
716
- this._output = "", this._startTime = performance.now();
717
- const child = x(typecheck.checker, args, {
718
- nodeOptions: {
719
- cwd: root,
720
- stdio: "pipe"
721
- },
722
- throwOnError: false
723
- });
724
- this.process = child.process;
725
- let rerunTriggered = false, dataReceived = false;
726
- return new Promise((resolve, reject) => {
727
- if (!child.process || !child.process.stdout) {
728
- reject(/* @__PURE__ */ new Error(`Failed to initialize ${typecheck.checker}. This is a bug in Vitest - please, open an issue with reproduction.`));
729
- return;
730
- }
731
- child.process.stdout.on("data", (chunk) => {
732
- if (dataReceived = true, this._output += chunk, watch) {
733
- if (this._output.includes("File change detected") && !rerunTriggered) this._onWatcherRerun?.(), this._startTime = performance.now(), this._result.sourceErrors = [], this._result.files = [], this._tests = null, rerunTriggered = true;
734
- if (/Found \w+ errors*. Watching for/.test(this._output)) rerunTriggered = false, this.prepareResults(this._output).then((result) => {
735
- this._result = result, this._onParseEnd?.(result);
736
- }), this._output = "";
737
- }
738
- });
739
- const timeout = setTimeout(() => reject(/* @__PURE__ */ new Error(`${typecheck.checker} spawn timed out`)), this.project.config.typecheck.spawnTimeout);
740
- function onError(cause) {
741
- clearTimeout(timeout), reject(new Error("Spawning typechecker failed - is typescript installed?", { cause }));
742
- }
743
- if (child.process.once("spawn", () => {
744
- if (this._onParseStart?.(), child.process?.off("error", onError), clearTimeout(timeout), process.platform === "win32")
745
- // on Windows, the process might be spawned but fail to start
746
- // we wait for a potential error here. if "close" event didn't trigger,
747
- // we resolve the promise
748
- setTimeout(() => {
749
- resolve({ result: child });
750
- }, 200);
751
- else resolve({ result: child });
752
- }), process.platform === "win32") child.process.once("close", (code) => {
753
- if (code != null && code !== 0 && !dataReceived) onError(/* @__PURE__ */ new Error(`The ${typecheck.checker} command exited with code ${code}.`));
754
- });
755
- child.process.once("error", onError);
756
- });
757
- }
758
- async start() {
759
- if (this.process) return;
760
- const { watch } = this.project.config, { result: child } = await this.spawn();
761
- if (!watch) await child, this._result = await this.prepareResults(this._output), await this._onParseEnd?.(this._result);
762
- }
763
- getResult() {
764
- return this._result;
765
- }
766
- getTestFiles() {
767
- return Object.values(this._tests || {}).map((i) => i.file);
768
- }
769
- getTestPacksAndEvents() {
770
- const packs = [], events = [];
771
- for (const { file } of Object.values(this._tests || {})) {
772
- const result = convertTasksToEvents(file);
773
- packs.push(...result.packs), events.push(...result.events);
774
- }
775
- return {
776
- packs,
777
- events
778
- };
779
- }
780
- }
781
- function findGeneratedPosition(traceMap, { line, column, source }) {
782
- const found = generatedPositionFor(traceMap, {
783
- line,
784
- column,
785
- source
786
- });
787
- if (found.line !== null) return found;
788
- // find the next source token position when the exact error position doesn't exist in source map.
789
- // this can happen, for example, when the type error is in the comment "// @ts-expect-error"
790
- // and comments are stripped away in the generated code.
791
- const mappings = [];
792
- eachMapping(traceMap, (m) => {
793
- if (m.source === source && m.originalLine !== null && m.originalColumn !== null && (line === m.originalLine ? column < m.originalColumn : line < m.originalLine)) mappings.push(m);
794
- });
795
- const next = mappings.sort((a, b) => a.originalLine === b.originalLine ? a.originalColumn - b.originalColumn : a.originalLine - b.originalLine).at(0);
796
- return next ? {
797
- line: next.generatedLine,
798
- column: next.generatedColumn
799
- } : {
800
- line: null,
801
- column: null
802
- };
803
- }
804
-
805
- export { TypeCheckError as T, Typechecker as a, ancestor as b, createDefinesScript as c, convertTasksToEvents as d, getOutputFile as g, hasFailedSnapshot as h, wrapSerializableConfig as w };