diff --git a/packages/vite/src/node/plugins/ssrRequireHook.ts b/packages/vite/src/node/plugins/ssrRequireHook.ts index 2fee8b6c3ce7b2..d540171ab36bb6 100644 --- a/packages/vite/src/node/plugins/ssrRequireHook.ts +++ b/packages/vite/src/node/plugins/ssrRequireHook.ts @@ -2,54 +2,6 @@ import MagicString from 'magic-string' import { ResolvedConfig } from '..' import { Plugin } from '../plugin' -type NodeResolveFilename = ( - request: string, - parent: NodeModule, - isMain: boolean, - options?: Record -) => string - -export function dedupeRequire(dedupe: string[], module: NodeModule) { - const Module = require('module') as { _resolveFilename: NodeResolveFilename } - const resolveFilename = Module._resolveFilename - Module._resolveFilename = getDedupeRequire(resolveFilename)(dedupe, module) - return () => { - Module._resolveFilename = resolveFilename - } -} - -const getDedupeRequire = (resolveFilename?: NodeResolveFilename) => { - const dedupeRequireImpl = ( - dedupe: string[], - module: NodeModule - ): NodeResolveFilename => - function (request, parent, isMain, options) { - if (request[0] !== '.' && request[0] !== '/') { - const parts = request.split('/') - const pkgName = - parts[0][0] === '@' ? parts[0] + '/' + parts[1] : parts[0] - if (dedupe.includes(pkgName)) { - // Use this module as the parent. - parent = module - } - } - return resolveFilename!(request, parent, isMain, options) - } - - if (resolveFilename) { - return dedupeRequireImpl - } - - return new Function( - `dedupe`, - `var Module = require("module"); -var resolveFilename = Module._resolveFilename; -Module._resolveFilename = ${dedupeRequireImpl - .toString() - .replace(/^.+? => /, '')};` - ) -} - export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null { if (config.command !== 'build' || !config.resolve.dedupe?.length) { return null @@ -61,7 +13,7 @@ export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null { if (moduleInfo?.isEntry) { const s = new MagicString(code) s.prepend( - `;(${getDedupeRequire()})(${JSON.stringify( + `;(${dedupeRequire.toString()})(${JSON.stringify( config.resolve.dedupe )});\n` ) @@ -75,3 +27,36 @@ export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null { } } } + +type NodeResolveFilename = ( + request: string, + parent: NodeModule, + isMain: boolean, + options?: Record +) => string + +/** Respect the `resolve.dedupe` option in production SSR. */ +function dedupeRequire(dedupe: string[]) { + const Module = require('module') as { _resolveFilename: NodeResolveFilename } + const resolveFilename = Module._resolveFilename + Module._resolveFilename = function (request, parent, isMain, options) { + if (request[0] !== '.' && request[0] !== '/') { + const parts = request.split('/') + const pkgName = parts[0][0] === '@' ? parts[0] + '/' + parts[1] : parts[0] + if (dedupe.includes(pkgName)) { + // Use this module as the parent. + parent = module + } + } + return resolveFilename!(request, parent, isMain, options) + } +} + +export function hookNodeResolve(resolver: NodeResolveFilename) { + const Module = require('module') as { _resolveFilename: NodeResolveFilename } + const resolveFilename = Module._resolveFilename + Module._resolveFilename = resolver + return () => { + Module._resolveFilename = resolveFilename + } +} diff --git a/packages/vite/src/node/ssr/ssrModuleLoader.ts b/packages/vite/src/node/ssr/ssrModuleLoader.ts index d0e54e9a21d0e1..fb9e10beb74f4c 100644 --- a/packages/vite/src/node/ssr/ssrModuleLoader.ts +++ b/packages/vite/src/node/ssr/ssrModuleLoader.ts @@ -1,4 +1,5 @@ import path from 'path' +import { Module } from 'module' import { ViteDevServer } from '..' import { unwrapId } from '../utils' import { ssrRewriteStacktrace } from './ssrStacktrace' @@ -11,7 +12,7 @@ import { } from './ssrTransform' import { transformRequest } from '../server/transformRequest' import { InternalResolveOptions, tryNodeResolve } from '../plugins/resolve' -import { dedupeRequire } from '../plugins/ssrRequireHook' +import { hookNodeResolve } from '../plugins/ssrRequireHook' interface SSRContext { global: NodeJS.Global @@ -173,7 +174,7 @@ async function instantiateModule( return ssrModule } -function nodeRequire( +function nodeResolve( id: string, importer: string | null, resolveOptions: InternalResolveOptions @@ -182,20 +183,28 @@ function nodeRequire( if (!resolved) { throw Error(`Cannot find module '${id}'`) } + return resolved.id +} - const unhookRequire = resolveOptions.dedupe?.length - ? dedupeRequire(resolveOptions.dedupe, module) - : null +function nodeRequire( + id: string, + importer: string | null, + resolveOptions: InternalResolveOptions +) { + id = nodeResolve(id, importer, resolveOptions) + const loadModule = importer ? Module.createRequire(importer) : require + const unhookNodeResolve = hookNodeResolve((id, importer) => + nodeResolve(id, importer.id, resolveOptions) + ) try { - var mod = require(resolved.id) + var mod = loadModule(id) } finally { - unhookRequire?.() + unhookNodeResolve() } - const defaultExport = mod.__esModule ? mod.default : mod - // rollup-style default import interop for cjs + const defaultExport = mod.__esModule ? mod.default : mod return new Proxy(mod, { get(mod, prop) { if (prop === 'default') return defaultExport