From 32413cec52ab12a835d3b3ec7a1e294946d11505 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 26 Jan 2021 10:36:34 -0500 Subject: [PATCH] fix(optimizer): handle special case where esm entry gets converted to cjs by esbuild fix #1724 --- packages/vite/src/node/optimizer/index.ts | 43 +++++++++++++++++------ 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/packages/vite/src/node/optimizer/index.ts b/packages/vite/src/node/optimizer/index.ts index fb05113d355c5f..9db023108e39f7 100644 --- a/packages/vite/src/node/optimizer/index.ts +++ b/packages/vite/src/node/optimizer/index.ts @@ -266,7 +266,7 @@ export async function optimizeDeps( for (const output in meta.outputs) { if (/\.vite[\/\\]chunk\.\w+\.js$/.test(output) || output.endsWith('.map')) continue - const { inputs, exports: generatedExports } = meta.outputs[output] + const { inputs, exports } = meta.outputs[output] const relativeOutput = normalizePath( path.relative(cacheDir, path.resolve(output)) ) @@ -279,18 +279,9 @@ export async function optimizeDeps( if (!id) { continue } - // check if this is a cjs dep. - const [imports, exports] = parse(fs.readFileSync(entry, 'utf-8')) data.optimized[id] = { file: normalizePath(path.resolve(output)), - needsInterop: - // entry has no ESM syntax - likely CJS or UMD - (!exports.length && !imports.length) || - // if a peer dep used require() on a ESM dep, esbuild turns the - // ESM dep's entry chunk into a single default export... detect - // such cases by checking exports mismatch, and force interop. - (isSingleDefaultExport(generatedExports) && - !isSingleDefaultExport(exports)) + needsInterop: needsInterop(id, entry, exports) } break } @@ -299,6 +290,36 @@ export async function optimizeDeps( writeFile(dataPath, JSON.stringify(data, null, 2)) } +// https://github.com/vitejs/vite/issues/1724#issuecomment-767619642 +// a list of modules that pretends to be ESM but still uses `require`. +// this causes esbuild to wrap them as CJS even when its entry appears to be ESM. +const KNOWN_INTEROP_IDS = new Set(['moment']) + +function needsInterop( + id: string, + entry: string, + generatedExports: string[] +): boolean { + if (KNOWN_INTEROP_IDS.has(id)) { + return true + } + const [imports, exports] = parse(fs.readFileSync(entry, 'utf-8')) + // entry has no ESM syntax - likely CJS or UMD + if (!exports.length && !imports.length) { + return true + } + // if a peer dep used require() on a ESM dep, esbuild turns the + // ESM dep's entry chunk into a single default export... detect + // such cases by checking exports mismatch, and force interop. + if ( + isSingleDefaultExport(generatedExports) && + !isSingleDefaultExport(exports) + ) { + return true + } + return false +} + function isSingleDefaultExport(exports: string[]) { return exports.length === 1 && exports[0] === 'default' }