fix: respect mainFields when resolving browser/module field (fixes #8659) (#10071)

This commit is contained in:
翠 / green 2022-09-23 18:54:44 +09:00 committed by GitHub
parent 6233c83020
commit 533d13c27a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 60 additions and 35 deletions

View File

@ -158,6 +158,16 @@ Export keys ending with "/" is deprecated by Node and may not work well. Please
List of fields in `package.json` to try when resolving a package's entry point. Note this takes lower precedence than conditional exports resolved from the `exports` field: if an entry point is successfully resolved from `exports`, the main field will be ignored.
## resolve.browserField
- **Type:** `boolean`
- **Default:** `true`
- **Deprecated**
Whether to enable resolving to `browser` field.
In future, `resolve.mainFields`'s default value will be `['browser', 'module', 'jsnext:main', 'jsnext']` and this option will be removed.
## resolve.extensions
- **Type:** `string[]`

View File

@ -43,6 +43,8 @@ import {
CLIENT_ENTRY,
DEFAULT_ASSETS_RE,
DEFAULT_CONFIG_FILES,
DEFAULT_EXTENSIONS,
DEFAULT_MAIN_FIELDS,
ENV_ENTRY
} from './constants'
import type { InternalResolveOptions, ResolveOptions } from './plugins/resolve'
@ -321,7 +323,7 @@ export type ResolvedConfig = Readonly<
mainConfig: ResolvedConfig | null
isProduction: boolean
env: Record<string, any>
resolve: ResolveOptions & {
resolve: Required<ResolveOptions> & {
alias: Alias[]
}
plugins: readonly Plugin[]
@ -474,7 +476,12 @@ export async function resolveConfig(
)
const resolveOptions: ResolvedConfig['resolve'] = {
...config.resolve,
mainFields: config.resolve?.mainFields ?? DEFAULT_MAIN_FIELDS,
browserField: config.resolve?.browserField ?? true,
conditions: config.resolve?.conditions ?? [],
extensions: config.resolve?.extensions ?? DEFAULT_EXTENSIONS,
dedupe: config.resolve?.dedupe ?? [],
preserveSymlinks: config.resolve?.preserveSymlinks ?? false,
alias: resolvedAlias
}
@ -581,8 +588,8 @@ export async function resolveConfig(
const server = resolveServerOptions(resolvedRoot, config.server, logger)
const ssr = resolveSSROptions(
config.ssr,
config.legacy?.buildSsrCjsExternalHeuristics,
config.resolve?.preserveSymlinks
resolveOptions.preserveSymlinks,
config.legacy?.buildSsrCjsExternalHeuristics
)
const middlewareMode = config?.server?.middlewareMode
@ -649,7 +656,7 @@ export async function resolveConfig(
disabled: 'build',
...optimizeDeps,
esbuildOptions: {
preserveSymlinks: config.resolve?.preserveSymlinks,
preserveSymlinks: resolveOptions.preserveSymlinks,
...optimizeDeps.esbuildOptions
}
},

View File

@ -63,13 +63,18 @@ const debug = createDebugger('vite:resolve-details', {
export interface ResolveOptions {
mainFields?: string[]
/**
* @deprecated In future, `mainFields` should be used instead.
* @default true
*/
browserField?: boolean
conditions?: string[]
extensions?: string[]
dedupe?: string[]
preserveSymlinks?: boolean
}
export interface InternalResolveOptions extends ResolveOptions {
export interface InternalResolveOptions extends Required<ResolveOptions> {
root: string
isBuild: boolean
isProduction: boolean
@ -85,7 +90,6 @@ export interface InternalResolveOptions extends ResolveOptions {
tryPrefix?: string
skipPackageJson?: boolean
preferRelative?: boolean
preserveSymlinks?: boolean
isRequire?: boolean
// #3040
// when the importer is a ts module,
@ -238,6 +242,7 @@ export function resolvePlugin(resolveOptions: InternalResolveOptions): Plugin {
if (
targetWeb &&
options.browserField &&
(res = tryResolveBrowserMapping(fsPath, importer, options, true))
) {
return res
@ -308,6 +313,7 @@ export function resolvePlugin(resolveOptions: InternalResolveOptions): Plugin {
if (
targetWeb &&
options.browserField &&
(res = tryResolveBrowserMapping(
id,
importer,
@ -451,7 +457,7 @@ function tryFsResolve(
return res
}
for (const ext of options.extensions || DEFAULT_EXTENSIONS) {
for (const ext of options.extensions) {
if (
postfix &&
(res = tryResolveFile(
@ -892,7 +898,11 @@ export function resolvePackageEntry(
// This is because .mjs files can technically import .cjs files which would
// make them invalid for pure ESM environments - so if other module/browser
// fields are present, prioritize those instead.
if (targetWeb && (!entryPoint || entryPoint.endsWith('.mjs'))) {
if (
targetWeb &&
options.browserField &&
(!entryPoint || entryPoint.endsWith('.mjs'))
) {
// check browser field
// https://github.com/defunctzombie/package-browser-field-spec
const browserEntry =
@ -903,6 +913,7 @@ export function resolvePackageEntry(
// check if the package also has a "module" field.
if (
!options.isRequire &&
options.mainFields.includes('module') &&
typeof data.module === 'string' &&
data.module !== browserEntry
) {
@ -933,7 +944,8 @@ export function resolvePackageEntry(
}
if (!entryPoint || entryPoint.endsWith('.mjs')) {
for (const field of options.mainFields || DEFAULT_MAIN_FIELDS) {
for (const field of options.mainFields) {
if (field === 'browser') continue // already checked above
if (typeof data[field] === 'string') {
entryPoint = data[field]
break
@ -951,8 +963,8 @@ export function resolvePackageEntry(
for (let entry of entryPoints) {
// make sure we don't get scripts when looking for sass
if (
options.mainFields?.[0] === 'sass' &&
!options.extensions?.includes(path.extname(entry))
options.mainFields[0] === 'sass' &&
!options.extensions.includes(path.extname(entry))
) {
entry = ''
options.skipPackageJson = true
@ -960,7 +972,7 @@ export function resolvePackageEntry(
// resolve object browser field in package.json
const { browser: browserField } = data
if (targetWeb && isObject(browserField)) {
if (targetWeb && options.browserField && isObject(browserField)) {
entry = mapWithBrowserField(entry, browserField) || entry
}
@ -1001,7 +1013,7 @@ function resolveExports(
if (!options.isRequire) {
conditions.push('module')
}
if (options.conditions) {
if (options.conditions.length > 0) {
conditions.push(...options.conditions)
}
@ -1053,7 +1065,7 @@ function resolveDeepImport(
`${path.join(dir, 'package.json')}.`
)
}
} else if (targetWeb && isObject(browserField)) {
} else if (targetWeb && options.browserField && isObject(browserField)) {
// resolve without postfix (see #7098)
const { file, postfix } = splitFileAndPostfix(relativeId)
const mapped = mapWithBrowserField(file, browserField)

View File

@ -13,7 +13,7 @@ export function ssrRequireHookPlugin(config: ResolvedConfig): Plugin | null {
if (
config.command !== 'build' ||
!config.build.ssr ||
!config.resolve.dedupe?.length ||
!config.resolve.dedupe.length ||
config.ssr?.noExternal === true ||
config.ssr?.format !== 'cjs' ||
isBuildOutputEsm(config)

View File

@ -41,8 +41,8 @@ export interface ResolvedSSROptions extends SSROptions {
export function resolveSSROptions(
ssr: SSROptions | undefined,
buildSsrCjsExternalHeuristics?: boolean,
preserveSymlinks?: boolean
preserveSymlinks: boolean,
buildSsrCjsExternalHeuristics?: boolean
): ResolvedSSROptions {
ssr ??= {}
const optimizeDeps = ssr.optimizeDeps ?? {}

View File

@ -1,7 +1,7 @@
import fs from 'node:fs'
import path from 'node:path'
import { createRequire } from 'node:module'
import type { InternalResolveOptions } from '../plugins/resolve'
import type { InternalResolveOptions, ResolveOptions } from '../plugins/resolve'
import { tryNodeResolve } from '../plugins/resolve'
import {
bareImportRE,
@ -53,7 +53,7 @@ export function cjsSsrResolveExternals(
cjsSsrCollectExternals(
config.root,
config.resolve.preserveSymlinks,
config.resolve,
ssrExternals,
seen,
config.logger
@ -116,8 +116,8 @@ export function createIsConfiguredAsSsrExternal(
createFilter(undefined, noExternal, { resolve: false })
const resolveOptions: InternalResolveOptions = {
...config.resolve,
root,
preserveSymlinks: config.resolve.preserveSymlinks,
isProduction: false,
isBuild: true
}
@ -211,7 +211,7 @@ function createIsSsrExternal(
// is used reverting to the Vite 2.9 SSR externalization heuristics
function cjsSsrCollectExternals(
root: string,
preserveSymlinks: boolean | undefined,
resolveOptions: Required<ResolveOptions>,
ssrExternals: Set<string>,
seen: Set<string>,
logger: Logger
@ -227,9 +227,9 @@ function cjsSsrCollectExternals(
...rootPkg.dependencies
}
const resolveOptions: InternalResolveOptions = {
const internalResolveOptions: InternalResolveOptions = {
...resolveOptions,
root,
preserveSymlinks,
isProduction: false,
isBuild: true
}
@ -247,7 +247,7 @@ function cjsSsrCollectExternals(
esmEntry = tryNodeResolve(
id,
undefined,
resolveOptions,
internalResolveOptions,
true, // we set `targetWeb` to `true` to get the ESM entry
undefined,
true
@ -314,13 +314,7 @@ function cjsSsrCollectExternals(
}
for (const depRoot of depsToTrace) {
cjsSsrCollectExternals(
depRoot,
preserveSymlinks,
ssrExternals,
seen,
logger
)
cjsSsrCollectExternals(depRoot, resolveOptions, ssrExternals, seen, logger)
}
}

View File

@ -118,13 +118,15 @@ async function instantiateModule(
// CommonJS modules are preferred. We want to avoid ESM->ESM imports
// whenever possible, because `hookNodeResolve` can't intercept them.
const resolveOptions: InternalResolveOptions = {
dedupe,
mainFields: ['main'],
browserField: true,
conditions: [],
extensions: ['.js', '.cjs', '.json'],
dedupe,
preserveSymlinks,
isBuild: true,
isProduction,
isRequire: true,
mainFields: ['main'],
preserveSymlinks,
root
}