☰
Dinero.js 开发者指南:Monorepo 命令、路径别名与不可变设计约定
2026/10/9 1:25:58 网站建设 项目流程
  • 金融科技

【免费下载链接】dinero.js

Create, calculate, and format money in JavaScript and TypeScript

项目地址:https://gitcode.com/gh_mirrors/di/dinero.js
点击查看免费下载

导读

本文基于仓库根目录的 CLAUDE.md 展开,系统讲解 Dinero.js 这个 JavaScript/TypeScript 货币库的工程化骨架:npm workspaces + Turborepo 构成的 monorepo 形态、两条核心开发命令(类型检查与体积预算)、贯穿全库的纯函数不可变约定、构建期__DEV__/__TEST__全局替换的 tree-shaking 机制,以及dinero.js、dinero.js/currencies、dinero.js/bigint、test-utils四组路径别名。读完本文,你将能快速理解仓库布局、跑通常规开发与质量检查流程,并能在源码中准确找到类型、计算器、货币定义与共享测试工具的实现位置。

一、仓库形态:npm workspaces + Turborepo 的 Monorepo

CLAUDE.md 第一行即点明了项目的组织方式:"npm workspaces + Turborepo monorepo"。这不是一句空话,在仓库根目录的 package.json 中可以找到完整证据:

  • workspaces声明了三个工作区:packages/*(库本体)、examples/*(React/Vue 示例应用)、docs(VitePress 文档站);
  • packageManager固定为npm@11.18.0;
  • turbo作为 devDependency 引入,配合根目录的 turbo.json 做跨工作区任务编排。

turbo.json 定义了四个可被缓存与编排的任务:build:clean(关闭缓存,用于彻底清理产物)、build(依赖上游构建、以src/**/*.ts与tsdown.config.ts为输入、产出dist/**)、test(依赖src/**/*.ts)和lint;同时把.oxlintrc.json、.prettierrc、.size-limit.json、vitest.config.ts、tsconfig.json列为全局依赖,任一变更都会使相关任务缓存失效。

仓库中真正对外发布的包只有一个:packages/dinero.js/package.json。它声明了engines.node >= 20.0.0、"sideEffects": false(tree-shaking 友好),并通过exports暴露四条子路径(.、./currencies、./bigint、./bigint/currencies),分别对应 ESM 产物dist/esm/下的四个入口。CLAUDE.md 中提到的路径别名,正是这些导出路径在源码与测试层面的映射。

二、常用命令详解

CLAUDE.md 只列出了两条核心命令,但它们在提交与 CI 之前承担着最关键的质量把关。下面结合仓库配置逐一展开。

npm run test:types:全仓类型检查

npm run test:types # Type-check with TypeScript (noEmit)

对应根 package.json 中的"test:types": "tsc -p tsconfig.json --noEmit"。其关键点在 tsconfig.json:

  • noEmit: true——只做类型校验,不产出 JS 文件;
  • strict: true、noImplicitReturns: true、noFallthroughCasesInSwitch: true——保持严格模式;
  • module: "esnext"+moduleResolution: "bundler"——适配现代打包器与 ESM 生态;
  • types: ["vitest/globals"]——让测试文件可以直接使用describe/it/expect全局;
  • exclude掉docs、examples与各类产物目录,确保检查范围聚焦在库源码与测试。

这条命令不仅在 CI 中运行,还被纳入了提交钩子:lint-staged.config.cjs 对packages/**/*.ts的改动文件执行npx tsc -p tsconfig.json --noEmit,也就是说每次git commit都会对改动范围内的 TS 文件做一次增量类型校验。

npm run test:size:体积预算检查

npm run test:size # Check bundle sizes against limits

对应"test:size": "size-limit",由 .size-limit.json 定义预算:

[ { "path": "packages/dinero.js/dist/umd/index.production.js", "limit": "4.5 KB" }, { "path": "packages/dinero.js/dist/umd/bigint/index.production.js", "limit": "4.5 KB" } ]

即默认(number)与 bigint 两个 UMD 生产包的体积上限均为4.5 KB。这条命令必须与npm run build配合:只有先构建出dist/umd/产物,size-limit 才有可测量的对象。这个预算约束与"按需导入、tree-shaking"的库设计目标(见下文)是相互印证的——包体必须小到足以让"只打包你用到的函数"成为现实。

其他值得掌握的配套命令

CLAUDE.md 未列全,但仓库脚本(package.json)中还提供了完整的开发闭环:

