Wasp 教程动作执行器(TACTE):从 MDX 教程文档到可运行 Wasp 应用的自动化流水线
2026/9/14 12:04:57 网站建设 项目流程

Wasp 教程动作执行器(TACTE):从 MDX 教程文档到可运行 Wasp 应用的自动化流水线

【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp

导读

Wasp 官方文档内置了一套引导开发者从零构建完整 Wasp 应用的分步教程,每一节教程中都嵌入了<TutorialAction>组件来标注"创建新 Wasp 应用""添加认证""创建 Task 实体"等机器可执行动作。web/tutorial-actions-executor目录下的 Tutorial Actions Executor(TACTE)正是负责读取这些教程文件、抽取动作并按序执行、最终产出完整可运行 Wasp 应用的命令行工具。阅读本文,你将掌握 TACTE 的三大命令(generate-appedit-patch-actionlist-actions)的完整用法、教程 MDX 文件的动作标注格式、Patch 文件管理机制,以及它如何借助 Git 提交历史实现"修改某个动作并自动重放后续动作"的编辑能力。

1. 背景:TACTE 要解决什么问题

Wasp 文档的分步教程覆盖了从项目初始化、页面、实体、查询、动作到认证的完整开发过程。这些教程文档以 MDX 形式存放于web/docs/tutorial/,每个文档步骤用编号前缀命名(如01-create.md02-project-structure.md03-pages.md04-entities.md05-queries.md06-actions.md07-auth.md),并通过TutorialAction组件标注每一步对应的可执行动作。

TACTE 的核心使命(见 README.md)是:读取这些教程文件,抽取其中定义的<TutorialAction>动作,按文档顺序逐一执行,最终生成一个完整可用的 Wasp 应用。这意味着教程文档不只是给人读的文字,还变成了一份可以被机器执行的"食谱"。

<TutorialAction>组件本身定义在 web/docs/tutorial/TutorialAction.tsx 中,其注释明确说明它与 TACTE 的关联:在开发模式下它渲染出带action类型和id的调试信息条(便于作者排查),在生产环境则直接透传子内容。该组件支持三种动作类型(源码中的ActionProps类型):

  • INIT_APP:初始化应用,需额外提供starterTemplateName属性;
  • APPLY_PATCH:应用一个 Git Patch;
  • MIGRATE_DB:执行数据库迁移。

组件注释还强调:修改动作类型时,必须同步更新 src/actions/actions.ts 中的 TypeScript 类型定义——这正是文档组件与执行器之间"契约"的体现。

2. 整体架构:从 MDX 到可运行 App 的执行流水线

TACTE 是一个基于 Commander,注册了generate-appedit-patch-actionlist-actions三个子命令。从源码结构看,完整流水线可分为三个阶段:

2.1 抽取:解析 MDX,定位 TutorialAction 节点

extract-actions/mdxParsing.ts 使用mdast-util-from-markdown配合micromark-extension-mdx-jsx扩展把 MDX 文件解析为 AST,随后 astTraversal.ts 通过unist-util-visit遍历 AST,找出所有名为TutorialActionmdxJsxFlowElement节点,并读取其属性:

  • idaction必填属性,缺失时直接抛出错误;
  • starterTemplateName仅在INIT_APP动作中使用。

文件读取顺序由 fileOperations.ts 保证:只读取.md/.mdx文件,并按文件名数字前缀01-02-…)升序排列,从而保证动作的执行顺序与教程步骤一致。

2.2 映射:从 JSX 节点到内部 Action 模型

nodeMapping.ts 负责把 AST 节点映射为 actions.ts 中定义的联合类型Action = InitAppAction | ApplyPatchAction | MigrateDbAction。每种动作都携带id(唯一标识)与sourceTutorialFilePath(来源文件):

动作类型额外字段执行时行为
INIT_APPwaspStarterTemplateName调用wasp new <app-name> -t <template>创建应用并初始化 Git 仓库
APPLY_PATCHpatchFilePathdisplayNamegit apply应用对应的 Patch 文件
MIGRATE_DB运行wasp db migrate-dev --name <action-id>生成并应用迁移

