esm 和 cjs 共用同一份类型 - 让 type 先 move to esm-only

太长了不想看?直接用 Rslib 就完事了

Move to esm-only (type)

在 node22 中支持了 require esm 之后,算是吹响了 dual 包(同时发 esm + cjs 产物) 到 esm-only 的最后号角,比如 antftu 的这篇文章 《Move to ESM-only》。但是大家似乎都没有关注到 .d.ts .d.mts .d.cjs,即类型文件关于 esm-only 的事情,本文是一些补充。

主要是解决这个报错:

The current file is a CommonJS module whose imports will produce 'require' calls

esm 和 cjs 共用同一份类型造成 FalseEsm 问题

.d.ts 文件 "使用 esm-only" 的历史比 .js 文件更早,在 github.com/egoist/tsup... 之前,大家已经不约而同的让 dual 包生成同一份类型,这实际上是一种错误用法,会造成 FalseEsm 和 FalseCjs 问题。

什么是 FalseEsmFalseCjs 问题?

当共用一份 .d.ts 时,根据 package.json 中是否含有 "type": "module" 有两种产物

1. 含有 "type": "module"

sh 复制代码
├── dist
│   ├── index.cjs
│   ├── index.d.ts
│   └── index.js
└── package.json
json 复制代码
// package.json
{
  "name": "my-package",
  "type": "module",
  "exports": {
    "types": "./dist/index.d.ts",
    "require": "./dist/index.cjs",
    "import": "./dist/index.js"
  }
}

由于设置了 "type": "module",需要 .js 和 .cjs 扩展名产物,对应到类型产物应该是 .d.ts 和 .d.cts。 .d.ts 会被认为是 esm,因此该包只发布了 esm 的 type ,在 are the types wrong? 会报以下错误

2. 不含有 "type": "module"

sh 复制代码
├── dist
│   ├── index.js
│   ├── index.d.ts
│   └── index.mjs
└── package.json
json 复制代码
// package.json
{
  "name": "my-package",
  "exports": {
    "types": "./dist/index.d.ts",
    "require": "./dist/index.js",
    "import": "./dist/index.mjs"
  }
}

FalseEsm 问题会更严重一些,因为会抛出一个这个错误

因为 ts 只拿到了 esm type,认为它是一个 pure esm 包,不能被 require 导入,所以无奈只能报错。

但其实这是个 dual 包,js 产物是对的且可正常运行,这个类型错误是一个噪声。

为 esm 和 cjs 都生成对应的 dts 文件

如何解决 FalseEsm 和 FalseCjs 问题?

github.com/egoist/tsup... 中,为 esm 和 cjs 都生成了 dts 文件

对应的产物为

sh 复制代码
├── dist
│   ├── index.d.cts <--- 
│   ├── index.cjs
│   │
│   ├── index.d.ts <---
│   └── index.js
└── package.json
json 复制代码
// package.json
{
  "name": "my-package",
  "type": "module",
  "exports": {
    "import": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    },
    "require": {
      "types": "./dist/index.d.cts",
      "default": "./dist/index.cjs"
    }
  }
}

忽略 .d.cts 的生成细节和正确性不谈,这样修改起码扩展名是对的。

还是回到 esm 和 cjs 共用同一份类型

随着 node 22 中支持了 require esm,ts 也有了对应更改,在 TS 5.8 中,无法 require esm 的这个错误在 module: "nodenext" 中不会再报

www.typescriptlang.org/docs/handbo...

这一改变,FalseEsm 问题的报错有了更多解决方案,

  • moduleResolution: bundler
  • 升级 ts 5.8 并设置 module: nodenext

这个问题后续只会影响到 "使用 tsc 编译到 commonjs" 并且 "需要支持 node <= 18" 的开发者,甚至大多数情况下只是噪声,而且使用 esm 的 type 为未来切到 pure esm 有利。

反而去生成 .d.cts 会困难许多。

总结

综上,在社区从 dual 包到 esm only 的大趋势里,esm 之前的诸多痛点得到了解决,包括 FalseEsm 类型问题。

我认为的两种最佳实践应是:

  • pure esm(.js + .d.ts, js 和 dts 产物均是 esm)
  • esm + cjs (.js + .cjs + .d.ts,js 产物是 dual,dts 产物是 esm)

而且这也是 Rslib 的两套默认模板

不需要再考虑 .d.cts 了,让 type 先 move to esm-only 吧。

参考

相关推荐
moshuying12 小时前
别让AI焦虑,偷走你本该有的底气
前端·人工智能
GIS之路13 小时前
ArcPy,一个基于 Python 的 GIS 开发库简介
前端
可夫小子14 小时前
OpenClaw基础-为什么会有两个端口
前端
喝拿铁写前端15 小时前
Dify 构建 FE 工作流:前端团队可复用 AI 工作流实战
前端·人工智能
喝咖啡的女孩15 小时前
React 合成事件系统
前端
从文处安16 小时前
「九九八十一难」组合式函数到底有什么用?
前端·vue.js
用户59625857360616 小时前
戴上AI眼镜逛花市——感受不一样的体验
前端
yuki_uix16 小时前
Props、Context、EventBus、状态管理:组件通信方案选择指南
前端·javascript·react.js
老板我改不动了16 小时前
前端面试复习指南【代码演示多多版】之——HTML
前端
panshihao16 小时前
Mac 环境下通过 SSH 操作服务器,完成前端静态资源备份与更新(全程实操无坑)
前端