
Bitwarden clients 客户端代码规范实战libs/common 跨客户端架构下的 Angular 与 TypeScript 编写指南【免费下载链接】clientsBitwarden client apps (web, browser extension, desktop, and cli).项目地址: https://gitcode.com/GitHub_Trending/cl/clients本指南基于仓库中 writing-client-code 技能文档系统梳理 Bitwarden 多客户端web、browser、desktop、cli统一代码库中的 Angular 与 TypeScript 编写约定。你将掌握libs/common为何不能依赖 Angular、薄组件与组合优于继承的架构原则、新代码必须遵守的硬性规则以及 const 对象替代枚举、tw-前缀等可直接落地的编码范式从而在改动或新增客户端代码时保持与现有代码库一致的风格与边界。一、文档定位与适用场景writing-client-code是仓库 .claude/skills 目录下的一组 AI 辅助技能之一其 frontmatter 明确声明了使用场景Bitwarden client code conventions for Angular and TypeScript. Use when creating components, services, or modifying web/browser/desktop apps.也就是说只要你在创建组件、服务或修改 web / browser / desktop 三个客户端应用时这份规范就是必须遵守的编码基线。它同时兼顾了命令行客户端 cli 的约束——因为 cli 与其余客户端共享libs/common的领域逻辑却无法运行 Angular。二、架构基石为什么libs/common不能依赖 Angular文档的第一句话点出了整个规范体系的出发点CLI is a first-class client. Any code inlibs/commonmust work without Angulars dependency injection, decorators, or lifecycle hooks.Bitwarden 客户端体系是一个多客户端单仓库monorepo从仓库根目录的 package.json 与 angular.json 可以看出它同时构建 web、browser 扩展、desktopElectron与 CLI 四个客户端。其中 CLI 是一等公民客户端它运行在纯 Node 环境中没有 Angular 运行时。由此推导出两条关键约束libs/common中的任何代码都不能使用 Angular 的依赖注入、装饰器或生命周期钩子——否则一旦被 CLI 引入将直接破坏构建与运行跨客户端的服务以抽象类作为接口abstract classes as interfaces具体实现Default*、Web*、Browser*、Desktop*、Cli*命名分别落在各自的应用目录中。这种接口在 common、实现在客户端的模式让各端可以注入自己的平台能力同时共享同一份类型契约。作为补充文档在新代码硬性规则中进一步强调从bitwarden/common导入的内容不得引入任何 Angular 专属代码。这既是一条导入纪律也是上述架构约束的可执行化表达。三、架构原则Architectural Rationale1. 薄组件Thin components组件只承载视图逻辑业务逻辑必须下沉到服务service中。这样做的好处是组件保持可测试性——视图层可以被 Jest 等工具单独验证组件可复用——不因内嵌业务规则而难以被其他页面使用避免 Angular 生命周期与领域逻辑耦合——业务逻辑不依赖ngOnInit、ngOnDestroy等生命周期钩子天然可移植。2. 组合优于继承Composition over inheritance文档明确禁止跨客户端继承组件应当使用共享子组件进行组合。原因是继承会在客户端专属 UI 与共享行为之间制造紧耦合当某个客户端的诉求发生分叉时被继承的组件将难以安全演进。这与仓库中大量可复用的共享组件库libs/components的设计哲学一脉相承——通过组合共享组件而不是复制或继承。3. 不要主动现代化既有代码Dont modernize existing code unless asked代码库中同时存在遗留与现代两种 Angular 写法。修改已有文件时遵循该文件已有的模式除非被明确要求否则不得迁移以下语法*ngIf→if、*ngFor→forInput()/Output()→input()/output()信号构造函数注入 →inject()默认变更检测 →OnPushNgModule 声明 → standalone 组件若确实被要求现代化则须按照 Angular 官方迁移指南的顺序执行standalone → 控制流语法 → input/output 信号 → view queries → signals → computed → OnPush最后且仅在信号迁移全部完成后。这个顺序保证了每一步迁移都建立在前一步的基础之上降低回归风险。4. 状态管理Signals 与 RxJS 的分工文档给出了明确的选型边界场景方案原因组件局部状态、仅限 Angular 的服务Signals轻量、响应式、模板友好跨客户端服务libs/commonRxJSCLI 不支持 Angular Signals在订阅管理上规范要求避免手动订阅优先使用| async管道当确有必要手动订阅时必须经takeUntilDestroyed()管道处理这一条由prefer-takeUntillint 规则强制保证。从仓库的 lint 配置eslint.config.mjs与 .claude/rules/angular.md 中可以进一步看到该团队对响应式编程纪律的重视。5. 禁用 TypeScript 枚举ADR-0025文档规定使用Object.freeze()as const的冻结常量对象并配合同名类型别名companion type alias。理由是枚举具有运行时行为会在 tree-shaking摇树优化时引发隐蔽的 bug。四、新代码硬性规则Critical Rules for New Code以下规则严格适用于新文件和新建组件对于既有代码继续遵循文件内已有模式。规则要求变更检测与模块新组件必须使用ChangeDetectionStrategy.OnPush且standalone: trueNgModule 仅允许用于聚合相关的 standalone 组件依赖注入Angular 原语组件、管道、指令优先使用inject()函数与 CLI 等非 Angular 客户端共享的代码使用构造函数注入模板控制流新模板必须使用控制流语法if、for、switch而非结构性指令宿主绑定在组件装饰器中使用host属性不用HostBinding/HostListener表单仅使用响应式表单Reactive Forms禁用模板驱动表单文件命名kebab-case.component.ts、.service.ts、.pipe.ts、.directive.ts模型类使用.request.ts、.response.ts、.view.ts、.data.ts后缀ADR-0012样式类名所有 Tailwind 类必须带tw-前缀如tw-flex、tw-mt-2而非flex、mt-2测试使用 Jest以jest-mock-extended模拟服务用describe/it块不用test()导入边界从bitwarden/common导入不得夹带 Angular 专属代码会破坏 CLI关于tw-前缀的底层依据tw-前缀并非凭空约定它与仓库的 Tailwind 构建配置强相关。仓库根目录的 tailwind.config.js 汇总了共享库与各客户端的配置其中明确Safelist is required for dynamic color classes... Tailwinds JIT compiler cannot detect dynamically constructed class names liketw-bg-${name}...config.safelist [{ pattern: /tw-bg-(.*)/ }]也就是说Tailwind 的 JIT 编译器按tw-前缀识别并生成工具类不带前缀的类名如flex不会被编译进产物——这正是文档中WRONG — missing tw- prefix, will be stripped缺少前缀的类会被剥离注释的工程含义。共享库的样式基线位于 libs/components/tailwind.config.base.js各客户端各自维护 apps/web/tailwind.config.js、apps/browser/tailwind.config.js、apps/desktop/tailwind.config.js。五、代码示例详解1. 依赖注入inject()与构造函数注入的双轨制// CORRECT — inject() for Angular primitives export class VaultComponent { private vaultService inject(VaultService); } // ALSO CORRECT — constructor injection for code shared with CLI export class CryptoService { constructor(private stateService: StateService) {} }判断标准只有一个这段代码是否会被非 Angular 客户端CLI使用。Angular 原语天然运行在 Angular 应用内可用函数式inject()获得更简洁的字段声明而CryptoService这类跨客户端服务运行在 CLI 中必须使用构造函数注入以保证不依赖 Angular 的 DI 容器。2. Tailwindtw-前缀!-- CORRECT -- div classtw-flex tw-gap-2 tw-mt-4 !-- WRONG — missing tw- prefix, will be stripped -- div classflex gap-2 mt-4/div /div结合上文 Tailwind 配置可知正确的写法会被 JIT 编译器识别并生成对应工具类错误的写法在构建产物中被剥离样式直接丢失且难以排查。3. 用 const 对象替代枚举ADR-0025 的仓库实证文档给出了标准写法// CORRECT — with companion type alias export const CipherType Object.freeze({ Login: 1, SecureNote: 2, } as const); export type CipherType (typeof CipherType)[keyof typeof CipherType]; // WRONG — TypeScript enums have runtime side effects export enum CipherType { Login 1, SecureNote 2, }这一模式在仓库中有大量落地实例。以 libs/common/src/auth/enums/two-factor-provider-type.ts 为例export const TwoFactorProviderType Object.freeze({ Authenticator: 0, Email: 1, Duo: 2, Yubikey: 3, // U2f: 4, - deprecated in favor of WebAuthn Remember: 5, OrganizationDuo: 6, WebAuthn: 7, RecoveryCode: 8, } as const); export type TwoFactorProviderType (typeof TwoFactorProviderType)[keyof typeof TwoFactorProviderType];该文件还配套了一个类型守卫函数用于校验来自 CLI 参数或 API 响应的不可信输入export function isTwoFactorProviderType(value: unknown): value is TwoFactorProviderType { return (Object.values(TwoFactorProviderType) as number[]).includes(value as number); }对应的单元测试 two-factor-provider-type.spec.ts 用it.each参数化用例验证了三类边界所有合法值返回true含0这类 falsy 值、非法数值-1、4、9、100、NaN、Infinity、1.5返回false、非数值类型null、undefined、字符串、布尔、对象、数组返回false。这套冻结常量对象 类型别名 类型守卫 参数化测试的组合正是文档所提倡范式的完整工程化样板。类似的模式还可见于 integration-type.enum.ts 等数十处文件尽管命名为*.enum.ts实现却是 const 对象。六、从技能文档到编码纪律配套规则与测试基线writing-client-code只是 Bitwarden 编码规范体系中的一份技能文档仓库还提供了可直接查阅的配套规则文件.claude/rules/angular.md——Angular 编码约定细化.claude/rules/typescript.md——TypeScript 风格基线.claude/rules/angular-components.md——组件编写细则.claude/rules/tailwind.md——Tailwind 样式约定.claude/rules/testing.md——Jest 测试规范.claude/rules/i18n.md——国际化约束配合仓库根目录的 lint 配置eslint.config.mjs与各项目独立的 Jest 配置如 libs/common/jest.config.js这些规则共同构成了可被 CI 强制执行的编码边界。对于想要深入理解整体结构的读者仓库的 README.md 提供了各客户端与共享库的模块总览而 libs/common/src 下的抽象服务与 apps 各客户端中的Default*/Web*/Browser*/Desktop*/Cli*实现则是理解抽象类即接口这一核心架构的最佳阅读入口。结语总而言之writing-client-code规范的核心可以浓缩为一句话共享逻辑在libs/common中保持框架无关客户端视图保持薄而组合化新代码遵循现代 Angular 范式且所有改动都不越界侵入既有模式。遵循这份指南你将能够在 web、browser、desktop、cli 四个客户端之间写出风格统一、边界清晰、可测试、可维护的代码。【免费下载链接】clientsBitwarden client apps (web, browser extension, desktop, and cli).项目地址: https://gitcode.com/GitHub_Trending/cl/clients创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考