Skip to content

Commit

Permalink
feat(config): friendly ESM file require error (#13283)
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red committed Jun 6, 2023
1 parent a11b6f6 commit b9a6ba0
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 27 deletions.
17 changes: 17 additions & 0 deletions docs/guide/troubleshooting.md
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
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 @@ -1003,20 +999,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 @@ -1036,16 +1057,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

0 comments on commit b9a6ba0

Please sign in to comment.