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

参考

相关推荐
程序视点3 小时前
IObit Uninstaller Pro专业卸载,免激活版本,卸载清理注册表,彻底告别软件残留
前端·windows·后端
前端程序媛-Tian3 小时前
【dropdown组件填坑指南】—怎么实现下拉框的位置计算
前端·javascript·vue
嘉琪0013 小时前
实现视频实时马赛克
linux·前端·javascript
烛阴4 小时前
Smoothstep
前端·webgl
若梦plus4 小时前
Eslint中微内核&插件化思想的应用
前端·eslint
爱分享的程序员4 小时前
前端面试专栏-前沿技术:30.跨端开发技术(React Native、Flutter)
前端·javascript·面试
超级土豆粉4 小时前
Taro 位置相关 API 介绍
前端·javascript·react.js·taro
若梦plus4 小时前
Webpack中微内核&插件化思想的应用
前端·webpack
若梦plus4 小时前
微内核&插件化设计思想
前端
柯北(jvxiao)4 小时前
搞前端还有出路吗?如果有,在哪里?
前端·程序人生