- 金融科技
【免费下载链接】dinero.js
Create, calculate, and format money in JavaScript and TypeScript
导读
本文基于仓库根目录的 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"。仓库中与之相关的配置有三处,构成了完整的"声明—替换—验证"链路:
类型声明:global.d.ts 用
declare const __DEV__: boolean;与declare const __TEST__: boolean;让源码与测试能安全引用这两个全局变量(否则 TypeScript 会报未定义)。构建期替换: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'——保留开发分支、剔除测试分支。
- ESM 构建(第 36-39 行):
测试期替换: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.js | packages/dinero.js/src/index.ts | "." |
dinero.js/currencies | packages/dinero.js/src/currencies/index.ts | "./currencies" |
dinero.js/bigint | packages/dinero.js/src/bigint/index.ts | "./bigint" |
test-utils | test/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等),覆盖算术不可变性、从浮点创建、非十进制货币格式化、快照序列化等主题,是理解库的设计意图与常见陷阱的一手资料。
七、新贡献者的最小上手路径
综合以上约定,从零开始参与该仓库的推荐流程是:
- 安装依赖:仓库根执行
npm install(npm workspaces 会一次性安装packages/*、examples/*、docs三组依赖); - 跑通测试:
npm test(vitest 单测)与npm run test:types(全仓类型检查)应全部通过; - 验证构建:
npm run build生成dist/esm/与dist/umd/产物,随后npm run test:size确认两个 UMD 生产包不超 4.5 KB; - 遵守提交纪律:从
main切出专用分支,PR 标题使用type(scope): subject语义化格式并引用关联 issue,提交时 lint-staged 会自动执行增量类型检查与格式化(lint-staged.config.cjs); - 查阅文档:改动机器可进一步阅读 架构说明,理解
api → core的分层调用链(公开 API 只做参数转发,真正逻辑在core/中实现),避免破坏纯函数与不可变约定。
理解这份 CLAUDE.md,就等于拿到了进入 Dinero.js 代码库的第一把钥匙:命令告诉你如何验证,约定告诉你如何实现,别名告诉你去哪里找代码,参考文档则告诉你整个仓库为什么长成这样。
- 金融科技
【免费下载链接】dinero.js
Create, calculate, and format money in JavaScript and TypeScript
相关推荐
Qwen2.5-14B-Instruct技术选型指南:企业级大语言模型架构评估与部署策略
Qwen2.5 14B Instruct技术选型指南:企业级大语言模型架构评估与部署策略 在人工智能技术快速发展的今天,Qwen2.5 14B Instruct
Apache Arrow C++ API 设计约定:命名空间、智能指针、不可变性与 Status/Result 错误处理实战指南
Apache Arrow C++ API 设计约定:命名空间、智能指针、不可变性与 Status/Result 错误处理实战指南 Apache Arrow 的
数据工程大数据序列化数据分析免费微信聊天记录导出指南:3 条命令,10 分钟把聊天搬进自己电脑
免费微信聊天记录导出指南:3 条命令,10 分钟把聊天搬进自己电脑 换电话前一晚,你想把妈妈在群里发的那串银行卡号存下来。你在微信里翻了对话、按关键词搜索,还是
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考