news 2026/10/9 1:46:43

Plasmic WAB(Web Application Builder)全解析:Plasmic Studio 代码库的架构、目录约定与测试体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Plasmic WAB(Web Application Builder)全解析:Plasmic Studio 代码库的架构、目录约定与测试体系
  • 低代码
  • 前端
  • 后端

【免费下载链接】plasmic

Visual builder for React. Build apps, websites, and content. Integrate with your codebase.

项目地址:https://gitcode.com/gh_mirrors/pl/plasmic
点击查看免费下载

导读

platform/wab是 Plasmic 可视化建站平台(Visual builder for React)的核心代码库,wab 即 "web application builder"。本文以 platform/wab/README.md 为主线,结合仓库源码,系统讲解 Plasmic Studio 的模块划分(React 客户端、主应用服务器、代码生成服务器)、client/server/shared/commons四层代码边界、internal/、enterprise/、__testonly__/、__snapshots__/等特殊目录约定,以及以.test.ts/.spec.ts区分、通过_testonly导出测试内部实现的测试体系。读完本文,你将理解如何在这个大型全栈代码库中定位模块、编写与放置测试,并掌握对应的开发脚本与命令。

WAB 是什么:Plasmic Studio 的全部应用功能

根据 platform/wab/README.md 的 Overview 说明,"wab" 代表web application builder,该目录承载了 Plasmic Studio 应用的全部功能,主要分为四大部分:

  1. React 客户端(React client)——Studio 的可视化设计器前端,位于 src/wab/client/;
  2. 主应用服务器(Main app server)——提供 Studio 核心业务能力的后端服务,位于 src/wab/server/;
  3. 代码生成服务器(Codegen server)——负责将可视化产物生成代码的后端,同样位于 src/wab/server/(如codegen-backend.ts、codegen-backend-real.ts);
  4. 大量杂项工具与脚本(Misc tools and scripts)——数据库迁移、模型生成、样式 token 生成等,集中在 tools/ 与src/wab/server/scripts/。

从 package.json 可以看到,该包名为wab(描述为 "Plasmic main codebase"),其依赖横跨前后端:React 18 生态(react、mobx、mobx-react)、服务端框架(express、passport、typeorm、pg)、AI 能力(@ai-sdk/*、openai)、可视化编辑器常用库(slate、monaco-editor、react-beautiful-dnd),并声明engines.node >= 22.12.0。依赖中还包含多个本仓库自研包(@plasmicapp/react-web、@plasmicapp/loader-react、@plasmicpkgs/*),说明 Studio 自身也是 Plasmic 可视化能力的"重度用户"——Studio 的 UI 大量由 Plasmic 自身构建。

注:README 明确提到这是 "shared package.json for client and servers",即客户端与服务端共用一份 package.json,这也是 wab 作为单体仓库子包的一大特点。

目录结构:四层代码边界

README 给出的目录结构示意如下:

. ├── package.json # shared package.json for client and servers │ ├── docs/ # docs for working in this directory │ ├── playwright/ # playwright e2e tests for wab client+server │ ├── src/wab/ │ │ ├── client/ # client-only code (can only be imported by other client code) │ │ ├── commons/ # generic code not specific to Plasmic │ │ ├── server/ # server-only code (can only be imported by other server code) │ │ ├── shared/ # code specific to Plasmic │ │ └── ... # TODO: move other files to one of the above directories │ └── ... └── ...

对照实际仓库,各目录职责如下:

src/wab/client/——仅客户端代码

存放 Studio 可视化编辑器的全部前端逻辑。从 client/ 目录 可以看出其覆盖面:main.tsx(客户端入口)、studio-ctx/(Studio 上下文)、frame-ctx/(画布帧上下文)、components/(UI 组件与 widgets)、state-management/、undo-log.ts(撤销栈)、shortcuts/(快捷键)、clipboard/、figma-importer/(Figma 导入)、web-importer/、copilot/(AI 辅助)、app-auth/(应用内鉴权)等。README 强调这类代码只能被其他客户端代码导入,与服务端隔离。

src/wab/server/——仅服务端代码

存放主应用服务器与代码生成服务器的实现。从 server/ 目录 可见:main.ts(服务端入口)、AppServer.ts、codegen-backend.ts/codegen-backend-real.ts(代码生成服务器)、db/(数据库实体、迁移与脚本)、auth/(Passport 鉴权:Google OAuth、local、Okta 等)、routes/、emails/(邮件模板)、migrations/、workers/、usage/等。同样,这类代码只能被其他服务端代码导入。

src/wab/commons/——与 Plasmic 无关的通用代码

README 将commons/定义为 "generic code not specific to Plasmic"。从 commons/ 目录 可以印证:DataToken.ts、StyleToken.ts、semver.ts、asyncutil.ts、methodForwarder.ts、collections.ts、values.ts等均为通用工具,不依赖 Plasmic 业务模型,可被各层复用。

src/wab/shared/——与 Plasmic 业务相关的共享代码

shared/是"specific to Plasmic"的代码,可被客户端与服务端共同引用,是整个架构中信息密度最高的目录。shared/ 目录 包含:model/(数据模型)、core/(核心模块)、codegen/(代码生成逻辑)、css.ts(样式系统)、variants.ts(变体系统)、bundler.ts(序列化/反序列化)、ApiSchema.ts(前后端 API 契约)、data/与data-sources/(数据绑定)、plume/(组件库元模型)、insertable-templates/等。README 也提示仍有一部分文件尚未归入上述四层之一(即...注释中的 TODO),新代码应尽量归入对应目录。

从源码结构看,client、server、shared、commons的边界既是物理目录划分,也是依赖关系约束:commons 最底层、shared 依赖 commons、client 与 server 各自依赖 shared,这样的分层保证前后端可以安全地共享业务模型与工具,同时避免客户端代码意外泄漏到服务端。

特殊目录约定:internal、enterprise 与测试专用目录

README 指出,代码库各处可能出现以下具有特殊含义的目录:

internal/ # Plasmic-internal-only code that is not synced publicly enterprise/ # Plasmic-internal-and-enterprise code that is not synced publicly __testonly__/ # code only used by tests (see "Testing" below) __snapshots__/ # snapshots written by the test runner (see "Testing" below)
  • internal/:仅 Plasmic 内部使用的代码,不会同步到公开仓库;
  • enterprise/:仅 Plasmic 内部与企业版使用的代码,同样不会同步到公开仓库(公开镜像中这两类目录不可见,属于发布时的过滤规则);
  • __testonly__/:仅供测试使用的代码。README 强调它由 ESLint 规则校验,防止生产代码误引用。在 shared/testonly/ 与 client/testonly/ 等目录中可以看到mocks.ts、fixtures.ts这类测试夹具文件;
  • __snapshots__/:测试运行器写入的快照目录,通常与测试文件一一对应,例如__snapshots__/module.test.ts.snap。仓库中 client/snapshots/ 等目录即为此用途。

测试体系:就近放置 + 单元/集成区分 +_testonly模式

wab 的测试策略可以概括为三句话:测试与代码就近放置、按是否需要外部服务区分测试类型、通过_testonly统一暴露内部实现。

测试目录结构:与代码同目录共存

README 给出的约定是"Colocate tests next to the code they test"(把测试放在被测代码旁边),结构如下:

src/wab/shared/core/ ├── module.ts ├── module.test.ts # unit test, needs nothing outside the process ├── module.spec.ts # integration or e2e test, needs a running service such as Postgres ├── __snapshots__/ # snapshots written by the test runner, one per test file │ └── module.test.ts.snap └── __testonly__/ # code only used by tests, validated by ESLint rule ├── mocks.ts └── fixtures.ts

两种测试文件后缀具有明确的语义差异:

文件后缀类型运行前提
module.test.ts单元测试进程内即可运行,不需要外部服务
module.spec.ts集成 / e2e 测试需要运行中的外部服务(如 Postgres)

这一约定在源码中有大量实例:例如 shared/core/custom-functions.test.ts、shared/eval.test.ts 属于单元测试;而 server/trigger-webhooks.spec.ts 属于需要运行服务的集成测试。

快照与夹具:__snapshots__/与__testonly__/

  • __snapshots__/由测试运行器自动生成与维护,命名规则为被测试文件.ts.snap,与测试文件一一对应;仓库中已存在大量快照文件(如 client/snapshots/ 下的*.snap);
  • __testonly__/存放仅测试使用的mocks.ts、fixtures.ts等,且有 ESLint 规则保证只有测试文件能引用,防止测试专用代码进入生产路径。

用_testonly导出内部实现

当一个测试需要访问模块本不应公开导出的函数或值时,README 推荐的模式是:在模块底部用一个聚合的_testonly导出统一暴露,而不是逐个单独导出:

export function publicFunction() { return internalFunction(); } function internalFunction() { ... } export const _testonly = { internalFunction };

规则要点:

  • 测试通过_testonly间接访问内部实现;
  • 只有测试文件可以引用_testonly,生产代码必须使用模块的真实导出(_testonly引用同样受 ESLint 规则约束)。

这一模式在仓库中已得到实际应用。例如 shared/core/custom-functions.ts 与 shared/code-components/code-components.ts 中都存在_testonly导出,并由custom-functions.test.ts、exprs.test.ts、selection.test.ts、style-props-tpl.test.ts、theme-styles.test.ts、tpls.test.ts、val-nodes.test.ts等测试文件消费。这种"集中导出 + 规则约束"的方式,既避免了为测试而污染公开 API,又让内部函数保持私密。

端到端测试:Playwright 测试套件

除单元/集成测试外,wab 还配备了一套完整的Playwright e2e 测试,用于同时驱动"client + server"全链路,目录位于 playwright/。从 playwright/e2e/ 可见其覆盖范围极广,例如:

  • 编辑器核心操作:components.spec.ts、variants.spec.ts、data-binding.spec.ts、style-sections.spec.ts;
  • 交互与状态管理:interactions-*.spec.ts 系列、state-management-counter.spec.ts;
  • 路由与发布:routing-arenas.spec.ts、routing-branches.spec.ts、publish.spec.ts;
  • 数据源与第三方宿主组件:data-sources/、hostless-*.spec.ts 系列;
  • 多人与协作:comments.spec.ts、multiplayer-cursor.spec.ts。

e2e 运行所需的浏览器在安装阶段自动下载(见 package.json 的prepare脚本:playwright install chromium,可通过环境变量PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD跳过)。

常用开发与测试命令

结合 package.json 的 scripts,日常开发中常用的命令如下:

命令作用
pnpm dev启动完整开发环境(bash tools/dev.bash)
pnpm dev:frontend仅启动前端(端口 3003,等待后端 3004 就绪)
pnpm dev:backend仅启动后端(NODE_ENV=development)
pnpm test运行全部单元/集成测试(Vitest)
pnpm test:coverage运行测试并输出覆盖率
pnpm test:update-snapshots更新快照(vitest run --update)
pnpm typecheckTypeScript 类型检查(增量模式,注意设置了较大的堆内存上限)
pnpm buildrsbuild 前端构建
pnpm db:setup/pnpm db:reset初始化 / 重置开发数据库(docker-dev 脚本)
pnpm seed写入种子数据(DbInit.ts)
pnpm plasmic:sync将 Plasmic 项目同步进仓库(npx plasmic sync)
pnpm storybook启动 Storybook(端口 6006)

几个值得注意的细节:

  • 测试使用Vitest(而非 Jest),类型检查、测试、前端构建分别在tsconfig.main.json、vitest.config.ts、rsbuild.config.ts等独立配置文件中定义;
  • 前端端口约定为 3003,后端为 3004,dev:frontend会先wait-on http://localhost:3004等待后端就绪;
  • run-ts是一个通用入口(bash tools/run.bash),数据库脚本、代码生成脚本等均通过npm run run-ts -- <ts 文件>执行。

配套文档:图标(Icons)规范

README 指向的补充文档是 docs/ICONS.md,其中给出了 Studio 图标体系的完整规范,对在 wab 中新增或使用图标非常重要:

  • 图标来源:Studio 图标是由 Plasmic 生成的 SVG React 组件(不是手写文件),绝大多数来自名为[PlasmicKit] Icons的 Plasmic 项目,落地到src/wab/client/plasmic/plasmic_kit_icons/icons/PlasmicIcon__*.tsx;
  • 新增图标的流程:先检索现有约 600 个图标 → 在 Plasmic 项目里添加 SVG 资源 → 执行pnpm plasmic:sync同步(会生成组件并注册进plasmic.json,两者都要提交)→ 在 client/icons.tsx 中给它一个语义化名字,而不是到处直接 import 生成的PlasmicIcon__*;
  • SVG 约定:viewBox="0 0 24 24"(按 24×24 绘制,默认以 16px 渲染)、fill="none"配合描边绘制、stroke="currentColor"继承文字颜色(严禁硬编码颜色)、stroke-width="1.5"、圆角线帽与连接、尽量用单个<path>、保持 24×24 内边距一致,使图标并排时视觉重量统一。参考示例:PlasmicIcon__Braces.tsx;
  • 使用方式:通过Icon组件渲染(默认 16px 尺寸并应用custom-svg-icon类),可传size覆盖默认尺寸,极少数需要保留自身颜色的图标传monochromeExempt:
import { Icon } from "@/wab/client/components/widgets/Icon"; import BracesIcon from "@/wab/client/plasmic/plasmic_kit_icons/icons/PlasmicIcon__Braces"; <Icon icon={BracesIcon} />;

小结

platform/wab是 Plasmic Studio 的单体核心代码库,其组织方式值得大型全栈应用借鉴:

  • 职责分层的目录边界:client/、server/、shared/、commons/四层,配合internal/、enterprise/的发布过滤,以及__testonly__/、__snapshots__/的测试专属目录;
  • 清晰可判定的测试体系:.test.ts(单元)与.spec.ts(集成)后缀区分运行前提,测试与被测代码就近放置,_testonly聚合导出 + ESLint 规则约束实现"内部可达但生产不泄漏",另有 playwright/e2e/ 覆盖全链路的端到端测试;
  • 完整的工程配套:一份package.json同时管理客户端与服务端脚本,Vitest 测试、rsbuild 构建、数据库脚本、plasmic 同步一应俱全。

无论你是想为 Studio 贡献新功能、补充测试,还是研究一个大型 React + Node 全栈可视化应用如何组织代码,platform/wab都是一个值得通读的参考实现。

  • 低代码
  • 前端
  • 后端

【免费下载链接】plasmic

Visual builder for React. Build apps, websites, and content. Integrate with your codebase.

项目地址:https://gitcode.com/gh_mirrors/pl/plasmic
点击查看免费下载
上一篇:How to GraphQL:GraphQL生态系统贡献指南
下一篇:Review Heatmap自定义教程:5种配色方案+2种日历模式,打造个性化学习看板

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 1:46:36

Pi编程智能体实战:概念厘清、skill导入与subagent编排全解析

不整虚的&#xff0c;直接聊点真东西。最近“pi”这个词热度很诡异&#xff0c;你搜出来一堆结果&#xff0c;有讲树莓派的&#xff0c;有讲圆周率的&#xff0c;还有讲控制器的PI参数的。但真正在开发者圈子里炸开锅的&#xff0c;是那个叫 Pi 的 Agent——也就是大家口中的 p…

作者头像 李华
网站建设 2026/10/9 1:46:14

AI Native团队落地手册:从CLAUDE.md到多Agent编排的完整SDLC实践

1. 从“用AI写代码”到“AI Native团队”&#xff1a;差的不是工具&#xff0c;是整套协作骨架很多团队嘴上说着“我们已经 AI Native 了”&#xff0c;实际干的事无非是给每个人开了个 AI 编程助手的账号&#xff0c;然后继续用三年前那套需求评审、排期、联调、提测的流程。结…

作者头像 李华
网站建设 2026/10/9 1:45:44

Agent Memory架构设计与落地:为大模型打造外挂大脑

做过 Agent 的同学&#xff0c;应该都踩过同一个坑&#xff1a;Agent 明明能理解复杂指令&#xff0c;可你让它处理完跟用户的整个对话流程&#xff0c;或者隔几天再回来看它&#xff0c;发现它什么都不记得了。我也在这上面翻过车&#xff0c;最后发现根子不在模型能力上&…

作者头像 李华
网站建设 2026/10/9 1:45:33

LangGraph 实战教程:从 ReAct Agent 到 StateGraph 状态机,构建复杂决策链

在前四篇文章中,我们构建了能够渲染组件、流式解析、多工具并发和实时搜索的 AI 助手。然而,当面对复杂的多步骤任务时,传统的 while 循环开始显露局限性… 你是否遇到过这样的场景: 用户说:“我要去一个北京现在气温在 15 度以上的公园”AI 需要先搜索气温 → 如果不满足再搜…

作者头像 李华