- 低代码
- 前端
- 后端
【免费下载链接】plasmic
Visual builder for React. Build apps, websites, and content. Integrate with your codebase.
导读
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 应用的全部功能,主要分为四大部分:
- React 客户端(React client)——Studio 的可视化设计器前端,位于 src/wab/client/;
- 主应用服务器(Main app server)——提供 Studio 核心业务能力的后端服务,位于 src/wab/server/;
- 代码生成服务器(Codegen server)——负责将可视化产物生成代码的后端,同样位于 src/wab/server/(如
codegen-backend.ts、codegen-backend-real.ts); - 大量杂项工具与脚本(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 typecheck | TypeScript 类型检查(增量模式,注意设置了较大的堆内存上限) |
pnpm build | rsbuild 前端构建 |
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.
相关推荐
在 Plasmic Studio 中可视化拖拽 Contentful 数据:Contentful + Plasmic 数据获取代码组件实战
在 Plasmic Studio 中可视化拖拽 Contentful 数据:Contentful + Plasmic 数据获取代码组件实战 本指南基于仓库中的
低代码前端后端Plasmic 与 react-dnd:在 Next.js 中注册拖拽代码组件(react-dnd example with Plasmic)
Plasmic 与 react dnd:在 Next.js 中注册拖拽代码组件(react dnd example with Plasmic) 本文基于仓库中
低代码前端后端Plasmic × Split.io A/B 测试集成实战:用 Feature Flag 为 Plasmic 页面注入实验变体
Plasmic × Split.io A/B 测试集成实战:用 Feature Flag 为 Plasmic 页面注入实验变体 本文基于仓库中 examples
低代码前端后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考