1- import type { ResolvedConfig , UserConfig , Plugin as VitePlugin } from 'vite'
1+ import type { ResolveOptions , UserConfig , Plugin as VitePlugin } from 'vite'
22import { builtinModules } from 'node:module'
33import { normalize } from 'pathe'
4- import { mergeConfig } from 'vite'
54import { escapeRegExp } from '../../utils/base'
65import { resolveOptimizerConfig } from './utils'
76
87export function ModuleRunnerTransform ( ) : VitePlugin {
8+ let testConfig : NonNullable < UserConfig [ 'test' ] >
9+ const noExternal : ( string | RegExp ) [ ] = [ ]
10+ const external : ( string | RegExp ) [ ] = [ ]
11+ let noExternalAll = false
12+
913 // make sure Vite always applies the module runner transform
1014 return {
1115 name : 'vitest:environments-module-runner' ,
1216 config : {
1317 order : 'post' ,
1418 handler ( config ) {
15- const testConfig = config . test || { }
19+ testConfig = config . test || { }
1620
1721 config . environments ??= { }
1822
@@ -53,11 +57,6 @@ export function ModuleRunnerTransform(): VitePlugin {
5357 testConfig . deps ??= { }
5458 testConfig . deps . moduleDirectories = moduleDirectories
5559
56- const external : ( string | RegExp ) [ ] = [ ]
57- const noExternal : ( string | RegExp ) [ ] = [ ]
58-
59- let noExternalAll : true | undefined
60-
6160 for ( const name of names ) {
6261 config . environments [ name ] ??= { }
6362
@@ -73,117 +72,52 @@ export function ModuleRunnerTransform(): VitePlugin {
7372 }
7473 environment . dev . preTransformRequests = false
7574 environment . keepProcessEnv = true
75+ }
76+ } ,
77+ } ,
78+ configEnvironment : {
79+ order : 'post' ,
80+ handler ( name , config ) {
81+ if ( name === '__vitest_vm__' || name === '__vitest__' ) {
82+ return
83+ }
7684
77- const resolveExternal = name === 'client'
78- ? config . resolve ?. external
79- : [ ]
80- const resolveNoExternal = name === 'client'
81- ? config . resolve ?. noExternal
82- : [ ]
83-
84- const topLevelResolveOptions : UserConfig [ 'resolve' ] = { }
85- if ( resolveExternal != null ) {
86- topLevelResolveOptions . external = resolveExternal
87- }
88- if ( resolveNoExternal != null ) {
89- topLevelResolveOptions . noExternal = resolveNoExternal
90- }
91-
92- const currentResolveOptions = mergeConfig (
93- topLevelResolveOptions ,
94- environment . resolve || { } ,
95- ) as ResolvedConfig [ 'resolve' ]
96-
97- const envNoExternal = resolveViteResolveOptions ( 'noExternal' , currentResolveOptions , moduleDirectories )
98- if ( envNoExternal === true ) {
99- noExternalAll = true
100- }
101- else if ( envNoExternal . length ) {
102- noExternal . push ( ...envNoExternal )
103- }
104- else if ( name === 'client' || name === 'ssr' ) {
105- const deprecatedNoExternal = resolveDeprecatedOptions (
106- name === 'client'
107- ? config . resolve ?. noExternal
108- : config . ssr ?. noExternal ,
109- moduleDirectories ,
110- )
111- if ( deprecatedNoExternal === true ) {
112- noExternalAll = true
113- }
114- else {
115- noExternal . push ( ...deprecatedNoExternal )
116- }
117- }
118-
119- const envExternal = resolveViteResolveOptions ( 'external' , currentResolveOptions , moduleDirectories )
120- if ( envExternal !== true && envExternal . length ) {
121- external . push ( ...envExternal )
122- }
123- else if ( name === 'client' || name === 'ssr' ) {
124- const deprecatedExternal = resolveDeprecatedOptions (
125- name === 'client'
126- ? config . resolve ?. external
127- : config . ssr ?. external ,
128- moduleDirectories ,
129- )
130- if ( deprecatedExternal !== true ) {
131- external . push ( ...deprecatedExternal )
132- }
133- }
134-
135- // remove Vite's externalization logic because we have our own (unfortunetly)
136- environment . resolve ??= { }
137-
138- environment . resolve . external = [
139- ...builtinModules ,
140- ...builtinModules . map ( m => `node:${ m } ` ) ,
141- ]
142- // by setting `noExternal` to `true`, we make sure that
143- // Vite will never use its own externalization mechanism
144- // to externalize modules and always resolve static imports
145- // in both SSR and Client environments
146- environment . resolve . noExternal = true
147-
148- // Workaround `noExternal` merging bug on Vite 6
149- // https://github.com/vitejs/vite/pull/20502
150- if ( name === 'ssr' ) {
151- delete config . ssr ?. noExternal
152- delete config . ssr ?. external
153- }
154-
155- if ( name === '__vitest_vm__' || name === '__vitest__' ) {
156- continue
157- }
158-
159- const currentOptimizeDeps = environment . optimizeDeps || (
160- name === 'client'
161- ? config . optimizeDeps
162- : name === 'ssr'
163- ? config . ssr ?. optimizeDeps
164- : undefined
165- )
166-
167- const optimizeDeps = resolveOptimizerConfig (
168- testConfig . deps ?. optimizer ?. [ name ] ,
169- currentOptimizeDeps ,
170- )
85+ config . resolve ??= { }
86+ const envNoExternal = resolveViteResolveOptions ( 'noExternal' , config . resolve , testConfig . deps ?. moduleDirectories )
87+ if ( envNoExternal === true ) {
88+ noExternalAll = true
89+ }
90+ else if ( envNoExternal . length ) {
91+ noExternal . push ( ...envNoExternal )
92+ }
17193
172- // Vite respects the root level optimize deps, so we override it instead
173- if ( name === 'client' ) {
174- config . optimizeDeps = optimizeDeps
175- environment . optimizeDeps = undefined
176- }
177- else if ( name === 'ssr' ) {
178- config . ssr ??= { }
179- config . ssr . optimizeDeps = optimizeDeps
180- environment . optimizeDeps = undefined
181- }
182- else {
183- environment . optimizeDeps = optimizeDeps
184- }
94+ const envExternal = resolveViteResolveOptions ( 'external' , config . resolve , testConfig . deps ?. moduleDirectories )
95+ if ( envExternal !== true && envExternal . length ) {
96+ external . push ( ...envExternal )
18597 }
18698
99+ // remove Vite's externalization logic because we have our own (unfortunately)
100+ config . resolve . external = [
101+ ...builtinModules ,
102+ ...builtinModules . map ( m => `node:${ m } ` ) ,
103+ ]
104+
105+ // by setting `noExternal` to `true`, we make sure that
106+ // Vite will never use its own externalization mechanism
107+ // to externalize modules and always resolve static imports
108+ // in both SSR and Client environments
109+ config . resolve . noExternal = true
110+
111+ config . optimizeDeps = resolveOptimizerConfig (
112+ testConfig ?. deps ?. optimizer ?. [ name ] ,
113+ config . optimizeDeps ,
114+ )
115+ } ,
116+ } ,
117+ configResolved : {
118+ order : 'pre' ,
119+ handler ( config ) {
120+ const testConfig = config . test !
187121 testConfig . server ??= { }
188122 testConfig . server . deps ??= { }
189123
@@ -207,7 +141,7 @@ export function ModuleRunnerTransform(): VitePlugin {
207141
208142function resolveViteResolveOptions (
209143 key : 'noExternal' | 'external' ,
210- options : ResolvedConfig [ 'resolve' ] ,
144+ options : Pick < ResolveOptions , 'noExternal' | 'external' > ,
211145 moduleDirectories : string [ ] | undefined ,
212146) : true | ( string | RegExp ) [ ] {
213147 if ( Array . isArray ( options [ key ] ) ) {
@@ -229,22 +163,6 @@ function resolveViteResolveOptions(
229163 return [ ]
230164}
231165
232- function resolveDeprecatedOptions (
233- options : string | RegExp | ( string | RegExp ) [ ] | true | undefined ,
234- moduleDirectories : string [ ] | undefined ,
235- ) : true | ( string | RegExp ) [ ] {
236- if ( options === true ) {
237- return true
238- }
239- else if ( Array . isArray ( options ) ) {
240- return options . map ( dep => processWildcard ( dep , moduleDirectories ) )
241- }
242- else if ( options != null ) {
243- return [ processWildcard ( options , moduleDirectories ) ]
244- }
245- return [ ]
246- }
247-
248166function processWildcard ( dep : string | RegExp , moduleDirectories : string [ ] | undefined ) {
249167 if ( typeof dep !== 'string' ) {
250168 return dep
0 commit comments