You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Rollup将Node.js API打包为CommonJS时,如何解决ERR_REQUIRE_ESM错误?

问题

我用Rollup打包采用ES Module(import/export)语法编写的Node.js API,目标输出格式为CommonJS(CJS)。打包完成启动应用后,遇到大量仅支持ESM的第三方模块问题,错误信息如下:

Error [ERR_REQUIRE_ESM]: require() of ES Module /api/node_modules/.pnpm/node-fetch@3.3.2/node_modules/node-fetch/src/index.js from /api/dist/route-DpPSLTSv.js not supported.
Instead, change the require of index.js in /api/dist/route-DpPSLTSv.js to a dynamic import() which is available in all CommonJS modules.

我希望通过配置Rollup自动处理ESM转CJS的问题,不用手动把所有require()改成动态import()。当前Rollup配置如下:

import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import resolve from '@rollup/plugin-node-resolve'
import typescript from '@rollup/plugin-typescript'
import nodeExternals from 'rollup-plugin-node-externals'
import sentryRollupPlugin from 'rollup-plugin-sentry'

const { SENTRY_ORG, SENTRY_PROJECT, SENTRY_AUTH_TOKEN } = process.env

/**
 * @type {import('rollup').RollupOptions}
 */
const config = {
  input: ['src/index.ts', 'instrument.js'],
  output: {
    dir: 'dist',
    format: 'cjs',
    sourcemap: true
  },
  plugins: [
    resolve({
      extensions: ['.ts', '.js', '.tsx', '.jsx', '.json', '.mjs', '.cjs']
    }),
    commonjs(),
    nodeExternals(),
    typescript(),
    json(),
    sentryRollupPlugin({
      org: SENTRY_ORG,
      project: SENTRY_PROJECT,
      authToken: SENTRY_AUTH_TOKEN,
      include: ['dist'],
      ignore: ['node_modules']
    })
  ],
  onwarn(warning, handler) {
    if (warning.code === 'THIS_IS_UNDEFINED') {
      return
    }

    handler(warning)
  }
}

export default config

解决方案

1. 调整@rollup/plugin-node-resolve配置,适配Node.js环境并指定需处理的ESM依赖

修改resolve插件配置,添加Node.js环境专属参数,并指定需要转换的ESM模块:

resolve({
  extensions: ['.ts', '.js', '.tsx', '.jsx', '.json', '.mjs', '.cjs'],
  preferBuiltins: true, // 优先使用Node.js内置模块
  browser: false, // 明确为Node.js环境,而非浏览器
  resolveOnly: ['node-fetch', /* 其他需要转换的ESM依赖 */] // 指定要处理的ESM模块
})

2. 增强@rollup/plugin-commonjs的转换能力

开启混合模块转换,让插件处理node_modules中的ESM模块:

commonjs({
  esmExternals: false, // 不把ESM模块当作外部依赖处理
  transformMixedEsModules: true, // 自动转换混合ESM/CJS的模块
  include: /node_modules/ // 覆盖所有node_modules中的模块
})

3. 修改rollup-plugin-node-externals配置,排除需转换的ESM依赖

默认插件会把所有node_modules模块排除在打包外,导致ESM依赖直接被require引用。需将需要转换的模块从排除列表中移除:

nodeExternals({
  exclude: ['node-fetch', /* 其他需要转换的ESM依赖 */]
})

4. 可选:添加@rollup/plugin-legacy处理顽固ESM模块

如果某些ESM模块转换仍有问题,可添加该插件辅助转换:
先安装插件:

npm install @rollup/plugin-legacy --save-dev

再在配置中引入并使用:

import legacy from '@rollup/plugin-legacy'

// 加入plugins数组
legacy({
  'node-fetch': 'default' // 指定模块的导出方式
})

完整修改后的配置示例

import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import resolve from '@rollup/plugin-node-resolve'
import typescript from '@rollup/plugin-typescript'
import nodeExternals from 'rollup-plugin-node-externals'
import sentryRollupPlugin from 'rollup-plugin-sentry'
import legacy from '@rollup/plugin-legacy'

const { SENTRY_ORG, SENTRY_PROJECT, SENTRY_AUTH_TOKEN } = process.env

/**
 * @type {import('rollup').RollupOptions}
 */
const config = {
  input: ['src/index.ts', 'instrument.js'],
  output: {
    dir: 'dist',
    format: 'cjs',
    sourcemap: true,
    interop: 'auto' // 自动处理ESM与CJS的互操作问题
  },
  plugins: [
    resolve({
      extensions: ['.ts', '.js', '.tsx', '.jsx', '.json', '.mjs', '.cjs'],
      preferBuiltins: true,
      browser: false,
      resolveOnly: ['node-fetch']
    }),
    commonjs({
      esmExternals: false,
      transformMixedEsModules: true,
      include: /node_modules/
    }),
    nodeExternals({
      exclude: ['node-fetch']
    }),
    legacy({
      'node-fetch': 'default'
    }),
    typescript(),
    json(),
    sentryRollupPlugin({
      org: SENTRY_ORG,
      project: SENTRY_PROJECT,
      authToken: SENTRY_AUTH_TOKEN,
      include: ['dist'],
      ignore: ['node_modules']
    })
  ],
  onwarn(warning, handler) {
    // 忽略无关警告
    if (warning.code === 'THIS_IS_UNDEFINED' || warning.code === 'MODULE_LEVEL_DIRECTIVE') {
      return
    }
    handler(warning)
  }
}

export default config

关键说明

  • 将需要转换的ESM依赖从externals中排除,让Rollup把它们打包进输出文件,再通过commonjs插件转为CJS格式。
  • interop: 'auto'会自动处理默认导出等互操作细节,无需手动调整导入语句。
  • 若有多个ESM依赖,只需在resolveOnly、nodeExternals.exclude和legacy配置中添加对应模块名即可。

内容的提问来源于stack exchange,提问作者octavemirbeau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 19:47:01