wencaizhang

如何在 CJS 模块中引入 EJS 模块

Published onApril 19, 2024
-Views
1Minutes Read
前面写了关于如何在 ESM 模块中引入 CJS 模块的文章, 没过多久就又遇到了在 CJS 模块中引入 ES 模块的需求😅

问题介绍

事情是这样的, 我基于 Nestjs 开发一个项目, 引入了一个爬虫库 coder-hxl/x-crawl. 但是引入这个库之后总是找不到对应的模块, 因此我还去对应 issues 下提了一句(见创建实例时候 报错 TypeError: (0 , x_crawl_1.default) is not a function · Issue #89 · coder-hxl/x-crawl). 但是经过再次审查发现, 这不是人家 x-crawl 的 bug, 而是因为在 CJS 模块中引入 ES 模块引起的问题.

问题分析

在具体分析问题之前, 我先大致梳理一下不同规范的 npm 包是怎么引入的.
npm 包大致分为三类, 第一类是仅支持 CJS 规范的, 这一类一般都是年代特别久远的 npm 包, 而且已经很久不更新了, 第二类是同时支持 CJS 和 ESM 规范, 它们一般通过打包工具完成对不同规范的支持, 第三类则是只支持 ESM 规范, 因为创建时间比较晚, 所以没有 CJS 的历史包袱.
说回这次的问题, 因为 Nestjs 默认就是 ESM 规范的写法, 例如
, 所以我就认为 Nestjs 使用的是 ESM 规范, 而 coder-hxl/x-crawl 也是支持 ESM 规范的, 但引入之后却始终报错提示模块引入错误.
但其实呢问题出在 Nestjs 框架上, 使用 Nestjs 框架写代码使用 ESM 规范, 但是它最终打包产物却使用了 CJS 规范 😯. 我按照 ESM 规范写出来的代码, 最终会编译成 CJS 规范的代码, 而通过
引入模块的语句, 也会变成 CJS 规范下的
语句.
问题就出现在这一步, 如果是引入了 npm 包, 因为打包之后变成了 CJS 规范的
语句进行引入, 因此遇到只支持 ESM 规范, 不支持 CJS 规范的 npm 包, 那自然是模块导入失败, 提示引入错误也就合情合理了吧 🤔
coder-hxl/x-crawl 恰恰是在 V10 版本已经不再支持 CJS ,只能使用 ESM 了, 这就导致了在 Nestjs 中无法直接导入

解决方案选择

其实遇到不同规范的模块相互引入的问题, 可能大家第一反应是把项目整体改成 CJS 规范或者 ESM 规范, 具体操作大致是给
添加
,给
添加
等等, 我尝试了一下, 发现这个方法不太合适.
一来是代码改动太大, 你需要把已经写好的代码全部改成另一个规范, 二来是涉及到第三方 npm 包和打包构建工具, 会引起更多无法预测的问题, 总之这是一个解决方案, 但是改动成本也非常大.
而上一篇文章如何在 ESM 模块中引入 CJS 模块就是一个局部引入不同规范模块的方案, 所以这一次, 我们的目标就是不改动整体代码, 只想办法在 CJS 模块中引入 ES 模块.
经过一番百之谷之之后, 终于在 Compile a package that depends on ESM only library into a CommonJS package - Stack Overflow 找到了一个很简洁的解决方案.
则是 ESM 模块的相对路径或者绝对路径, 如果是 npm 包则直接写包名即可.
关于这个问题的复现代码我也单独创建了一个 repo 在这里 https://github.com/wencaizhang/nestjs-crawl-bug-demo 有兴趣可以狠狠点击查看具体代码.

其他

只支持 ESM 的 npm 包

搜到的资料

一些文档

Tags:
#JS 模块规范