Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): friendly ESM file require error #13283

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/guide/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@ You will need to either:
- Switch to another package manager (e.g. `pnpm`, `yarn`)
- Remove `&` from the path to your project

## Config

### This package is ESM only

When importing a ESM only package by `require`, the following error happens.

> Failed to resolve "foo". This package is ESM only but it was tried to load by `require`.

> "foo" resolved to an ESM file. ESM file cannot be loaded by `require`.

ESM files cannot be loaded by [`require`](<https://nodejs.org/docs/latest-v18.x/api/esm.html#require:~:text=Using%20require%20to%20load%20an%20ES%20module%20is%20not%20supported%20because%20ES%20modules%20have%20asynchronous%20execution.%20Instead%2C%20use%20import()%20to%20load%20an%20ES%20module%20from%20a%20CommonJS%20module.>).

We recommend converting your config to ESM by either:

- adding `"type": "module"` to the nearest `package.json`
- renaming `vite.config.js`/`vite.config.ts` to `vite.config.mjs`/`vite.config.mts`

## Dev Server

### Requests are stalled forever
Expand Down
99 changes: 72 additions & 27 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,7 @@ import {
ENV_ENTRY,
FS_PREFIX,
} from './constants'
import type {
InternalResolveOptions,
InternalResolveOptionsWithOverrideConditions,
ResolveOptions,
} from './plugins/resolve'
import type { InternalResolveOptions, ResolveOptions } from './plugins/resolve'
import { resolvePlugin, tryNodeResolve } from './plugins/resolve'
import type { LogLevel, Logger } from './logger'
import { createLogger } from './logger'
Expand Down Expand Up @@ -998,20 +994,45 @@ async function bundleConfigFile(
{
name: 'externalize-deps',
setup(build) {
const options: InternalResolveOptionsWithOverrideConditions = {
root: path.dirname(fileName),
isBuild: true,
isProduction: true,
preferRelative: false,
tryIndex: true,
mainFields: [],
browserField: false,
conditions: [],
overrideConditions: ['node'],
dedupe: [],
extensions: DEFAULT_EXTENSIONS,
preserveSymlinks: false,
packageCache: new Map(),
const packageCache = new Map()
const resolveByViteResolver = (
id: string,
importer: string,
isRequire: boolean,
) => {
return tryNodeResolve(
id,
importer,
{
root: path.dirname(fileName),
isBuild: true,
isProduction: true,
preferRelative: false,
tryIndex: true,
mainFields: [],
browserField: false,
conditions: [],
overrideConditions: ['node'],
dedupe: [],
extensions: DEFAULT_EXTENSIONS,
preserveSymlinks: false,
packageCache,
isRequire,
},
false,
)?.id
}
const isESMFile = (id: string): boolean => {
if (id.endsWith('.mjs')) return true
if (id.endsWith('.cjs')) return false

const nearestPackageJson = findNearestPackageData(
path.dirname(id),
packageCache,
)
return (
!!nearestPackageJson && nearestPackageJson.data.type === 'module'
)
}

// externalize bare imports
Expand All @@ -1031,16 +1052,40 @@ async function bundleConfigFile(
return { external: true }
}

const isIdESM = isESM || kind === 'dynamic-import'
let idFsPath = tryNodeResolve(
id,
importer,
{ ...options, isRequire: !isIdESM },
false,
)?.id
if (idFsPath && isIdESM) {
const isImport = isESM || kind === 'dynamic-import'
let idFsPath: string | undefined
try {
idFsPath = resolveByViteResolver(id, importer, !isImport)
} catch (e) {
if (!isImport) {
let canResolveWithImport = false
try {
canResolveWithImport = !!resolveByViteResolver(
id,
importer,
false,
)
} catch {}
if (canResolveWithImport) {
throw new Error(
`Failed to resolve ${JSON.stringify(
id,
)}. This package is ESM only but it was tried to load by \`require\`. See http://vitejs.dev/guide/troubleshooting.html#this-package-is-esm-only for more details.`,
)
}
}
throw e
}
if (idFsPath && isImport) {
idFsPath = pathToFileURL(idFsPath).href
}
if (idFsPath && !isImport && isESMFile(idFsPath)) {
throw new Error(
`${JSON.stringify(
id,
)} resolved to an ESM file. ESM file cannot be loaded by \`require\`. See http://vitejs.dev/guide/troubleshooting.html#this-package-is-esm-only for more details.`,
)
}
return {
path: idFsPath,
external: true,
Expand Down