fix(resolve): respect order of browser in mainFields when resolving (#15137)

This commit is contained in:
Dominik G 2023-11-29 11:52:39 +01:00 committed by GitHub
parent 793cebd2ee
commit 4a111aafd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 98 additions and 44 deletions

View File

@ -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,

View File

@ -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',
)
})

View File

@ -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]')
})

View File

@ -0,0 +1 @@
export const msg = 'resolved browser field'

View File

@ -0,0 +1 @@
export const msg = 'resolved custom field'

View File

@ -0,0 +1 @@
export const msg = '[fail] resolved main field'

View File

@ -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"
}

View File

@ -158,6 +158,9 @@
<h2>resolve.mainFields</h2>
<p class="custom-main-fields"></p>
<h2>resolve.mainFields.custom-browser-main</h2>
<p class="custom-browser-main-field"></p>
<h2>resolve.conditions</h2>
<p class="custom-condition"></p>
@ -341,6 +344,9 @@
import { msg as customMainMsg } from '@vitejs/test-resolve-custom-main-field'
text('.custom-main-fields', customMainMsg)
import { msg as customBrowserMsg } from '@vitejs/test-resolve-custom-browser-main-field'
text('.custom-browser-main-field', customBrowserMsg)
import { msg as customConditionMsg } from '@vitejs/test-resolve-custom-condition'
text('.custom-condition', customConditionMsg)

View File

@ -28,6 +28,7 @@
"@vitejs/test-resolve-browser-module-field3": "link:./browser-module-field3",
"@vitejs/test-resolve-custom-condition": "link:./custom-condition",
"@vitejs/test-resolve-custom-main-field": "link:./custom-main-field",
"@vitejs/test-resolve-custom-browser-main-field": "link:./custom-browser-main-field",
"@vitejs/test-resolve-exports-and-nested-scope": "link:./exports-and-nested-scope",
"@vitejs/test-resolve-exports-env": "link:./exports-env",
"@vitejs/test-resolve-exports-from-root": "link:./exports-from-root",

View File

@ -0,0 +1,6 @@
import config from './vite.config.js'
config.resolve.mainFields = [
'custom',
...config.resolve.mainFields.filter((f) => f !== 'custom'),
]
export default config

View File

@ -1071,6 +1071,9 @@ importers:
'@vitejs/test-resolve-browser-module-field3':
specifier: link:./browser-module-field3
version: link:browser-module-field3
'@vitejs/test-resolve-custom-browser-main-field':
specifier: link:./custom-browser-main-field
version: link:custom-browser-main-field
'@vitejs/test-resolve-custom-condition':
specifier: link:./custom-condition
version: link:custom-condition
@ -1140,6 +1143,8 @@ importers:
playground/resolve/browser-module-field3: {}
playground/resolve/custom-browser-main-field: {}
playground/resolve/custom-condition: {}
playground/resolve/custom-main-field: {}