𝗕𝗲𝘆𝗼𝗻𝗱 𝗠𝗶𝘀𝘀𝗶𝗻𝗴 𝗘𝘅𝗽𝗼𝗿𝘁𝘀: 𝗕𝘂𝗶𝗹𝗱𝗶𝗻𝗴 𝗮𝗻 𝗘𝗮𝗿𝗹𝘆 𝗚𝗮𝗿𝗯𝗮𝗴𝗲 𝗖𝗼𝗹𝗹𝗲𝗰𝘁𝗼𝗿

我曾尝试使用标准插件来修复 Webpack 文档中缺失的链接。但失败了。

将插件集成到大型代码库中并非易事。这演变成了一个架构挑战。我必须处理抽象语法树 (AST) 操作、内存使用以及递归循环。

标准插件的问题

我进行了三次实验来寻找解决方案。

实验 1:插件找回了 135 个类型。然而,它将这些类型放在了一个内部模块中。我们的工具为它们创建了一个单独的文件夹。我们不得不手动进行分类。文档的结构也是错误的。

实验 2:我开启了一个隐藏外部类型的设置。这在某些方面奏效了,将有效载荷减少到了 60 个 Webpack 类型。但 TypeDoc 忽略了嵌套依赖。这导致许多类型虽然被隐藏了,却仍然占用内存。

实验 3:我尝试将类型映射回它们原始的模块。这导致了递归循环。插件不断提取嵌套接口,直到生成了 630 个类型。文档中充斥着大量噪音,严重破坏了用户体验。

解决方案:早期垃圾回收

我需要将类型放在正确的模块中,同时又不产生额外的噪音。我不再试图修复输出结果,而是开始着手优化处理流程。

我使用了一个名为 EVENT_RESOLVE_END 的钩子。这让我能在解析完成后立即拦截 AST。我在 TypeDoc 分配类别或消耗大量内存之前完成了这一操作。

我的逻辑分为三个步骤:

结果:我保留了 240 个关键类型。它们现在可见且路由正确。我避免了第三次实验中出现的递归噪音。

经验教训

• 尽早处理 AST。移除不需要的节点可以防止后续的内存浪费。 • 使用钩子而非 hack。理解编译器的生命周期可以让工作更加优雅。 • 反馈能优化架构。维护者的评审帮助我构建了一个更好的系统。 • 数据并非越多越好。优秀的工程实践意味着在数据量与可用性之间找到平衡。

超越缺失的导出:为 Webpack 的 TypeDoc AST 构建早期垃圾回收器

在大型 TypeScript 项目中,随着代码库的不断扩张,管理“死代码”(dead code)——即那些定义了但从未被使用的导出(exports)——变得越来越具有挑战性。虽然 Webpack 的 Tree-shaking 机制可以在构建阶段移除这些代码,但这种优化发生在流水线的后期。

如果我们能在开发早期就识别出这些未使用的导出,将会大大提高开发效率并保持代码库的整洁。本文将探讨如何利用 TypeDoc 的 AST(抽象语法树)构建一个“早期垃圾回收器”。

为什么是 TypeDoc?

通常,开发者会使用 Babel 或 TypeScript 编译器来解析代码。然而,TypeDoc 提供了一个独特的优势:它不仅解析语法,还理解类型系统。通过使用 TypeDoc 的 AST,我们可以获得关于符号(symbols)及其关系的更深层次的语义信息,而不仅仅是简单的语法节点。

核心思路

我们的目标是构建一个工具,它能够:

  1. 解析项目:利用 TypeDoc 解析整个 TypeScript 项目。
  2. 识别导出:遍历 AST,识别出所有被标记为导出的符号。
  3. 追踪引用:遍历 AST,寻找对这些导出符号的所有引用(包括导入、类型引用等)。
  4. 识别垃圾:找出那些被导出但从未被引用的符号。

实现细节

1. 利用 TypeDoc 获取反射信息

TypeDoc 的核心在于其“反射”(Reflection)模型。通过调用 TypeDoc API,我们可以获得一个 ProjectReflection 对象,它代表了整个项目的结构。

2. 遍历声明

我们可以遍历 ProjectReflection 中的所有 DeclarationReflection。对于每一个声明,我们可以检查它是否是一个导出项:

if (declaration.exports) {
  // 这是一个导出项
}

3. 查找引用

这是最关键的一步。我们需要检查该符号是否在项目的其他部分被引用。由于 TypeDoc 提供了丰富的语义信息,我们可以通过检查符号的引用计数或通过遍历 AST 节点来判断。

结论

通过在构建流程之前引入这种早期的垃圾回收机制,我们可以更早地发现并清理无用代码,而不是等待 Webpack 在打包时才处理它们。这不仅能减小最终产物的体积,还能让开发者在编写代码时就获得即时的反馈,从而保持代码库的精简和高效。