让一个包既支持commonjs又支持esm
在 JavaScript 社区中,有两种主要的模块系统:CommonJS(CJS)和 ECMAScript 模块(ESM)。CJS 是 Node.js 默认的模块系统,而 ESM 是在浏览器和现代 JavaScript 运行时环境中广泛使用的模块系统。
在开发一个 npm 包时,为了确保包能够同时支持这两种模块系统,下面是一些可能的做法:
在 package.json 中指定入口文件:可以在 package.json 文件中使用 "main" 和 "module" 字段分别指定 CommonJS 和 ES modules 的入口文件。
"main": "lib/index.js",
"module": "src/index.mjs"
这样,当用户在使用 CommonJS 模块系统时,将会使用 "main" 字段指定的 CommonJS 模块入口文件;当用户使用 ES modules 时,将会使用 "module" 字段指定的 ES module 入口文件。
使用 Babel 编译
使用 Babel 编译同时支持 CommonJS 和 ESM(ECMAScript Modules),可以通过配置 Babel 的 presets 和 plugins 来实现。
以下是一个基本的配置示例:
1. 安装 Babel 和相关依赖
确保你安装了 Babel 及其相关插件:
npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-modules-commonjs
2. 创建 Babel 配置文件
在项目根目录下创建一个 `babel.config.js` 文件,内容如下:
{ "presets": [ ["@babel/preset-env", { "modules": false, // 默认设置为 false,以支持 ESM // 目标环境的浏览器版本 "targets": { "node": "current" // 或者指定你的 Node.js 版本 } }] ] }
这里将 "modules" 参数设为 false,这将会保留 ES modules 的语法,并将其编译为 ES modules 格式的代码。
3. 编写代码
可以使用 ESM 语法编写你的代码,例如:
// src/index.js export const hello = () => { console.log("Hello, world!"); }; // CommonJS 导入 const { hello } = require('./index.js'); hello();
4. 添加构建脚本
在 `package.json` 中添加构建脚本,以便生成 CJS 和 ESM 格式:
{ "scripts": { "build:cjs": "babel src --out-dir lib/cjs --copy-files --extensions '.js,.jsx'", "build:esm": "babel src --out-dir lib/esm --copy-files --extensions '.js,.jsx' --plugins @babel/plugin-transform-modules-esm" } }
5. 运行构建
运行以下命令生成两个版本的包:
npm run build:cjs
npm run build:esm
6. 配置 `package.json`
在 `package.json` 中,添加指向 CJS 和 ESM 的入口字段:
{ "main": "lib/cjs/index.js", "module": "lib/esm/index.js" }
现在,你的包可以支持 CommonJS 和 ES Modules。使用 npm 发布时,确保包含 `lib` 目录。
使用 TypeScript 编译
如果使用 TypeScript 开发,可以在 tsconfig.json 中配置输出 CommonJS 和 ES modules。
1、在 `tsconfig.json` 中设置不同的输出目标。可以使用 `tsc` 的 `--outDir` 选项分别编译为 CommonJS 和 ESM。
{
"compilerOptions": {
"target": "ES2015",
"module": "ESNext", // 对于 ESM
"outDir": "./dist/esm",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"]
}
2、创建两个不同的配置文件,例如 `tsconfig.cjs.json` 和 `tsconfig.esm.json`。
tsconfig.cjs.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "CommonJS",
"outDir": "./dist/cjs"
}
}
tsconfig.esm.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"outDir": "./dist/esm"
}
}
3、在 `package.json` 中添加脚本来编译这两个版本:
"scripts": {
"build:cjs": "tsc --project tsconfig.cjs.json",
"build:esm": "tsc --project tsconfig.esm.json",
"build": "npm run build:cjs && npm run build:esm"
}
4、在 `package.json` 中指定不同的入口点:
{
"name": "your-package",
"main": "dist/cjs/index.js", // CommonJS 入口
"module": "dist/esm/index.js", // ESM 入口
"types": "dist/index.d.ts",
"scripts": {
"build": "npm run build:cjs && npm run build:esm"
},
"files": [
"dist/cjs",
"dist/esm"
]
}
通过这些步骤,你的包就可以同时支持 CommonJS 和 ESM。
@babel/plugin-transform-modules-commonjs和@babel/plugin-transform-modules-esm
`@babel/plugin-transform-modules-commonjs` 和 `@babel/plugin-transform-modules-esm` 是用于将 ES 模块语法(导入/导出)转换为 CommonJS 或 ESM 格式的 Babel 插件。
- @babel/plugin-transform-modules-commonjs 将 ES 模块转换为 CommonJS 语法,适用于不支持 ES 模块的环境,比如较旧的 Node.js 版本。
- @babel/plugin-transform-modules-esm 将 CommonJS 模块转换回 ES 模块语法,适用于打包或目标环境支持原生 ES 模块的情况。
根据你的目标环境和模块系统需求,通常选择其中一个插件。
@babel/preset-env的"modules": false` 与 @babel/plugin-transform-modules-commonjs组合
当 modules为false时,Babel 不会转换 ES6 模块语法(`import` 和 `export`),保留原始的 ES6 模块格式。这对于打包工具(如 Webpack)非常有用,因为它们可以直接处理 ES6 模块。
@babel/plugin-transform-modules-commonjs插件会将 ES6 模块语法转换为 CommonJS 模块语法(`require` 和 `module.exports`)。
如果将这两个配置一起使用,实际上会导致冗余。因为 `@babel/plugin-transform-modules-commonjs` 会尝试将 ES6 模块转换成 CommonJS,而 `"modules": false` 则会阻止这种转换。
在大多数情况下,选择一种方式即可:
- 如果你希望使用 ES6 模块,设置 `"modules": false` 即可;
- 如果希望转换为 CommonJS,则不需要设置 `"modules": false"`,使用@babel/plugin-transform-modules-commonjs即可。

浙公网安备 33010602011771号