来源在于查询Electron 中使用 ESM 包问题的时候,发现了一个 iuess: How can i import esm in electron,下面的讨论感觉很有意思,所以记录一下

双重包风险

因为NodeJs最开始的时候不支持 ESM,所以在创建Package的时候,大部分的包要么导出 CJS,要么同时包含CJSESM导出,package.json 中的main 指定的是CommonJS入口点,pagkage.json 中的module 指定了 ES模块入口点,NodeJS运行CommonJs入口点,构建工具webpack等使用ES模块入口点。这就会导致双重包风险

双重包风险

当应用程序使用同时提供CommonJS和ES模块元的包时,如果同时加载了两个版本的包,则存在某些错误的风险,比如一个包的两个版本可以在同一个运行时环境中加载,虽然程序或包不太可能有意直接加载两个版本,但应用程序加载一个版本而应用程序的依赖项加载另一个版本是很常见的,这种危险的来源是因为NodeJs支持混合CommonJS和ES模块

Tip: 如果扩展名为.jspackage.jsontype:‘module’ 会将此类文件视为ES模块

// package.json
"type":"module"

如何避免双重包风险有两种方式,NodeJs的官网也有介绍:

  1. 使用ES模块包装器
    2
    . 隔离状态

回到一开始说的,两位老哥在《如何在Electron中使用ESM的讨论》

同时导出CommonJS和ESM存在的问题:

  • 同时导出CommonJS和ESS会带来双重包风险
    • 一个包同时发布后来那个两次构建同一个API是不正确的
      • 一个CommonJS
      • 一个ESM
    • 80%已发布的双CJS/ESM 包会导致双重包风险(这么严重?表示怀疑)
  • 包的作者必须主要维护和编写CMJ模块(没太理解)
    • 需要确保所有的开发工具都支持ESM和CMJ模块,并且可以告诉任何给定文件的模式应该是什么
    • 因为同时存在两种模块的包,所以包的体积会变大(是指node_modules 吧)
    • 你的所谓的ESM包只是表面上的ESM包
      • 不能在浏览器或Deno中使用,因为这些模块要么都是CJS,要么是导入CJS的ESM,这些运行时无法处理CJS。

长期以来JavaScript社区的优先级都是错误的,需要优先考虑的是创建一个包的技术优雅、简单、DX和文档,而不是包的使用者 (嗯?)
如果能在几年前,Node的Npm生态系统更小的时候,NodeJs 放弃CMJ并支持ESM的话就更好了,也就不会导致这么多的问题。

推崇使用纯ESM的方式,他认为最好都要切换到纯ESM包,快速撕掉创可贴,专注于发布简单、符合标准的软件包。作者还说了一句:

所有抱怨的人都不知道Deno很快就会破坏他们的整个世界,一个只适用于真正ESM的运行时,类似浏览器的 HTTPS导入

嗯?

这个评论的人似乎比较悲观,多年来他一直在为一些开源包做纯ESM的工作,例如一直在推动Next.js支持原生ESM,为style-jsx提交的支持ESM的PR,但是他发现这些项目的所有者完全没有紧迫感。

一些TypeScript用户以为他们在使用ESM,而实际上他们在不知情的情况下正在转译为CJS。

双重包是需要避免的风险,CJS不能再NodeJS以外的其他环境中使用,并且NodeJS现在已经支持ESM了,为什么还要使用CJS呢?

有人必须在某个时候开始转换为纯 ESM。我们不能坐等所有依赖 node-fetch 的 23k 包先切换到 esm。或者反过来。我们不能坐等子依赖或构建工具首先成为 ESM。而且我们无法控制 gulp、webpackjestrolluptypescriptbabel 等如何处理 cjsesm。那么我们将永远被 cjs/dual 包所困。

目前已经有很多纯ESM的包了,例如:

这是一种好的未来