引入纯 ESM 模块化的第三方包

CSDN中文章不一定能及时更新,欢迎点击前往我的博客查看最新版本:许盛的博客

背景

今天要做个 CLI 工具,一路调研学习加实践都比较顺利,但是在引入 globby 这个库时,就开始报错了。

40pLrj.png

/Users/xusheng/workspace/test/mit-cli/dist/lib/utils/zip.js:4
var globby_1 = require("globby");
               ^

Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/xusheng/workspace/test/mit-cli/node_modules/globby/index.js from /Users/xusheng/workspace/test/mit-cli/dist/lib/utils/zip.js not supported.
Instead change the require of index.js in /Users/xusheng/workspace/test/mit-cli/dist/lib/utils/zip.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (/Users/xusheng/workspace/test/mit-cli/dist/lib/utils/zip.js:4:16)
    at Object.<anonymous> (/Users/xusheng/workspace/test/mit-cli/dist/lib/publish.js:5:13)
    at Object.<anonymous> (/Users/xusheng/workspace/test/mit-cli/dist/lib/index.js:5:17) {
  code: 'ERR_REQUIRE_ESM'

错误信息简化一下就是 require() of ES Module xxxx from xxxxx not supported

字面意思来理解,就是不支持 require 一个 ES Module 的包。

这个时候就很迷了,我的 tsconfig 中设置的 module: "commonjs" ,检查了一下 tsc 编译后的文件,也都转换为了 commonjs 模块化方案。

虽然说目前 nodejs 已经原生支持了 esm ,浏览器也提供 type="module" 来支持 esm,但是考虑到兼容性问题,一般我们在编译项目时,还是会输出 commonjs 模块标准的代码,所以一开始压根就没往第三方库的问题上去想。

问题定位

经过一番检索和思考,发现 globby 这个库居然直接发布的 esm 模块标准的代码,也就是说,我的代码虽然 tsc 转换为了 commonjs 标准,但是引入的 globby 还是 esm 标准的代码,这就导致了错误。

这个时候引入一个概念 Pure ESM package,也就是纯 ESM 模块化的包,如果要使用这种第三方库,可以阅读文档:https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c

如果一个库是 Pure ESM package 的话,它就没办法再被 commonjs 标准的代码使用 require 引用了,如果要解决这个问题,文档中提出了三种方案:

  1. Use ESM yourself. **(preferred)**Use import foo from 'foo' instead of const foo = require('foo') to import the package. You also need to put "type": "module" in your package.json and more. Follow the below guide.
  2. If the package is used in an async context, you could use [await import(…)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports) from CommonJS instead of require(…).
  3. Stay on the existing version of the package until you can move to ESM.

关于 nodejs 中如何处理 ES6 模块的,可以参考:https://www.ruanyifeng.com/blog/2020/08/how-nodejs-use-es6-module.html

尝试解决

参考上面文档中的三种解决方式。

1. Use ESM yourself

第一种方式比较扯,就是把你自己的库也改成 ESM 标准,这就很坑了,这不是扩大了兼容性的问题了嘛。

2. use await import(…)

第二种方式,就是将静态的 import 语句,改为动态的 import() 方法,例如:

// before
import { xxx } from 'globby';

// after
const { xxx } = await import('globby');

理论上讲好像可以,但是我实际尝试的时候,发现如果 tsc 编译后为 commonjs 标准的话, import() 方法会被转化为一个 __importStar(require('globby')) 方法,本质上还是 require() ?所以还是会报错。

需要进一步调研看看。

3. Stay on the existing version of the package until you can move to ESM

这个方法也很扯淡,就是在你可以将你的项目改为 ESM 标准之前,使用旧版本的 commonjs 标准的第三方库。

解决方法

上面的三种方式,都没有解决问题,只能采取一种治标不治本的方式了。

既然第三方库是 ESM 标准,那么我们在 tsc 编译时,把它也编译一下好了。

globby 为例,在 tsconfig 文件中加入以下代码:

{
    "compilerOptions": {
        ...
        // 因为 globby 是用 js 写的,所以在 tsconfig 中要将 allowJs 设置为 true
        "allowJs": true
    },
	"include": [
		"node_modules/globby/**/*"
	]
}

此时再运行 tsc 编译,会发现在输出的 dist 目录中,新增了一个 node_modules 目录,其中包含了编译后的 globby 包代码。

但是这里需要注意下,再次运行项目,发现还是报同样的错,只是报错的库由 globby 变成了 array-union,这是因为 globbypure ESM package,经过 tsc 编译后变成了 commonjs 标准,但是 globby 引用了 array-union,而 array-union 也是 pure ESM package

以此类推,需要把所有的 pure ESM package 都编译一下:

{
	"include": [
        "node_modules/globby/**/*",
        "node_modules/array-union/**/*",
        "node_modules/slash/**/*"	
	]
}

完美,问题解决。

这里要吐槽一下,array-union 这个库,其实就一行代码:
constarrayUnion = (...arguments_) => [...newSet(arguments_.flat())];
就这还引入一个额外的库,坑!

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值