Last active
January 8, 2026 11:16
-
-
Save mdubourg001/aa561344f3f912513863409e7f288ad5 to your computer and use it in GitHub Desktop.
Vitest import time reporter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { TestModule } from 'vitest/node'; | |
| import { DefaultReporter } from 'vitest/reporters'; | |
| type ImportTimeConfig = { | |
| limit: 'all' | number; | |
| orderBy: 'avg' | 'total'; | |
| }; | |
| type TestModuleWithImportTimings = TestModule & { | |
| task: { importDurations: Record<string, { selfTime: number }> }; | |
| }; | |
| /** | |
| * This reporter collects and reports the time taken to import each module during the test run. | |
| */ | |
| export class ImportTimeReporter extends DefaultReporter { | |
| private config: ImportTimeConfig; | |
| private timings = new Map<string, { avg: number; total: number }>(); | |
| constructor( | |
| config: ImportTimingConfig = { | |
| limit: 'all', | |
| orderBy: 'avg', | |
| }, | |
| ) { | |
| super(); | |
| this.config = config; | |
| } | |
| onTestModuleCollected(module: TestModule): void { | |
| try { | |
| for (const [path, meta] of Object.entries( | |
| (module as TestModuleWithImportTimings).task.importDurations, | |
| )) { | |
| const actual = this.timings.get(path); | |
| this.timings.set( | |
| path, | |
| // average the previous timing with the new one to smooth out variations | |
| { | |
| avg: actual | |
| ? (actual.avg + meta.selfTime) / 2 | |
| : meta.selfTime, | |
| total: (actual ? actual.total : 0) + meta.selfTime, | |
| }, | |
| ); | |
| } | |
| } catch (e) { | |
| this.error('Error collecting import timings:', e); | |
| } | |
| } | |
| onTestRunEnd(): void { | |
| // log timings by avg ascending order | |
| const sortedTimings = Array.from(this.timings.entries()).sort( | |
| (a, b) => | |
| this.config.orderBy === 'avg' | |
| ? a[1].avg - b[1].avg | |
| : a[1].total - b[1].total, | |
| ); | |
| this.log('\nImport Timing Report:'); | |
| for (const [path, timings] of sortedTimings.slice( | |
| this.config.limit === 'all' | |
| ? 0 | |
| : sortedTimings.length - this.config.limit, | |
| sortedTimings.length, | |
| )) { | |
| this.log( | |
| ` ${timings.avg.toFixed(2).padStart(8)}ms (total: ${timings.total.toFixed(2).padStart(8)}ms) - ${path}`, | |
| ); | |
| } | |
| this.log(''); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { defineConfig } from 'vitest/config'; | |
| import { ImportTimeReporter } from './import-time-reporter'; | |
| export default defineConfig({ | |
| // ... | |
| test: { | |
| globals: true, | |
| // ... | |
| // default { limit: 'all', orderBy: 'avg' } | |
| reporters: [new ImportTimeReporter({ limit: 10, orderBy: 'total' })], | |
| } | |
| }); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Imports (and often barrel files) are what is killing Vitest performances the most. This plugin allows listing which module take the most time importing in order to know what to improve (most probably barrel files or big external dependencies).
Example output: