☰
基于AST的代码结构分析工具t3code:快速扫描项目依赖与函数清单
2026/10/9 16:57:48 网站建设 项目流程

先聊个具体的场景:你刚接手一个别人写了三年的后端仓库,package.json 里躺着几十个依赖,src 目录下嵌套了七八层文件夹,你只知道这个系统“大概”是做什么的,但完全不知道核心模块有哪些、模块之间怎么调用、哪些函数是公开 API、哪块代码能拆掉重构。

这时候如果有个工具能在十几秒内把整个项目的代码结构、依赖关系、函数清单和文档草稿全部扫出来,你会不会觉得捡到宝了?这就是我为什么花了几周时间折腾 t3code 的原因——一个基于 AST 的终端代码结构分析工具,专门用来回答“这个项目的代码到底长什么样”这种看似简单、实际却很麻烦的问题。

t3code 这个名字里的 t3,不是 T3 Stack 那个 t3,而是三个维度的缩写:Type(类型即结构)、Tree(树即依赖)、Text(文本即检索)。工具本身没有任何 AI 魔法,就是一个老老实实的静态扫描器,用 Node.js 写的,跑在终端里,识别 TypeScript 和 JavaScript 项目的代码结构,输出结构树、依赖关系、函数清单,还能顺手生成一份 README 文档草稿。对于刚接手历史项目的程序员、准备做重构的技术负责人、想给开源仓库补齐文档的维护者来说,这个工具属于“用过一次就回不去”的类型。

我自己就是在一次临时救火中手写了几个脚本分析老项目,感觉不过瘾,索性把脚本沉淀成了一个正经工具,于是就有了 t3code。下面从设计思路、技术内核、实操流程、踩坑记录到后续规划,完整拆解一遍。如果你正打算给自己的项目做类似的东西,或者单纯想找个体面的代码结构分析方案,这篇应该能帮到你。

1. 先说清楚:t3code 到底是个什么东西

1.1 为什么是“t3”这个命名

很多工具喜欢给自己的名字带上华丽的技术字眼,但 t3code 的命名逻辑极其朴素:我要在终端里完成对代码的三层理解。

第一层是 Type,也就是类型和结构。一个函数是 export 的还是私有?一个类是抽象类还是普通类?接口里定义了哪些成员?常规的 grep 和 ripgrep 只能告诉你“这里有一个 foo 函数”,但告诉你“foo 是公开 API,参数是两个对象,返回值是个 Promise”这种信息的,必须是能解析语法树的东西。

第二层是 Tree,也就是目录和依赖树。项目里一共有哪些文件?main 模块依赖了哪些子模块?有没有循环引用?我把这些信息组织成一棵依赖树,让你一眼看明白模块之间的上下级关系。这种信息在做模块拆分的时候尤其有用——那些被根节点大量依赖的文件,往往是整个系统的核心枢纽。

第三层是 Text,也就是全文检索与信息抽取。代码里有没有 TODO?所有的 API 注册入口分散在哪些文件?某个函数在哪些地方被引用过?基于 AST 的引用分析比 Ctrl+F 精确得多,因为它理解作用域,不会把同名不同作用的变量混为一谈。

工具的最终形态就是一个终端命令。没有 GUI,没有 Web 管理面板,没有常驻后台服务。装完就用,用完就走。这是我一开始就定下的原则:开发者的日常工作是围绕终端展开的,t3code 就应该老实待在终端里。

1.2 现有工具解决不了的痛点

你可能觉得这活儿用现成工具也能干,比如 ctags,比如 ESLint 的代码规则,比如 IDE 内置的结构化视图。确实能干,但都有点隔靴搔痒。

ctags 是古老而可靠的符号检索工具,但它只会告诉你“某个符号在哪个文件哪一行”,然后就没有然后了。标签之间的调用关系、导出范围、依赖网络,它一概不管,它就是一把特别锋利的刀没错,可你需要的是一整套厨房设备。

ESLint 做的是规则检查,它当然能解析 AST,但它的输出是为规则检查服务的。你想让它列出某个模块对外暴露的全部函数签名?不好意思,这不在它的工作范围内。

