Polar 客户端 Monorepo 完全指南:基于 Turborepo 的 TypeScript 前端工作区架构与构建实践
2026/9/16 22:08:13 网站建设 项目流程

Polar 客户端 Monorepo 完全指南:基于 Turborepo 的 TypeScript 前端工作区架构与构建实践

【免费下载链接】polarPolar — A billing platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/po/polar

导读

本文面向希望理解 Polar 前端工程化架构的开发者,系统讲解仓库根目录下clients/这一基于 Turborepo 的 TypeScript Monorepo:它的应用与包划分(Web、移动端、设计系统、内部 API 客户端)、pnpm workspace 与 catalog 依赖管理、构建/开发/测试的 Turbo 任务编排,以及如何从 OpenAPI 规范一键生成内部 API 客户端。读完本文,你将掌握 Polar 客户端代码库的整体布局、每一条核心命令的实际作用与底层实现,并能在自己的多包项目中复刻这套工程实践。

一、Polar Clients:一个 100% TypeScript 的 Turborepo 工作区

clients/目录是 Polar 项目的前端与客户端代码集合,其官方定位记录在 clients/README.md 中:它是一个基于 Turborepo 的 Monorepo,所有应用与包均由 100% TypeScript 编写。这里的“客户端”是一个广义概念,覆盖从浏览器 Web 应用到 iOS/Android 原生 App、设计系统、共享 UI 组件、内部 API 客户端以及面向各大框架(Next.js、Nuxt、TanStack Start、Better Auth)的适配器。

从目录结构看,clients/下包含三类核心实体(见 clients/ 目录列表):

  • apps/:三个可独立运行的应用;
  • packages/:被应用消费的共享包;
  • adapters/:面向外部框架/生态的集成适配器;
  • 以及scripts/(codemod 与构建测量脚本)、patches/(依赖补丁)、codemods/(p-span-to-text 等迁移脚本)。

应用与包一览

根据 clients/README.md 的 “Apps and Packages” 一节,工作区包含:

实体类型技术栈定位
apps/web应用Next.jspolar.sh 官网与 Web 主站
apps/app应用Expo + React NativeiOS 与 Android 应用
apps/orbit应用Next.jsOrbit 设计系统的文档与组件展示台
packages/uiReact + Tailwind共享 UI 资源
packages/clientopenapi-fetch由 OpenAPI 规范生成的内部 API 客户端
packages/orbitReact + StyleXPolar 设计系统(组件与设计令牌)

