Skip to content

Commit baba1f9

Browse files
natemoo-realeclarsonmatthewp
authored
fix: add import support to ssrModuleLoader (#5197)
Co-authored-by: Alec Larson <1925840+aleclarson@users.noreply.github.com> Co-authored-by: Matthew Phillips <matthew@skypack.dev>
1 parent 504d700 commit baba1f9

File tree

4 files changed

+45
-34
lines changed

4 files changed

+45
-34
lines changed

packages/vite/src/node/ssr/ssrExternal.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ const debug = createDebugger('vite:ssr-external')
1717
/**
1818
* Heuristics for determining whether a dependency should be externalized for
1919
* server-side rendering.
20-
*
21-
* TODO right now externals are imported using require(), we probably need to
22-
* rework this when more libraries ship native ESM distributions for Node.
2320
*/
2421
export function resolveSSRExternal(
2522
config: ResolvedConfig,
@@ -97,6 +94,10 @@ export function resolveSSRExternal(
9794
// entry is not js, cannot externalize
9895
continue
9996
}
97+
if (pkg.type === "module" || entry.endsWith('.mjs')) {
98+
ssrExternals.add(id)
99+
continue
100+
}
100101
// check if the entry is cjs
101102
const content = fs.readFileSync(entry, 'utf-8')
102103
if (/\bmodule\.exports\b|\bexports[.\[]|\brequire\s*\(/.test(content)) {

packages/vite/src/node/ssr/ssrModuleLoader.ts

+22-13
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'fs'
22
import path from 'path'
33
import { pathToFileURL } from 'url'
44
import { ViteDevServer } from '..'
5-
import { cleanUrl, resolveFrom, unwrapId } from '../utils'
5+
import { dynamicImport, cleanUrl, isBuiltin, resolveFrom, unwrapId, usingDynamicImport } from '../utils'
66
import { rebindErrorStacktrace, ssrRewriteStacktrace } from './ssrStacktrace'
77
import {
88
ssrExportAllKey,
@@ -95,12 +95,7 @@ async function instantiateModule(
9595

9696
const ssrImport = async (dep: string) => {
9797
if (dep[0] !== '.' && dep[0] !== '/') {
98-
return nodeRequire(
99-
dep,
100-
mod.file,
101-
server.config.root,
102-
!!server.config.resolve.preserveSymlinks
103-
)
98+
return nodeImport(dep, mod.file, server.config)
10499
}
105100
dep = unwrapId(dep)
106101
if (!isCircular(dep) && !pendingImports.get(dep)?.some(isCircular)) {
@@ -178,15 +173,29 @@ async function instantiateModule(
178173
return Object.freeze(ssrModule)
179174
}
180175

181-
function nodeRequire(
176+
// In node@12+ we can use dynamic import to load CJS and ESM
177+
async function nodeImport(
182178
id: string,
183179
importer: string | null,
184-
root: string,
185-
preserveSymlinks: boolean
180+
config: ViteDevServer['config']
186181
) {
187-
const mod = require(resolve(id, importer, root, preserveSymlinks))
188-
const defaultExport = mod.__esModule ? mod.default : mod
189-
// rollup-style default import interop for cjs
182+
let url: string
183+
// `resolve` doesn't handle `node:` builtins, so handle them directly
184+
if (id.startsWith('node:') || isBuiltin(id)) {
185+
url = id
186+
} else {
187+
url = resolve(id, importer, config.root, !!config.resolve.preserveSymlinks)
188+
if (usingDynamicImport) {
189+
url = pathToFileURL(url).toString()
190+
}
191+
}
192+
const mod = await dynamicImport(url)
193+
return proxyESM(id, mod)
194+
}
195+
196+
// rollup-style default import interop for cjs
197+
function proxyESM(id: string, mod: any) {
198+
const defaultExport = mod.__esModule ? mod.default : mod.default ? mod.default : mod
190199
return new Proxy(mod, {
191200
get(mod, prop) {
192201
if (prop === 'default') return defaultExport

packages/vite/src/node/utils.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -586,9 +586,14 @@ export function toUpperCaseDriveLetter(pathName: string): string {
586586
export const multilineCommentsRE = /\/\*(.|[\r\n])*?\*\//gm
587587
export const singlelineCommentsRE = /\/\/.*/g
588588

589+
export const usingDynamicImport = typeof jest === 'undefined';
589590
/**
590591
* Dynamically import files. It will make sure it's not being compiled away by TS/Rollup.
591592
*
593+
* As a temporary workaround for Jest's lack of stable ESM support, we fallback to require
594+
* if we're in a Jest environment.
595+
* See https://github.com/vitejs/vite/pull/5197#issuecomment-938054077
596+
*
592597
* @param file File path to import.
593598
*/
594-
export const dynamicImport = new Function('file', 'return import(file)')
599+
export const dynamicImport = usingDynamicImport ? new Function('file', 'return import(file)') : require;

pnpm-lock.yaml

+13-17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)