给新项目写代码生成工具这件事,我前后折腾了快两个月。作为一个常年在 TypeScript 全栈项目里打转的开发者,我最大的痛点不是写业务代码,而是每次开工前那一大堆初始化工作:项目结构、依赖版本、环境变量、Dockerfile、CI 脚本。t3code 就是我基于这些真实痛点写的一套脚手架 CLI,核心目标只有一个:让新项目从零到跑通首屏,尽量控制在三分钟以内。它面向的是一线开发者和 5~20 人规模的技术团队,也适合想快速验证想法的独立开发者。如果你也被初始化配置拖后腿,这篇文章会把 t3code 的设计思路、实现细节和实践中的坑一次讲清楚。
1. 项目整体设计与思路拆解
1.1 t3code 解决什么问题
我刚带团队那阵子,最烦的一件事是:每次开新项目,大家都从不同的起点出发。有人从 CRA 起步,有人直接手动初始化 package.json,有人把老项目整个复制过来然后改个名。结果就是,同一个团队里同时存在三四种目录风格、两套 lint 规则、完全不统一的端口约定,甚至连数据库连接串的命名都能吵起来。这种事单独看都不大,但叠在一起,就成了团队协作里最磨人的隐性成本。
t3code 的设计初衷,就是把这些重复劳动收口。它不是一个业务框架,而是一个“项目生成器”,负责在你敲完一条命令之后,把一整套可运行、可部署、符合团队规范的项目骨架铺好。具体来说,它能做四件事:生成前端和服务端的目录结构、写入统一的基础配置文件、初始化数据库 ORM 和迁移工具、输出可直接构建部署的 Dockerfile 和 CI 脚本。这四个动作做完,新项目就已经从一个空白目录变成了可以pnpm dev就跑起来的工程。
这个定位很关键。做脚手架最怕两件事:要么管得太宽,把业务代码也塞进模板里,导致后期改起来痛不欲生;要么管得太窄,只帮忙生成了 package.json 和 README,剩下的还是要手写。t3code 走的是中间路线:只负责“工程化骨架”,不碰具体业务实现。就像装修公司先把水电、墙面、地板这些基础设施搞定,至于沙发怎么摆、挂画选哪幅,那是业主的事。
1.2 技术选型:为什么用 Node.js 写脚手架
脚手架本身是一门“元技术”,它要去操作文件、解析命令、调用子进程,这时候选择一个合适的宿主语言就非常重要。我见过很多团队用 Shell 或者 Python 写脚手架,也能跑,但到了后期要处理复杂交互、跨平台路径、模板渲染时,维护成本会指数级上升。
t3code 最终选择 Node.js + TypeScript,不是因为它最炫,而是因为三个很实际的理由:
- 生态最直接:模板里要装的前后端依赖本身就是 npm 包,用 Node.js 写脚手架,可以在代码里直接读取 package.json、操作 node_modules、调用 npx,不需要额外桥接。
- 跨平台行为一致:Shell 脚本在 macOS 上好好的,到 Windows 上经常因为路径分隔符、
rm -rf语法炸掉。Node.js 的fs模块天然处理了这些差异。 - 可调试性:团队里的前端开发者都会写 TypeScript,碰到脚手架问题可以直接进 IDE 断点调试,不需要换语言思维。
命令解析我用了 Commander,这是 Node 社区非常成熟的方案,支持子命令、参数校验、help 自动生成。文件操作主要靠fs-extra,它在原生 fs 的基础上提供了copy、ensureDir、remove这些实用函数,省掉大量样板代码。模板渲染用的是 EJS,原因是它的语法门槛低,团队里任何人接手都能快速看懂<%= variable %>这种写法。
还有个容易被忽略的点:脚手架要支持“本地开发时即时调试”。我用npm link把 t3code 挂到全局,改完代码立刻就能在任意目录执行,不需要反复重新发布。等到版本稳定了,再走npm publish分发到团队私有 registry。
1.3 模板化与可配置化的平衡
脚手架的核心矛盾是:模板写得太死,遇到不同需求就要 fork 一份;配置做得太灵活,CLI 交互会变成一场噩梦。t3code 给出的解法是“静变动分”:把大概率相同的文件做成静态模板,把因项目而异的内容做成占位符,让 CLI 通过几个精准的提问来收集变量。
我用一个表格来对比三种方案的特点:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 纯静态复制 | 简单稳定,不涉及渲染逻辑 | 每个项目都要手改配置 | 内部工具、固定技术栈 |
| 全动态生成 | 灵活度高,参数任意组合 | 代码复杂,难以维护 | 偏平台化的低代码系统 |
| 静态为主 + 条件渲染 | 上手快,常见场景开箱即用 | 少数高级场景需二次调整 | 多数团队的真实选择 |
t3code 采用的是第三种。比如“是否需要 Dockerfile”就是一个条件:用户选了--docker,渲染逻辑才去复制 docker 相关的模板文件;默认不选,就直接跳过。核心渲染代码并不复杂,维护成本都在模板上,业务代码零侵入。
我一直坚持一个原则:交互式提问不要超过 6 个。超过这个数,使用者的耐心就会快速流失,生成器也会从“提效工具”变成“负担工具”。所以 t3code 把重参数放在命令行参数里(比如--name、--template、--db),交互只用于确认几个无法用默认值覆盖的关键项,比如管理员邮箱、是否需要登录模块。
2. 核心细节解析与实操要点
2.1 安装与运行环境准备
t3code 的安装很简单:
npm i -g t3code装完可以先看版本和帮助:
t3code --version t3code --help正常会输出主命令列表,我的习惯是先跑一下t3code doctor做环境检查,它会一次性验明 Node 版本、包管理器、Git 配置以及 Docker 是否可用。
$ t3code doctor [OK] Node.js v20.11.1 (>= 18.0.0) [OK] pnpm v9.12.0 [OK] git config user.name 已设置 [OK] Docker 未安装(可选依赖)这里有个小细节:Node 版本我要求至少 18,因为模板里生成的工程会用到原生fetch和较新的node:test特性。如果环境里的 Node 太老,跑出来的项目经常报各种莫名其妙的语法错误,排查起来费时费力。所以生成器在启动时会先做一次版本检查,不满足就直接拒绝执行,把问题扼杀在源头。
包管理器方面,我用 pnpm 作为默认推荐。原因很简单:模板工程是 monorepo 结构,pnpm 的 workspace 支持非常顺滑,而且依赖安装速度比 npm 快不少。实测同一个项目,pnpm 安装耗时不到 npm 的一半。当然,用户也可以用--pm npm显式切换到 npm 模式,生成器会在.npmrc和packageManager字段里同步做相应调整。
2.2 命令设计与参数约定
t3code 目前对外提供五个子命令,每个都是围绕一个真实场景设计的:
| 命令 | 作用 | 典型用法 |
|---|---|---|
init | 初始化一个新项目 | t3code init --name demo --template fullstack |
add | 往已有项目里添加模块 | t3code add api --name user |
list | 查看所有可用模板 | t3code list |
doctor | 检查本机环境依赖 | t3code doctor |
upgrade | 升级本地模板缓存 | t3code upgrade |
重点说一下init。它的核心参数有几个:
--name:项目目录名,同时会作为 package name 使用。我会做一层健康检查,只允许小写字母、数字、中划线,避免出现“A项目_v2”这种在 npm 和 Docker 里都不好使的名字。--template:选择模板类型,目前有frontend、backend、fullstack三种。--db:数据库类型,可选postgres、mysql、sqlite和none。选了none就不会生成 ORM 相关的代码。--pm:包管理器,pnpm或npm。--docker:布尔开关,是否生成容器化配置。--skip-git:布尔开关,跳过 Git 初始化和首次提交。
参数之间会有联动。比如选了--db sqlite,生成的DATABASE_URL就不是连接串,而是一个本地文件路径;选--docker时,SQLite 模式会立刻产生一个警告,因为容器里的 SQLite 文件默认是临时存储,需要额外挂载卷才能持久化。这种边界情况的处理,正是 CLI 工具做好和做坏的分水岭。
2.3 模板目录结构与渲染原理
t3code 的模板不是一个“文件包”,而是一个有约定的目录树。我把它设计成下面这样:
templates/ fullstack/ static/ .gitignore .dockerignore README.md .editorconfig render/ _package.json _env.example apps/ api/ _tsconfig.json src/ index.ts web/ _vite.config.ts db/ _prisma.schema conditional/ docker/ Dockerfile.api docker-compose.yml目录里最关键的是下划线前缀规则:
static:里面的文件原样复制,不做任何变量替换。render:以_开头的文件都会被解析,把文件里的<%= projectName %>这类占位符替换成实际值,渲染完成后会自动去掉文件名里的下划线。conditional:只有在用户选择了对应选项时才复制。
渲染层我用 EJS 的renderFile处理:
import { renderFile } from 'ejs' import { promises as fs } from 'fs' const context = { projectName: 'shop-demo', packageName: 'shop-demo', port: 3000, dbType: 'postgres', orm: 'prisma', } const output = await renderFile( path.resolve(templateRoot, 'render/_package.json'), context ) await fs.writeFile( path.join(outputDir, 'package.json'), output )后面我会细讲一个模板转义的坑。这里先记住一点:所有带_的模板文件,写的时候要避免把业务代码里的${}模板字符串<%= %>风格的代码,容易被误渲染。最稳妥的办法是模板文件里凡是需要“保留给运行时”的表达式,都改用普通的字符串拼接或者\${}转义。
2.4 关键注意点:环境变量、命名规范与 Git 初始化
很多脚手架翻车,不是坏在生成阶段,而是坏在生成之后的“第一次运行”。为了避免这种情况,我在 t3code 里设了几个硬性约定:
- 生成目录只输出
.env.example,不输出.env。.env必须由开发者运行cp .env.example .env后自行修改。这样可以防止数据库密码之类的敏感信息被误提交到 Git。 - 端口默认值集中管理。后端固定给 3000,前端 Vite 固定给 5173。如果检测到端口被占用,生成器不会自作主张去换端口,而是提示用户通过环境变量覆盖。
- 命名规范全部收敛到 kebab-case(全小写加中划线),代码里的变量名、ORM 模型名则由生成器转换为 camelCase 和 PascalCase。这个转换逻辑在 npm 包
change-case的基础上做了二次封装,保证跨语言命名习惯的一致性。 - Git 初始化放在文件全部写完之后。如果检测到当前目录已经有
.git,就直接跳过。首次提交我习惯用feat: init project作为 commit message,看起来零散,但能让历史记录从一开始就保持整洁。
还有一个细节:Windows 用户经常遇到.env.example复制出来变成.env.example.txt的情况。这是 Windows 资源管理器隐藏扩展名的锅,跟工具无关。我在文档开头就写了一条提示:复制环境变量文件请用命令行cp,不要用“另存为”。
3. 实操过程与核心环节实现
3.1 从零生成一个基础工程
下面用一个真实的“电商后台”例子完整走一遍。假设项目名叫shop-demo,选 fullstack 模板,用 pnpm 装依赖:
npm i -g t3code t3code init --name shop-demo --template fullstack --db postgres --docker cd shop-demo cp .env.example .env pnpm install pnpm dev执行init之后,终端会打印一份清单告诉你生成了哪些部分:
✔ 项目目录 shop-demo 已创建 ✔ 前端工程 apps/web 已创建(Vite + React + TS) ✔ 后端工程 apps/api 已创建(Fastify + TS) ✔ 共享类型包 packages/shared 已创建 ✔ Prisma Schema 已生成,数据库类型: postgres ✔ Docker 配置已写入 docker-compose.yml ✔ Git 仓库已初始化并完成首次提交整个过程在我的机器上实测大约 2.8 秒。这个速度不算什么黑科技,无非是提前把模板文件准备好了,运行时只做内存里的字符串替换和文件复制。真正省下的时间在后续:你不用再去想“ESLint 要不要装”“Prettier 跟 ESLint 冲突怎么办”“Dockerfile 怎么写多阶段构建”,这些答案已经被固化成模板里的最佳实践了。
生成完的工程结构是这样:
shop-demo/ apps/ web/ src/ pages/ components/ vite.config.ts api/ src/ routes/ plugins/ tsconfig.json db/ schema.prisma migrations/ packages/ shared/ src/ types.ts .env.example .gitignore .npmrc docker-compose.yml README.md从目录可以看出,我刻意把db独立成包,而不是塞在后端应用里。原因很简单:Prisma 的 client 生成、数据库迁移、种子数据脚本都涉及跨应用依赖,独立成包之后,前端也能直接引用部分类型,不会出现“类型定义在后端包里被前端硬借”这种脏依赖。
3.2 接入数据库与 ORM:参数计算与选择过程
t3code 里对数据库的支持默认是 PostgreSQL + Prisma。为什么这么选?
论关系型数据库,PostgreSQL 在类型、扩展、生态上都更稳;论 ORM,Prisma 的类型安全和迁移体验在 TypeScript 社区里几乎是首选。所以这个组合不是拍脑袋定的。在add命令里你也可以把 ORM 换成 TypeORM,但生成器会提示“非默认组合,部分模板片段可能需要手动调整”。
当用户选择了 Postgres,生成的.env.example里会有一个标准连接串:
DATABASE_URL="postgresql://t3code:t3code@localhost:5432/shop_demo?schema=public"我解释一下这个串的拆解:postgresql://是协议名;t3code:t3code分别是用户名和密码,本地开发用默认值即可;localhost:5432是数据库地址和端口;最后的shop_demo是库名。在schema.prisma里,库名会由生成器自动从项目名转换过来,把中划线换成下划线。这一步很多人容易忽略,结果项目叫shop-demo,数据库名写成shop_demo,两边看着像又对不上,排查半天才发现是连库失败。
生成器里对应这个逻辑的伪代码大概是:
const dbName = projectName.replace(/-/g, '_') const prismaSchema = ` datasource db { provider = "${dbProvider}" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" } model User { id String @id @default(uuid()) email String @unique name String? createdAt DateTime @default(now()) } `选了--db sqlite的话,DATABASE_URL会变成file:./dev.db,同时生成器会主动移除docker-compose.yml里的数据库服务,避免用户拿到一份根本跑不起来的 Compose 配置。这类细节,全是在真实项目里被用户反馈逼出来的。
3.3 前后端联调与统一代理配置
全栈项目最常见的坑就是“前端调后端接口,跨域报错”。t3code 的默认方案不是教你在后端写一堆 CORS 配置,而是在前端开发服务器上做代理转发。
Vite 的vite.config.ts里,我会生成这样一段:
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, }, }, }, })这样前端代码里可以直接写fetch('/api/users'),浏览器请求先到 Vite 的 5173 端口,再由 Vite 把请求转发给后端 3000 端口。浏览器端看到的都是同源请求,不需要处理 CORS,也不需要在前端代码里写死后端地址。
我自己的习惯是:开发环境一律用代理,生产环境走网关或者 Nginx 转发。这条约定同时写进了 README 里,避免新人接手时把后端地址写死进 axios 的 baseURL。如果你非要在生产环境开 CORS,也不是不行,但至少得明确知道自己在冒什么风险。
3.4 一键生成 Dockerfile 与 CI 配置
生成器支持--docker之后,会在项目根目录产出Dockerfile.api和docker-compose.yml。Dockerfile 我采用的是多阶段构建:
FROM node:20-alpine AS base WORKDIR /app FROM base AS deps COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile FROM base AS build COPY . . RUN pnpm build FROM base AS runtime ENV NODE_ENV=production COPY --from=deps /app/node_modules ./node_modules COPY --from=build /app/apps/api/dist ./apps/api/dist EXPOSE 3000 CMD ["node", "apps/api/dist/index.js"]多阶段构建的核心思路是:把安装依赖、编译打包、运行产物三个环节拆开,每一层都可以利用 Docker 的缓存,同时最终镜像里只保留运行所需的最小文件。实测下来,一个 Fastify + Prisma 的后端服务,构建镜像体积可以从 1GB 级别降到 300MB 以内,启动速度也有明显提升。
docker-compose.yml里我同时配了 API 和数据库两个服务,并且把本地开发用的端口映射和依赖关系都写清楚:
services: db: image: postgres:16-alpine environment: POSTGRES_USER: t3code POSTGRES_PASSWORD: t3code POSTGRES_DB: shop_demo ports: - "5432:5432" volumes: - pgdata:/var/lib/postgresql/data api: build: context: . dockerfile: Dockerfile.api environment: DATABASE_URL: postgresql://t3code:t3code@db:5432/shop_demo ports: - "3000:3000" depends_on: - db volumes: pgdata:这里最容易踩的坑是数据库地址。本地开发时后端访问数据库用的是localhost:5432,但进了 Docker 网络,localhost指向容器本身而不是宿主机,必须换成服务名db。我在模板里已经把.env.example和docker-compose.yml的配置区分开,但还是经常看到有人把localhost直接带进容器里跑,结果链路全通但连不上库,卡一个晚上。
CI 脚本我默认生成一份 GitHub Actions 的配置,用于跑 lint、构建、测试:
name: CI on: push: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm lint - run: pnpm test - run: pnpm build这份配置最需要注意的是cache: pnpm,它能命中 pnpm 的缓存策略,把 CI 构建时间从七八分钟压到两分钟左右。如果你切换成了 npm 包管理器,生成器会自动把这一步改成npm对应的缓存字段,代码里做了一个简单的映射判断。
4. 常见问题与排查技巧实录
4.1 常见报错与修复速查表
这段时间里,我在社区和团队里收集了不少真实使用 t3code 碰到的报错,整理成一张速查表:
| 报错信息 | 常见原因 | 解决办法 |
|---|---|---|
EADDRINUSE: address already in use :::3000 | 端口被占用 | 打开.env改PORT,或直接停掉旧进程 |
PrismaClientInitializationError | DATABASE_URL未设置或格式错误 | 确认.env已从.env.example复制并修改 |
EACCES: permission denied | 全局安装 npm 包权限不足 | 建议用 nvm 管理 Node,不要用 sudo 硬装 |
Template render error: projectName is not defined | 模板变量名与上下文不一致 | 检查模板里是否拼错了占位符,或 context 是否漏传 |
ERR_PNPM_NO_LOCKFILE | 根目录缺少 lockfile | 在项目根目录执行pnpm install生成 |
Cannot find module '@prisma/client' | Prisma Client 未生成 | 执行pnpm db:generate重新生成客户端 |
这张表看起来是零散报错,背后其实有共性:大部分问题都出在“环境变量没配对”和“生成后的依赖没重新安装”这两个环节。所以排查顺序我建议反着来:先看.env,再看node_modules,最后才去看代码逻辑。
4.2 模板里变量被误渲染的问题
这是写模板引擎时最容易翻车的一个点,也是我踩得最深的一个坑。早期的模板文件里,后端代码经常会有这样的内容:
const apiUrl = `${process.env.API_BASE_URL || 'http://localhost:3000'}`如果模板引擎用 EJS 的默认标签,它会把<%= ... %>当作渲染标记。而上面这段代码里只有一个${process.env...},EJS 默认不会处理它,看上去没问题。但当你需要把某个变量写进模板里,比如端口号:
const port = <%= port %>这行会正常渲染。可一旦模板里同时出现<%=和${,后续维护者很容易把两者混在一起,渲染出来的代码要么多了一串变量名,要么直接抛错。
我最终的解决方式是:t3code 模板内部统一使用<%= varName %>语法做变量注入;运行时拿到process.env的原生${}写法,则在模板里写成\${}或直接拆成字符串拼接:
const dbUrl = process.env.DATABASE_URL || 'postgresql://localhost:5432/shop_demo'这样既不用改变 EJS 的默认标签,又避免了模板字符串的误渲染。这个经验后来被写进了团队的模板开发规范里,简单一句话:模板文件的业务代码里不要直接使用模板字符串拼接变量,全部先取到常量再拼接。
4.3 不同 Node 版本的兼容问题
团队里经常有人用 Node 16,有人用 Node 20,还有人用公司预装的 LTS 版本。t3code 正常跑在 Node 18 以上,但用户机器上如果版本太老,生成出来的工程大概率会在某个环节报错。比如 Node 16 不支持node:test、原生fetch在 18 之前需要安装 polyfill、find-up这类库的高版本可能直接拒绝老运行时。
我在package.json里统一维护了engines字段:
{ "engines": { "node": ">=18.0.0" } }但这只是“声明”,不会强制拦截。真正有用的是t3code doctor,它在生成前就把 Node 版本、pnpm 版本、Git 配置、Docker 可用性全部检查一遍,任何一项不合格都会明确提示,而不是让报错在工程跑起来之后才慢慢冒出来。
这里还有一个容易忽略的坑:如果你先用 npm 装了依赖再切换到 pnpm,根目录会同时存在package-lock.json和pnpm-lock.yaml,很容易导致 CI 混乱。我在生成器的模板里已经加了.gitignore规则,但如果项目不是由 t3code 生成的,强烈建议手动确认一下 lockfile 的一致性。
4.4 部署阶段的配置遗留问题
本地开发一切正常,一到部署就挂,这是全栈项目最常见的焦虑来源。t3code 生成的项目也不例外,尤其是涉及数据库容器化之后,最容易出问题的是这几个点:
第一,localhost穿帮。本地连 Postgres 用localhost,在 Docker 网络里必须换成服务名db。如果构建时用的环境变量是从本机.env带过去的,那必然连不上。
第二,.env被带进了镜像。有些人图省事,写 Dockerfile 时直接COPY . .,把本地.env文件里的密码、密钥也都打进了镜像。t3code 默认生成的.dockerignore会排除.env,但如果你自己手写 Dockerfile,一定要检查这一层。
第三,Prisma 的引擎文件与镜像不匹配。prisma的 query engine 是二进制文件,在多阶段构建时如果只复制dist而没有复制node_modules/.prisma目录,容器里跑起来就会提示找不到 engine。我生成的模板已经处理了这个问题,但后续如果手动精简镜像,很容易把它删掉。遇到底层依赖问题,不要硬猜,先跑一句npx prisma generate重新生成客户端,往往比改半天代码更有效。
5. 我在实际迭代中总结的几条硬原则
5.1 模板不要存业务假设
第一版 t3code 的模板里我写死了PORT=3000,还默认所有后端路由都带/api前缀。在当时那个项目里没毛病,但后来被复用到一个对端口有特殊要求的项目时,直接翻车。从那以后,模板里凡是遇到端口、数据库名、路由前缀、管理员账号这类可能变化的量,全部改成环境变量或生成时注入的占位符,绝不把一次业务实践当成普适标准。
另外,交互式提问不要贪多。每个问题都意味着额外的维护成本和使用负担。t3code 目前 init 只问三个必须问的问题,其他全部走参数默认值。如果用户需要更极端的定制,他们可以 fork 模板再改,生成器提供--template /path/to/custom来支持本地模板路径,这样既保留了灵活性,又不让主流程复杂化。
5.2 版本锁定与升级策略
生成器自己的版本和模板的版本是分开维护的。模板目录里有一个.t3code-version文件,记录当前模板快照的版本号。当t3code upgrade检测到远端有新版本时,会增量更新模板目录,而不是整包覆盖。这个设计是为了避免生成器升到新版,用户本地却还在用旧模板,导致生成出来的工程风格不统一。
实际使用中,我建议每个团队固定一个模板版本,不要追求最新。脚手架这类工具的价值在于“稳定可复现”,而不是“功能日新月异”。生成器每更新一版,团队应该走一次内部评审,确认新模板与现有规范一致,再统一升级。
5.3 t3code 后续可以怎么扩展
目前 t3code 已经能覆盖“新建一个标准全栈项目”的场景,但我自己清楚它还缺几块拼图。
第一个方向是插件机制。现在模板是内置的,团队想要自定义规范,只能改模板目录。更友好的方式是让 t3code 支持插件,用户可以通过一条t3code add plugin命令安装第三方模板包,彼此之间版本隔离,互不影响。
第二个方向是接 OpenAPI 文档生成代码。后端接口定义一旦规整,就可以自动生成前端的请求方法、类型定义和 mock 服务,大幅减少前后端联调阶段的低级错误。这不是 t3code 当前版本的范畴,但架构上已经预留了add api的入口,下一步就是把 OpenAPI 解析器接进去。
第三个方向是可视化配置界面。CLI 对小团队很友好,但到了组织层面,很多非技术角色也想知道项目初始化时到底选了哪些参数。做一个简单的 Web 向导页,把同样的模板渲染逻辑通过接口暴露出来,也是顺理成章的事。
就我个人而言,t3code 目前最让我满意的不是它多智能,而是它足够“无趣”——稳定、可预期、没有隐藏惊喜。写完模板之后,我几乎不用再想“这次生成的工程会不会和上次不一样”。作为工具,这种确定性本身就是最大的价值。如果你也想做一套自己的脚手架,我很建议你先从手头最重复的那个初始化步骤开始,别急着追求大而全,把一个痛点解决透,比做十个半成品功能有用得多。