如何用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其实是更轻量的选择,配置也更简单:
- 安装依赖:
npm install @babel/core @babel/cli @babel/preset-env --save-dev
- 创建
.babelrc配置:
{ "presets": [ ["@babel/preset-env", { "targets": { "node": "current" }, "modules": "commonjs" }] ] }
- 执行转译命令:
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
相关产品推荐
相关产品推荐

