diff --git a/packages/vite/src/node/ssr/ssrExternal.ts b/packages/vite/src/node/ssr/ssrExternal.ts index 2950846b8c8ff5..a0bfd782b8a251 100644 --- a/packages/vite/src/node/ssr/ssrExternal.ts +++ b/packages/vite/src/node/ssr/ssrExternal.ts @@ -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, @@ -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)) { diff --git a/packages/vite/src/node/ssr/ssrModuleLoader.ts b/packages/vite/src/node/ssr/ssrModuleLoader.ts index e9bb20b0806f61..2be123db077229 100644 --- a/packages/vite/src/node/ssr/ssrModuleLoader.ts +++ b/packages/vite/src/node/ssr/ssrModuleLoader.ts @@ -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, @@ -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)) { @@ -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 diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index b23c774058572b..c62e29da900e05 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -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; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54bb0b9132f248..09f4236084690d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,7 +118,7 @@ importers: specifiers: tailwindcss: ^2.2.4 dependencies: - tailwindcss: 2.2.15_ts-node@10.2.1 + tailwindcss: 2.2.15 packages/playground/cli: specifiers: {} @@ -510,7 +510,7 @@ importers: vue-router: ^4.0.0 dependencies: autoprefixer: 10.3.5 - tailwindcss: 2.2.15_e444c4a095b9e9ba5c645754953637f2 + tailwindcss: 2.2.15_autoprefixer@10.3.5 vue: 3.2.16 vue-router: 4.0.11_vue@3.2.16 devDependencies: @@ -717,7 +717,7 @@ importers: '@rollup/plugin-dynamic-import-vars': 1.4.0_rollup@2.57.0 '@rollup/plugin-json': 4.1.0_rollup@2.57.0 '@rollup/plugin-node-resolve': 13.0.5_rollup@2.57.0 - '@rollup/plugin-typescript': 8.2.5_283b0408a190511f1240c12469d64cd5 + '@rollup/plugin-typescript': 8.2.5_rollup@2.57.0+tslib@2.3.1 '@rollup/pluginutils': 4.1.1 '@types/convert-source-map': 1.5.2 '@types/debug': 4.1.7 @@ -761,7 +761,7 @@ importers: open: 8.2.1 periscopic: 2.0.3 postcss-import: 14.0.2_postcss@8.3.8 - postcss-load-config: 3.1.0_ts-node@10.2.1 + postcss-load-config: 3.1.0 postcss-modules: 4.2.2_postcss@8.3.8 resolve.exports: 1.0.2 rollup-plugin-license: 2.5.0_rollup@2.57.0 @@ -771,7 +771,7 @@ importers: source-map-support: 0.5.20 strip-ansi: 6.0.0 terser: 5.9.0 - tsconfck: 1.0.0_typescript@4.4.3 + tsconfck: 1.0.0 tslib: 2.3.1 types: link:types ws: 7.5.5 @@ -1882,7 +1882,7 @@ packages: rollup: 2.57.0 dev: true - /@rollup/plugin-typescript/8.2.5_283b0408a190511f1240c12469d64cd5: + /@rollup/plugin-typescript/8.2.5_rollup@2.57.0+tslib@2.3.1: resolution: {integrity: sha512-QL/LvDol/PAGB2O0S7/+q2HpSUNodpw7z6nGn9BfoVCPOZ0r4EALrojFU29Bkoi2Hr2jgTocTejJ5GGWZfOxbQ==} engines: {node: '>=8.0.0'} peerDependencies: @@ -1894,7 +1894,6 @@ packages: resolve: 1.20.0 rollup: 2.57.0 tslib: 2.3.1 - typescript: 4.4.3 dev: true /@rollup/pluginutils/3.1.0_rollup@2.57.0: @@ -6882,7 +6881,7 @@ packages: postcss: 8.3.7 dev: false - /postcss-load-config/3.1.0_ts-node@10.2.1: + /postcss-load-config/3.1.0: resolution: {integrity: sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==} engines: {node: '>= 10'} peerDependencies: @@ -6893,7 +6892,6 @@ packages: dependencies: import-cwd: 3.0.0 lilconfig: 2.0.3 - ts-node: 10.2.1_3b624d72c50530188ff09826d1b48ebf yaml: 1.10.2 /postcss-modules-extract-imports/3.0.0_postcss@8.3.8: @@ -8097,7 +8095,7 @@ packages: strip-ansi: 6.0.0 dev: true - /tailwindcss/2.2.15_e444c4a095b9e9ba5c645754953637f2: + /tailwindcss/2.2.15: resolution: {integrity: sha512-WgV41xTMbnSoTNMNnJvShQZ+8GmY86DmXTrCgnsveNZJdlybfwCItV8kAqjYmU49YiFr+ofzmT1JlAKajBZboQ==} engines: {node: '>=12.13.0'} hasBin: true @@ -8106,7 +8104,6 @@ packages: postcss: ^8.0.9 dependencies: arg: 5.0.1 - autoprefixer: 10.3.5 bytes: 3.1.0 chalk: 4.1.2 chokidar: 3.5.2 @@ -8128,7 +8125,7 @@ packages: normalize-path: 3.0.0 object-hash: 2.2.0 postcss-js: 3.0.3 - postcss-load-config: 3.1.0_ts-node@10.2.1 + postcss-load-config: 3.1.0 postcss-nested: 5.0.6 postcss-selector-parser: 6.0.6 postcss-value-parser: 4.1.0 @@ -8142,7 +8139,7 @@ packages: - ts-node dev: false - /tailwindcss/2.2.15_ts-node@10.2.1: + /tailwindcss/2.2.15_autoprefixer@10.3.5: resolution: {integrity: sha512-WgV41xTMbnSoTNMNnJvShQZ+8GmY86DmXTrCgnsveNZJdlybfwCItV8kAqjYmU49YiFr+ofzmT1JlAKajBZboQ==} engines: {node: '>=12.13.0'} hasBin: true @@ -8151,6 +8148,7 @@ packages: postcss: ^8.0.9 dependencies: arg: 5.0.1 + autoprefixer: 10.3.5 bytes: 3.1.0 chalk: 4.1.2 chokidar: 3.5.2 @@ -8172,7 +8170,7 @@ packages: normalize-path: 3.0.0 object-hash: 2.2.0 postcss-js: 3.0.3 - postcss-load-config: 3.1.0_ts-node@10.2.1 + postcss-load-config: 3.1.0 postcss-nested: 5.0.6 postcss-selector-parser: 6.0.6 postcss-value-parser: 4.1.0 @@ -8400,7 +8398,7 @@ packages: yn: 3.1.1 dev: true - /tsconfck/1.0.0_typescript@4.4.3: + /tsconfck/1.0.0: resolution: {integrity: sha512-9+flUmolxrnyKAYbNzXZX1UPHUuZsVqM42ioZ1H55r0I+UfJgYui4b7zUxhdJ4o5emEuZo89Cx4TiWG/Dd0c/A==} engines: {node: ^12.20 || ^14.13.1 || >= 16, pnpm: '>=6.7.0'} hasBin: true @@ -8409,8 +8407,6 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - typescript: 4.4.3 dev: true /tslib/1.14.1: