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 吧。

参考

相关推荐
How_doyou_do41 分钟前
样式冲突修复组件
前端·javascript·html
IT_陈寒44 分钟前
SpringBoot实战:这5个高效开发技巧让我节省了50%编码时间!
前端·人工智能·后端
isixe1 小时前
Uniapp IOS 和 Android 下的文件写入用户目录
前端·uni-app
蓝莓味的口香糖1 小时前
【npm】npm命令大全
前端·npm·node.js
我是天龙_绍1 小时前
uniapp一个关于自定义导航栏高度计算的问题
前端
彭一1 小时前
uniapp评论弹窗
前端
**之火1 小时前
中止 Web 请求新方式 - AbortController API
开发语言·前端·javascript
我有一棵树1 小时前
如何优雅的布局,height: 100% 的使用和 flex-grow: 1 的 min-height 陷阱
前端·css·html
知识分享小能手1 小时前
微信小程序入门学习教程,从入门到精通,微信小程序页面交互 —— 知识点详解与案例实现(3)
前端·javascript·学习·react.js·微信小程序·小程序·交互
石小石Orz1 小时前
思考许久,我还是提交了离职申请
前端