IDE 内置结构视图通常很好用,比如 IDEA 的 Structure 面板、VS Code 的资源管理器大纲。但问题有两个:一个是 IDE 需要打开项目并完成索引,历史仓库动辄几万行代码,等你打开等它索引完,可能已经不想看了;另一个是 IDE 的结构视图不能批量导出、不能跑进 CI、不能自动生成文档。t3code 的价值就在于它把这些能力全部下沉到命令行,用一条命令代替点击十几次鼠标。

另外一个常被人忽略的点是:t3code 是面向“整个项目”而不是“单个文件”的。传统工具都是单文件维度,而现代项目的复杂度恰恰体现在跨文件关系上。某个工具函数被 20 个文件引用,这种全局视角只有站在项目级扫描的前提下才能建立起来。

1.3 目标用户和使用场景

我在设计阶段就给 t3code 圈定了三类用户,这让后面的功能取舍变得非常轻松。

第一类是新接手的程序员。刚开始看老项目时最怕的就是一头扎进细节里,拨开一层又一层,最后迷失在代码的海洋里。用 t3code 先扫一遍全貌,再顺着依赖树逐层下钻,学习路径就自然脱出来了:核心模块 → 依赖模块 → 叶子模块。

第二类是准备做架构调整的技术负责人。重构最需要的是信息和胆量。信息来自对现状的理解——不要只凭脑补去判断代码边界,让工具告诉你边界在哪里。t3code 输出的依赖图谱能直接暴露那些“隐藏的核心节点”和“混乱的循环依赖”,这些信息比任何架构评审都更客观。

第三类是开源项目维护者。他们通常很头疼 README 里的技术架构部分怎么写——因为 README 是给人看的,但你可能已经忘了代码的细节。t3code 的 doc 命令能自动生成一个文档草稿,把模块清单、关键函数列表全部列出来。你只需要在这个草稿基础上补充业务语义描述,文档工作就完成了 60%。

2. 技术选型背后的那点私心

2.1 为什么是 Node.js + TypeScript

t3code 本身是分析 TypeScript 项目的,用 TypeScript 来写它属于顺手为之。但更重要的原因是生态:Babel 的解析器、TypeScript 的编译器 API、Recast 的代码打印器,这个组合在 JS/TS 工具链领域成熟到不行,绕开就是给自己增加复杂度。

如果用 Go 或 Rust,性能和二进制分发确实更香,但那是后话。t3code 的核心工作时长也就是十几秒,Node.js 完全扛得住。与其纠结性能,不如优先把解析正确性和开发体验做好。用 npx 直接跑也是 Node 生态的天然便利,对于开发者用户来说,npx t3code 比下载二进制安装包流畅自然得多。

依赖选择上我极其克制,核心依赖只有几个:

  • commander:命令行参数解析
  • @babel/parser + @babel/traverse:JS/TS 的语法解析与遍历
  • recast:AST 的还原打印(用于提取函数体等场景)
  • chokidar 的可选关联:用于 watch 模式的增量监听(这个严格来说是 devDependencies 场景)

没有引入大型框架,没有引入 ORM,没有引入配置中心。每多一个依赖,就多一个出问题的地方。工具本身就是做“结构分析”的,自己的结构必须干净。

2.2 用 Babel 而不是正则或 ctags

这里是我最想展开讲的一个技术决策:为什么必须用真正的语法解析器,而不是正则表达式或者 ctags 之类的符号索引工具。

正则擅长处理的是“形状规整的文本”,而代码恰恰是“形状规整但语义复杂”的东西。用正则匹配 function foo() 确实能匹配到函数声明,但匹配不到以下几种情况:函数声明被注释掉但字符串里存在;导出名称是动态计算出来的;同样的函数名出现在不同作用域里;异步生成器函数、装饰器函数这种加了修饰符的声明。正则的边界天然就不适合做这种事,强上正则就是给自己埋雷。

