如何将ES6格式的node_modules依赖转译为CommonJS并纳入npm包?
解决Node.js CommonJS包中导入ES6模块依赖的问题
方案1:用tsup打包(推荐,配置简单)
tsup基于esbuild,能快速将TS代码和指定的ES6依赖一起打包成CommonJS,无需复杂配置。
安装依赖:
npm install tsup typescript --save-dev创建
tsup.config.ts:
import { defineConfig } from 'tsup' export default defineConfig({ entry: ['src/index.ts'], // 你的入口文件路径 format: 'cjs', // 输出CommonJS格式 outDir: 'dist', // 构建输出目录 noExternal: ['es6-dep-package', 'another-es6-dep'], // 填入需要转译的ES6依赖包名 target: 'node16', // 适配你的Node运行版本 clean: true, // 构建前清空dist目录 dts: true, // 生成类型声明文件(按需开启) })
- 修改
package.json的脚本和配置:
{ "scripts": { "build": "tsup" }, "main": "dist/index.js", "types": "dist/index.d.ts", "files": ["dist"] }
执行npm run build后,dist目录会包含打包后的CommonJS代码和所有指定的ES6依赖,发布时直接提交dist即可。
方案2:用Rollup定制打包
Rollup适合更精细的模块打包控制,能精准选择要打包的ES6依赖。
安装依赖:
npm install rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript @rollup/plugin-babel @babel/preset-env --save-dev创建
rollup.config.js:
import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import typescript from '@rollup/plugin-typescript'; import babel from '@rollup/plugin-babel'; export default { input: 'src/index.ts', output: { file: 'dist/index.js', format: 'cjs', sourcemap: true }, plugins: [ resolve({ preferBuiltins: true }), // 解析node_modules中的模块 commonjs(), // 将CommonJS模块转成ES6格式以便Rollup处理 typescript(), // 处理TS代码 babel({ babelHelpers: 'bundled', presets: [['@babel/preset-env', { targets: { node: '16' } }]], exclude: /node_modules\/(?!es6-dep-package)/ // 只转译指定的ES6依赖 }) ], external: ['fs', 'path'] // 填入不需要打包的Node内置模块或其他CommonJS依赖 };
- 同样在
package.json配置build脚本和files字段,确保dist目录被纳入发布范围。
方案3:用Babel单独转译指定ES6依赖
如果你不想整体打包,只想转译特定依赖并纳入构建产物:
- 修改
babel.config.json,允许转译指定的node_modules包:
{ "presets": [ ["@babel/preset-env", { "targets": { "node": "16" } }], "@babel/preset-typescript" ], "exclude": /node_modules\/(?!es6-dep-package)/ }
- 编写脚本转译目标依赖到dist目录,比如创建
scripts/transpile-deps.js:
const fs = require('fs-extra'); const { transformFileSync } = require('@babel/core'); const path = require('path'); const depPath = path.join(__dirname, '../node_modules/es6-dep-package'); const outputPath = path.join(__dirname, '../dist/node_modules/es6-dep-package'); // 递归转译文件 function transpileDir(src, dest) { fs.ensureDirSync(dest); const files = fs.readdirSync(src); for (const file of files) { const srcFile = path.join(src, file); const destFile = path.join(dest, file); if (fs.statSync(srcFile).isDirectory()) { transpileDir(srcFile, destFile); } else if (file.endsWith('.js') || file.endsWith('.ts')) { const result = transformFileSync(srcFile, { configFile: path.join(__dirname, '../babel.config.json') }); fs.writeFileSync(destFile.replace(/\.ts$/, '.js'), result.code); } else { fs.copyFileSync(srcFile, destFile); } } } transpileDir(depPath, outputPath);
- 修改
package.json的build脚本,先转译依赖再构建自己的代码:
{ "scripts": { "transpile-deps": "node scripts/transpile-deps.js", "build": "npm run transpile-deps && tsc", }, "main": "dist/index.js", "files": ["dist"] }
转译后的依赖会在dist/node_modules下,发布时会被包含,你的代码可以直接导入该依赖(Node会优先读取dist下的node_modules)。
关键注意事项
- Babel可以转译node_modules中的模块,只要修改
exclude配置,精确包含需要处理的包,避免全局转译所有node_modules导致构建变慢。 - 确保
package.json的files数组包含所有需要发布的文件(比如dist目录),避免转译后的依赖被遗漏。 - 测试构建后的包:在本地用
npm pack生成tgz包,然后在另一个项目中安装测试,确认可以通过require正常加载,且依赖能正常运行。
内容的提问来源于stack exchange,提问作者Jeff Pettis
相关产品推荐
相关产品推荐

