diff --git a/docs/config/index.md b/docs/config/index.md index 73131c2c866c..5722359d1b29 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -101,46 +101,52 @@ Include globs for in-source test files. When defined, Vitest will run all matched files with `import.meta.vitest` inside. -### deps +### server -- **Type:** `{ external?, inline?, ... }` +- **Type:** `{ sourcemap?, deps?, ... }` +- **Version:** Since Vitest 0.34.0 -Handling for dependencies resolution. +Vite-Node server options. -#### deps.experimentalOptimizer +#### server.sourcemap -- **Type:** `{ ssr?, web? }` -- **Version:** Since Vitest 0.29.0 -- **See also:** [Dep Optimization Options](https://vitejs.dev/config/dep-optimization-options.html) +- **Type:** `'inline' | boolean` +- **Default:** `'inline'` -Enable dependency optimization. If you have a lot of tests, this might improve their performance. +Inject inline sourcemap to modules. -When Vitest encounters the external library listed in `include`, it will be bundled into a single file using esbuild and imported as a whole module. This is good for several reasons: +#### server.debug -- Importing packages with a lot of imports is expensive. By bundling them into one file we can save a lot of time -- Importing UI libraries is expensive because they are not meant to run inside Node.js -- Your `alias` configuration is now respected inside bundled packages -- Code in your tests is running closer to how it's running in the browser +- **Type:** `{ dumpModules?, loadDumppedModules? }` -Be aware that only packages in `deps.experimentalOptimizer?.[mode].include` option are bundled (some plugins populate this automatically, like Svelte). You can read more about available options in [Vite](https://vitejs.dev/config/dep-optimization-options.html) docs. By default, Vitest uses `experimentalOptimizer.web` for `jsdom` and `happy-dom` environments, and `experimentalOptimizer.ssr` for `node` and `edge` environments, but it is configurable by [`transformMode`](#transformmode). +Vite-Node debugger options. -This options also inherits your `optimizeDeps` configuration (for web Vitest will extend `optimizeDeps`, for ssr - `ssr.optimizeDeps`). If you redefine `include`/`exclude` option in `deps.experimentalOptimizer` it will extend your `optimizeDeps` when running tests. Vitest automatically removes the same options from `include`, if they are listed in `exclude`. +#### server.debug.dumpModules -::: tip -You will not be able to edit your `node_modules` code for debugging, since the code is actually located in your `cacheDir` or `test.cache.dir` directory. If you want to debug with `console.log` statements, edit it directly or force rebundling with `deps.experimentalOptimizer?.[mode].force` option. -::: +- **Type:** `boolean | string` + +Dump the transformed module to filesystem. Passing a string will dump to the specified path. + +#### server.debug.loadDumppedModules + +- **Type:** `boolean` + +Read dumped module from filesystem whenever exists. Useful for debugging by modifying the dump result from the filesystem. -#### deps.external +#### server.deps + +- **Type:** `{ external?, inline?, ... }` + +Handling for dependencies resolution. + +#### server.deps.external - **Type:** `(string | RegExp)[]` - **Default:** `[/\/node_modules\//]` -Externalize means that Vite will bypass the package to native Node. Externalized dependencies will not be applied Vite's transformers and resolvers, so they do not support HMR on reload. Typically, packages under `node_modules` are externalized. - -When using strings they need to be paths inside your [`deps.moduleDirectories`](/config/#deps-moduledirectories). For example `external: ['module/folder']` with the default `moduleDirectories` option will externalize `node_modules/module/folder`. -Regular expressions on the other hand are matched against the whole path. +Externalize means that Vite will bypass the package to native Node. Externalized dependencies will not be applied Vite's transformers and resolvers, so they do not support HMR on reload. All packages under `node_modules` are externalized. -#### deps.inline +#### server.deps.inline - **Type:** `(string | RegExp)[] | true` - **Default:** `[]` @@ -149,7 +155,7 @@ Vite will process inlined modules. This could be helpful to handle packages that If `true`, every dependency will be inlined. All dependencies, specified in [`ssr.noExternal`](https://vitejs.dev/guide/ssr.html#ssr-externals) will be inlined by default. -#### deps.fallbackCJS +#### server.deps.fallbackCJS - **Type** `boolean` - **Default:** `false` @@ -158,6 +164,42 @@ When a dependency is a valid ESM package, try to guess the cjs version based on This might potentially cause some misalignment if a package has different logic in ESM and CJS mode. +#### server.deps.cacheDir + +- **Type** `string` +- **Default**: `'node_modules/.vite'` + +Directory to save cache files. + +### deps + +- **Type:** `{ experimentalOptimizer?, registerNodeLoader?, ... }` + +Handling for dependencies resolution. + +#### deps.experimentalOptimizer + +- **Type:** `{ ssr?, web? }` +- **Version:** Since Vitest 0.29.0 +- **See also:** [Dep Optimization Options](https://vitejs.dev/config/dep-optimization-options.html) + +Enable dependency optimization. If you have a lot of tests, this might improve their performance. + +When Vitest encounters the external library listed in `include`, it will be bundled into a single file using esbuild and imported as a whole module. This is good for several reasons: + +- Importing packages with a lot of imports is expensive. By bundling them into one file we can save a lot of time +- Importing UI libraries is expensive because they are not meant to run inside Node.js +- Your `alias` configuration is now respected inside bundled packages +- Code in your tests is running closer to how it's running in the browser + +Be aware that only packages in `deps.experimentalOptimizer?.[mode].include` option are bundled (some plugins populate this automatically, like Svelte). You can read more about available options in [Vite](https://vitejs.dev/config/dep-optimization-options.html) docs. By default, Vitest uses `experimentalOptimizer.web` for `jsdom` and `happy-dom` environments, and `experimentalOptimizer.ssr` for `node` and `edge` environments, but it is configurable by [`transformMode`](#transformmode). + +This options also inherits your `optimizeDeps` configuration (for web Vitest will extend `optimizeDeps`, for ssr - `ssr.optimizeDeps`). If you redefine `include`/`exclude` option in `deps.experimentalOptimizer` it will extend your `optimizeDeps` when running tests. Vitest automatically removes the same options from `include`, if they are listed in `exclude`. + +::: tip +You will not be able to edit your `node_modules` code for debugging, since the code is actually located in your `cacheDir` or `test.cache.dir` directory. If you want to debug with `console.log` statements, edit it directly or force rebundling with `deps.experimentalOptimizer?.[mode].force` option. +::: + #### deps.registerNodeLoader - **Type:** `boolean` diff --git a/packages/vite-node/src/server.ts b/packages/vite-node/src/server.ts index cad850034238..17834bb2e367 100644 --- a/packages/vite-node/src/server.ts +++ b/packages/vite-node/src/server.ts @@ -39,8 +39,7 @@ export class ViteNodeServer { const ssrOptions = server.config.ssr options.deps ??= {} - - options.deps.cacheDir = relative(server.config.root, server.config.cacheDir) + options.deps.cacheDir = relative(server.config.root, options.deps.cacheDir || server.config.cacheDir) if (ssrOptions) { // we don't externalize ssr, because it has different semantics in Vite diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index 087076ac6375..50ea5fd85c31 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -124,22 +124,7 @@ export function resolveConfig( if (resolved.coverage.provider === 'v8' && resolved.coverage.enabled && isBrowserEnabled(resolved)) throw new Error('@vitest/coverage-v8 does not work with --browser. Use @vitest/coverage-istanbul instead') - resolved.deps = resolved.deps || {} - // vitenode will try to import such file with native node, - // but then our mocker will not work properly - if (resolved.deps.inline !== true) { - // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error - // @ts-ignore ssr is not typed in Vite 2, but defined in Vite 3, so we can't use expect-error - const ssrOptions = viteConfig.ssr - - if (ssrOptions?.noExternal === true && resolved.deps.inline == null) { - resolved.deps.inline = true - } - else { - resolved.deps.inline ??= [] - resolved.deps.inline.push(...extraInlineDeps) - } - } + resolved.deps ??= {} resolved.deps.moduleDirectories ??= [] resolved.deps.moduleDirectories = resolved.deps.moduleDirectories.map((dir) => { if (!dir.startsWith('/')) @@ -151,6 +136,48 @@ export function resolveConfig( if (!resolved.deps.moduleDirectories.includes('/node_modules/')) resolved.deps.moduleDirectories.push('/node_modules/') + resolved.server ??= {} + resolved.server.deps ??= {} + + const deprecatedDepsOptions = ['inline', 'external', 'fallbackCJS'] as const + deprecatedDepsOptions.forEach((option) => { + if (resolved.deps[option] === undefined) + return + + if (option === 'fallbackCJS') { + console.warn(c.yellow(`${c.inverse(c.yellow(' Vitest '))} "deps.${option}" is deprecated. Use "server.deps.${option}" instead`)) + } + else { + const transformMode = resolved.environment === 'happy-dom' || resolved.environment === 'jsdom' ? 'web' : 'ssr' + console.warn( + c.yellow( + `${c.inverse(c.yellow(' Vitest '))} "deps.${option}" is deprecated. If you rely on vite-node directly, use "server.deps.${option}" instead. Otherwise, consider using "deps.optimizer.${transformMode}.${option === 'external' ? 'exclude' : 'include'}"`, + ), + ) + } + + if (resolved.server.deps![option] === undefined) + resolved.server.deps![option] = resolved.deps[option] as any + }) + + // vitenode will try to import such file with native node, + // but then our mocker will not work properly + if (resolved.server.deps.inline !== true) { + // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error + // @ts-ignore ssr is not typed in Vite 2, but defined in Vite 3, so we can't use expect-error + const ssrOptions = viteConfig.ssr + if (ssrOptions?.noExternal === true && resolved.server.deps.inline == null) { + resolved.server.deps.inline = true + } + else { + resolved.server.deps.inline ??= [] + resolved.server.deps.inline.push(...extraInlineDeps) + } + } + + resolved.server.deps.moduleDirectories ??= [] + resolved.server.deps.moduleDirectories.push(...resolved.deps.moduleDirectories) + if (resolved.runner) { resolved.runner = resolveModule(resolved.runner, { paths: [resolved.root] }) ?? resolve(resolved.root, resolved.runner) diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index bfde636754ed..05ef4e94eed2 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -89,7 +89,7 @@ export class Vitest { if (this.config.watch && this.mode !== 'typecheck') this.registerWatcher() - this.vitenode = new ViteNodeServer(server, this.config) + this.vitenode = new ViteNodeServer(server, this.config.server) const node = this.vitenode this.runner = new ViteNodeRunner({ root: server.config.root, diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index b785382e858d..92e20d67f886 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -2,6 +2,7 @@ import type { AliasOptions, CommonServerOptions, DepOptimizationConfig } from 'v import type { PrettyFormatOptions } from 'pretty-format' import type { FakeTimerInstallOpts } from '@sinonjs/fake-timers' import type { SequenceHooks, SequenceSetupFiles } from '@vitest/runner' +import type { ViteNodeServerOptions } from 'vite-node' import type { BuiltinReporters } from '../node/reporters' import type { TestSequencerConstructor } from '../node/sequencers/types' import type { ChaiConfig } from '../integrations/chai' @@ -112,6 +113,8 @@ interface DepsOptions { * And does not support HMR on reload. * * Typically, packages under `node_modules` are externalized. + * + * @deprecated If you rely on vite-node directly, use `server.deps.external` instead. Otherwise, consider using `deps.optimizer.{web,ssr}.exclude`. */ external?: (string | RegExp)[] /** @@ -120,6 +123,8 @@ interface DepsOptions { * This could be helpful to handle packages that ship `.js` in ESM format (that Node can't handle). * * If `true`, every dependency will be inlined + * + * @deprecated If you rely on vite-node directly, use `server.deps.inline` instead. Otherwise, consider using `deps.optimizer.{web,ssr}.include`. */ inline?: (string | RegExp)[] | true @@ -136,6 +141,9 @@ interface DepsOptions { * cause some misalignment if a package have different logic in ESM and CJS mode. * * @default false + * + * @deprecated Use `server.deps.fallbackCJS` instead. + * */ fallbackCJS?: boolean @@ -149,6 +157,7 @@ interface DepsOptions { * A list of directories relative to the config file that should be treated as module directories. * * @default ['node_modules'] + * */ moduleDirectories?: string[] } @@ -188,9 +197,15 @@ export interface InlineConfig { /** * Handling for dependencies inlining or externalizing + * */ deps?: DepsOptions + /** + * Vite-node server options + */ + server?: Omit + /** * Base directory to scan for the test files * diff --git a/test/core/vitest.config.ts b/test/core/vitest.config.ts index 837aed1e3085..9da89210a05d 100644 --- a/test/core/vitest.config.ts +++ b/test/core/vitest.config.ts @@ -66,10 +66,14 @@ export default defineConfig({ seed: 101, }, deps: { - external: ['tinyspy', /src\/external/, /esm\/esm/], - inline: ['inline-lib'], moduleDirectories: ['node_modules', 'projects', 'packages'], }, + server: { + deps: { + external: ['tinyspy', /src\/external/, /esm\/esm/], + inline: ['inline-lib'], + }, + }, alias: [ { find: 'test-alias', diff --git a/test/resolve/vitest.config.ts b/test/resolve/vitest.config.ts index f18228bd9474..ccd1abd987b3 100644 --- a/test/resolve/vitest.config.ts +++ b/test/resolve/vitest.config.ts @@ -6,8 +6,10 @@ export default defineConfig({ ['**/web.test.ts', 'happy-dom'], ['**/ssr.test.ts', 'node'], ], - deps: { - external: [/pkg-/], + server: { + deps: { + external: [/pkg-/], + }, }, }, })