Babel 的做法是把你写的代码拆成 AST(抽象语法树),每一个函数声明、变量定义、导入导出语句都会变成树上的一个节点。在 AST 层面,判断“这是一个导出函数”不再依赖文本匹配,而是直接读取节点类型的属性。比如 ExportNamedDeclaration 节点下面挂着一个 FunctionDeclaration,那么这个函数就是命名导出的,没有任何歧义。

AST 还有一个隐藏优势:支持块级结构与作用域分析。我可以直接通过 Scope 对象查到某个变量的作用域边界,可以判断一个标识符引用跳转到的是哪个声明节点。引用计数、依赖追踪这些都是基于这种跳转关系计算出来的。

ctags 只是建立了“名称→位置”的索引,而 AST 是建立了“语法单元→关系网络”的全量模型。这两者在信息量上是质变不是量变。

2.3 整体架构与数据流

t3code 的数据流设计很直白,一共四步,像一个浓缩的数据管道:

第一步是文件收集。通过 glob 匹配源码目录下的 .ts、.tsx、.js、.jsx 文件,按照 .gitignore 规则过滤掉 node_modules、dist、build 等目录。这里有个细节值得说:直接忽略 node_modules 是基线要求,但要小心项目里的某些文件会引用 node_modules 里的类型定义,所以类型信息补全用的是 TypeScript 编译器 API 而不是 Babel,原因后面会展开。

第二步是语法解析。每个文件交给 @babel/parser 解析成 AST,解析失败的文件会被记录下来,不影响整体流程继续跑。容错设计在这里至关重要,一个大项目里总有一两个文件是语法不标准的或者内嵌了特殊模板,因为一个文件崩溃整个工具是不可接受的。

第三步是信息抽取。遍历 AST,提取文件名、行号范围、导出符号、类型签名、依赖模块、注释里的 JSDoc/TSDoc 标记。这个阶段的数据就非常结构化,全部存成 JSON。

第四步是输出。根据命令参数选择格式化方式:终端树状图、JSON 数据包、Markdown 文档、或者简单的纯文本列表。输出层和抽取层分开,是后期扩展的底气所在。

如果你要在 t3code 基础上做二次开发,关注的就是第三步的信息抽取模块和第四步的输出格式模块。这两块是插件化的关键位置。

3. 从零跑通 t3code:核心命令实操

3.1 安装与初始化项目

安装非常简单,因为就是 npm 生态的标准姿势:

npm install -g t3code

如果你不想全局安装,也可以直接用 npx:

npx t3code --version

首次运行时会检查当前目录是否包含 package.json,如果没有,会提示你初始化一个配置文件。这个配置文件叫 t3code.config.json,放在项目根目录即可。

下面是我的一个真实项目中的配置片段,拿来自查自用正好:

{ "root": "src", "include": ["**/*.ts", "**/*.tsx"], "exclude": ["**/*.d.ts", "**/__tests__/**"], "entryPoints": ["src/index.ts", "src/main.ts"], "outputDir": ".t3code", "maxDepth": 4, "ignorePatterns": ["*.generated.ts"] }

几个字段的用意我挨个说一下:root 是扫描根目录,默认是项目根目录,但一般建议指到 src,因为依赖和测试文件往往会污染分析结果。include 和 exclude 是文件匹配规则,遵循常见 glob 的写法。entryPoints 是入口模块列表,在绘制依赖树时入口会出现在最顶层。outputDir 是输出目录,默认 .t3code 隐藏文件夹,避免污染仓库。maxDepth 是树形展示的最大深度,防止那种动辄七八层的嵌套把终端刷屏。ignorePatterns 用来跳过自动生成的代码,比如 graphql 生成的 types、API client 的自动生成模板,这类文件对结构分析没有帮助。

3.2 扫描与树状输出

先跑一条最基础的扫描命令,看全貌:

t3code scan

命令运行后会在终端打印出项目结构树,每一行代表一个模块或一个文件,缩进表示层级。输出示例:

src/ ├── index.ts exports: createApp, startServer ├── config/ │ ├── env.ts exports: loadEnv, EnvConfig │ └── logger.ts exports: createLogger ├── core/ │ ├── app.ts exports: createApp │ ├── router.ts exports: registerRoutes │ └── errors.ts exports: AppError, ErrorType └── services/ ├── auth.service.ts exports: login, logout, refreshToken ├── user.service.ts exports: createUser, getUser, updateUser └── db.ts exports: connectDB, getConnection

在文件后面直接列出导出符号,是为了解决“看到文件名还要 grep 一把才能确认这个文件干吗”的麻烦。你扫完一遍结构,心里基本就有了一张地图。

如果你只关心某个子模块,可以加路径过滤:

t3code scan --filter services

这个命令只会扫描 src/services 目录下面的内容。配合 --depth 参数,能进一步控制展示深度,比如只看前两层:

t3code scan --depth 2

在实际现场使用中,我最常用的反而是一条组合命令:

t3code scan --format tree --min-exports 3

加了 --min-exports 之后,只有导出符号数量大于等于 3 的文件才会出现在输出里。这可以快速过滤掉那些只有一个导出的小文件,把注意力集中在核心模块上,非常适合用来做架构层级的概览。

3.3 按需查询与文档导出

scan 是全局视角,query 则是定点深挖。比如你想知道某个函数在哪里被引用过:

t3code query --symbol createApp --refs

命令会列出所有引用了 createApp 的文件清单,以及每个引用位置的行号。这是基于 AST 作用域分析得到的,不会把注释里的单词算进去。

如果想看某个模块对外暴露的完整 API 列表,可以用 export 子命令:

t3code export --file src/services/auth.service.ts

输出的是一组 Markdown 表格,列出每个导出函数的:名称、签名、参数类型、返回值类型、JSDoc 注释摘要。这个功能在写接口文档的时候几乎就是救命稻草——你不需要从头读业务代码来理解接口语义,只需在注释里补一句人话,文档质量就直接到位。

导出文档的命令是全套里最有效的:

t3code doc --format markdown --output README.code.md

它会把扫描结果、模块清单、关键函数签名组合成一份完整的代码结构文档。我一般生成的文档加上项目标题、一段业务简介,就直接可用了。不过有一点必须提醒一下:它生成的是“代码结构文档”,不是“业务说明文档”。代码能给你的信息,它都能整理出来;代码里没写清楚的信息,它也变不出来。业务愿景、设计权衡这种内容还是得你自己补。

3.4 配置文件与常用参数

t3code 的配置设计遵循“零配置可用,有配置更强”的原则。默认配置对大多数中小项目已经够用,但真实项目总有些怪癖,所以配置能力必须铺到位。