值得说明的是,README 中列出的六个实体只是工作区的“门面”。从 clients/pnpm-workspace.yaml 可以看到,pnpm 工作区实际还纳入了adapters/*(Better Auth、Next.js、Nuxt、TanStack Start 等适配器)、examples/*scripts/*,即“Monorepo 边界”比 README 列举的更广。

二、依赖管理:pnpm workspace + catalog 与 only-allow 强制约束

2.1 workspace 范围

clients/pnpm-workspace.yaml 声明了工作区包含的目录:

packages: - apps/* - adapters/* - packages/* - examples/* - scripts/*

2.2 catalog:跨包统一版本

该文件还大量使用了 pnpm 的catalog 特性catalog:依赖引用),例如@radix-ui/react-dialog: '^1.1.15'tailwindcss: '^4.2.2'vitest: '^5.0.0'等被统一收口。随后各包在package.json中通过"@radix-ui/react-dialog": "catalog:"的方式引用同一版本,例如 clients/apps/web/package.json 与 clients/packages/ui/package.json。这种做法让整个工作区对 React 19、Tailwind 4、Radix UI、Vitest 等基础依赖的版本保持单一事实来源,避免多包版本漂移。

2.3 强制 pnpm 与版本固定

根 clients/package.json 中:

"packageManager": "pnpm@10.29.3", "scripts": { "preinstall": "npx only-allow pnpm" }

preinstall通过only-allow强制只能使用 pnpm 安装;packageManager字段则让 Corepack 自动切换到指定版本。此外还通过pnpm.overrides统一覆盖@types/react@types/react-domnuxi的版本,并通过pnpm.patchedDependenciesopenapi-typescript@7.10.1应用 clients/patches/openapi-typescript@7.10.1.patch 补丁(后文详述)。

onlyBuiltDependencies白名单允许esbuildsharp@tailwindcss/oxide等执行安装脚本,minimumReleaseAge: 1440则要求依赖发布满 24 小时才可被安装(@polar-sh/sdk除外),这在供应链安全上提供了额外防线。

三、从零开始:安装、构建、开发与 API 客户端生成

clients/README.md 给出了四条核心命令,下面逐一讲解其真实行为与适用场景。

3.1 安装依赖

pnpm install

在仓库根目录执行后,pnpm 会依据pnpm-workspace.yaml解析全部工作区包,并创建统一的node_modules链接。安装完成后,根package.jsonprepare钩子会自动执行:

"prepare": "pnpm --filter polar-cli exec effect-language-service patch && turbo run build --filter='./packages/*'"

即先为polar-cli打上 effect-language-service 补丁,再通过 Turborepo 预构建所有packages/*(含@polar-sh/client@polar-sh/orbit等),保证首次pnpm dev时各包产物已就绪。

3.2 构建全部应用与包

pnpm build

该命令等价于turbo run build(见 clients/package.json),由 clients/turbo.json 定义任务语义:

"build": { "dependsOn": ["^build"], "outputs": [".next/**", "!.next/cache/**", "dist/**"] }
  • dependsOn: ["^build"]:先构建所有依赖包,再构建上层应用,保证@polar-sh/client等产物在 Web 应用编译前可用;
  • outputs:缓存.next/**dist/**(排除.next/cache),实现增量构建。

各应用构建入口分别为:apps/web使用next build --turbopack(Next.js 16 + Turbopack),apps/orbit同样使用 Turbopack 并在 3333 端口运行,apps/app则使用eas build构建 iOS/Android 包(对应 clients/apps/app/package.json 中的build:production/build:development/build:simulator三个 EAS profile)。

3.3 开发模式

pnpm dev

根脚本定义为turbo watch dev,即 Turborepo 的 watch 模式:依赖变更会自动触发下游应用热更新。turbo.jsondev任务dependsOn: ["^build"]cache: falsepersistent: true,保证 watch 进程常驻且不做无效缓存。如果只想启动 Web 应用,可以运行:

pnpm dev-web # 等价于 turbo run dev --filter=web

3.4 从 OpenAPI 规范生成 API 客户端

pnpm generate

这是 README 中最具 Polar 特色的命令。根脚本为pnpm -C packages/client generate,即进入 clients/packages/client/package.json,执行:

"generate": "uv run --directory ../../../server/ -m scripts.generate_openapi | openapi-typescript --enum-values -o ./src/v1.ts && oxfmt ./src && pnpm run build"

整条流水线可以拆解为三步:

  1. 生成 OpenAPI 规范:使用uvserver/目录中运行 server/scripts/generate_openapi.py,把 Polar 后端(FastAPI)路由实时导出为标准 OpenAPI 文档并输出到 stdout;
  2. 转换为 TypeScript 类型:通过管道交给openapi-typescript --enum-values,生成 clients/packages/client/src/v1.ts(将枚举导出为 TS enum,而非字面量联合类型);
  3. 格式化与构建oxfmt ./src统一格式化,随后tsup打包出dist/

openapi-typescript@7.10.1之所以需要补丁,是因为项目对 enum 生成的--enum-values行为做了定制,补丁内容记录在 clients/patches/openapi-typescript@7.10.1.patch。

生成的客户端包@polar-sh/client还包含 clients/packages/client/src/enums.ts(运行时枚举)与 clients/packages/client/src/metrics.ts(指标辅助),并以openapi-fetch作为 HTTP 层(见其 dependencies)。因此API 类型与服务端永远保持同步,后端接口变化后只需重新执行pnpm generate

四、应用层解析:Web、移动端与设计系统展示台

4.1 apps/web:polar.sh 主站

clients/apps/web/package.json 表明这是一个 Next.js 16(next ^16.3.1)+ React 19.2 应用,Node 版本被严格锁定为engines.node: "=24"。它的技术栈极具代表性:

  • AI 能力@ai-sdk/*(openai/anthropic/google/mcp/react)与@posthog/ai,说明 Web 端深度集成了 AI SDK;
  • 支付@stripe/react-stripe-js@stripe/stripe-js
  • 样式体系:Tailwind 4 + StyleX(@stylexjs/stylex)+next-themes主题切换;
  • 文档渲染@next/mdx+@mdx-js/*+shiki代码高亮;
  • 数据与图表@tanstack/react-query@tanstack/react-tablerecharts

Web 应用大量消费工作区内部包:@polar-sh/checkout(结账流程)、@polar-sh/client@polar-sh/currency@polar-sh/i18n@polar-sh/orbit@polar-sh/ui。测试体系为 Vitest(单测)与 Playwright(e2e,见test:e2e脚本)。

4.2 apps/app:Expo 移动端

clients/apps/app/package.json 中,@polar-sh/app使用 Expo SDK 54 + React Native 0.81.5 +expo-router6,入口为expo-router/entry。值得注意的工程细节:

  • 样式nativewind(Tailwind for RN)+@shopify/restyle
  • 图表victory-native+@shopify/react-native-skia
  • 推送与安全expo-notificationsexpo-secure-store、Sentry(@sentry/react-native);
  • OTA 更新ota脚本运行tooling/ota-preflight,通过expo-updates做热更新预检;
  • iOS 小组件prewidget脚本利用@bacons/apple-targets的 prebuild 模板生成 Apple 小组件 target(对应apps/app/targets/下的 Swift 文件);
  • postinstall:自动构建@polar-sh/client并运行patch-package
  • EAS 构建兼容eas-build-pre-install脚本会在 CI 环境中重建缺少adapters/的迷你pnpm-workspace.yaml

4.3 apps/orbit:设计系统展示台

clients/apps/orbit/package.json 中的orbit-playground是 Orbit 设计系统的文档与组件 showcase,基于 Next.js 16 + Turbopack,固定运行在 3333 端口。它直接引用@polar-sh/orbit@polar-sh/ui,并内置gen:props脚本(scripts/extract-props.mjs)用 ts-morph 从组件源码提取 props 元数据用于文档渲染。

五、共享包:UI、设计系统与内部 API 客户端

5.1 packages/ui:私有共享组件库

clients/packages/ui/package.json 明确标注“This is a private library for the Polar project”,其定位是 Polar 项目内部的共享 UI 资源,不建议外部项目直接使用。它基于 Radix UI(dialog、dropdown-menu、popover、select、tooltip、tabs、toast 等一整套无头组件)+cva(class-variance-authority)+recharts+react-hook-form,并依赖@polar-sh/currency@polar-sh/orbit。通过exports: { "./*": ... }支持按子路径按需导入。

5.2 packages/orbit:Polar 设计系统

clients/packages/orbit/package.json 中的@polar-sh/orbit是 Polar 的设计系统,包含组件与设计令牌(design tokens):

  • 设计令牌./theme导出指向 clients/packages/orbit/src/tokens/tokens.stylex.ts,基于 StyleX 变量实现主题令牌,支持亮/暗主题;
  • 组件:基于 Radix UI 无头组件(checkbox、select、switch、tabs、tooltip)+@tanstack/react-table(数据表格)+motion(动画)+lucide-react(图标);
  • 样式方案:StyleX(@stylexjs/stylex)编译期 CSS-in-JS,而非 Tailwind,这与apps/web中 Tailwind + StyleX 并存的策略相互印证;
  • 测试vitest run,配置见 clients/packages/orbit/vitest.config.ts。

5.3 packages/client:OpenAPI 生成的内部 API 客户端

作为 “Internal API Client”,@polar-sh/client(见 clients/packages/client/package.json)是 Polar 前后端契约自动化的枢纽:

  • 源码由pnpm generate从服务端 OpenAPI 规范自动生成(src/v1.ts),并附带手写的src/enums.tssrc/metrics.tssrc/index.ts
  • HTTP 层使用openapi-fetch,类型系统基于openapi-typescript-helpers
  • 同时服务 Web 应用与移动端(apps/app的 postinstall 也会构建它)。

六、适配器生态:面向主流框架的集成层

虽然 README 未在 “Apps and Packages” 中罗列,但adapters/是工作区的重要组成(见 clients/pnpm-workspace.yaml 的adapters/*):

适配器定位
adapters/better-auth将 Polar 集成进 Better Auth 认证体系(含 example 应用)
adapters/nextjsNext.js 专用集成工具
adapters/nuxtNuxt 集成(含 playground 与 test)
adapters/tanstack-startTanStack Start 集成
adapters/adapter-utils适配器共享工具函数

它们与packages/checkoutpackages/clipackages/i18n等同属工作区,但各自独立发布(release-packages脚本会构建./adapters/*后通过 changeset 发布)。

七、质量与工程化:lint、类型检查、测试与发布

7.1 根级质量命令

clients/package.json 提供一整套开箱即用的质量命令:

pnpm lint # oxlint . pnpm lint:fix # oxlint --fix . pnpm typecheck # turbo run typecheck pnpm test # turbo run test pnpm format # oxfmt pnpm format:check # oxfmt --check pnpm knip # knip(查找未使用文件与依赖)

值得注意的是,Polar 前端使用Oxc 生态oxlint+oxfmt+oxlint-tsgolint)而非 ESLint/Prettier,且typechecktest任务同样由 Turborepo 编排(typecheck依赖^build)。knip.json用于检测未使用的导出与依赖。

7.2 发布流程

pnpm release-packages

等价于先对packages/*adapters/*(排除polar-cli)执行turbo run build test,再对其余包执行 lint,最后通过changeset publish发布到 npm。根目录还引入了@changesets/cli@manypkg/cli管理多包版本与依赖一致性检查(manypkg.ignoredRules豁免了INTERNAL_MISMATCH)。

7.3 环境变量治理

clients/turbo.json 的globalEnv集中声明了影响构建的环境变量,包括:

  • 后端地址类:POLAR_API_URLNEXT_PUBLIC_API_URLEXPO_PUBLIC_POLAR_SERVER_URL
  • 第三方服务类:NEXT_PUBLIC_STRIPE_KEYNEXT_PUBLIC_POSTHOG_TOKENNEXT_PUBLIC_SENTRY_DSNSENTRY_AUTH_TOKEN
  • 预览/CI 类:VERCEL_*CIPOLAR_PREVIEW_*POLAR_PREVIEW_ACCESS_TOKEN
  • 图片/上传类:S3_PUBLIC_IMAGES_BUCKET_*S3_UPLOAD_ORIGINS
  • AI/MCP 类:PYDANTIC_AI_GATEWAY_API_KEYMCP_OAUTH2_CLIENT_ID/SECRETATTIO_API_KEY

这些变量一旦变化,Turborepo 会使依赖它们的任务缓存失效,确保环境切换后产物不被错误复用。

八、从源码看 Turborepo 任务依赖图

结合 clients/turbo.json 与各包脚本,可以还原出 Polar 客户端的任务依赖拓扑:

pnpm install └─ prepare: turbo run build --filter='./packages/*' pnpm dev(turbo watch dev) └─ dev: dependsOn [^build], cache=false, persistent=true pnpm build(turbo run build) └─ build: dependsOn [^build], outputs [.next/**, dist/**] ├─ @polar-sh/orbit → @polar-sh/ui → apps/web ├─ @polar-sh/client → apps/web / apps/app └─ @polar-sh/orbit + @polar-sh/ui → apps/orbit pnpm generate └─ server/scripts/generate_openapi.py → openapi-typescript --enum-values → src/v1.ts → oxfmt → tsup → dist/

从依赖关系可以推断:@polar-sh/client是前后端唯一的契约通道,@polar-sh/orbit@polar-sh/ui是所有 UI 消费方的公共底座,这种“契约生成 + 分层共享”的结构让三个应用共享同一套 API 类型与设计语言,同时保持各自独立演进。

九、实践要点小结

  1. 版本一致性:所有工作区包通过workspace:*相互引用,公共三方依赖通过 pnpm catalog 统一版本,覆盖层(overrides)兜底;
  2. 构建顺序自动化dependsOn: ["^build"]保证依赖先于消费者构建,outputs声明让 Turborepo 高效缓存;
  3. API 契约自动化pnpm generate把“后端 OpenAPI → TS 类型 → 打包产物”全链路打通,接口变更零手工维护;
  4. 多端复用:Web(Next.js)、移动端(Expo)、设计系统 showcase(Orbit)共享@polar-sh/client@polar-sh/orbit@polar-sh/ui,并通过adapters/向外部框架输出集成能力;
  5. 工程护栏only-allow pnpm、Node 版本锁定(=24)、minimumReleaseAge、补丁依赖(patchedDependencies)共同保障了工作区的可复现性与供应链安全。

如果你正在规划自己的多包前端工作区,Polar 的这套“Turborepo + pnpm catalog + OpenAPI 客户端生成 + 设计系统共享”组合是一个可以直接借鉴的完整样本:从 clients/README.md 出发,对照 clients/package.json 与 clients/turbo.json 即可复刻其全部任务编排;而 clients/packages/client/package.json 中的generate脚本则展示了如何让前后端契约成为可自动化的工程流水线。

【免费下载链接】polarPolar — A billing platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/po/polar

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

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

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

立即咨询