2.3 执行:逐动作执行并逐个提交

核心执行循环位于 execute-actions.ts。对每个动作按kind分发处理,并在动作完成后统一调用commitActionChanges提交:

  1. INIT_APP→ init.ts 中的initWaspAppWithGitRepo:先清空旧目录,再调用 waspCli.ts 中的waspNew(即wasp new <name> -t minimal),随后执行git init并把主分支重命名为main
  2. APPLY_PATCH→ 先尝试applyPatchForAction(底层即 git.ts 的git apply --verbose),若失败则进入"重新生成 Patch"流程(见第 5 节);
  3. MIGRATE_DB→ 调用waspDbMigrate执行wasp db migrate-dev --name <migrationName>。注意 waspCli.ts 中对该命令显式设置了stdio: ["ignore", "pipe", "pipe"],源码注释说明这是为了避免非交互环境(如 e2e 测试)下命令因等待 stdin 输入而挂起。

每个动作执行后都会以动作的id作为提交信息生成一个独立的 Git commit(见 git.ts 的commitAllChangesgit add .+git commit -m <message>)。这一设计是后续edit-patch-action能够"回退重放"的基础,也是 e2e 快照测试校验 Git 历史的依据。

3. 命令一:generate-app —— 一键生成完整应用

npm run generate-app # 可选:指定自定义的 Wasp CLI 二进制/命令 npm run generate-app -- --wasp-cli-command wasp

该命令执行以下流程(generate-app/index.ts):

  • 读取教程目录下所有按编号命名的教程文件;
  • 从每个文件的<TutorialAction>组件中抽取动作;
  • 按序执行每个动作(初始化应用、应用 Patch、迁移数据库);
  • 全部完成后输出成功信息,并给出生成应用的目录路径。

命令会先打印Found N actions in tutorial files.以确认抽取到的动作总数。如果某个 Patch 应用失败,generate-app会暂停并进入人工解决流程(详见第 5 节)。

在仓库中运行package.json中预设的脚本已绑定默认参数--app-name TodoApp --tutorial-dir ../docs/tutorial,即直接针对web/docs/tutorial/下真实教程生成TodoApp应用。

4. 命令二:edit-patch-action —— 修改某个 Patch 并自动重放后续动作

当教程内容调整后,某个 Patch 可能不再准确。edit-patch-action让你修改指定动作的代码,并自动把后续所有动作重新应用到新的基础上:

# 非交互式:按 ID 直接指定 npm run edit-patch-action -- --action-id "create-task-entity" # 交互式:从列表中选择要编辑的动作 npm run edit-patch-action # 可选参数: # - 跳过编辑前的应用生成 npm run edit-patch-action -- --skip-generating-app # - 指定自定义 Wasp CLI npm run edit-patch-action -- --wasp-cli-command wasp

该命令的完整逻辑见 edit-patch-action/index.ts:

  1. 生成应用(除非传入--skip-generating-app):先完整执行一遍generate-app,使每个动作都对应一个独立的 Git commit;
  2. 回退到目标动作:通过 git.ts 中的findCommitSHAForExactMessage按提交信息(即动作id)精确查找对应 commit,然后git switch --force-create fixes <commit>创建一个名为fixes的分支并切到该动作的提交;
  3. 进入编辑态:执行git reset --soft HEAD~1把该 commit 的改动放回暂存区,接着调用 git.ts 的askUserToEditAndCreatePatch——如果设置了$EDITOR环境变量,editor.ts 会询问是否用该编辑器打开生成的应用目录(./.result/<app-name>),随后用@inquirer/promptsconfirm提示你改完后回车确认;
  4. 生成新 Patch 并提交:把工作区改动导出为新的 Patch 文件(内部通过"临时提交 →git showgit reset --hard HEAD~1"实现),再应用该 Patch 并以动作id重新提交;
  5. 重放后续动作:把fixes分支 rebase 回main分支之上(git switch main+git rebase fixes)。如果后续动作与你的修改产生冲突,命令会暂停并提示你手动解决后回车继续;
  6. 回写 Patch 文件extractCommitsIntoPatches遍历所有APPLY_PATCH动作,从各自的 commit 重新生成 Patch 内容并写回patches目录,保证磁盘上的 Patch 文件与新的提交历史保持一致。

