[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

perf: improve performance in hot paths (#10446)

authored by

Vladimir and committed by
GitHub
(May 25, 2026, 6:00 PM +0200) 03faf6db 31e81fe2

+41 -21
+5 -7
packages/browser/src/node/rpc.ts
··· 409 409 function cloneByOwnProperties(value: any) { 410 410 // Clones the value's properties into a new Object. The simpler approach of 411 411 // Object.assign() won't work in the case that properties are not enumerable. 412 - return Object.getOwnPropertyNames(value).reduce( 413 - (clone, prop) => ({ 414 - ...clone, 415 - [prop]: value[prop], 416 - }), 417 - {}, 418 - ) 412 + const clone: Record<string, unknown> = {} 413 + for (const prop of Object.getOwnPropertyNames(value)) { 414 + clone[prop] = value[prop] 415 + } 416 + return clone 419 417 } 420 418 421 419 /**
+7 -3
packages/runner/src/utils/collect.ts
··· 18 18 allowOnly?: boolean, 19 19 ): void { 20 20 const matchedLocations: number[] = [] 21 + const testLocationsSet = testLocations !== undefined && testLocations.length !== 0 22 + ? new Set(testLocations) 23 + : undefined 24 + const testIdsSet = testIds ? new Set(testIds) : undefined 21 25 22 26 const traverseSuite = (suite: Suite, parentIsOnly?: boolean, parentMatchedWithLocation?: boolean) => { 23 27 const suiteIsOnly = parentIsOnly || suite.mode === 'only' ··· 54 58 // Match test location against provided locations, only run if present 55 59 // in `testLocations`. Note: if `includeTaskLocation` is not enabled, 56 60 // all test will be skipped. 57 - if (testLocations !== undefined && testLocations.length !== 0) { 58 - if (t.location && testLocations?.includes(t.location.line)) { 61 + if (testLocationsSet !== undefined) { 62 + if (t.location && testLocationsSet.has(t.location.line)) { 59 63 t.mode = 'run' 60 64 matchedLocations.push(t.location.line) 61 65 hasLocationMatch = true ··· 72 76 if (namePattern && !getTaskFullName(t).match(namePattern)) { 73 77 t.mode = 'skip' 74 78 } 75 - if (testIds && !testIds.includes(t.id)) { 79 + if (testIdsSet && !testIdsSet.has(t.id)) { 76 80 t.mode = 'skip' 77 81 } 78 82 if (testTagsFilter && !testTagsFilter(t.tags || [])) {
+11 -1
packages/vitest/src/node/ast-collect.ts
··· 49 49 const debug = createDebugger('vitest:ast-collect-info') 50 50 const verbose = createDebugger('vitest:ast-collect-verbose') 51 51 52 + const INTERMEDIATE_CALL_PROPERTIES = new Set([ 53 + 'each', 54 + 'for', 55 + 'skipIf', 56 + 'runIf', 57 + 'extend', 58 + 'scoped', 59 + 'override', 60 + ]) 61 + 52 62 function isTestFunctionName(name: string) { 53 63 return name === 'it' || name === 'test' || name.startsWith('test') || name.endsWith('Test') 54 64 } ··· 153 163 const properties = getProperties(callee) 154 164 const property = callee?.property?.name 155 165 // intermediate calls like .each(), .for() will be picked up in the next iteration 156 - if (property && ['each', 'for', 'skipIf', 'runIf', 'extend', 'scoped', 'override'].includes(property)) { 166 + if (property && INTERMEDIATE_CALL_PROPERTIES.has(property)) { 157 167 return 158 168 } 159 169 // skip properties on return values of calls - e.g., test('name', fn).skip()
+1 -1
packages/vitest/src/node/pools/pool.ts
··· 346 346 } 347 347 348 348 for (const key of keys1) { 349 - if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) { 349 + if (!Object.prototype.hasOwnProperty.call(obj2, key) || !deepEqual(obj1[key], obj2[key])) { 350 350 return false 351 351 } 352 352 }
+17 -9
packages/vitest/src/node/reporters/base.ts
··· 679 679 return 680 680 } 681 681 682 - const dangerImports = allImports.filter(imp => imp.totalTime >= thresholds.danger) 683 - const warnImports = allImports.filter(imp => imp.totalTime >= thresholds.warn) 684 - const hasDangerImports = dangerImports.length > 0 685 - const hasWarnImports = warnImports.length > 0 682 + let dangerImportsCount = 0 683 + let hasWarnImports = false 684 + let totalSelfTime = 0 685 + let totalTotalTime = 0 686 + for (const imp of allImports) { 687 + if (imp.totalTime >= thresholds.danger) { 688 + dangerImportsCount++ 689 + } 690 + if (imp.totalTime >= thresholds.warn) { 691 + hasWarnImports = true 692 + } 693 + totalSelfTime += imp.selfTime 694 + totalTotalTime += imp.totalTime 695 + } 696 + const hasDangerImports = dangerImportsCount > 0 686 697 687 698 // Determine if we should print 688 699 const shouldFail = failOnDanger && hasDangerImports ··· 695 706 const maxTotalTime = sortedImports[0].totalTime 696 707 const limit = this.ctx.config.experimental.importDurations.limit 697 708 const topImports = sortedImports.slice(0, limit) 698 - 699 - const totalSelfTime = allImports.reduce((sum, imp) => sum + imp.selfTime, 0) 700 - const totalTotalTime = allImports.reduce((sum, imp) => sum + imp.totalTime, 0) 701 709 const slowestImport = sortedImports[0] 702 710 703 711 this.log() ··· 750 758 if (shouldFail) { 751 759 this.log() 752 760 this.ctx.logger.error( 753 - `ERROR: ${dangerImports.length} import(s) exceeded the danger threshold of ${thresholds.danger}ms`, 761 + `ERROR: ${dangerImportsCount} import(s) exceeded the danger threshold of ${thresholds.danger}ms`, 754 762 ) 755 763 process.exitCode = 1 756 764 } ··· 993 1001 } 994 1002 995 1003 for (const key of keysA) { 996 - if (!keysB.includes(key) || !deepEqual(a[key], b[key])) { 1004 + if (!Object.prototype.hasOwnProperty.call(b, key) || !deepEqual(a[key], b[key])) { 997 1005 return false 998 1006 } 999 1007 }