Skip to content

Commit

Permalink
fix(optimizer): handle special case where esm entry gets converted to…
Browse files Browse the repository at this point in the history
… cjs by esbuild

fix #1724
  • Loading branch information
yyx990803 committed Jan 26, 2021
1 parent 52c9416 commit 32413ce
Showing 1 changed file with 32 additions and 11 deletions.
43 changes: 32 additions & 11 deletions packages/vite/src/node/optimizer/index.ts
Expand Up @@ -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))
)
Expand All @@ -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
}
Expand All @@ -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'
}
Expand Down

3 comments on commit 32413ce

@walmon
Copy link

@walmon walmon commented on 32413ce Mar 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yyx990803 thanks for this commit! I know it's a bit old fix that's being included until now, but it seems to fix an issue I'm having.

However even if GH says it should be on 4.2.0 I can't find it in the released code (and the fix doesn't work either). Any idea what could be happening? I'm sure I pulled 4.2.0 from npm.

image

@patak-dev
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @walmon, this commit is from 2 years ago and the code changed quite a bit since then. The code is still there though. If you found a regression or bug, please create a minimal reproduction and a new issue in the repo so we can properly track it.

@walmon
Copy link

@walmon walmon commented on 32413ce Mar 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@patak-dev thanks, yeah I just found this #11502 I misread the contents of the release.
image

Please sign in to comment.