命令作用底层实现
npm test运行全部单元测试vitest,测试文件约定为packages/*/src/**/__tests__/**/*.test.ts(见 vitest.config.ts)
npm run build构建所有包turbo build --filter=./packages/*,由 tsdown 执行打包与类型声明生成
npm run lint静态检查oxlint 扫描packages/与test/
npm run format全仓格式化prettier --write .
npm run docs:dev/docs:build/docs:preview本地预览文档站委托给@dinero.js/docs工作区的 vitepress(见 docs/package.json)

三、核心约定一:所有函数纯函数且不可变

CLAUDE.md 强调:"All functions are pure and immutable, never mutate Dinero objects"。这一约定在源码层面有非常直观的体现。

工厂函数每次返回全新对象

Dinero 对象的唯一构造入口是createDinero工厂(packages/dinero.js/src/core/helpers/createDinero.ts)。它每次调用都返回一个全新的对象字面量,且该对象只包含四个只读字段:

  • calculator:当前金额类型对应的计算器;
  • formatter:默认的数值/字符串转换器;
  • create:递归指向工厂本身,供后续 API 生成新对象;
  • toJSON:输出{ amount, currency, scale }快照。

默认(number)入口 packages/dinero.js/src/dinero.ts 通过createDinero注入 number 计算器,并在onCreate钩子中校验amount与scale必须为整数;bigint 入口 packages/dinero.js/src/bigint/dinero.ts 则注入 bigint 计算器,不做整数校验(bigint 天然只有整数值)。

运算函数从不修改入参

以add为例(packages/dinero.js/src/api/add.ts,底层实现在 packages/dinero.js/src/core/api/add.ts):它先断言两个对象货币一致(否则抛出Objects must have the same currency.),再通过normalizeScale统一刻度,最后调用augend.create({ amount, currency, scale })生成一个全新的 Dinero 对象返回——入参augend与addend自始至终未被修改。测试(packages/dinero.js/src/api/tests/add.test.ts)也正是围绕"输入不变、返回新快照"来断言的,例如toSnapshot(add(d1, d2))应等于{ amount: 600, currency: USD, scale: 2 }。

所有公开 API(add、subtract、allocate、multiply、convert、toDecimal等)都通过 packages/dinero.js/src/api/index.ts 统一导出,且逐一遵循同样的纯函数模式。

纯函数约定的工程回报

  • 组合安全:add(d1, d2)之后d1仍可继续参与其他运算,不会产生隐式状态污染;
  • 序列化友好:每个 Dinero 对象都可通过toJSON()得到可复制的快照(DineroSnapshot类型定义在 packages/dinero.js/src/core/types/DineroSnapshot.ts);
  • 摇树友好:sideEffects: false让打包器可以放心删除未被引用的导出。

四、核心约定二:__DEV__/__TEST__构建期替换与 Tree-shaking

CLAUDE.md 提到:"__DEV__and__TEST__globals are replaced at build time for tree-shaking"。仓库中与之相关的配置有三处,构成了完整的"声明—替换—验证"链路:

  1. 类型声明:global.d.ts 用declare const __DEV__: boolean;与declare const __TEST__: boolean;让源码与测试能安全引用这两个全局变量(否则 TypeScript 会报未定义)。

  2. 构建期替换:packages/dinero.js/tsdown.config.ts 针对不同产物给出不同的替换值:

    • ESM 构建(第 36-39 行):__DEV__替换为process.env.NODE_ENV !== 'production',__TEST__替换为process.env.NODE_ENV === 'test'——把判定交给运行时环境变量;
    • UMD 生产包:__DEV__: 'false'、__TEST__: 'false'——常量折叠后,开发/测试分支代码会被直接剔除;
    • UMD 开发包:__DEV__: 'true'、__TEST__: 'false'——保留开发分支、剔除测试分支。
  3. 测试期替换:vitest.config.ts 第 31-34 行把两个全局固定为true,保证测试运行时所有相关分支都被覆盖。

从源码检索结果看,当前src/主体并未直接引用这两个全局变量——它们更像是一套"预留的约定":未来任何仅用于开发/测试的分支逻辑(如更友好的报错信息、调试输出)只要写在if (__DEV__)里,发布构建就会自动将其摇树移除。这正是 4.5 KB 体积预算(见 .size-limit.json)能够长期守住的基础设施。

五、路径别名体系

CLAUDE.md 列出的四条别名在 tsconfig.json 的compilerOptions.paths中定义,且与 vitest.config.ts 的test.alias一一对应,保证IDE、tsc 与 vitest 三套解析体系行为一致:

别名解析目标对应包导出
dinero.jspackages/dinero.js/src/index.ts"."
dinero.js/currenciespackages/dinero.js/src/currencies/index.ts"./currencies"
dinero.js/bigintpackages/dinero.js/src/bigint/index.ts"./bigint"
test-utilstest/utils/index.ts(测试专用)

test-utils是仓库内共享测试工具的统一入口(test/utils/index.ts),它聚合导出:

  • createNumberDinero:基于 number 计算器创建 Dinero(test/utils/createNumberDinero.ts);
  • createBigintDinero:基于 bigint 计算器创建 Dinero(test/utils/createBigintDinero.ts);
  • createBigjsDinero与两个castTo*Currency工具:用于 big.js 与计算器类型转换的测试场景。

实际使用示例可见 packages/dinero.js/src/api/tests/add.test.ts:测试文件用import { createNumberDinero, createBigintDinero, ... } from 'test-utils'分别驱动 number/bigint 两套实现,用import { EUR, USD, MGA, MRU } from '../../currencies'覆盖十进制与非十进制货币(如马达加斯加阿里亚里 MGA、毛里塔尼亚乌吉亚 MRU)。

了解这些别名还有一个实际收益:写文档或示例时,dinero.js/currencies与dinero.js/bigint是官方支持且可摇树的真实导入路径——参考 examples/cart-react/src/lib/money.ts 中import { USD, EUR } from 'dinero.js/currencies'的写法。

六、参考文档:架构、Git 工作流与 Linear 集成

CLAUDE.md 的 References 一节指向了.claude/docs/下的三份文档,它们是理解仓库与参与协作的"说明书":

架构说明

概括了仓库的四个层面:

  • 项目结构:单包packages/dinero.js,src/下按api(公开 API 函数)、bigint(BigInt 入口)、calculator(number/bigint 两套计算器实现)、core(类型、helper、工具)、currencies(ISO 4217 货币导出)、dinero(工厂)划分;
  • 核心概念:Dinero 对象 =amount(最小货币单位)+currency+scale;可插拔的 calculator 模式(默认 number,bigint 提供高精度);纯函数返回新对象;
  • 构建系统:tsdown(基于 Rolldown)统一负责打包与类型声明生成;Turborepo 编排;__DEV__/__TEST__构建期替换。产物为dist/esm/(主入口,含 .d.ts)与dist/umd/(script 标签用);
  • 测试:测试文件统一放在packages/dinero.js/src/**/__tests__/*.test.ts,共享工具经test-utils导入。

其中 calculator 模式值得展开:number 与 bigint 两套计算器(packages/dinero.js/src/calculator/number/calculator.ts 与 packages/dinero.js/src/calculator/bigint/calculator.ts)实现了同一组DineroCalculator接口(add、compare、decrement、integerDivide、modulo、multiply、power、subtract、zero 等,见 packages/dinero.js/src/core/types/DineroCalculator.ts),工厂通过createDinero({ calculator })把对应实现注入 Dinero 对象——这就是"可插拔精度"的实现原理。bigint 的integerDivide直接用原生整除(packages/dinero.js/src/calculator/bigint/api/integerDivide.ts),在超大金额场景下没有浮点精度损失。

Git 工作流

规定了协作纪律:

  • 分支:一律从main切出专用分支(stacked PR 场景则从目标分支切出);
  • PR:正文必须引用要关闭的 issue(如Fixes #123),标题遵循 Conventional Commits 语义化格式type(scope): subject。

Linear 集成

项目使用 Linear 做项目管理(当前项目为Dinero.js v2.0.0 Stable Release)。仓库在 .claude/skills/commit/SKILL.md 提供/commit技能:分析暂存改动、检索关联 Linear issue、自动在 commit message 中附带Fixes SAR-XXX或Part of SAR-XXX。

此外,.claude/skills/下还沉淀了多组开发技能(dinero-best-practices、dinero-currency-patterns、dinero-formatting等),覆盖算术不可变性、从浮点创建、非十进制货币格式化、快照序列化等主题,是理解库的设计意图与常见陷阱的一手资料。

七、新贡献者的最小上手路径

综合以上约定,从零开始参与该仓库的推荐流程是:

  1. 安装依赖:仓库根执行npm install(npm workspaces 会一次性安装packages/*、examples/*、docs三组依赖);
  2. 跑通测试:npm test(vitest 单测)与npm run test:types(全仓类型检查)应全部通过;
  3. 验证构建:npm run build生成dist/esm/与dist/umd/产物,随后npm run test:size确认两个 UMD 生产包不超 4.5 KB;
  4. 遵守提交纪律:从main切出专用分支,PR 标题使用type(scope): subject语义化格式并引用关联 issue,提交时 lint-staged 会自动执行增量类型检查与格式化(lint-staged.config.cjs);
  5. 查阅文档:改动机器可进一步阅读 架构说明,理解api → core的分层调用链(公开 API 只做参数转发,真正逻辑在core/中实现),避免破坏纯函数与不可变约定。

理解这份 CLAUDE.md,就等于拿到了进入 Dinero.js 代码库的第一把钥匙:命令告诉你如何验证,约定告诉你如何实现,别名告诉你去哪里找代码,参考文档则告诉你整个仓库为什么长成这样。

  • 金融科技

【免费下载链接】dinero.js

Create, calculate, and format money in JavaScript and TypeScript

项目地址:https://gitcode.com/gh_mirrors/di/dinero.js
点击查看免费下载

相关推荐

上一篇:Ant Design Blazor 技术文档
下一篇:热门项目推荐:llm-cookbook - 大模型开发者的中文实践指南

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

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

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

立即咨询