在配置文件之外,优先级最高的是命令行参数。比如配置里写了 exclude**/__tests__/**,你在代码评审时突然想看看测试文件的结构,可以直接在命令行里临时覆盖:

t3code scan --no-exclude-tests

优先级从上到下依次是:命令行参数 > 项目配置文件 > 默认配置。这个规则在几乎所有 CLI 工具里都是通行的,遵循起来别别扭扭设计就是了,不用想太多。

还有一个参数容易被忽略,但是建议任何第一次跑 t3code 的人都先用一次:

t3code scan --dry-run

这个模式下不解析 AST,只输出文件收集清单。先确认扫描范围符合预期,再正式跑全量解析,可以避免“花了半天解析了一堆不该解析的东西”的尴尬。

4. 深入到原理层:AST、依赖与缓存是怎么打通的

4.1 AST 提取的边界与动作

前文提到了 AST,这里展开讲一下实战中的细节。Babel 解析出的 AST 节点类型非常多:File、Program、ImportDeclaration、FunctionDeclaration、VariableDeclaration、ClassDeclaration、ExportNamedDeclaration……如果你是第一次做 AST 工具,面对这么庞杂的节点类型肯定会发懵。

我的办法是只抽取自己关心的几类节点,其余的一律忽略。t3code 内部定义了一个剪裁后的“语义节点”模型,把原始 AST 映射成以下结构:

{ "file": "src/core/app.ts", "symbols": [ { "name": "createApp", "type": "function", "params": [], "returnType": "Server", "exported": true, "startLine": 18, "endLine": 42, "doc": "创建并返回 HTTP 服务实例" } ], "imports": [ { "source": "./router", "specifiers": ["registerRoutes"] } ] }

把复杂的 AST 转换成这种扁平语义结构,是 t3code 整个分析流程的核心环节。后续的依赖分析、文档生成、引用追踪全都基于这种扁平结构,跟 Babel 的 AST 模型解耦,让工具更容易扩展支持其他语言。如果未来要支持 Python 或者 Go,只需要新增一个“解析器适配层”,把不同语言的语法树映射到同一套语义模型上即可。

这里有个关键取舍:提取符号时要不要提取函数体内容?我的答案是默认不要。函数体是执行逻辑,对于结构分析而言是噪音;你关心的是签名、边界、相互关系,而不是它的内部实现。扫描一个 5000 行的核心服务文件时,直接把函数体内容撇开,速度和噪声都能优化很多。

4.2 依赖分析与循环引用检测

依赖分析是 t3code 最实用的能力之一。它的实现基于对 ImportDeclaration 节点的收集:遍历每个文件的 import 语句,把模块路径标准化后映射到具体文件,然后建立一张“文件→文件”的依赖邻接表。

为了处理路径别名(比如 @/core/config 映射到 src/core/config.ts),配置文件里专门加了 alias 字段:

{ "alias": { "@": "./src", "@core": "./src/core" } }

这样做有两个直接收益:一是依赖图更准确,二是引用追踪能跨过别名完成跳转。如果你项目里用了 tsconfig 的 paths,其实可以直接读取 tsconfig.json 来解析别名,t3code 也支持了这种模式,省掉一份手工映射的配置。

循环依赖的检测走的是深度优先遍历算法,每访问一个节点就标记当前路径;如果在递归过程中重新遇到路径里已存在的节点,说明出现了环。检测完成会输出一条链式提示,比如:

Detected circular dependency: src/core/app.ts → src/services/auth.service.ts → src/services/user.service.ts → src/core/app.ts

坦率说,绝大多数循环依赖并不至于导致运行时崩溃(现代打包器大多能处理),但它会让代码的认知负担直线上升:你没法单靠读代码判断一个模块的初始化顺序,也没法单独提取模块做测试,因为一测试就把整个环都拉进来了。t3code 的循环检测在某些团队里已经进化成了一种防守机制,在 CI 里挂一个 t3code depcheck --strict 步骤,一旦新代码引入环就阻断合并,这个玩法我认为比人工评审循环依赖靠谱得多。

4.3 增量缓存:让二次扫描变快

项目第一次全量扫描可能耗时 3~5 秒,第二次再跑如果还要这么久,体验就有点差了。所以 t3code 内置了增量缓存机制,做法如下:

在 .t3code/cache 目录下,为每个扫描过的文件保存一份哈希值文件与解析结果。文件未修改时,直接用缓存结果;只有文件内容发生变化或新增文件时,才重新解析。因为这个 RSS 驱动的机制,第二次扫描通常会提速 5~10 倍。

这里有个和 git 相关的细节问题:要不要把 .t3code 目录加入 .gitignore?我的答案是看场景。如果是团队共享模式,缓存目录不要入库,因为每台机器的路径、依赖版本都不一样,缓存的意义不大,还容易引发 diff 噪点;如果你是做持续集成,建议在 CI 里配置一个专门的 cache 目录,这样每次构建时缓存还能复用。

还有一种埋点思路是把缓存模式和环境变量剥离:本地全量扫描,CI 只用 --no-cache 强制新鲜。这种策略能保证 CI 拿到的永远是最新状态,拖稿几天再跑出来的缓存包根本不可信。

提示:t3code 的缓存设计不是为了替代全量扫描,而是为了辅助开发者的迭代节奏。做长期分析的场景(如依赖监控)里,还是建议定期做一次全量扫描,避免因为缓存策略错误导致分析结果长期滞后。

5. 真实踩坑记录:这些问题你一定也会遇到

5.1 扫描时间爆炸:文件太多怎么办

第一次在真实项目上跑 t3code 的时候,我遇到的第一个事故就是扫描时间爆炸。那个项目大概有 1000 多个源文件,加上 node_modules 里一堆类型声明,全量解析花了快一分钟。虽然不至于不能用吧,但对于一个主打“快速扫一眼”的工具,一分钟已经算慢了。

排查思路是这样的:先确认是不是文件收集阶段出了问题——看了 --dry-run 的输出才发现,glob 正则把 node_modules 里一些深层嵌套的目录也给收进来了。我的 .gitignore 过滤规则写得过于宽松,把排除逻辑做反了。修复规则后,文件数量从 2700 骤降到 900 不到,耗时随之降到 5 秒左右。

如果你的项目文件量更大(单仓超过 5000 个源文件),建议做两件事:第一是给扫描命令加 --exclude-vendor 这类后缀过滤规则,明确排除第三方代码;第二是考虑把周期性的全量扫描交给 CI 跑,本地开发流程全部使用增量缓存模式,这样体验就能完全拉开。

5.2 结果乱码与编码问题

有一次我在一台服务器上跑 t3code,输出里的中文注释全部变成了乱码。原因特别基础:文件本身的编码是 GBK,而工具没有显式指定 UTF-8 解码。Node.js 的 fs 模块默认按 UTF-8 读文件,遇到 GBK 编码的字节流就会按替换字符处理,结果自然就花了。

修复方案分两层。第一层是在配置里显式声明文件编码;第二层是提供编码探测机制,优先根据文件开头的 BOM 判断,其次再根据配置文件指定。如果你接手的是老一代编码混排的项目,强烈建议先跑一次批量编码转换,把项目统一到 UTF-8 再做结构分析,不要让工具替你做编码转码这件事,工具的角色应该是正常分析而不是隐式转换。

5.3 Windows 路径和符号链接

Windows 上用 t3code 有个容易踩的隐形坑:路径分隔符和大小写敏感性问题。node_modules 目录结构复杂,符号链接(junction)在 Windows 上随处可见,如果用不成熟的过滤逻辑,很容易因为循环符号链接导致扫描死循环。

我的处理方式是:在文件收集阶段就统一转换为 POSIX 风格路径(把反斜杠转成正斜杠),并且在 glob 匹配时对 Windows 路径做大小写不敏感处理。另外一个更实用的经验是:在 Windows 上如果项目本体放在路径很长的深目录下(比如 D:\projects\myapp-frontend\目录),建议先测一下路径长度,确保 file_path + 文件名总长不超过 260 字符——这个限制在 Node 的 fs 模块里极容易触到,而很多人根本没意识到。

5.4 解析失败的兜底方案

再稳的解析器也挡不住历史项目里的奇葩文件。有些文件因为年份久远,包含 Babel 的新语法都不认识的老代码;有些文件则是模板生成器产出的半成品代码,语法本身就残缺不全。

t3code 的兜底策略是这样的:解析失败的单个文件会被记录到 .t3code/errors.json,同时跳过该文件继续跑完整个项目。在最后的汇总报告里,除了结构分析结果,还会给出一个错误文件清单。这样用户不会因为几个坏文件丢掉整份报告,也知道还有哪些文件是待修复的。

至于是不是要用 TypeScript 编译器 API 来兜底解析这些坏文件,我试过但放弃了。TypeScript 的解析器容错能力确实更强,但它输出的 AST 属性模型和 Babel 不兼容,两套解析器的语义字段对不上,强行兼容后整个数据模型会被迫让步,维护成本立刻上去。与其追求百分之百的覆盖率,不如老老实实保留错误清单,让用户自己去处理有问题的文件。

5.5 常见问题速查表

这里整理一份实际使用中高频出现的问题小结,可直接对照排查:

症状原因解决方式
扫描目标比预期多很多glob 规则过宽,包进了依赖目录核对 include/exclude,用 --dry-run 先确认范围
中文注释乱码文件编码非 UTF-8批量转换编码,或在配置中显式声明编码
Windows 下偶发崩溃路径过长或符号链接循环统一使用正斜杠路径,增加路径长度检查
二次扫描没有变快缓存配置不对或文件被修改检查 .t3code/cache 目录权限,尽量保持输出文件目录稳定
有的文件不进结果解析失败被跳过查看 .t3code/errors.json,先修复那些文件的语法问题
依赖分析有缺失路径别名未解析成功在配置或 tsconfig.json 里正确配置 alias
循环依赖提示过多控制器的内部依赖环用 --ignore-circular 定向过滤已知环,但别默认关闭检测

6. 还能往哪走:扩展方向与我的个人心得

6.1 插件化:迟早要做的架构预留

t3code 目前的输出格式比较固定,但从结构上我已经预留了插件机制。插件按两个维度扩展:一是新提取器,比如从 Vue 组件里提取自定义指令、从 Prisma schema 里提取数据模型,这类信息都能并入结构分析;二是新输出器,比如导出 PlantUML 类图文本、生成 C4 模型描述,或者输出成 JSON 给自定义脚本二次加工。

如果你关注的是如何避免把自己长期锁死在现有实现上——插件接口需要在关键时刻做性能妥协,插件接口本身要尽量薄,不要设计成什么都提供的万能插件。观察开发者的真实需求,看大家反馈最多的是哪类插件,然后把官方插件做好,才是正循环。

6.2 与 IDE 和 CI 的联动

t3code 的终点不是终端,而是整个开发工作流。

往 IDE 方向上,最顺手的是把 t3code 的输出注入到编辑器的问题面板里。VS Code 的任务 API 完全可以直接调用 t3code 的 scan 命令,把解析错误、循环依赖、冲突点以问题表格的形式呈现出来。这比做一个完整的 IDE 插件成本低得多,但体验提升是立竿见影的。你甚至不需要离开编辑器,就能看到当前项目的最新结构全貌。

往 CI 方向上,我强烈建议在每次合并请求里加一道结构分析检查。代码合并时最容易漏掉的就是隐性依赖:新增一个 import 就可能导致循环依赖,重构时改了一个 export 就会影响下游几十个文件。维护一份结构快照,让 CI 对比分支之间的差异,在评审阶段就警告“本次改动新增了一个循环依赖”,这种机制比口头约定管用太多了。

如果你不想搭复杂的 CI 链路,可以直接用 t3code 的 JSON 输出配合 jq 写一个简单的检查脚本,三五十行就能搞定,效果也不差。这类工具的终极正确性是给自动化流程用的,不要让它孤零零死在终端里。

6.3 我个人最满意的一个小设计

最后聊点偏心意的东西:t3code 最让我骄傲的不是 AST 解析,也不是依赖图谱,而是一个小到几乎没人注意的细节——错误文件列表。

很多工具遇到解析失败就直接跳过,或者直接报错退出。但 t3code 会把所有失败文件写进 errors.json,并在汇总报告里给出明确的位置。别小看这个行为——在真实项目里,那些语法不标准的“坏文件”往往是最需要重构的痕迹。它们可能是历史包袱的坟场,可能是临时补丁留下的垃圾。一套工具如果能把它们的名单自动列出来,就已经帮工程师省掉了一大笔人工排查的时间。

我当时问自己一个问题:t3code 的使命是什么?不是分析代码本身,而是降低理解项目的成本。错误文件列表本质上降低了“发现坏代码”的成本。从这之后我得到一个做工具的核心心法:少做那些看起来厉害的复杂逻辑,多想想那些真正减少用户痛感的简单动作。

如果你也想做类似的代码分析工具,我给三条建议:第一,先守住最小的使用链路,宁可用一条命令跑完整流程,也别把功能拆得七零八碎;第二,输出结果一定要支持 JSON,因为自动化的想象空间全在那扇门后面;第三,多去真实项目上跑,你会发现在示例项目里根本遇不到的那些边界情况,才是工具真正值钱的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询