diff --git a/docs/guide/improving-performance.md b/docs/guide/improving-performance.md index ce5fec491..efbbc804d 100644 --- a/docs/guide/improving-performance.md +++ b/docs/guide/improving-performance.md @@ -77,6 +77,20 @@ export default defineConfig({ You can limit the working directory when Vitest searches for files using [`test.dir`](/config/dir) option. This should make the search faster if you have unrelated folders and files in the root directory. +## Caching Between Reruns + +In watch mode, Vitest caches all transformed files in memory, which makes reruns fast. However, this cache is discarded once the test run finishes. By enabling [`experimental.fsModuleCache`](/config/experimental#experimental-fsmodulecache), Vitest persists this cache to the file system so it can be reused across reruns. + +This improvement is most noticeable when rerunning a small number of tests that depend on a large module graph. For full test suites, parallelization already mitigates the cost because other tests populate the in-memory cache while earlier tests are still running. For example, running one test file with a huge module graph (>900 modules): + +```shell +# the first run +Duration 8.75s (transform 4.02s, setup 629ms, import 5.52s, tests 2.52s, environment 0ms, prepare 3ms) + +# the second run +Duration 5.90s (transform 842ms, setup 543ms, import 2.35s, tests 2.94s, environment 0ms, prepare 3ms) +``` + ## Pool By default Vitest runs tests in `pool: 'forks'`. While `'forks'` pool is better for compatibility issues ([hanging process](/guide/common-errors.html#failed-to-terminate-worker) and [segfaults](/guide/common-errors.html#segfaults-and-native-code-errors)), it may be slightly slower than `pool: 'threads'` in larger projects. diff --git a/docs/guide/profiling-test-performance.md b/docs/guide/profiling-test-performance.md index da6a0952e..f8248dee1 100644 --- a/docs/guide/profiling-test-performance.md +++ b/docs/guide/profiling-test-performance.md @@ -112,20 +112,101 @@ test('formatter works', () => { Vitest UI demonstrating barrel file issues -To see how files are transformed, you can use `VITEST_DEBUG_DUMP` environment variable to write transformed files in the file system: +To see how files are transformed, you can open the "Module Info" view in the UI: + +The module info view for an inlined module +The module info view for an inlined module + +## File Import + +Some modules just take a long time to load. To identify which modules are the slowest, enable [`experimental.importDurations`](/config/experimental#experimental-importdurations) in your configuration: + +```ts [vitest.config.ts] +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + experimental: { + importDurations: { + print: true, + }, + }, + }, +}) +``` + +This will print a breakdown of the slowest imports after your tests finish: + +```bash +Import Duration Breakdown (Top 10) + +Module Self Total +my-test.test.ts 5ms 620ms [████████████████████] +date-fns/index.js 500ms 500ms [████████████████░░░░] # [!code error] +src/utils/helpers.ts 10ms 120ms [████████░░░░░░░░░░░░] +``` + +You can also use `--experimental.importDurations.print` from the CLI without changing your configuration: ```bash -$ VITEST_DEBUG_DUMP=true vitest --run +vitest --experimental.importDurations.print +``` + +Once you've identified the slow modules, there are several strategies to speed up imports: + +### Use Specific Entry Points + +Many libraries ship multiple entry points. Importing the main entry point (which is often a [barrel file](https://vitejs.dev/guide/performance.html#avoid-barrel-files)) can pull in far more code than you need. - RUN v2.1.1 /x/vitest/examples/profiling -... +For example, `date-fns` re-exports hundreds of functions from its main entry point. Instead of importing from the top-level module, import directly from the specific function: -$ ls .vitest-dump/ -_x_examples_profiling_global-setup_ts-1292904907.js -_x_examples_profiling_test_prime-number_test_ts-1413378098.js -_src_prime-number_ts-525172412.js +```ts +import { format } from 'date-fns' // [!code --] +import { format } from 'date-fns/format' // [!code ++] ``` +### Use `resolve.alias` to Redirect Imports + +If a dependency doesn't provide granular entry points, or if third-party code imports the heavy entry point, you can use [`resolve.alias`](https://vite.dev/config/shared-options#resolve-alias) to redirect imports to a lighter alternative: + +```ts [vitest.config.ts] +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + resolve: { + alias: [ + { + find: /^date-fns$/, + replacement: join(dirname(require.resolve('date-fns/package.json')), 'index.cjs'), + }, + ] + }, +}) +``` + +### Use the Dependency Optimizer + +Vitest can bundle external libraries into a single file using [`deps.optimizer`](/config/deps#deps-optimizer), which reduces the overhead of importing packages with many internal modules: + +```ts [vitest.config.ts] +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + deps: { + optimizer: { + ssr: { + enabled: true, + include: ['date-fns'], + }, + }, + }, + }, +}) +``` + +This is especially effective for UI libraries and packages with deep import trees. Use `optimizer.ssr` for `node`/`edge` environments and `optimizer.client` for `jsdom`/`happy-dom` environments. + ## Code Coverage If code coverage generation is slow on your project you can use `DEBUG=vitest:coverage` environment variable to enable performance logging.