Skip to content

Commit

Permalink
feat: support ssr optimizer (#3490)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Jun 1, 2023
1 parent b6c4a6c commit 898422b
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 84 deletions.
2 changes: 1 addition & 1 deletion packages/vitest/src/node/create.ts
Expand Up @@ -28,7 +28,7 @@ export async function createVitest(mode: VitestRunMode, options: UserConfig, vit
const server = await createServer(mergeConfig(config, mergeConfig(viteOverrides, { root: options.root })))

// optimizer needs .listen() to be called
if (ctx.config.api?.port || ctx.config.deps?.experimentalOptimizer?.enabled)
if (ctx.config.api?.port || ctx.config.deps?.experimentalOptimizer?.web?.enabled || ctx.config.deps?.experimentalOptimizer?.ssr?.enabled)
await server.listen()
else
await server.pluginContainer.buildStart({})
Expand Down
64 changes: 16 additions & 48 deletions packages/vitest/src/node/plugins/index.ts
@@ -1,5 +1,3 @@
import { builtinModules } from 'node:module'
import { version as viteVersion } from 'vite'
import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite'
import { relative } from 'pathe'
import { configDefaults } from '../../defaults'
Expand All @@ -14,6 +12,7 @@ import { GlobalSetupPlugin } from './globalSetup'
import { CSSEnablerPlugin } from './cssEnabler'
import { CoverageTransform } from './coverageTransform'
import { MocksPlugin } from './mocks'
import { resolveOptimizerConfig } from './utils'

export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('test')): Promise<VitePlugin[]> {
const userConfig = deepMerge({}, options) as UserConfig
Expand Down Expand Up @@ -42,13 +41,13 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t
// preliminary merge of options to be able to create server options for vite
// however to allow vitest plugins to modify vitest config values
// this is repeated in configResolved where the config is final
const preOptions = deepMerge(
const testConfig = deepMerge(
{} as UserConfig,
configDefaults,
options,
removeUndefinedValues(viteConfig.test ?? {}),
)
preOptions.api = resolveApiServerConfig(preOptions)
testConfig.api = resolveApiServerConfig(testConfig)

if (viteConfig.define) {
delete viteConfig.define['import.meta.vitest']
Expand Down Expand Up @@ -90,8 +89,8 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t

let open: string | boolean | undefined

if (preOptions.ui && preOptions.open)
open = preOptions.uiBase ?? '/__vitest__/'
if (testConfig.ui && testConfig.open)
open = testConfig.uiBase ?? '/__vitest__/'

const config: ViteConfig = {
root: viteConfig.test?.root || options.root,
Expand All @@ -105,24 +104,24 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t
// by default Vite resolves `module` field, which not always a native ESM module
// setting this option can bypass that and fallback to cjs version
mainFields: [],
alias: preOptions.alias,
alias: testConfig.alias,
conditions: ['node'],
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-ignore we support Vite ^3.0, but browserField is available in Vite ^3.2
browserField: false,
},
server: {
...preOptions.api,
...testConfig.api,
watch: {
ignored: preOptions.watchExclude,
ignored: testConfig.watchExclude,
},
open,
hmr: false,
preTransformRequests: false,
},
}

const classNameStrategy = (typeof preOptions.css !== 'boolean' && preOptions.css?.modules?.classNameStrategy) || 'stable'
const classNameStrategy = (typeof testConfig.css !== 'boolean' && testConfig.css?.modules?.classNameStrategy) || 'stable'

if (classNameStrategy !== 'scoped') {
config.css ??= {}
Expand All @@ -135,45 +134,14 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t
}
}

const optimizeConfig: Partial<ViteConfig> = {}
const optimizer = preOptions.deps?.experimentalOptimizer
const [major, minor] = viteVersion.split('.').map(Number)
const allowed = major >= 5 || (major === 4 && minor >= 3)
if (!allowed && optimizer?.enabled === true)
console.warn(`Vitest: "deps.experimentalOptimizer" is only available in Vite >= 4.3.0, current Vite version: ${viteVersion}`)
if (!allowed || optimizer?.enabled !== true) {
optimizeConfig.cacheDir = undefined
optimizeConfig.optimizeDeps = {
// experimental in Vite >2.9.2, entries remains to help with older versions
disabled: true,
entries: [],
}
}
else {
const cacheDir = preOptions.cache !== false ? preOptions.cache?.dir : null
optimizeConfig.cacheDir = cacheDir ?? 'node_modules/.vitest'
const exclude = [
'vitest',
...builtinModules,
...optimizer.exclude || [],
...viteConfig.optimizeDeps?.exclude || [],
]
const include = [
...optimizer.include || [],
...viteConfig.optimizeDeps?.include || [],
].filter((n: string) => !exclude.includes(n))

optimizeConfig.optimizeDeps = {
...viteConfig.optimizeDeps,
...optimizer,
noDiscovery: true,
disabled: false,
entries: [],
exclude,
include,
}
const webOptimizer = resolveOptimizerConfig(testConfig.deps?.experimentalOptimizer?.web, viteConfig.optimizeDeps, testConfig)
const ssrOptimizer = resolveOptimizerConfig(testConfig.deps?.experimentalOptimizer?.ssr, viteConfig.ssr?.optimizeDeps, testConfig)

config.cacheDir = webOptimizer.cacheDir || ssrOptimizer.cacheDir || config.cacheDir
config.optimizeDeps = webOptimizer.optimizeDeps
config.ssr = {
optimizeDeps: ssrOptimizer.optimizeDeps,
}
Object.assign(config, optimizeConfig)

return config
},
Expand Down
34 changes: 34 additions & 0 deletions packages/vitest/src/node/plugins/utils.ts
@@ -0,0 +1,34 @@
import { builtinModules } from 'node:module'
import { version as viteVersion } from 'vite'
import type { DepOptimizationOptions } from 'vite'
import type { DepsOptimizationOptions, InlineConfig } from '../../types'

export function resolveOptimizerConfig(testOptionc: DepsOptimizationOptions | undefined, viteOptions: DepOptimizationOptions | undefined, testConfig: InlineConfig) {
const newConfig: { cacheDir?: string; optimizeDeps: DepOptimizationOptions } = {} as any
const [major, minor] = viteVersion.split('.').map(Number)
const allowed = major >= 5 || (major === 4 && minor >= 3)
if (!allowed && testOptionc?.enabled === true)
console.warn(`Vitest: "deps.optimizer" is only available in Vite >= 4.3.0, current Vite version: ${viteVersion}`)
if (!allowed || testOptionc?.enabled !== true) {
newConfig.cacheDir = undefined
newConfig.optimizeDeps = {
// experimental in Vite >2.9.2, entries remains to help with older versions
disabled: true,
entries: [],
}
}
else {
const cacheDir = testConfig.cache !== false ? testConfig.cache?.dir : null
newConfig.cacheDir = cacheDir ?? 'node_modules/.vitest'
newConfig.optimizeDeps = {
...viteOptions,
...testOptionc,
noDiscovery: true,
disabled: false,
entries: [],
exclude: ['vitest', ...builtinModules, ...(testOptionc.exclude || viteOptions?.exclude || [])],
include: (testOptionc.include || viteOptions?.include || []).filter((n: string) => n !== 'vitest'),
}
}
return newConfig
}
37 changes: 8 additions & 29 deletions packages/vitest/src/node/plugins/workspace.ts
@@ -1,7 +1,5 @@
import { builtinModules } from 'node:module'
import { dirname, relative } from 'pathe'
import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite'
import { version as viteVersion } from 'vite'
import { configDefaults } from '../../defaults'
import { generateScopedClassName } from '../../integrations/css/css-modules'
import { deepMerge } from '../../utils/base'
Expand All @@ -12,6 +10,7 @@ import { CSSEnablerPlugin } from './cssEnabler'
import { EnvReplacerPlugin } from './envReplacer'
import { GlobalSetupPlugin } from './globalSetup'
import { MocksPlugin } from './mocks'
import { resolveOptimizerConfig } from './utils'

interface WorkspaceOptions extends UserWorkspaceConfig {
root?: string
Expand Down Expand Up @@ -118,34 +117,14 @@ export function WorkspaceVitestPlugin(project: WorkspaceProject, options: Worksp
}
}

const optimizeConfig: Partial<ViteConfig> = {}
const optimizer = testConfig.deps?.experimentalOptimizer
const [major, minor] = viteVersion.split('.').map(Number)
const allowed = major >= 5 || (major === 4 && minor >= 3)
if (!allowed && optimizer?.enabled === true)
console.warn(`Vitest: "deps.experimentalOptimizer" is only available in Vite >= 4.3.0, current Vite version: ${viteVersion}`)
if (!allowed || optimizer?.enabled !== true) {
optimizeConfig.cacheDir = undefined
optimizeConfig.optimizeDeps = {
// experimental in Vite >2.9.2, entries remains to help with older versions
disabled: true,
entries: [],
}
}
else {
const cacheDir = testConfig.cache !== false ? testConfig.cache?.dir : null
optimizeConfig.cacheDir = cacheDir ?? 'node_modules/.vitest'
optimizeConfig.optimizeDeps = {
...viteConfig.optimizeDeps,
...optimizer,
noDiscovery: true,
disabled: false,
entries: [],
exclude: ['vitest', ...builtinModules, ...(optimizer.exclude || viteConfig.optimizeDeps?.exclude || [])],
include: (optimizer.include || viteConfig.optimizeDeps?.include || []).filter((n: string) => n !== 'vitest'),
}
const webOptimizer = resolveOptimizerConfig(testConfig.deps?.experimentalOptimizer?.web, viteConfig.optimizeDeps, testConfig)
const ssrOptimizer = resolveOptimizerConfig(testConfig.deps?.experimentalOptimizer?.ssr, viteConfig.ssr?.optimizeDeps, testConfig)

config.cacheDir = webOptimizer.cacheDir || ssrOptimizer.cacheDir || config.cacheDir
config.optimizeDeps = webOptimizer.optimizeDeps
config.ssr = {
optimizeDeps: ssrOptimizer.optimizeDeps,
}
Object.assign(config, optimizeConfig)

return config
},
Expand Down
11 changes: 8 additions & 3 deletions packages/vitest/src/node/workspace.ts
Expand Up @@ -52,7 +52,7 @@ export async function initializeProject(workspacePath: string | number, ctx: Vit
const server = await createServer(config)

// optimizer needs .listen() to be called
if (ctx.config.api?.port || project.config.deps?.experimentalOptimizer?.enabled)
if (ctx.config.api?.port || project.config.deps?.experimentalOptimizer?.web?.enabled || project.config.deps?.experimentalOptimizer?.ssr?.enabled)
await server.listen()
else
await server.pluginContainer.buildStart({})
Expand Down Expand Up @@ -269,8 +269,13 @@ export class WorkspaceProject {
reporters: [],
deps: {
...this.config.deps,
experimentalOptimizer: {
enabled: this.config.deps?.experimentalOptimizer?.enabled ?? false,
optimizer: {
web: {
enabled: this.config.deps?.experimentalOptimizer?.web?.enabled ?? false,
},
ssr: {
enabled: this.config.deps?.experimentalOptimizer?.ssr?.enabled ?? false,
},
},
},
snapshotOptions: {
Expand Down
9 changes: 7 additions & 2 deletions packages/vitest/src/types/config.ts
Expand Up @@ -70,12 +70,17 @@ interface SequenceOptions {
hooks?: SequenceHooks
}

export type DepsOptimizationOptions = Omit<DepOptimizationConfig, 'disabled' | 'noDiscovery'> & {
enabled: boolean
}

interface DepsOptions {
/**
* Enable dependency optimization. This can improve the performance of your tests.
*/
experimentalOptimizer?: Omit<DepOptimizationConfig, 'disabled' | 'noDiscovery'> & {
enabled: boolean
experimentalOptimizer?: {
web?: DepsOptimizationOptions
ssr?: DepsOptimizationOptions
}
/**
* Externalize means that Vite will bypass the package to native Node.
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/utils/base.ts
Expand Up @@ -129,7 +129,7 @@ export function stdout(): NodeJS.WriteStream {
}

export function getEnvironmentTransformMode(config: ResolvedConfig, environment: VitestEnvironment) {
if (!config.deps?.experimentalOptimizer?.enabled)
if (!config.deps?.experimentalOptimizer?.ssr?.enabled && !config.deps?.experimentalOptimizer?.web?.enabled)
return undefined
return (environment === 'happy-dom' || environment === 'jsdom') ? 'web' : 'ssr'
}
Expand Down

0 comments on commit 898422b

Please sign in to comment.