diff --git a/docs/config/index.md b/docs/config/index.md index b0764ea364b5..7f83796df3b3 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -534,6 +534,28 @@ Vitest will not fail, if no tests will be found. Show heap usage after each test. Useful for debugging memory leaks. +### css + +- **Type**: `boolean | { include?, exclude? }` + +Configure if CSS should be processed. When excluded, CSS files will be replaced with empty strings to bypass the subsequent processing. + +By default, processes only CSS Modules, because it affects runtime. JSDOM and Happy DOM don't fully support injecting CSS, so disabling this setting might help with performance. + +#### css.include + +- **Type**: `RegExp | RegExp[]` +- **Default**: `[/\.module\./]` + +RegExp pattern for files that should return actual CSS and will be processed by Vite pipeline. + +#### css.exclude + +- **Type**: `RegExp | RegExp[]` +- **Default**: `[]` + +RegExp pattern for files that will return en empty CSS file. + ### maxConcurrency - **Type**: `number` diff --git a/packages/vitest/src/defaults.ts b/packages/vitest/src/defaults.ts index 7f7a9e6694e9..d757080c296a 100644 --- a/packages/vitest/src/defaults.ts +++ b/packages/vitest/src/defaults.ts @@ -70,6 +70,9 @@ const config = { ui: false, uiBase: '/__vitest__/', open: true, + css: { + include: [/\.module\./], + }, coverage: coverageConfigDefaults, fakeTimers: fakeTimersDefaults, maxConcurrency: 5, diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index d2609b705db8..bab2db662e67 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -175,5 +175,9 @@ export function resolveConfig( if (resolved.changed) resolved.passWithNoTests ??= true + resolved.css ??= {} + if (typeof resolved.css === 'object') + resolved.css.include ??= [/\.module\./] + return resolved } diff --git a/packages/vitest/src/node/plugins/cssEnabler.ts b/packages/vitest/src/node/plugins/cssEnabler.ts new file mode 100644 index 000000000000..3c3dc3d116bb --- /dev/null +++ b/packages/vitest/src/node/plugins/cssEnabler.ts @@ -0,0 +1,34 @@ +import type { Plugin as VitePlugin } from 'vite' +import { toArray } from '../../utils' +import type { Vitest } from '../core' + +const cssLangs = '\\.(css|less|sass|scss|styl|stylus|pcss|postcss)($|\\?)' +const cssLangRE = new RegExp(cssLangs) + +const isCSS = (id: string) => { + return cssLangRE.test(id) +} + +export function CSSEnablerPlugin(ctx: Vitest): VitePlugin { + const shouldProcessCSS = (id: string) => { + const { css } = ctx.config + if (typeof css === 'boolean') + return css + if (toArray(css.exclude).some(re => re.test(id))) + return false + if (toArray(css.include).some(re => re.test(id))) + return true + return false + } + + return { + name: 'vitest:css-enabler', + enforce: 'pre', + transform(code, id) { + if (!isCSS(id)) + return + if (!shouldProcessCSS(id)) + return '' + }, + } +} diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index 750f11e149a0..5a66f5de5d09 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -7,6 +7,7 @@ import { Vitest } from '../core' import { EnvReplacerPlugin } from './envRelacer' import { GlobalSetupPlugin } from './globalSetup' import { MocksPlugin } from './mock' +import { CSSEnablerPlugin } from './cssEnabler' export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()): Promise { let haveStarted = false @@ -144,6 +145,7 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()) EnvReplacerPlugin(), MocksPlugin(), GlobalSetupPlugin(ctx), + CSSEnablerPlugin(ctx), options.ui ? await UIPlugin() : null, diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index 2ecaaf7d0704..f5abed1209bf 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -345,6 +345,17 @@ export interface InlineConfig { */ onConsoleLog?: (log: string, type: 'stdout' | 'stderr') => false | void + /** + * Indicates if CSS files should be processed. + * + * When excluded, the CSS files will be replaced with empty strings to bypass the subsequent processing. + * + * @default { include: [/\.module\./] } + */ + css?: boolean | { + include?: RegExp | RegExp[] + exclude?: RegExp | RegExp[] + } /** * A number of tests that are allowed to run at the same time marked with `test.concurrent`. * @default 5