其中"选择要编辑的动作"支持两种方式:传入--action-id时精确匹配(找不到会报Apply patch action with ID "..." not found.),未传参时用@inquirer/promptsselect弹出交互式列表。

5. 命令三:list-actions —— 盘点全部教程动作

npm run list-actions

list-actions/index.ts 会读取全部教程动作,按来源文件名分组展示每个动作的idkind,并按类型着色:INIT_APP黄色、APPLY_PATCH绿色、MIGRATE_DB蓝色。这在修改教程、核对动作完整性时非常实用,输出形如:

04-entities.md - prisma-task (APPLY_PATCH) - migration-add-task (MIGRATE_DB)

6. 公共选项:三个命令的必填配置

三个命令共享 tacteCommand.ts 中定义的公共选项,用于配置教程应用生成环境:

选项说明默认值是否必填
--app-name <name>要生成的应用名称(也是输出目录下的子目录名)是(makeOptionMandatory
--output-dir <path>应用生成目录./.result
--tutorial-dir <path>包含教程 MDX 文件的目录是(makeOptionMandatory

另外 commonOptions.ts 提供--wasp-cli-command <command>选项,默认值为wasp,用于覆盖执行wasp new/wasp db migrate-dev时使用的 CLI 命令(例如传入wasp-cli或自定义路径)。

示例:

npm run generate-app -- --app-name MyApp --output-dir ./custom-output --tutorial-dir ./my-tutorial

路径解析规则见 tutorialApp.ts:生成应用的目录为<output-dir>/<app-name>,Patch 目录固定为<tutorial-dir>/patches

7. Patch 文件管理:命名、缺失与冲突处理

7.1 命名规范

Patch 文件必须存放在教程目录下的patches子目录中。文件名由来源教程文件名(去扩展名)与动作id拼接而成,格式为<tutorial-file-name>__<action-id>.patch(见 actions/index.ts 的getPatchFilename)。

以仓库真实数据为例,web/docs/tutorial/patches/ 下的文件03-pages__prepare-project.patch04-entities__prisma-task.patch06-actions__action-create-task.patch等,分别对应03-pages.md中的prepare-project动作、04-entities.md中的prisma-task动作。每个文件内容是一份标准 Git Diff,例如 04-entities__prisma-task.patch 展示了向schema.prisma追加Task模型的改动。

7.2 缺失或无法应用时的交互流程

如果某个 Patch 文件缺失或git apply失败,generate-app会暂停,并执行 actions/git.ts 中的regeneratePatchForAction

  1. 若旧 Patch 文件存在,先删除它;
  2. 打开生成的应用目录(./.result/<app-name>),提示你按当前<TutorialAction>的描述手动修改代码;
  3. 回车确认后,工具会把工作区改动导出为新的 Patch 文件写入patches目录,并自动以该动作id提交;
  4. 之后重新应用新 Patch 并继续后续动作。

注意:流程中所有提交都由执行器自动完成,不要在生成的应用里手动提交,否则会破坏"每动作一提交"的对应关系。

7.3 与 LLM 配合的 Human-in-the-Loop 工作流

README 明确给出了一套与 LLM 协作的流程:让generate-app保持运行,采用"人在回路"模式——当提示指向当前动作时,请 LLM 修改./.result/<app-name>中该动作对应的代码,人工审查改动后在终端确认,如此反复直至命令成功跑完。这也是在教程需要批量更新、Patch 大面积失效时,借助 LLM 自动生成新 Patch 的实用方式。

8. 教程文件格式:如何用<TutorialAction>标注动作

教程文件是 MDX,动作通过 JSX 组件<TutorialAction>标注,并用它包裹与该动作关联的教程正文:

# Step 4: Create Task Entity In this action, we'll create the Task entity: <TutorialAction id="create-task-entity" action="APPLY_PATCH"> ```prisma model Task { id Int @id @default(autoincrement()) } ``` </TutorialAction>

关键属性:

  • id:动作的唯一标识,同时也是 Git 提交信息(必须唯一);
  • action:动作类型,可选INIT_APPAPPLY_PATCHMIGRATE_DB

在真实仓库中,INIT_APP的用法可见 01-create.md:<TutorialAction id="create-wasp-app" action="INIT_APP" starterTemplateName="minimal">包裹了wasp new TodoApp -t minimal的命令;MIGRATE_DB的用法可见 04-entities.md:<TutorialAction id="migration-add-task" action="MIGRATE_DB" />是自闭合标签,紧随其后的正文是wasp db migrate-dev命令。

e2e 测试夹具(e2e-tests/fixtures/tutorial/)提供了最简可运行示例:01-init.md(INIT_APP,starterTemplateName="minimal")、02-patch.md(APPLY_PATCH 新增src/testUtils.ts)、03-migrate.md(APPLY_PATCH 新增Post模型 + MIGRATE_DB 迁移),与之配套的 Patch 文件位于e2e-tests/fixtures/tutorial/patches/

9. 测试体系:单元测试 + e2e 快照测试

项目同时包含单元测试与端到端(e2e)快照测试,运行全部测试:

npm run test

9.1 单元测试

tests/目录下的单元测试覆盖了解析与执行的关键环节:

  • extract-actions/:测试 MDX 解析、AST 遍历、节点映射与文件操作(如数字前缀排序);
  • actions/:测试 Git 相关操作与动作创建工厂;
  • commands/:测试list-actions的分组展示逻辑。

9.2 E2E 快照测试

e2e 测试(generate-app.test.ts)验证完整的教程动作执行流程,采用快照对比:首次运行把执行器的输出(生成的文件清单、src/testUtils.ts内容、schema.prisma、Git 提交历史等)保存为"快照",后续运行再与快照比对,确保行为没有意外变化。

目录结构约定:

  • 测试夹具:e2e-tests/fixtures/tutorial/(最小化的教程文件);
  • 生成输出:e2e-tests/.result/
  • 快照存储:e2e-tests/__snapshots__/

测试通过WASP_CLI_COMMAND环境变量指定所用的 Wasp CLI(默认wasp-cli),并在比对前把 Prisma 迁移文件名中的时间戳归一化处理,避免时间戳差异导致快照误报。当你有意修改了执行器行为、需要更新快照时,使用更新模式:

npm test -- -u

10. 总结:TACTE 的价值与适用场景

TACTE 把"撰写教程"与"验证教程"合二为一:文档中的每一步都变成可执行的机器指令,Git 提交历史成为动作的"账本",Patch 文件成为动作的可复用载体。从源码实现看,这套设计带来了三个直接收益:

  1. 教程可验证:任何对教程内容的改动都可以通过generate-app立即验证能否生成可运行应用,list-actions帮助快速盘点动作全貌;
  2. 改动可追溯:每个动作一个 commit,edit-patch-action通过分支 + rebase 实现了对历史步骤的安全修改与自动重放;
  3. 人力成本可替代:配合 LLM 的 human-in-the-loop 流程,即使 Patch 大面积失效,也可以半自动地重建整套教程产物。

如果你需要为 Wasp 的教程体系贡献内容或排查生成问题,从 web/tutorial-actions-executor/README.md 出发,结合 src/commands/generate-app/execute-actions.ts 与 src/extract-actions/mdxParsing.ts 两处核心实现,即可快速建立完整的代码心智模型。

【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询