Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce server option #3725

Merged
merged 18 commits into from Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
109 changes: 88 additions & 21 deletions docs/config/index.md
Expand Up @@ -101,43 +101,74 @@ 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?, transformMode?, ... }`
- **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.transformMode

- 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:** `{ web?, ssr? }`

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).
Determine the transform method for all modules inported inside a test that matches the glob pattern. By default, relies on the environment. For example, tests with JSDOM environment will process all files with `ssr: false` flag and tests with Node environment process all modules with `ssr: true`.
sheremet-va marked this conversation as resolved.
Show resolved Hide resolved

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.transformMode.ssr

::: 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:** `string[]`
- **Default:** `[]`

Use SSR transform pipeline for all modules inside specified tests.<br>
Vite plugins will receive `ssr: true` flag when processing those files.

#### server.transformMode&#46;web

- **Type:** `string[]`
- **Default:** `[]`

First do a normal transform pipeline (targeting browser), then do a SSR rewrite to run the code in Node.<br>
Vite plugins will receive `ssr: false` flag when processing those files.

#### server.debug

- **Type:** `{ dumpModules?, loadDumppedModules? }`

Vite-Node debugger options.

#### server.debug.dumpModules

- **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/**']`
sheremet-va marked this conversation as resolved.
Show resolved Hide resolved

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.

#### deps.inline
#### server.deps.inline

- **Type:** `(string | RegExp)[] | true`
- **Default:** `[]`
Expand All @@ -146,7 +177,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`
Expand All @@ -155,6 +186,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<NonProjectOption />

- **Type:** `boolean`
Expand Down
3 changes: 1 addition & 2 deletions packages/vite-node/src/server.ts
Expand Up @@ -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
Expand Down
50 changes: 35 additions & 15 deletions packages/vitest/src/node/config.ts
Expand Up @@ -124,30 +124,50 @@ 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 || {}
resolved.deps ??= {}
resolved.deps.moduleDirectories ??= ['/node_modules/']
sheremet-va marked this conversation as resolved.
Show resolved Hide resolved
resolved.deps.moduleDirectories = resolved.deps.moduleDirectories.map((dir) => {
if (!dir.startsWith('/'))
dir = `/${dir}`
if (!dir.endsWith('/'))
dir += '/'
return normalize(dir)
})

resolved.server ??= {}
resolved.server.deps ??= {}

const deprecatedDepsOptions = ['inline', 'external', 'fallbackCJS'] as const
deprecatedDepsOptions.forEach((option) => {
if (resolved.deps[option] !== undefined) {
console.warn(
c.yellow(
`${c.inverse(c.yellow(' Vitest '))} "deps.${option}" is deprecated, use "server.deps.${option}" instead`,
fenghan34 marked this conversation as resolved.
Show resolved Hide resolved
),
)

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.deps.inline !== true) {
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.deps.inline == null) {
resolved.deps.inline = true
if (ssrOptions?.noExternal === true && resolved.server.deps.inline == null) {
resolved.server.deps.inline = true
}
else {
resolved.deps.inline ??= []
resolved.deps.inline.push(...extraInlineDeps)
resolved.server.deps.inline ??= []
resolved.server.deps.inline.push(...extraInlineDeps)
}
}
resolved.deps.moduleDirectories ??= ['/node_modules/']
resolved.deps.moduleDirectories = resolved.deps.moduleDirectories.map((dir) => {
if (!dir.startsWith('/'))
dir = `/${dir}`
if (!dir.endsWith('/'))
dir += '/'
return normalize(dir)
})

resolved.server.deps.moduleDirectories ??= []
resolved.server.deps.moduleDirectories.push(...resolved.deps.moduleDirectories)
sheremet-va marked this conversation as resolved.
Show resolved Hide resolved

if (resolved.runner) {
resolved.runner = resolveModule(resolved.runner, { paths: [resolved.root] })
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/core.ts
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions packages/vitest/src/types/config.ts
Expand Up @@ -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'
Expand Down Expand Up @@ -112,6 +113,8 @@ interface DepsOptions {
* And does not support HMR on reload.
*
* Typically, packages under `node_modules` are externalized.
*
* @deprecated Use `server.deps.external` instead
sheremet-va marked this conversation as resolved.
Show resolved Hide resolved
*/
external?: (string | RegExp)[]
/**
Expand All @@ -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 Use `server.deps.inline` instead
*/
inline?: (string | RegExp)[] | true

Expand All @@ -136,6 +141,8 @@ 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

Expand All @@ -149,6 +156,7 @@ interface DepsOptions {
* A list of directories relative to the config file that should be treated as module directories.
*
* @default ['node_modules']
*
*/
moduleDirectories?: string[]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moduleDiretories is not strictly server option, it's also reused in mocking. Maybe we can even move it our if deps into a root config, and duplicate in server if needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. It will duplicate deps.moduleDiretories in server.

}
Expand Down Expand Up @@ -188,9 +196,15 @@ export interface InlineConfig {

/**
* Handling for dependencies inlining or externalizing
*
*/
deps?: DepsOptions

/**
* Vite-node server options
*/
server?: ViteNodeServerOptions

/**
* Base directory to scan for the test files
*
Expand Down
8 changes: 6 additions & 2 deletions test/core/vitest.config.ts
Expand Up @@ -66,10 +66,14 @@ export default defineConfig({
seed: 101,
},
deps: {
external: ['tinyspy', /src\/external/],
inline: ['inline-lib'],
moduleDirectories: ['node_modules', 'projects', 'packages'],
},
server: {
deps: {
external: ['tinyspy', /src\/external/],
inline: ['inline-lib'],
},
},
alias: [
{
find: 'test-alias',
Expand Down
6 changes: 4 additions & 2 deletions test/resolve/vitest.config.ts
Expand Up @@ -6,8 +6,10 @@ export default defineConfig({
['**/web.test.ts', 'happy-dom'],
['**/ssr.test.ts', 'node'],
],
deps: {
external: [/pkg-/],
server: {
deps: {
external: [/pkg-/],
},
},
},
})