355 - 《optimizePackageImports》
发布于 2023年10月19日
脑暴了下 Next.js 的 optimizePackageImports 在 Mako 下实现的 RFC,实现需要较多构建和 Mako 相关的背景知识,比较晦涩难懂。
背景
背景是看到 Next.js 的文章 https://vercel.com/blog/how-we-optimized-package-imports-in-next-js ,其中的 optimizePackageImports 看起来很心动。这个方法能大量减少模块总数,从构建工具的角度看,不仅减少了 build 的工作,也减少了 module graph 里的模块数,进而降低了 tree-shaking 和 code splitting 的压力。
我之前写过的 babel-plugin-import 也是这个目的,但不如 optimizePackageImports 通用和覆盖面广。覆盖面是决定最终效果的关键因素,之前做 mfsu 第一版的时候就深有体会。
问题
先看要达到的效果。
比如我们这么写。
import { Button } from 'antd';
然后 antd 的入口文件是 antd/es/index.js
,其内容如下。
export { default as App } from './App';
那前面我们写代码如果被优化成这样,就可以大量减少模块数量了。
import Button from 'antd/es/App';
实现
怎么做到这一点呢?下面花 30 分钟脑暴下实现思路。
这个场景比较复杂,edge case 比较多,感觉测试驱动会比较好。所以先想下需要覆盖的用例。
基础用例如下。
- import { x } from ‘foo’,如果 foo 是桶文件,期望会被替换
- import { x } from ‘foo’,如果 foo 不是桶文件,期望不会被替换
- 支持 import { x as y }
- 支持 import x from ‘foo’ 的用 default specifier 的场景
- 不支持 import * as x from ‘foo’,不管 foo 是不是桶文件,都期望不会被替换
进阶用例如下。
- 支持递归桶文件,比如项目 > a,a 的入口文件依赖 a/button/index,而 a/button/index 也是个桶文件,期望拿到的是最终不是桶文件的路径
- 支持 node_modules 下依赖的场景,项目 > a > b,a 里有 import { x } from ‘b’,如果 b 是桶文件,