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

如何用Rollup仅转ES6模块为CommonJS且不打包?路径问题求解

解决Rollup转换ES6到CommonJS时的相对路径问题

问题背景

我正在开发名为foo的库,对外暴露两个ES6模块a.js和b.js(支持import a from foo/a与import b from foo/b的导入方式)。源文件结构如下:

// src/a.js
export default function a(x) { return x+'!' }
// src/b.js
import a from './a.js'
export default function b() { return a('Hi') }

我希望将这两个文件转为CommonJS模块并保存到项目根目录,仅需将import转换为require,不进行打包操作,期望输出:

// a.js
module.exports = function a(x) { return x+'!' }
// b.js
const a = require('./a')
module.exports = function b() { return a('Hi') }

但使用Rollup的external: (id) => id!==currentFile配置时,输出的b.js中路径被改写为const a = require('./src/a'),指向了src目录下的源文件,不符合预期。


解决方案

方案1:用Rollup的output.paths手动映射路径

你可以通过Rollup的output.paths选项,直接指定外部依赖的目标路径,让相对导入指向根目录的转换后文件,而非src下的源文件。

修改rollup.config.js如下:

import { babel } from '@rollup/plugin-babel';

export default [
  {
    input: 'src/a.js',
    output: {
      file: 'a.js',
      format: 'cjs'
    },
    plugins: [babel({ babelHelpers: 'bundled' })]
  },
  {
    input: 'src/b.js',
    output: {
      file: 'b.js',
      format: 'cjs',
      // 把对./a.js的导入映射到根目录的./a
      paths: {
        './a.js': './a'
      }
    },
    external: ['./a.js'], // 将a.js标记为外部依赖
    plugins: [babel({ babelHelpers: 'bundled' })]
  }
];

方案2:自定义Rollup插件自动调整路径

如果你的模块数量较多,手动写paths太繁琐,可以写一个轻量的自定义插件,在转换阶段自动修正导入路径:

import { babel } from '@rollup/plugin-babel';

const adjustImportPaths = () => ({
  name: 'adjust-import-paths',
  resolveId(source, importer) {
    // 只处理src目录内的相对导入
    if (source.startsWith('./') && importer.includes('/src/')) {
      // 移除.js后缀,让路径指向根目录的转换后文件
      return source.replace('.js', '');
    }
    return null;
  }
});

export default [
  {
    input: 'src/a.js',
    output: { file: 'a.js', format: 'cjs' },
    plugins: [babel({ babelHelpers: 'bundled' })]
  },
  {
    input: 'src/b.js',
    output: { file: 'b.js', format: 'cjs' },
    external: ['./a.js'],
    plugins: [adjustImportPaths(), babel({ babelHelpers: 'bundled' })]
  }
];

方案3:换用Babel单独转换(更适合纯转译场景)

如果你的需求只是简单的ES6到CommonJS转译、不涉及打包,Babel其实是更轻量的选择,配置也更简单:

  1. 安装依赖:
npm install @babel/core @babel/cli @babel/preset-env --save-dev
  1. 创建.babelrc配置:
{
  "presets": [
    ["@babel/preset-env", {
      "targets": { "node": "current" },
      "modules": "commonjs"
    }]
  ]
}
  1. 执行转译命令:
npx babel src/a.js --out-file a.js
npx babel src/b.js --out-file b.js

转译后的b.js会自动把import a from './a.js'转为const a = require('./a'),完全符合预期。

方案4:用esbuild极速转译

esbuild是一个超快速的构建工具,也能轻松完成单文件转译,且无需复杂配置:

直接执行命令:

npx esbuild src/a.js --format=cjs --outfile=a.js
npx esbuild src/b.js --format=cjs --outfile=b.js

它会自动处理相对导入路径,转译后的b.js里的require路径正好是./a,完美满足需求,速度还远快于Rollup和Babel。


内容的提问来源于stack exchange,提问作者Már Örlygsson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:56:09