ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Hardhat 仓库开发协作指南:从命令工作流到源码架构规范的完整解读

Hardhat 仓库开发协作指南:从命令工作流到源码架构规范的完整解读 Hardhat 仓库开发协作指南从命令工作流到源码架构规范的完整解读【免费下载链接】hardhatHardhat is a development environment to compile, deploy, test, and debug your Ethereum software.项目地址: https://gitcode.com/GitHub_Trending/ha/hardhat本指南基于 Hardhat 仓库根目录的 AGENTS.md 展开系统解读该开源项目Hardhat 是以太坊智能合约编译、部署、测试与调试的开发环境仓库采用 pnpm workspace 多包结构面向开发者与 AI 编码 Agent 的协作约定包括最常用的单文件 lint / 测试 / 拼写检查命令、仓库目录结构、包结构约定、hardhat-utils 优先与错误处理规范、Hardhat 3 动态导入await import的六条判定标准、插件index.ts的零逻辑约束、懒加载参考实现、开发工作流以及文档阅读清单。文中所有源码证据均来自当前仓库读者读完可以掌握如何快速定位文件所属包、安全地使用动态导入、以及如何在不触发全局副作用的前提下完成单文件开发闭环。一、命令工作流单文件开发的四个高频命令AGENTS.md首先给出四个仓库级命令。需要特别强调的是这四个命令都要求从仓库根目录执行因为它们需要先构建上游依赖pnpm workspace 内的增量tsc --build再针对给定路径运行对应工具。1. 安装、构建与全量测试pnpm install # 安装全部 workspace 依赖 pnpm build # 全量构建 pnpm test # 全量测试从根目录 package.json 可以看到这三个脚本的真实定义build是pnpm run --recursive --if-present build即对 workspace 内所有定义了build脚本的包递归执行test同理为pnpm run --recursive --if-present test。这意味着任意包的开发流程都遵循递归、按需--if-present的执行模型。2. 单文件 Lintpnpm lint:file path/to/file.ts # prettier --check eslint pnpm lint:file --fix path/to/file.ts # 自动修复格式该命令由 scripts/lint-file.ts 实现。它的工作流程非常能说明本仓库的开发心智通过groupByPackage(paths)将传入的每个文件解析到其所属的 workspace 包因此一条命令可以同时处理跨包的多文件如pnpm lint:file packages/hardhat/src/a.ts packages/hardhat-utils/src/b.ts对该包及其依赖执行pnpm --filter pkg... run --if-present build做增量构建已是最新时约 100ms 的 no-op先跑eslint再跑prettier --check其注释说明了顺序原因eslint --fix如 import/order 规则可能留下空白行最后跑 prettier 可以保证结果是 prettier 干净的只有每个文件同时通过 prettier 与 eslint命令才以 0 退出多包场景下会打印每包的✓ passed/✗ failed汇总。3. 单文件测试与单条测试pnpm test:file path/to/test.ts pnpm test:file --only path/to/test.ts # 配合测试中的 .only 使用由 scripts/test-file.ts 实现同样先按包分组、增量构建所属包然后在包目录内以node --import tsx/esm --test --test-reporternomicfoundation/hardhat-node-test-reporter的方式调用 Node 原生测试运行器并使用仓库自研的 hardhat-node-test-reporter 输出报告。传入--only会追加 Node 的--test-only标志对应包级test:only脚本。这里有一个重要的使用前提test:file只支持采用node --test的包。UNSUPPORTED_TEST_RUNNERS集合中明确列出了四个使用 Mocha 或复合运行器的包nomicfoundation/ignition-ui、nomicfoundation/hardhat-ignition、nomicfoundation/ignition-core、nomicfoundation/example-project对这些包运行该命令会直接报错并提示请在包目录内运行pnpm test。另外对nomicfoundation/hardhat-ignition-viem包会额外执行其pretest脚本因为它的 fixture 项目编译不在常规build覆盖范围内。4. 单文件拼写检查pnpm spellcheck:file path/to/file.md由 scripts/spellcheck-file.ts 实现在仓库根目录调用cspell --no-progress从而自动拾取根目录的 cspell.config.mts 与 cspell.dictionary.txt自定义词典。它无需构建步骤。该脚本还包含一个贴心的错误诊断当 cspell 输出Files checked: 0时会提示传入路径可能是符号链接、被 gitignore 或不在 cspell 配置的 glob 范围内。二、仓库目录结构多包 workspace 与核心包定位AGENTS.md仅用两行描述了仓库布局但其背后是完整的 pnpm workspace 结构见 pnpm-workspace.yamlpackages/*—— 所有可发布publishable的 npm 包。包括核心的hardhat以及hardhat-ethers、hardhat-viem、hardhat-ignition、hardhat-verify、hardhat-utils、hardhat-errors、hardhat-keystore、hardhat-node-test-runner、hardhat-toolbox-mocha-ethers、hardhat-toolbox-viem等一整套插件与基础设施包packages/hardhat—— 核心逻辑与 CLI。CLI 入口在 packages/hardhat/src/internal/cli/main.ts其中createDebug(hardhat:core:cli:main)的命名空间见下文 N2 命名规范正是从该文件路径推导而来。此外仓库还包含scripts/构建、发布、端到端、基准等工程脚本见 scripts/README.md、e2e/与end-to-end/fixture 项目与真实场景端到端测试等目录。三、编码规则包结构、工具复用与错误处理1. 包结构src/与src/internal/的边界规则Exported code and types (viapackage#exports) live undersrc/, non-exported internals undersrc/internal/是整个代码库的公共 API 边界。这在 docs/engineering-guidelines.md 的 A5 中进一步明确src/中不在src/internal/下的任何东西都应视为公共 API公共 API 应进入package.json#exports需要被仔细评审并重点关注向后兼容新增导出/文件需要与团队讨论不得随意扩张。例如packages/hardhat-ethers/src/index.ts对外暴露插件入口而其实现细节全部收纳在src/internal/中packages/hardhat/src/hre.ts对外暴露createHardhatRuntimeEnvironment而HardhatRuntimeEnvironmentImplementation则位于src/internal/core/hre.ts。2.hardhat-utils优先规则Before usingnode:fsor writing a utility, checknomicfoundation/hardhat-utils与 GC1不要直接使用node:fs一脉相承。原因在工程指南中写得很直接node:fs的错误没有堆栈信息排障困难。查看 packages/hardhat-utils/src/fs.ts 即可看到仓库自研的 fs 工具集如getRealPath、readClosestPackageJson等这些正是main.ts顶部 import 的辅助函数。hardhat-utils还覆盖 fs、crypto、hex、错误处理等多个领域。3. 统一错误模型只抛HardhatError规则要求只抛HardhatError绝不throw new Error()用HardhatError.isHardhatError()判断而非instanceofcatch 块中用ensureError()。这是 Hardhat 3 与 Nomic 系插件的统一错误策略对应工程指南 E1新错误应声明在 packages/hardhat-errors/src/errors.ts导出HardhatError、HardhatPluginError、assertHardhatInvariant并从hardhat-errors包引用确保每个错误都有错误码与官网文档isHardhatError的实现基于一个内部属性标记_isHardhatError而不是instanceof这使得跨包、跨副本的错误对象也能被可靠识别ensureError由 packages/hardhat-utils/src/error.ts 提供用于在 catch 中保证拿到真正的Error实例。同时E1 列出了四条例外会造成循环依赖的场景、JSON-RPC 特定错误抛ProviderError、天然低层的工具类代码、以及 keystore 加密模块为了自包含、易审计。四、Hardhat 3 动态导入await import的六条判定标准这是AGENTS.md中篇幅最长、也最核心的工程规则。背景是 Hardhat 3 的插件系统本身已处理了懒加载——插件的业务逻辑、依赖、条件依赖、hook handler 工厂与 task action 全部通过动态导入加载。因此绝大多数 import 应该是顶层 importtop-level imports只有在满足以下任一条件时才允许使用await import启动路径上的非必需模块文件属于hardhat包且总在启动时被导入即被packages/hardhat/src/internal/cli/main.ts或packages/hardhat/src/index.ts直接或传递性导入但导入的模块并非总是被用到例如main.ts中的./init/init.js导入路径是动态的例如用户的 config 路径只有在运行时才能确定由包装器按需加载包装器导出与首次访问时才加载的模块相同的接口主要用于 HRE 扩展例如packages/hardhat/src/internal/builtin-plugins/network-manager/hook-handlers/hre.ts避免循环依赖例如在运行时才导入 HRE必须在特定时间点导入主要用于 import 副作用await import(...)后不消费被导入模块有注释说明理由且模块被缓存不是每次都执行await import(...)而是类似if (cachedModule undefined) { cachedModule await import(...) }的模式允许少量代码重复以避免引入不必要的 async 逻辑。补充约束测试文件可以自由使用await import。1. 插件index.ts的零逻辑约束规则同样被工程指南 A2 详细展开插件入口文件无论内置还是外部插件只允许导入自己的type-extension、来自hardhat、hardhat/config、hardhat/plugins用于definePlugin的类型与枚举以及一个简单的常量文件其余一切都要由注册在插件对象中的回调来导入。type extension 必须以类型导出形式从入口导出export type * from ./type-extensions.js而不是为副作用而导入从而保证编译后的index.js没有额外运行时导入。2. 始终运行的 hook 工厂ConfigHooks与HardhatRuntimeEnvironmentHooks的工厂在每次 Hardhat 调用时都会执行因此它们必须遵循与插件index.ts相同的标准——不能把重型依赖放进这些工厂。3. 懒加载参考实现与缓存 getter 的时序NetworkHooks并非每次都会运行但扩展NetworkConnection的newConnectionhandler 应尽可能懒初始化业务逻辑除非该逻辑几乎总是被使用。文档给出的正反示例很有说服力hardhat-ethers插件几乎每个用户在创建新网络后都会用到不需要懒初始化而hardhat-ignition-ethers并非总是被使用所以应该懒初始化。参考模式是 packages/hardhat-ignition-ethers/src/internal/hook-handlers/network.ts。源码展示了完整的LazyEthersIgnitionHelper实现模块级缓存EthersIgnitionHelperImpl与实例级缓存#ethersIgnitionHelper双缓存其#getEthersIgnitionHelper()中有一句非常重要的源码注释——await import必须在实例缓存检查之前执行这样并发调用者共享同一个 microtask 去重点否则每个挂起的调用者都会重新进入分支、各自构造一份实现导致不同调用者持有不同实例与状态引发并发问题。对应的完整规则在 docs/engineering-guidelines.md 的 GC3 Ordering in cached lazy getters 一节getter 必须先await import(...)再做实例缓存检查同时在读取this.#instance undefined与赋值this.#instance之间不能有任何await否则并发调用者会观察到仍为undefined的实例并构造出第二个实例。标准代码形状如下import type { Impl as ImplT } from ./impl.js; let Impl: typeof ImplT | undefined; async #get(): PromiseImplT { if (Impl undefined) { ({ Impl } await import(./impl.js)); } if (this.#instance undefined) { this.#instance new Impl(...); } return this.#instance; }五、开发工作流与文档阅读清单1. 修改一个包后的标准流程AGENTS.md明确给出了修改包后的三步验证pnpm lintpnpm buildpnpm test修改测试文件后则用上文单文件测试命令单独运行它。这与根目录package.json中lint递归 lint scripts e2e workflows 检查、build、test的聚合定义一一对应。2. 修改scripts/前必读规则要求如果改动涉及./scripts/先阅读 scripts/README.md。这是仓库对工程脚本发布、端到端测试、性能基准、verdaccio 私有源等的权威文档防止破坏 CI 与发布链路。3. 进阶工程指南AGENTS.md是给 Agent/贡献者的速查入口而完整规范沉淀在 docs/engineering-guidelines.md。该文档按架构A1–A6、通用编码GC1–GC3、测试T1–T5、依赖管理DM1–DM4、命名N1–N2、错误E1六个维度展开本文档中的多条规则A2、A5、GC3、E1、N2都是它的浓缩版。其中与日常开发最相关的要点包括A1hardhat/src/core/不得包含任何应用层逻辑它只承载最小基础设施config、tasks、hooks、global options、用户中断Ethereum/Solidity/测试等能力都应基于 core 实现插件集成hardhat而非coreA3在hardhat包内需要 HRE 实例时必须使用src/internal/hre-initialization.ts暴露的createHardhatRuntimeEnvironment它负责正确解析 config、加载内置插件而不是直接用包的入口模块或HardhatRuntimeEnvironmentImplementation唯一的例外是src/core自身的测试它们必须直接使用create工厂A4barrel 文件只用于向包外消费者导出包内导入应始终从定义处引入避免性能问题与逻辑/类型散落A6不要在领域对象里保存HookManager引用而是通过构造函数传入回调如JsonRpcRequestWrapperFunction避免系统耦合、便于测试GC2不得用对象字面量构造或 mock 复杂类型必须使用构造函数/工厂保证内部状态正确、代码库不易碎T1/T2/T3v3 测试不需要 fixture 项目可用createHardhatRuntimeEnvironment手动初始化 HRE例如const hre await createHardhatRuntimeEnvironment(hardhatConfig)来自hardhat/hre断言应基于错误码/错误类型而非错误消息HardhatError断言辅助不得使用全局 HRE 测试插件显式创建实例以便多实例并存T5优先集成式测试而非过度 mock并选择恰好够用的最低层级概念如用 HRE 而不是建一个磁盘上的完整项目DM1–DM4最小化依赖以降低供应链攻击面内部依赖大多应为peerDependencies例外hardhat-errors、hardhat-utils、hardhat-zod-utils用 dependencyhardhat-test-utils用 devDependencypnpm 下workspace:peer 依赖不会自动安装其 peer需同时把 peer 加为 devDependencies插件应以hardhat为 peer 依赖N1v3 命名约定是TheInterface/TheInterfaceImplementationv2 的I前缀已废弃N2createDebug命名空间按包位置取值——packages/hardhat/内用hardhat:core:area[:sub...]如hardhat:core:cli:main其他 workspace 包用hardhat:plugin[:sub...]如hardhat:ethers:provider并刻意保留hook-handlers段让用户可以用DEBUGhardhat:*:hook-handlers:*一次看到所有插件的 hook handler 日志。六、实践清单把这份指南用起来对开发者与 AI Agent 而言这份AGENTS.md最终可以浓缩为一份可执行清单单文件改动在仓库根目录用pnpm lint:file path可加--fix与pnpm test:file path可加--only快速闭环改 Markdown 再用pnpm spellcheck:file path过一遍拼写包级改动在该包目录内依次pnpm lint→pnpm build→pnpm test写代码前自查文件系统操作先查hardhat-utils错误只抛HardhatError并尽量在hardhat-errors中登记错误码复杂类型用构造函数/工厂不要用对象字面量 mock做插件/改入口index.ts保持零逻辑type extension 用export type *导出需要懒加载时严格遵循先await import再查实例缓存、读值与赋值之间无await的时序测试优先createHardhatRuntimeEnvironment显式建 HRE断言错误码而非消息能集成测试就不要深度 mock动工程脚本先读 scripts/README.md完整规范再查 docs/engineering-guidelines.md。按照这套约定开发既能保证与 Hardhat 3 的运行时模型懒加载、统一错误、HRE 初始化完全一致也能让每个改动在提交前就通过仓库现有的全部质量门禁。【免费下载链接】hardhatHardhat is a development environment to compile, deploy, test, and debug your Ethereum software.项目地址: https://gitcode.com/GitHub_Trending/ha/hardhat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表