Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add import support to ssrModuleLoader #5197

Merged
merged 22 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions packages/vite/src/node/ssr/ssrExternal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ const debug = createDebugger('vite:ssr-external')
/**
* Heuristics for determining whether a dependency should be externalized for
* server-side rendering.
*
* TODO right now externals are imported using require(), we probably need to
* rework this when more libraries ship native ESM distributions for Node.
*/
export function resolveSSRExternal(
config: ResolvedConfig,
Expand Down Expand Up @@ -97,6 +94,10 @@ export function resolveSSRExternal(
// entry is not js, cannot externalize
continue
}
if (pkg.type === "module" || entry.endsWith('.mjs')) {
ssrExternals.add(id)
continue
}
// check if the entry is cjs
const content = fs.readFileSync(entry, 'utf-8')
if (/\bmodule\.exports\b|\bexports[.\[]|\brequire\s*\(/.test(content)) {
Expand Down
35 changes: 22 additions & 13 deletions packages/vite/src/node/ssr/ssrModuleLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'fs'
import path from 'path'
import { pathToFileURL } from 'url'
import { ViteDevServer } from '..'
import { cleanUrl, resolveFrom, unwrapId } from '../utils'
import { dynamicImport, cleanUrl, isBuiltin, resolveFrom, unwrapId, usingDynamicImport } from '../utils'
import { rebindErrorStacktrace, ssrRewriteStacktrace } from './ssrStacktrace'
import {
ssrExportAllKey,
Expand Down Expand Up @@ -95,12 +95,7 @@ async function instantiateModule(

const ssrImport = async (dep: string) => {
if (dep[0] !== '.' && dep[0] !== '/') {
return nodeRequire(
dep,
mod.file,
server.config.root,
!!server.config.resolve.preserveSymlinks
)
return nodeImport(dep, mod.file, server.config)
}
dep = unwrapId(dep)
if (!isCircular(dep) && !pendingImports.get(dep)?.some(isCircular)) {
Expand Down Expand Up @@ -178,15 +173,29 @@ async function instantiateModule(
return Object.freeze(ssrModule)
}

function nodeRequire(
// In node@12+ we can use dynamic import to load CJS and ESM
async function nodeImport(
id: string,
importer: string | null,
root: string,
preserveSymlinks: boolean
config: ViteDevServer['config']
) {
const mod = require(resolve(id, importer, root, preserveSymlinks))
const defaultExport = mod.__esModule ? mod.default : mod
// rollup-style default import interop for cjs
let url: string
// `resolve` doesn't handle `node:` builtins, so handle them directly
if (id.startsWith('node:') || isBuiltin(id)) {
url = id
} else {
url = resolve(id, importer, config.root, !!config.resolve.preserveSymlinks)
if (usingDynamicImport) {
url = pathToFileURL(url).toString()
}
}
const mod = await dynamicImport(url)
return proxyESM(id, mod)
}

// rollup-style default import interop for cjs
function proxyESM(id: string, mod: any) {
const defaultExport = mod.__esModule ? mod.default : mod.default ? mod.default : mod
return new Proxy(mod, {
get(mod, prop) {
if (prop === 'default') return defaultExport
Expand Down
7 changes: 6 additions & 1 deletion packages/vite/src/node/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -569,9 +569,14 @@ export function toUpperCaseDriveLetter(pathName: string): string {
export const multilineCommentsRE = /\/\*(.|[\r\n])*?\*\//gm
export const singlelineCommentsRE = /\/\/.*/g

export const usingDynamicImport = typeof jest === 'undefined';
/**
* Dynamically import files. It will make sure it's not being compiled away by TS/Rollup.
*
* As a temporary workaround for Jest's lack of stable ESM support, we fallback to require
* if we're in a Jest environment.
* See https://github.com/vitejs/vite/pull/5197#issuecomment-938054077
*
* @param file File path to import.
*/
export const dynamicImport = new Function('file', 'return import(file)')
export const dynamicImport = usingDynamicImport ? new Function('file', 'return import(file)') : require;
30 changes: 13 additions & 17 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.