Skip to content

Commit d675cf0

Browse files
committed
feat(ssr): use tryNodeResolve in require hook
1 parent 44afbf6 commit d675cf0

File tree

2 files changed

+52
-58
lines changed

2 files changed

+52
-58
lines changed

packages/vite/src/node/plugins/ssrRequireHook.ts

Lines changed: 34 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,6 @@ import MagicString from 'magic-string'
22
import { ResolvedConfig } from '..'
33
import { Plugin } from '../plugin'
44

5-
type NodeResolveFilename = (
6-
request: string,
7-
parent: NodeModule,
8-
isMain: boolean,
9-
options?: Record<string, any>
10-
) => string
11-
12-
export function dedupeRequire(dedupe: string[], module: NodeModule) {
13-
const Module = require('module') as { _resolveFilename: NodeResolveFilename }
14-
const resolveFilename = Module._resolveFilename
15-
Module._resolveFilename = getDedupeRequire(resolveFilename)(dedupe, module)
16-
return () => {
17-
Module._resolveFilename = resolveFilename
18-
}
19-
}
20-
21-
const getDedupeRequire = (resolveFilename?: NodeResolveFilename) => {
22-
const dedupeRequireImpl = (
23-
dedupe: string[],
24-
module: NodeModule
25-
): NodeResolveFilename =>
26-
function (request, parent, isMain, options) {
27-
if (request[0] !== '.' && request[0] !== '/') {
28-
const parts = request.split('/')
29-
const pkgName =
30-
parts[0][0] === '@' ? parts[0] + '/' + parts[1] : parts[0]
31-
if (dedupe.includes(pkgName)) {
32-
// Use this module as the parent.
33-
parent = module
34-
}
35-
}
36-
return resolveFilename!(request, parent, isMain, options)
37-
}
38-
39-
if (resolveFilename) {
40-
return dedupeRequireImpl
41-
}
42-
43-
return new Function(
44-
`dedupe`,
45-
`var Module = require("module");
46-
var resolveFilename = Module._resolveFilename;
47-
Module._resolveFilename = ${dedupeRequireImpl
48-
.toString()
49-
.replace(/^.+? => /, '')};`
50-
)
51-
}
52-
535
export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null {
546
if (config.command !== 'build' || !config.resolve.dedupe?.length) {
557
return null
@@ -61,7 +13,7 @@ export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null {
6113
if (moduleInfo?.isEntry) {
6214
const s = new MagicString(code)
6315
s.prepend(
64-
`;(${getDedupeRequire()})(${JSON.stringify(
16+
`;(${dedupeRequire.toString()})(${JSON.stringify(
6517
config.resolve.dedupe
6618
)});\n`
6719
)
@@ -75,3 +27,36 @@ export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null {
7527
}
7628
}
7729
}
30+
31+
type NodeResolveFilename = (
32+
request: string,
33+
parent: NodeModule,
34+
isMain: boolean,
35+
options?: Record<string, any>
36+
) => string
37+
38+
/** Respect the `resolve.dedupe` option in production SSR. */
39+
function dedupeRequire(dedupe: string[]) {
40+
const Module = require('module') as { _resolveFilename: NodeResolveFilename }
41+
const resolveFilename = Module._resolveFilename
42+
Module._resolveFilename = function (request, parent, isMain, options) {
43+
if (request[0] !== '.' && request[0] !== '/') {
44+
const parts = request.split('/')
45+
const pkgName = parts[0][0] === '@' ? parts[0] + '/' + parts[1] : parts[0]
46+
if (dedupe.includes(pkgName)) {
47+
// Use this module as the parent.
48+
parent = module
49+
}
50+
}
51+
return resolveFilename!(request, parent, isMain, options)
52+
}
53+
}
54+
55+
export function hookNodeResolve(resolver: NodeResolveFilename) {
56+
const Module = require('module') as { _resolveFilename: NodeResolveFilename }
57+
const resolveFilename = Module._resolveFilename
58+
Module._resolveFilename = resolver
59+
return () => {
60+
Module._resolveFilename = resolveFilename
61+
}
62+
}

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import path from 'path'
2+
import { Module } from 'module'
23
import { ViteDevServer } from '..'
34
import { unwrapId } from '../utils'
45
import { ssrRewriteStacktrace } from './ssrStacktrace'
@@ -11,7 +12,7 @@ import {
1112
} from './ssrTransform'
1213
import { transformRequest } from '../server/transformRequest'
1314
import { InternalResolveOptions, tryNodeResolve } from '../plugins/resolve'
14-
import { dedupeRequire } from '../plugins/ssrRequireHook'
15+
import { hookNodeResolve } from '../plugins/ssrRequireHook'
1516

1617
interface SSRContext {
1718
global: NodeJS.Global
@@ -173,7 +174,7 @@ async function instantiateModule(
173174
return ssrModule
174175
}
175176

176-
function nodeRequire(
177+
function nodeResolve(
177178
id: string,
178179
importer: string | null,
179180
resolveOptions: InternalResolveOptions
@@ -182,20 +183,28 @@ function nodeRequire(
182183
if (!resolved) {
183184
throw Error(`Cannot find module '${id}'`)
184185
}
186+
return resolved.id
187+
}
185188

186-
const unhookRequire = resolveOptions.dedupe?.length
187-
? dedupeRequire(resolveOptions.dedupe, module)
188-
: null
189+
function nodeRequire(
190+
id: string,
191+
importer: string | null,
192+
resolveOptions: InternalResolveOptions
193+
) {
194+
id = nodeResolve(id, importer, resolveOptions)
189195

196+
const loadModule = importer ? Module.createRequire(importer) : require
197+
const unhookNodeResolve = hookNodeResolve((id, importer) =>
198+
nodeResolve(id, importer.id, resolveOptions)
199+
)
190200
try {
191-
var mod = require(resolved.id)
201+
var mod = loadModule(id)
192202
} finally {
193-
unhookRequire?.()
203+
unhookNodeResolve()
194204
}
195205

196-
const defaultExport = mod.__esModule ? mod.default : mod
197-
198206
// rollup-style default import interop for cjs
207+
const defaultExport = mod.__esModule ? mod.default : mod
199208
return new Proxy(mod, {
200209
get(mod, prop) {
201210
if (prop === 'default') return defaultExport

0 commit comments

Comments
 (0)