最近在折腾项目工程质量的时候,偶然被朋友安利了一个叫t3code的代码脚手架工具。一开始我以为只是个普通的代码格式化插件,实际用下来才发现,它把类型检查、规范约束、测试门禁全部揉成了一个统一的工作流,解决了我长期以来“配置一堆工具但根本跑不顺”的痛点。这篇博文就围绕t3code的核心玩法做一次完整拆解,从设计思路到实操步骤,再到我踩过的坑,一次性说明白。
如果你手头维护的仓库正处在“CLI 工具一堆、规则没人执行、CI 全靠自觉”的状态,或者你刚接触前端工程化和代码质量体系,想找一套能直接抄作业的方案,那我强烈建议你看完这篇文章。它不会给你画一张巨大的技术地图,而是带你从零把一条可落地的质量流水线搭起来。
1. 先搞懂 t3code 到底想解决什么问题
很多团队不是没有规范,而是规范太多、太散了:ESLint 管风格,TypeScript 管类型,Prettier 管格式,Jest 管测试,husky 管钩子,lint-staged 管暂存区。每个工具单独看都挺好,拼在一起就是灾难。配置之间互相冲突,版本升级一个牵动一片,新人入职光读配置文件就要读半天。
t3code的核心思路,是把这些散落的环节收敛成一个统一入口。它不是一个全新的代码检查引擎,而是在已有工具链之上做了一层编排和约束,让开发者只需要记住一个命令,就能完成全部质量检查。它的命名里那个“T3”,我理解下来对应三个关键词:Test、Type、Trust——测试兜底、类型先行、信任流程。这个理念听起来简单,真正拆开看会发现里面有不少设计细节。
1.1 四条铁律,藏在实际功能背后
我在实际使用中总结出t3code的四个设计原则,这也是它和“把所有工具都装进一个 package 里”这种堆砌方案的本质区别:
第一,渐进式接入。它不要求你一次性把老仓库全部改造完,而是允许按目录、按文件、按风险等级逐步开启检查。这很关键,因为大多数质量工具推不下去的根源就是“改造成本太高,业务排期不允许”。
第二,配置收敛。常见的 ESLint、Prettier、TypeScript 配置,被统一到一份配置文件里管理。工具链的版本和规则集也做了对齐,减少了“ESLint 规则和 TS 编译器理解不一致”的尴尬局面。
第三,门禁可量化。测试覆盖率、类型检查错误数、规范警告数都可以设置红线,超过红线直接终止流程,而不是只在终端里打几行黄色警告。
第四,本地优先。所有检查优先在开发者本地完成,CI 上只做复核,避免“本地过了、CI 红了”这种来回折腾。
这四个原则决定了t3code的形态:它不是某个单一检查器,更像一个质量网关。
1.2 我为什么愿意在项目里引入它
我之前维护过一个中型前端项目,四十多个开发者,技术栈是 React + TypeScript。每次提交代码,全靠 Code Review 阶段人工盯规范,结果就是:喜欢写any的人依旧在写,测试覆盖率悄悄往下掉,CI 上偶尔有人把console.log合进主分支。不是大家不想守规矩,而是“守规矩”的成本太高了——每个工具都要单独装、单独配、单独学。
t3code最打动我的点是它把“守规矩”这件事变成了流程的一部分。开发者提交代码时不需要主动想“我要跑哪些检查”,只需要执行t3code check,剩下的交给工具。对于团队管理者来说,规则能被强制执行,而不是靠“希望大家自觉”。这是很多质量工具做不到的。
2. 核心设计与目录结构拆解
任何工具拿到手,先别急着敲命令,把它的目录结构和配置体系摸清楚,后面会省很多事。t3code初始化的项目结构,和常见前端脚手架比较接近,但多了一些它自己的关键文件。
2.1 初始化后的目录长什么样
执行初始化之后,项目根目录会出现下面这些核心内容:
t3code-demo/ ├── src/ │ ├── components/ │ ├── hooks/ │ ├── utils/ │ └── index.ts ├── tests/ │ ├── unit/ │ └── integration/ ├── .t3code/ │ ├── config.json │ ├── rules/ │ ├── templates/ │ └── plugins/ ├── .t3coderc ├── .t3codeignore ├── package.json ├── tsconfig.json └── vitest.config.ts这里最需要注意的是.t3coderc和.t3code/这个组合。.t3coderc是文本格式的配置文件,适合存放快读快改的开关项;.t3code/目录则适合放更复杂的规则定义和自定义插件。这种“单文件轻量配置 + 目录式复杂配置”的结构,学习成本很低,又不至于把所有东西塞进一个大 JSON 里变成不可维护的怪兽。
2.2 配置文件的顶层设计
t3code的配置文件并不复杂,我找一个实际用过的简化版本来说明:
{ "version": 1, "projectType": "frontend", "language": "typescript", "layers": { "type": { "enabled": true, "strict": true }, "lint": { "enabled": true, "autofix": true }, "test": { "enabled": true, "minCoverage": 80 }, "format": { "enabled": true, "indent": 2 } }, "hooks": { "pre-commit": ["type", "lint", "format"], "pre-push": ["test"] }, "ignore": [".t3codeignore", "dist", "build"] }这个配置表达了几个关键信息:项目类型是前端、语言是 TypeScript;四条流水线分别控制类型、规范、测试、格式四个维度;pre-commit 阶段做快速的三项检查,pre-push 阶段跑完整测试。哪怕是不熟悉这个工具的人,读这份配置也能猜出七八分意图,这种直白的表达能力对团队协作很重要。
2.3 为什么把“分层”作为核心概念
t3code不是用一个笼统的“质量分数”来评价代码,而是把质量拆成四个可独立控制的维度。这样做的好处很实际:类型错误和代码风格是不同等级的问题,不应该用同一个开关控制。我在项目里可以把类型检查设为 error,把风格规范设为 warning,测试覆盖率则作为提交门禁。这种梯级控制,让团队可以分步收紧标准,而不是某天突然一刀切,导致所有人抱怨。
3. 实操:从零初始化一个 t3code 项目
理论说再多,不如实际跑通一遍。下面我按完整流程演示如何初始化一个全新的t3code工程,以及如何把已有老仓库改造成t3code结构。
3.1 环境准备
t3code基于 Node.js 运行,推荐用 18 或以上的 LTS 版本。我建议顺手把包管理器统一成pnpm,这样后续安装依赖更快,磁盘占用也小。全局安装 CLI 工具:
npm install -g t3code # 或者 pnpm add -g t3code安装完成后,确认版本:
t3code --version这一步如果出现类似v0.x.x的版本号,说明 CLI 安装成功。如果命令提示找不到,大概率是 npm 全局目录没写进 PATH,在.bashrc或.zshrc里补上全局bin路径就行。
3.2 初始化一个全新项目
在空目录里执行:
mkdir t3code-demo && cd t3code-demo t3code init交互式命令行会问几个问题:项目类型、主语言、包管理器、是否启用测试框架、是否初始化 Git 仓库等。按自己需求回答即可,我一般这样选:
- 项目类型:前端(React / Vue)
- 语言:TypeScript
- 测试框架:Vitest(比 Jest 在 TS 生态里更快)
- 是否初始化 Git:是
初始化完成后,t3code会自动生成package.json、tsconfig.json、.t3coderc以及基础目录结构。此时依赖还没有安装,继续执行:
pnpm install安装完成之后,可以立刻跑一次完整检查:
t3code check初始项目因为还没有业务代码,检查结果应该是干净的。看到类似All checks passed的输出,就说明基础流水线已经通了。
3.3 把已有老仓库改造成 t3code 结构
存量项目的接入方式和空项目不太一样。如果直接把老仓库的 ESLint 配置删掉重来,大概率引发大量代码报错,团队会炸锅。推荐的做法是低成本增量迁移。
第一步,先备份原有配置文件,包括.eslintrc*、.prettierrc*、jest.config.*。这些文件在后面配置规则映射时还有参考价值。
第二步,执行t3code init --existing,这是针对已有项目的初始化模式,它不会覆盖已有代码结构,只会生成t3code相关的配置文件和目录。
第三步,在.t3coderc里调整分层开关,比如先把 lint 层设为warning级别,让工具只提示不阻断:
{ "layers": { "type": { "enabled": true, "strict": false }, "lint": { "enabled": true, "level": "warning", "autofix": true }, "test": { "enabled": false }, "format": { "enabled": true, "level": "warning" } } }第四步,跑一次t3code check,接受大量 warning,再用t3code fix自动修复能修的问题,剩下的手动处理。这样老仓库能以最低成本进入质量流水线的轨道,后续再逐步收紧阈值。
3.4 接入 Git Hooks
只靠手动执行t3code check,质量门禁还是在靠自觉。真正能强制执行的方式是接入 Git Hooks。t3code提供了 hook 安装命令:
t3code hooks install这个命令会在.git/hooks/下生成pre-commit、pre-push等钩子脚本。pre-commit阶段默认执行类型检查、规范检查和格式检查,pre-push阶段执行完整测试。
验证钩子是否生效的办法很简单:故意把一份测试文件覆盖率降到阈值以下,然后提交一次代码,如果提交被拦截并给出失败报告,说明钩子生效了。
注意:在一些 Windows 环境下,
t3code hooks install生成的 shell 脚本可能因换行符问题无法执行。我的经验是,在package.json的scripts里配好对应的t3code命令,再用husky做桥接,稳定性会高很多。
4. 核心环节实现:三层检查流水线
t3code check听起来像是一条命令,背后实际上跑了一套固定流程。理解这个流程,才能在排查问题时快速定位到具体环节。
4.1 第一层:类型检查与依赖校验
类型检查层做的事情比tsc --noEmit更丰富一些。它会先扫描项目里的tsconfig,再读取t3code自己的类型规则,然后执行编译级检查。如果配置了strict: true,那么any、隐式any、空值赋值这类问题都会被标记。
依赖校验也是这层的一部分。t3code会检查package.json中声明的依赖是否都被实际引用,反过来也会检查代码里是否引用了未声明的包。这个能力在维护大型项目时非常实用,能够发现很多“删了代码但忘记删依赖”的历史遗留问题。
我的使用习惯是,类型层始终设为strict: true,因为类型问题通常是运行时 bug 的最早预警。
4.2 第二层:风格规范与自动修复
第二层对应的是传统 ESLint + Prettier 的活。t3code内置了一套推荐规范,同时也支持自定义规则。自定义规则有两种方式:简单场景直接在.t3coderc里写规则;复杂场景在.t3code/rules/目录下写独立规则文件,命名遵循rule-<名称>.js或rule-<名称>.ts。
autofix字段控制是否在检查时自动修复可修复的问题。我建议开成true,因为风格类问题手动改非常琐碎。执行:
t3code fix会自动完成格式修正和简单规则修复。实际体验下来,修复几百个空格、引号、缩进问题只需要几秒钟,这种即时反馈对开发者很友好。
有个需要注意的地方:自动修复不会处理所有问题,比如需要语义理解的代码异味(过度嵌套、复杂度过高)它不会瞎改。t3code fix之后仍然会列出无法自动处理的问题清单,这些需要人肉处理。
4.3 第三层:单元测试与覆盖率门禁
t3code测试层在初始化时默认集成了 Vitest 或 Jest,取决于你项目的选择。关键亮点是“覆盖率门禁”的配置方式:
{ "test": { "enabled": true, "minCoverage": 80, "strict": true } }minCoverage是全局覆盖率红线,如果低于 80,整个检查流程直接失败。strict: true则会把“条件分支覆盖率”也纳入统计,而不仅仅是看行覆盖率。
覆盖率数字不能盲目定高。我的经验是,新项目可以一开始就定 80 以上,老项目最好先从 60 起步,每个迭代提升 5 个点。如果你一开始就把红线画在 90,老项目的流水线会变成“每天都在补测试”,容易打击积极性。
4.4 把流水线接到 CI 和 Git Hook
本地执行是一回事,CI 上强制执行是另一回事。在 GitHub Actions 里,集成方式非常直接:
name: Quality Gate on: pull_request: push: branches: [main] jobs: t3code: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v3 - uses: actions/setup-node@v4 with: node-version: 20 cache: pnpm - run: pnpm install - run: t3code cit3code ci是专用干 CI 场景的命令,它会以最严格的模式执行全部检查。这里有个细节:在 CI 上不要用t3code check而要用t3code ci,因为后者更严格,并且不会执行任何修改类操作,确保流水线是确定性行为。
Git Hook 方面,前面已经提过t3code hooks install。如果你需要自己维护 hook 脚本,可以参照下面的最小模板:
#!/bin/sh t3code pre-commit这个命令只执行.t3coderc里hooks节点定义的 pre-commit 阶段任务,比跑完整检查快很多,适合提交频繁的日常场景。
5. 常见问题与排查技巧实录
再好的工具都会遇到问题,这里我把自己在t3code使用过程中积累的高频问题整理成一份速查表,每一条都是现场踩坑换来的经验。
5.1 “本地通过了,CI 上却失败”怎么办
这是最经典的问题。原因通常有三种:
第一种,依赖版本不一致。本地node_modules和 CI 上刚安装的依赖里有版本偏差。排查方式:锁定package.json里的版本号,使用pnpm-lock.yaml或package-lock.json保证版本一致。
第二种,Node 版本不一致。本地 Node 18 通过了,CI 上 Node 22 某些行为变了,导致测试失败。排查方式:在 CI 配置里固定 Node 版本,和本地保持一致。
第三种,环境变量差异。例如本地.env里有些测试依赖的变量,CI 上没有。排查方式:给测试层配置独立的配置项,确保不依赖运行时环境变量。
5.2 Git Hook 不生效
t3code hooks install之后提交代码没反应,先看这几个位置:
package.json中是否有prepare脚本重新安装了 husky,覆盖了.git/hooks/。如果有,需要把t3code hooks install补充到prepare脚本里,这样每次安装依赖后都会重新生成钩子。
还要注意 Git 版本,老版本 Git 对钩子路径的支持不如新版。升级到 Git 2.9 以上能减少很多诡异问题。
5.3 覆盖率门禁误杀
有时候只改了一行代码,测试覆盖率因为一小段未覆盖分支被拦下来了,这时候要区分两种情况:新增代码导致的覆盖率下降,必须补测试;只是覆盖率小数点波动,可以调整minCoverage的精度,或者让t3code只统计改动文件。
t3code支持增量覆盖率模式:
{ "test": { "enabled": true, "minCoverage": 80, "incremental": true } }incremental: true会只检查本次变更涉及的代码覆盖率,这个模式在老项目里尤其好用,能把门槛落在新增代码上,而不是被历史债务拖死。
5.4 新手容易踩的坑:忽略文件没配好
.t3codeignore这个文件的地位堪比.gitignore。如果没配好,t3code会检查dist、build、coverage等生成目录,轻则导致检查变慢,重则把自动生成的代码当成业务代码拉进流水线,产生一堆看不懂的报错。
我的一份标准忽略配置长这样:
dist/ build/ coverage/ node_modules/ *.min.js *.d.ts这里有个重要细节:*.d.ts建议忽略,因为它们是编译生成物;但如果你手写了类型声明文件用于发布,则需要单独确认是否要纳入检查范围。
另外,t3code重启之后配置是否生效有时候会让人困惑。改完.t3coderc后,不需要额外操作,下次执行检查时会自动加载新配置。如果感觉改了没生效,先检查配置文件的 JSON 格式是否合法,常见的坑是最后一个节点后面多了逗号。
5.5 插件机制:团队自定义规范怎么扩展
当内置规则不够用,或者团队有特殊规范时,t3code的插件机制就派上用场了。在.t3code/plugins/目录下新建一个插件文件,注册一组自定义检查规则:
import type { T3RuleContext, T3RuleResult } from 't3code'; export default function noMagicNumber(context: T3RuleContext): T3RuleResult { const issues = []; for (const file of context.files) { if (file.language === 'typescript') { // 在这里实现自定义检查逻辑 } } return { issues }; }然后在.t3coderc的插件列表里注册:
{ "plugins": ["noMagicNumber"] }插件机制的好处是可以在不入侵业务代码的前提下,把团队特殊规范落到自动化检查里。比如我们团队有一条“禁止在业务代码中使用any逃生舱,必须显式标注类型”的特殊规范,就是靠自定义检查在流水线里实现的。
这种能力让t3code不只是一个开箱即用的工具,还能随着团队演进长成自己的内部规范平台。配置一次之后,所有成员提交代码都会自动被约束,不需要每个人去读规范文档。
6. 写在最后的几点经验
试用t3code到现在,最大的感受是:一个质量工具能不能在团队里活下来,关键在于三条——接入成本够不够低、规则能不能渐进式收紧、本地行为与 CI 结果是否一致。t3code在这三条上都做得相当扎实,尤其是增量接入和增量覆盖率模式,对于维护中的老项目非常难得。
我之前踩过最深的坑,是把所有质量指标一次性拉满,结果团队在两周内提了二十多个投诉。后来把规范级别调成先警告、后阻断,按照类型错误、规范问题、覆盖率三条线逐步收紧,情况才稳定下来。如果你也正在带团队做代码质量治理,心态上一定要做好打持久战的准备:工具只是把流程固化,真正改变的是团队对“什么叫完成”的认知。
最后分享一个小技巧:不要只在 CI 里跑t3code ci,建议把t3code check也加进package.json的scripts里(比如"check": "t3code check"),方便日常开发随手执行。当一条命令变成肌肉记忆之后,代码质量就真正融进开发流程了。