1
- import fs from 'fs'
2
1
import path from 'path'
3
2
import { pathToFileURL } from 'url'
4
- import { ViteDevServer } from '..'
3
+ import { ViteDevServer } from '../server '
5
4
import {
6
5
dynamicImport ,
7
- cleanUrl ,
8
6
isBuiltin ,
9
- resolveFrom ,
10
7
unwrapId ,
11
8
usingDynamicImport
12
9
} from '../utils'
@@ -19,6 +16,8 @@ import {
19
16
ssrDynamicImportKey
20
17
} from './ssrTransform'
21
18
import { transformRequest } from '../server/transformRequest'
19
+ import { InternalResolveOptions , tryNodeResolve } from '../plugins/resolve'
20
+ import { hookNodeResolve } from '../plugins/ssrRequireHook'
22
21
23
22
interface SSRContext {
24
23
global : typeof globalThis
@@ -96,13 +95,32 @@ async function instantiateModule(
96
95
urlStack = urlStack . concat ( url )
97
96
const isCircular = ( url : string ) => urlStack . includes ( url )
98
97
98
+ const {
99
+ isProduction,
100
+ resolve : { dedupe } ,
101
+ root
102
+ } = server . config
103
+
104
+ const resolveOptions : InternalResolveOptions = {
105
+ conditions : [ 'node' ] ,
106
+ dedupe,
107
+ // Prefer CommonJS modules.
108
+ extensions : [ '.js' , '.mjs' , '.ts' , '.jsx' , '.tsx' , '.json' ] ,
109
+ isBuild : true ,
110
+ isProduction,
111
+ // Disable "module" condition.
112
+ isRequire : true ,
113
+ mainFields : [ 'main' ] ,
114
+ root
115
+ }
116
+
99
117
// Since dynamic imports can happen in parallel, we need to
100
118
// account for multiple pending deps and duplicate imports.
101
119
const pendingDeps : string [ ] = [ ]
102
120
103
121
const ssrImport = async ( dep : string ) => {
104
122
if ( dep [ 0 ] !== '.' && dep [ 0 ] !== '/' ) {
105
- return nodeImport ( dep , mod . file , server . config )
123
+ return nodeImport ( dep , mod . file ! , resolveOptions )
106
124
}
107
125
dep = unwrapId ( dep )
108
126
if ( ! isCircular ( dep ) && ! pendingImports . get ( dep ) ?. some ( isCircular ) ) {
@@ -185,21 +203,48 @@ async function instantiateModule(
185
203
// In node@12+ we can use dynamic import to load CJS and ESM
186
204
async function nodeImport (
187
205
id : string ,
188
- importer : string | null ,
189
- config : ViteDevServer [ 'config' ]
206
+ importer : string ,
207
+ resolveOptions : InternalResolveOptions
190
208
) {
209
+ // Node's module resolution is hi-jacked so Vite can ensure the
210
+ // configured `resolve.dedupe` and `mode` options are respected.
211
+ const viteResolve = ( id : string , importer : string ) => {
212
+ const resolved = tryNodeResolve ( id , importer , resolveOptions , false )
213
+ if ( ! resolved ) {
214
+ const err : any = new Error (
215
+ `Cannot find module '${ id } ' imported from '${ importer } '`
216
+ )
217
+ err . code = 'ERR_MODULE_NOT_FOUND'
218
+ throw err
219
+ }
220
+ return resolved . id
221
+ }
222
+
223
+ // When an ESM module imports an ESM dependency, this hook is *not* used.
224
+ const unhookNodeResolve = hookNodeResolve (
225
+ ( nodeResolve ) => ( id , parent , isMain , options ) =>
226
+ id [ 0 ] === '.' || isBuiltin ( id )
227
+ ? nodeResolve ( id , parent , isMain , options )
228
+ : viteResolve ( id , parent . id )
229
+ )
230
+
191
231
let url : string
192
232
// `resolve` doesn't handle `node:` builtins, so handle them directly
193
233
if ( id . startsWith ( 'node:' ) || isBuiltin ( id ) ) {
194
234
url = id
195
235
} else {
196
- url = resolve ( id , importer , config . root , ! ! config . resolve . preserveSymlinks )
236
+ url = viteResolve ( id , importer )
197
237
if ( usingDynamicImport ) {
198
238
url = pathToFileURL ( url ) . toString ( )
199
239
}
200
240
}
201
- const mod = await dynamicImport ( url )
202
- return proxyESM ( id , mod )
241
+
242
+ try {
243
+ const mod = await dynamicImport ( url )
244
+ return proxyESM ( id , mod )
245
+ } finally {
246
+ unhookNodeResolve ( )
247
+ }
203
248
}
204
249
205
250
// rollup-style default import interop for cjs
@@ -216,25 +261,3 @@ function proxyESM(id: string, mod: any) {
216
261
}
217
262
} )
218
263
}
219
-
220
- const resolveCache = new Map < string , string > ( )
221
-
222
- function resolve (
223
- id : string ,
224
- importer : string | null ,
225
- root : string ,
226
- preserveSymlinks : boolean
227
- ) {
228
- const key = id + importer + root
229
- const cached = resolveCache . get ( key )
230
- if ( cached ) {
231
- return cached
232
- }
233
- const resolveDir =
234
- importer && fs . existsSync ( cleanUrl ( importer ) )
235
- ? path . dirname ( importer )
236
- : root
237
- const resolved = resolveFrom ( id , resolveDir , preserveSymlinks , true )
238
- resolveCache . set ( key , resolved )
239
- return resolved
240
- }
0 commit comments