Skip to content

Commit

Permalink
fix: surface exception when failing to resolve package entry (#4426)
Browse files Browse the repository at this point in the history
  • Loading branch information
benmccann committed Aug 16, 2021
1 parent 4776114 commit f75e508
Showing 1 changed file with 87 additions and 76 deletions.
163 changes: 87 additions & 76 deletions packages/vite/src/node/plugins/resolve.ts
Expand Up @@ -552,99 +552,110 @@ export function resolvePackageEntry(
if (cached) {
return cached
}
let entryPoint: string | undefined | void
try {
let entryPoint: string | undefined | void

// resolve exports field with highest priority
// using https://github.com/lukeed/resolve.exports
if (data.exports) {
entryPoint = resolveExports(data, '.', options, targetWeb)
}
// resolve exports field with highest priority
// using https://github.com/lukeed/resolve.exports
if (data.exports) {
entryPoint = resolveExports(data, '.', options, targetWeb)
}

// if exports resolved to .mjs, still resolve other fields.
// 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'))) {
// 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 (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.
// the heuristics here is to actually read the browser entry when
// possible and check for hints of UMD. If it is UMD, 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 (
(/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
// if exports resolved to .mjs, still resolve other fields.
// 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'))) {
// 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 (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.
// the heuristics here is to actually read the browser entry when
// possible and check for hints of UMD. If it is UMD, 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 (
(/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 {
entryPoint = browserEntry
}
} else {
entryPoint = browserEntry
}
}
}

if (!entryPoint || entryPoint.endsWith('.mjs')) {
for (const field of options.mainFields || DEFAULT_MAIN_FIELDS) {
if (typeof data[field] === 'string') {
entryPoint = data[field]
break
if (!entryPoint || entryPoint.endsWith('.mjs')) {
for (const field of options.mainFields || DEFAULT_MAIN_FIELDS) {
if (typeof data[field] === 'string') {
entryPoint = data[field]
break
}
}
}
}

entryPoint = entryPoint || data.main || 'index.js'
entryPoint = entryPoint || data.main || 'index.js'

// make sure we don't get scripts when looking for sass
if (
options.mainFields?.[0] === 'sass' &&
!options.extensions?.includes(path.extname(entryPoint))
) {
entryPoint = ''
options.skipPackageJson = true
}
// make sure we don't get scripts when looking for sass
if (
options.mainFields?.[0] === 'sass' &&
!options.extensions?.includes(path.extname(entryPoint))
) {
entryPoint = ''
options.skipPackageJson = true
}

// resolve object browser field in package.json
const { browser: browserField } = data
if (targetWeb && isObject(browserField)) {
entryPoint = mapWithBrowserField(entryPoint, browserField) || entryPoint
}
// resolve object browser field in package.json
const { browser: browserField } = data
if (targetWeb && isObject(browserField)) {
entryPoint = mapWithBrowserField(entryPoint, browserField) || entryPoint
}

entryPoint = path.join(dir, entryPoint)
const resolvedEntryPoint = tryFsResolve(entryPoint, options)
entryPoint = path.join(dir, entryPoint)
const resolvedEntryPoint = tryFsResolve(entryPoint, options)

if (resolvedEntryPoint) {
isDebug &&
debug(
`[package entry] ${chalk.cyan(id)} -> ${chalk.dim(resolvedEntryPoint)}`
)
setResolvedCache('.', resolvedEntryPoint, targetWeb)
return resolvedEntryPoint
} else {
throw new Error(
`Failed to resolve entry for package "${id}". ` +
`The package may have incorrect main/module/exports specified in its package.json.`
)
if (resolvedEntryPoint) {
isDebug &&
debug(
`[package entry] ${chalk.cyan(id)} -> ${chalk.dim(
resolvedEntryPoint
)}`
)
setResolvedCache('.', resolvedEntryPoint, targetWeb)
return resolvedEntryPoint
} else {
packageEntryFailure(id)
}
} catch (e) {
packageEntryFailure(id, e.message)
}
}

function packageEntryFailure(id: string, details?: string) {
throw new Error(
`Failed to resolve entry for package "${id}". ` +
`The package may have incorrect main/module/exports specified in its package.json` +
(details ? ': ' + details : '.')
)
}

function resolveExports(
pkg: PackageData['data'],
key: string,
Expand Down

0 comments on commit f75e508

Please sign in to comment.