解决 npm link 三方包时导致的 babel 问题
一、背景
笔者在本地开发了一个三方包-画板库,该库主要是完成一些图形绘制的功能。开发完成后,画板库本地打包没有报错。
然后笔者在 web 项目中 npm link
该包,进行相关的调试,用 web 项目的 webpack
构建时,报了很多 core-js
的报错:
1 | Module not found: Error: Can't resolve 'core-js/modules/web.dom.iterable' |
画板库用了@babel/preset-env
+ @babel/plugin-transform-runtime
+ @babel/runtime-corejs3
的方案,具体 babel
配置如下:
1 | { |
web 项目用了@babel/preset-env
+ corejs3
的方案,具体 babel
配置如下:
1 | { |
二、原因
当画板库中使用 @babel/runtime-corejs3
后,转换后的文件中会注入垫片模块,这些垫片模块从 @babel/runtime-corejs3
中导入。本来该文件已经被 babel
处理过了,但是 npm link
到 web 项目使用时,babel-loader
的 exclude
排除不了,会再用 web 项目的 babel
处理一遍,引入对 core-js
的依赖,而画板库本身有没有对该包的依赖,最终导致了报错。
下面,笔者会结合几个主要的问题探讨造成的原因。
1、为什么 babel-loader
会重新编译画板库?
首先将 web 项目的 babel-loader
修改如下:
1 | { |
首先 exclude
的最终判断效果还是等同于exclude: /node_modules/
的,然后我们通过/draw[b|B]oard/.test(path)
正则找到画板库包,看看他的真实路径以及最终的 exclude
返回值。最终结果如下:
可以看到,path
是画板库的真实路径地址,是不包含 node_modules
的,exclude 并没有排除画板库的文件,所以最终 web 项目的 babel-loader 仍然会转换画板库。
2、为什么编译时画板库文件有 import corejs
的代码?
首先我们简化画板库中的文件,测试一下在 web 项目中调用 babel
后的效果。
简化后的代码如下:
1 | // 省略逻辑,测试转换后的文件 |
然后我们在 web 项目中手动执行 babel
代码,看看转换后的文件。
执行命令:
1 | ./node_modules/.bin/babel node_modules/@xxx/drawBoard/dist/index.js --out-dir dist |
转换后的结果:
1 | import 'core-js/modules/es.array.map.js'; |
可以看到,web 项目的 babel 会往画板库中注入import "core-js/modules/es.array.map.js";
,这正是报错的来源。说明在 web 项目的 webpack 编译时,babel-loader 会执行 web 项目的 babel 配置,然后重新转换画板库的文件,将import "core-js/modules/es.array.map.js";
这样的代码注入到画板库文件中。
3、为什么 import core-js
会报错?
由于画板库的 babel
用的是 preset-env
加上@babel/runtime-corejs3
的方案,而@babel/runtime-corejs3
默认依赖的是 core-js-pure
,所以画板库的 node_modules
是没有 core-js
包的。
所以,代码执行时因为找不到 core-js
包,所以报错。
三、解决
综上,我们想要解决这个问题可以通过babel-loader再次编译
或者画板库没有core-js
两个方向着手。
一、针对 babel-loader 再次编译的问题
1、使用 webpack
的 symlinks
属性
webpack
有个 symlinks
就是专门解决这类问题的,默认是 true
,会将资源映射到其真实的路径。改为 false
后,就会变成 link
后的地址。
当我在 web 项目中开启后,web 项目的 babel-loader
打印如下:
可以看到,路径变成了[web 项目]/node_modules/[画板库]
的格式,这样 babel-loader
的 exclude
就会排除画板库,就不会再次处理了。
2、修改画板库的 package.json
的 main,指向 src 文件
由于问题源于两次 babel
转换,所以只需要将画板库的 main 改为指向 src 文件,在调试时统一用 web 项目的 babel
转换就可以了。
当然,需要注意在发画板库的包时,将 main 恢复为指向 dist 目录的文件。
二、针对画板库没有 core-js
1、修改 web 项目 的 babel
配置
将 web 项目 babel
的 usage
改为 entry
。这样 babel
就只会在入口处引入所有垫片,就不会在画板库的代码中单独添加 core-js
的依赖代码了。
编译后的文件:
1 | var Drawboard = {}; |
缺点:修改项目的 babel
配置为 entry
,会使得项目的垫片数量多非常多。
2、修改画板库的 babel
配置
将画板库的 babel
配置改为 preset-env
+ core-js
的方案
1 | { |
这样,画板库的 node_modules
中就有 core-js
包了
3、不改配置,安装 core-js
包
如题所示,仍然使用 @babel/runtime-corejs3
的方案,只是在画板库中单独安装个 core-js
的包,解决编译问题。
四、总结
综上,我们讨论了 npm link
导致问题的原因,并总结了几种解决方式。在解决问题的过程中,笔者也更深入地了解了这几种 babel
配置的联系。后续如果遇到类似的问题,可参考上述的问题分析的过程和解决方案。
参考资料
Using @babel/runtime-corejs2 and @babel/runtime-corejs3 leads to larger bundle sizes