diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index f1878e89d17433..13ac18ae384371 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -3,6 +3,7 @@ import path from 'node:path' import colors from 'picocolors' import type { PartialResolvedId } from 'rollup' import { resolve as _resolveExports } from 'resolve.exports' +import { hasESMSyntax } from 'mlly' import type { Plugin } from '../plugin' import { DEFAULT_EXTENSIONS, @@ -850,9 +851,9 @@ export function resolvePackageEntry( ) { // if both are present, we may have a problem: some package points both // to ESM, with "module" targeting Node.js, while some packages points - // "module" to browser ESM and "browser" to UMD. + // "module" to browser ESM and "browser" to UMD/IIFE. // the heuristics here is to actually read the browser entry when - // possible and check for hints of UMD. If it is UMD, prefer "module" + // possible and check for hints of ESM. If it is not ESM, prefer "module" // instead; Otherwise, assume it's ESM and use it. const resolvedBrowserEntry = tryFsResolve( path.join(dir, browserEntry), @@ -860,15 +861,12 @@ export function resolvePackageEntry( ) if (resolvedBrowserEntry) { const content = fs.readFileSync(resolvedBrowserEntry, 'utf-8') - if ( - (/typeof exports\s*==/.test(content) && - /typeof module\s*==/.test(content)) || - /module\.exports\s*=/.test(content) - ) { - // likely UMD or CJS(!!! e.g. firebase 7.x), prefer module - entryPoint = data.module - } else { + if (hasESMSyntax(content)) { + // likely ESM, prefer browser entryPoint = browserEntry + } else { + // non-ESM, UMD or IIFE or CJS(!!! e.g. firebase 7.x), prefer module + entryPoint = data.module } } } else { diff --git a/playground/resolve/__tests__/resolve.spec.ts b/playground/resolve/__tests__/resolve.spec.ts index 8d788949b67e32..784e0a4de32cea 100644 --- a/playground/resolve/__tests__/resolve.spec.ts +++ b/playground/resolve/__tests__/resolve.spec.ts @@ -96,6 +96,10 @@ test('Resolve module field if browser field is likely UMD or CJS', async () => { expect(await page.textContent('.browser-module2')).toMatch('[success]') }) +test('Resolve module field if browser field is likely IIFE', async () => { + expect(await page.textContent('.browser-module3')).toMatch('[success]') +}) + test('css entry', async () => { expect(await page.textContent('.css')).toMatch('[success]') }) diff --git a/playground/resolve/browser-module-field3/index.js b/playground/resolve/browser-module-field3/index.js new file mode 100644 index 00000000000000..99af62f8e3700e --- /dev/null +++ b/playground/resolve/browser-module-field3/index.js @@ -0,0 +1 @@ +export default '[success] this should run in browser' diff --git a/playground/resolve/browser-module-field3/index.web.js b/playground/resolve/browser-module-field3/index.web.js new file mode 100644 index 00000000000000..843b376e2c4daa --- /dev/null +++ b/playground/resolve/browser-module-field3/index.web.js @@ -0,0 +1,7 @@ +var browserModuleField3 = (function () { + 'use strict' + + var main = '[fail] this should not run in the browser' + + return main +})() diff --git a/playground/resolve/browser-module-field3/package.json b/playground/resolve/browser-module-field3/package.json new file mode 100644 index 00000000000000..931c6a0ebd9eb4 --- /dev/null +++ b/playground/resolve/browser-module-field3/package.json @@ -0,0 +1,7 @@ +{ + "name": "resolve-browser-module-field3", + "private": true, + "version": "1.0.0", + "module": "index.js", + "browser": "index.web.js" +} diff --git a/playground/resolve/index.html b/playground/resolve/index.html index 7502c422e0eb82..c744d4b9317463 100644 --- a/playground/resolve/index.html +++ b/playground/resolve/index.html @@ -82,6 +82,9 @@
fail
+fail
+fail
@@ -225,6 +228,9 @@