mirror of
https://github.com/vitejs/vite.git
synced 2024-11-21 14:48:41 +00:00
fix(resolve): respect order of browser in mainFields when resolving (#15137)
This commit is contained in:
parent
793cebd2ee
commit
4a111aafd5
@ -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,
|
||||
|
@ -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',
|
||||
)
|
||||
})
|
@ -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]')
|
||||
})
|
||||
|
@ -0,0 +1 @@
|
||||
export const msg = 'resolved browser field'
|
@ -0,0 +1 @@
|
||||
export const msg = 'resolved custom field'
|
1
playground/resolve/custom-browser-main-field/index.js
Normal file
1
playground/resolve/custom-browser-main-field/index.js
Normal file
@ -0,0 +1 @@
|
||||
export const msg = '[fail] resolved main field'
|
@ -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"
|
||||
}
|
@ -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)
|
||||
|
||||
|
@ -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",
|
||||
|
@ -0,0 +1,6 @@
|
||||
import config from './vite.config.js'
|
||||
config.resolve.mainFields = [
|
||||
'custom',
|
||||
...config.resolve.mainFields.filter((f) => f !== 'custom'),
|
||||
]
|
||||
export default config
|
@ -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: {}
|
||||
|
Loading…
Reference in New Issue
Block a user