删除功能

移除用不到的内置模块——积分、文件、博客和文档、候补名单等——每份删除步骤都经过构建、类型检查和测试的验证。

ShipKit 自带的东西比大多数产品用得上的要多。每个可选模块都是一个垂直切片(见添加功能),每个都有一份删除步骤(recipe),而且这份步骤会被真正执行和检查,不只是写在纸上。用不到的模块尽早删:要读的代码更少,表更少,后台页面也更少。

原理

一个模块住在 src/features/<name>/。在这个文件夹之外,它和应用其他部分的接触点只有三类:

  • 自己的路由——用户页面、后台页面、API 路由。直接删掉。
  • 注册行——src/core/*/handlers.ts 里每个注册表一行 import,外加 schema 汇总文件 src/core/db/schema.ts 里的一行。删掉这些行。
  • 共享文件里的标记块——侧边栏的一行、仪表盘的一张卡片、一个菜单项——用// [feature: <name>] start / end 注释(JSX 里是 {/* … */})包起来,或者是单独一行、末尾标着 // [feature: <name>]。把它们剪掉。

core 里的分发器会留下来。audit()、notify()、track() 和邮件事件的发送函数都在 src/core/ 里;没有功能注册来接收事件时,调用它们什么也不做。所以删掉审计日志,并不需要去改每个记录审计的服务端函数。

删除步骤就是代码

scripts/verify-deletion.ts 里每个模块一份 recipe。每份都是一个函数,负责删除文件、修改共享文件——是确切的步骤,不是概述。

bun run verify:deletion credits      # 单个模块
bun run verify:deletion              # 所有 recipe,各自在独立的副本上跑
bun run verify:deletion all          # 所有 recipe 叠加在同一个副本上
bun scripts/verify-deletion.ts --list  # 列出 recipe 名称

对每个目标,脚本会把仓库复制到 /tmp/verify-deletion-<name>,执行 recipe,重新生成路由,然后要求 bun run build、bun run typecheck 和 bun run test 全部通过。它从不碰你的工作目录。每次都是一次完整的生产构建,所以全部跑完要花一些时间。CI 会在 pull request 上跑所有 recipe,改坏了哪一份,合并前就能发现。

all 对应的是一个可选模块都不要的买家:某份 recipe 去改一个已被另一份 recipe 删掉的文件,这种问题只有在这里才会暴露。

真正删除一个模块

最省事的是在 Claude Code 里用 /delete-feature 这个 skill(见 AI 编程助手):它先试跑 recipe,再把同样的步骤应用到你的代码上,并完成下面的收尾工作。手动操作的话:

  1. 先运行 bun run verify:deletion <name>。如果失败,说明 recipe 已经和代码对不上了——先修好 recipe,再动你的代码。
  2. 阅读 scripts/verify-deletion.ts 里对应的 recipe,照做同样的删除和修改。
  3. 收拾 recipe 没管的部分:
    • messages/en.json 和 messages/zh.json 里用不到的 key(recipe 只删少数几个前缀,比如该模块的 setting_*)
    • e2e/*.spec.ts 里该模块的用例
    • recipe 本身;如果你维护自己的文档,还有这一页
  4. 生成删表迁移并全面检查:
bun run db:generate && bun run db:migrate:local
bun run generate-routes && bun run typecheck && bun run build && bun run e2e

部署前用 bun run db:migrate:remote 把迁移应用到生产环境(见部署)。删表迁移会连表带数据一起删掉,本地和生产都一样。

逐个模块

“注册表”一列列出会少一行的 src/core/<注册表>/handlers.ts;schema 指汇总文件 src/core/db/schema.ts。页面按 URL 列出。

模块删除注册表另外修改
creditssrc/features/credits/、/credits、/admin/credits、/api/v1/admin/credits*、/api/v1/me/credits、/api/v1/me/run、src/core/payment/money-path.test.tsschema、payment、account、jobs、stats、user-detail、settings、authz侧边栏的行、_app.tsx 里的余额标签、仪表盘卡片和流水面板、账户弹窗里的标签页、账单区块、src/config/plans.ts 里的积分包及其测试、通知铃铛、seed
filessrc/features/files/、/files、/api/files、/api/files/$fileIdschema、account、user-detail、authz侧边栏的行、仪表盘卡片和最近文件面板
content(博客和文档)src/features/content/、content/blog/、content/docs/、public/covers/、/blog、/docs无_marketing.tsx 里的博客/文档链接,落地页 hero.tsx 和 cta.tsx 区块里的文档链接
auditsrc/features/audit/、/admin/audit、/api/v1/admin/auditschema、audit、stats、user-detail、payment、account、jobs、settings、authz后台导航行、seed
email-logsrc/features/email-log/、/admin/email-log、/api/v1/admin/emailsschema、email、stats、user-detail、account、jobs、settings后台导航行、seed
affiliatesrc/features/affiliate/、/affiliate、/admin/affiliate、/r/<code> 推广链接schema、payment、account、jobs、stats、settings、authz侧边栏的行、通知铃铛、概览指标名称、account-section.tsx 里的注销拦截提示、后台设置页、src/config/private-paths.ts、src/core/account/events.ts 里拦截码的联合类型
feedbacksrc/features/feedback/、/admin/feedback、/api/v1/admin/feedback、/api/feedback-image*、feedback-reply 邮件模板schema、stats、user-detail、account、settings、authz_app.tsx 里的“帮助与反馈”菜单项和弹窗、后台导航行、概览指标名称、通知铃铛、账户弹窗里的“我的反馈”、邮件预览样例、seed
waitlistsrc/features/waitlist/、/waitlist、/admin/waitlist、/api/v1/admin/waitlist、waitlist-approved 邮件模板schema、stats、user-detail、account、authz_app.tsx 里的准入拦截、app-config.ts 里的 waitlist 开关、落地页和顶栏的行动按钮、后台导航行、概览指标名称、通知铃铛、邮件预览样例、seed、e2e 初始化
analyticssrc/features/analytics/、/api/ph(PostHog 代理)audit、payment、account、analytics__root.tsx 里的 <PostHog />、app-config.ts 里的 analytics 配置块
demosrc/features/demo/jobsauth.ts、env.ts、_app.tsx、_auth/login.tsx、_marketing.tsx 里的标记块;demo_* 文案
notificationssrc/features/notifications/schema、notify、account_app.tsx 里的铃铛

哪些会留下,为什么

  • 没有积分,账单照常。 订阅和一次性订单都不受影响;账单和积分之间只共享支付事件流。见支付。
  • 没有文件模块,R2 还在。 wrangler.jsonc 里的存储桶绑定保留:头像属于核心功能,用的是同一个桶。文件模块和反馈模块已经上传的对象不会从桶里删除。
  • 没有内容模块,法律页面还在。 MDX 流水线、src/core/content/ 和 content/legal/ 都保留,因为服务条款和隐私政策页面属于核心。见内容。
  • 没有邮件日志,照样发信。 sendEmail 继续工作,只是不再记录投递情况。邮件预览页及其 email.read 权限属于核心。
  • 三个模块不需要迁移。 analytics、content 和 demo 没有自己的表。数据分析也可以不删、只把 PostHog key 留空来关闭;演示模式除非设置 DEMO_MODE=true,默认就是关的。见更多模块。

没有删除步骤的模块

账单、登录和管理后台不是可选模块,应用的其余部分都建立在它们之上,所以没有删除它们的 recipe。你自己加的模块,在写它的时候就把 recipe 一起写好——做法见添加功能。