让一个包既支持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即可。
posted @ 2024-11-10 23:46  李小菜丶  阅读(532)  评论(0)    收藏  举报