diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index aeafad144182de..0c93628f6cfc5a 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -982,53 +982,17 @@ export function resolvePackageEntry( ) } - // handle edge case with browser and module field semantics - if (!entryPoint && targetWeb && options.mainFields.includes('browser')) { - // check browser field - // https://github.com/defunctzombie/package-browser-field-spec - const browserEntry = - typeof data.browser === 'string' - ? data.browser - : isObject(data.browser) && data.browser['.'] - if (browserEntry) { - // check if the package also has a "module" field. - if ( - !options.isRequire && - options.mainFields.includes('module') && - typeof data.module === 'string' && - data.module !== browserEntry - ) { - // 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/IIFE. - // the heuristics here is to actually read the browser entry when - // 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), - options, - ) - if (resolvedBrowserEntry) { - const content = fs.readFileSync(resolvedBrowserEntry, 'utf-8') - 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 { - entryPoint = browserEntry - } - } - } - // fallback to mainFields if still not resolved if (!entryPoint) { for (const field of options.mainFields) { - if (field === 'browser') continue // already checked above - if (typeof data[field] === 'string') { + if (field === 'browser') { + if (targetWeb) { + entryPoint = tryResolveBrowserEntry(dir, data, options) + if (entryPoint) { + break + } + } + } else if (typeof data[field] === 'string') { entryPoint = data[field] break } @@ -1257,6 +1221,53 @@ function tryResolveBrowserMapping( } } +function tryResolveBrowserEntry( + dir: string, + data: PackageData['data'], + options: InternalResolveOptions, +) { + // handle edge case with browser and module field semantics + + // check browser field + // https://github.com/defunctzombie/package-browser-field-spec + const browserEntry = + typeof data.browser === 'string' + ? data.browser + : isObject(data.browser) && data.browser['.'] + if (browserEntry) { + // check if the package also has a "module" field. + if ( + !options.isRequire && + options.mainFields.includes('module') && + typeof data.module === 'string' && + data.module !== browserEntry + ) { + // 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/IIFE. + // the heuristics here is to actually read the browser entry when + // 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), + options, + ) + if (resolvedBrowserEntry) { + const content = fs.readFileSync(resolvedBrowserEntry, 'utf-8') + if (hasESMSyntax(content)) { + // likely ESM, prefer browser + return browserEntry + } else { + // non-ESM, UMD or IIFE or CJS(!!! e.g. firebase 7.x), prefer module + return data.module + } + } + } else { + return browserEntry + } + } +} + /** * given a relative path in pkg dir, * return a relative path in pkg dir, diff --git a/playground/resolve/__tests__/mainfields-custom-first/resolve-mainfields-custom-first.spec.ts b/playground/resolve/__tests__/mainfields-custom-first/resolve-mainfields-custom-first.spec.ts new file mode 100644 index 00000000000000..c15f0f56d72375 --- /dev/null +++ b/playground/resolve/__tests__/mainfields-custom-first/resolve-mainfields-custom-first.spec.ts @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest' +import { page } from '~utils' + +test('resolve.mainFields.custom-first', async () => { + expect(await page.textContent('.custom-browser-main-field')).toBe( + 'resolved custom field', + ) +}) diff --git a/playground/resolve/__tests__/resolve.spec.ts b/playground/resolve/__tests__/resolve.spec.ts index d5067698e13713..45b87c23e4ab10 100644 --- a/playground/resolve/__tests__/resolve.spec.ts +++ b/playground/resolve/__tests__/resolve.spec.ts @@ -167,6 +167,12 @@ test('resolve.mainFields', async () => { expect(await page.textContent('.custom-main-fields')).toMatch('[success]') }) +test('resolve.mainFields.browser-first', async () => { + expect(await page.textContent('.custom-browser-main-field')).toBe( + 'resolved browser field', + ) +}) + test('resolve.conditions', async () => { expect(await page.textContent('.custom-condition')).toMatch('[success]') }) diff --git a/playground/resolve/custom-browser-main-field/index.browser.js b/playground/resolve/custom-browser-main-field/index.browser.js new file mode 100644 index 00000000000000..a12f4a603c068d --- /dev/null +++ b/playground/resolve/custom-browser-main-field/index.browser.js @@ -0,0 +1 @@ +export const msg = 'resolved browser field' diff --git a/playground/resolve/custom-browser-main-field/index.custom.js b/playground/resolve/custom-browser-main-field/index.custom.js new file mode 100644 index 00000000000000..01ea529fad19b5 --- /dev/null +++ b/playground/resolve/custom-browser-main-field/index.custom.js @@ -0,0 +1 @@ +export const msg = 'resolved custom field' diff --git a/playground/resolve/custom-browser-main-field/index.js b/playground/resolve/custom-browser-main-field/index.js new file mode 100644 index 00000000000000..27f9fcfd43658b --- /dev/null +++ b/playground/resolve/custom-browser-main-field/index.js @@ -0,0 +1 @@ +export const msg = '[fail] resolved main field' diff --git a/playground/resolve/custom-browser-main-field/package.json b/playground/resolve/custom-browser-main-field/package.json new file mode 100644 index 00000000000000..0d372bc5eba9fc --- /dev/null +++ b/playground/resolve/custom-browser-main-field/package.json @@ -0,0 +1,8 @@ +{ + "name": "@vitejs/test-resolve-custom-browser-main-field", + "private": true, + "version": "1.0.0", + "main": "index.js", + "browser": "index.browser.js", + "custom": "index.custom.js" +} diff --git a/playground/resolve/index.html b/playground/resolve/index.html index 3d52ad6722e28c..89eced2e7149e9 100644 --- a/playground/resolve/index.html +++ b/playground/resolve/index.html @@ -158,6 +158,9 @@