Skip to content

Commit

Permalink
feat(ssr): use tryNodeResolve in require hook
Browse files Browse the repository at this point in the history
  • Loading branch information
aleclarson committed Jun 27, 2021
1 parent 44afbf6 commit d675cf0
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 58 deletions.
83 changes: 34 additions & 49 deletions packages/vite/src/node/plugins/ssrRequireHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, any>
) => 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
Expand All @@ -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`
)
Expand All @@ -75,3 +27,36 @@ export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null {
}
}
}

type NodeResolveFilename = (
request: string,
parent: NodeModule,
isMain: boolean,
options?: Record<string, any>
) => 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
}
}
27 changes: 18 additions & 9 deletions packages/vite/src/node/ssr/ssrModuleLoader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import path from 'path'
import { Module } from 'module'
import { ViteDevServer } from '..'
import { unwrapId } from '../utils'
import { ssrRewriteStacktrace } from './ssrStacktrace'
Expand All @@ -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
Expand Down Expand Up @@ -173,7 +174,7 @@ async function instantiateModule(
return ssrModule
}

function nodeRequire(
function nodeResolve(
id: string,
importer: string | null,
resolveOptions: InternalResolveOptions
Expand All @@ -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
Expand Down

0 comments on commit d675cf0

Please sign in to comment.