ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Astro 开源贡献实战指南:从 Monorepo 本地开发、测试体系到发布流程

Astro 开源贡献实战指南:从 Monorepo 本地开发、测试体系到发布流程 Astro 开源贡献实战指南从 Monorepo 本地开发、测试体系到发布流程【免费下载链接】astroThe web framework for content-driven websites. ⭐️ Star to support our work!项目地址: https://gitcode.com/GitHub_Trending/as/astro本文以 Astro 仓库根目录的 CONTRIBUTING.md 为蓝本结合仓库中真实的package.json、pnpm-workspace.yaml、configs/共享配置、测试脚本与基准测试 CLI 源码完整梳理了向 Astro 贡献代码的整条工作流环境准备、本地仓库搭建、变更验证方式、单元测试与 E2E 测试、代码结构约束、基准测试运行以及维护者视角的 Issue 分诊、版本发布与预发布模式。读完本篇你将能够独立完成 Astro 仓库从克隆、构建、跑测试到提交 Pull Request 的全流程并理解其 monorepo 工程架构背后的设计决策。前置条件Node.js 与 pnpm 版本要求贡献 Astro 的第一步是确认本地运行时环境。CONTRIBUTING.md 给出的最低要求是node: 22.12.0 pnpm: ^10.28.0 # otherwise, your build will fail这一要求与仓库根目录 package.json 中的实际声明互相印证engines.node为22.12.0。需要注意的是当前仓库通过packageManager字段将 pnpm 版本锁定为pnpm11.13.1见 package.json因此官方推荐启用 Corepack让 Corepack 自动按该字段选择正确的 pnpm 版本避免版本不匹配导致构建失败。仓库的 workspace 拓扑由 pnpm-workspace.yaml 定义包含packages/**/*、examples/**/*、scripts、benchmark等条目并有几条值得注意的全局策略preferWorkspacePackages: true与linkWorkspacePackages: true确保任何依赖astro的包都优先解析到本地 workspace 中的源码版本而不是 registry 上发布的版本——这也是 examples 能链接到本地 Astro 源码的底层原因下文会用到。overrides中对types/node22强制解析到^22.19.0注释中解释了这是为了减少因 peer 依赖版本区间不同而导致的重复安装。allowBuilds明确禁用了 esbuild、sharp、workerd 等第三方包的 postinstall 构建脚本以降低安装期执行第三方代码的安全风险。本地仓库搭建Astro 使用 pnpm workspaces因此必须始终从项目顶层目录运行pnpm install。在顶层安装会为astro本体及仓库内所有包安装依赖git clone cd ... pnpm install pnpm run build这里的pnpm run build对应根 package.json 中的 turbo 命令turbo run build --filterastro --filtercreate-astro --filterastrojs/* ...即一次性构建主包、create-astro 与全部astrojs/*集成包。文档还推荐了两项提升日常体验的本地 git 配置忽略全仓格式化提交对 blame 的干扰。仓库中存在 .git-blame-ignore-revs 文件用于记录诸如全仓重格式化之类的提交本地执行以下命令后git blame会自动跳过这些提交git config --local blame.ignoreRevsFile .git-blame-ignore-revs自动处理pnpm-lock.yaml的合并冲突。锁文件是贡献者最容易产生冲突的地方pnpm 官方提供了 merge driverpnpm add -g pnpm/merge-driver pnpm dlx npm-merge-driver install --driver-name pnpm-merge-driver --driver pnpm-merge-driver %A %O %B %P --files pnpm-lock.yaml安装后git 在合并 pnpm-lock.yaml 时会自动调用该 driver 做语义合并而不是留下手工解决的冲突块。使用 GitHub Codespaces 开发如果想跳过本地环境搭建可以直接为仓库创建一个 Codespace基于 Dev Containers 规范。CONTRIBUTING.md 说明新 codespace 会以 Web 版 VS Code 打开开发依赖预装、测试自动运行保证你从一个绿的基线开始工作。这一点可以直接从仓库中的 .devcontainer/devcontainer.json 得到印证postCreateCommand为pnpm install pnpm run build即创建容器后自动完成安装与构建postAttachCommand配置了 Astro tests 任务执行pnpm run test即打开终端后测试自动运行customizations.vscode.extensions预装了astro-build.astro-vscode与 Prettier 插件codespaces.openFiles则会自动打开README.md与CONTRIBUTING.md。Dev Containers 本身是一个开放规范除 GitHub Codespaces 外还被其他工具支持因此该配置在本地 VS Code 的 Dev Containers 扩展中同样可用。开发循环dev / build 与三种变更验证方式日常的监听式开发脚本与一次性构建命令为# starts a file-watching, live-reloading dev script for active development pnpm run dev # build the entire project, one time. pnpm run build对应根 package.json 中dev为turbo run dev --concurrency40 --parallel ...并发 40、并行监听所有包build则为一次性构建。如何测试自己的改动文档给出三种方式按改动规模递进运行/examples中的示例项目。examples 通过 workspace 链接使用本地 Astro 源码能直接看到你的改动效果pnpm --filter example/minimal run dev示例包名可以从 examples/minimal/package.json 确认name: example/minimal。这也是小改动下最轻量的验证方式。写一个测试并运行它。适合针对特定 bug 的修复验证改动是否按预期工作测试体系的完整命令见下文运行测试一节。创建独立项目通过pnpm link使用本地 Astro。适合较大的改动希望在一个干净的外部项目里完整测试。本地联调依赖包如 astrojs/compiler如果你在改的是 Astro 依赖的上游包例如编译器可以利用pnpm.overrides将其指向本地构建产物。在根 package.json 中加入{ pnpm: { overrides: { astrojs/compiler: file:../astro-compiler/packages/compiler } } }然后运行pnpm install完成链接。两个关键注意点先构建好依赖包确保其dist/是最新的提交前移除该 override否则会污染锁文件。根仓库 pnpm-workspace.yaml 本身就大量使用overrides管理依赖解析如types/node22、modern-tar可以视为该机制在仓库内的实际用法参考。命名约定、运行时边界与 Vite 调试CONTRIBUTING.md 对packages/astro源码提出了一条硬性约定注意这是较新的规范存量代码可能尚未完全遵循限制使用 Node.js 专属 API如node:前缀的内置模块。原因是 Astro 代码可能运行在非 Node 运行时例如 Cloudflare Workers运行时无关的代码必须放在名为runtime的目录或文件中runtime/或runtime.ts。这一点与 Code Structure 一节的目录划分完全一致packages/astro/src/runtime/目录确实存在且分为client/与server/子目录Vite 插件的实现内部可以使用 Node.js API但如果插件返回虚拟模块虚拟模块内部不允许使用 Node.js API虚拟模块最终会在更受限的作用域中执行。调试 Vite任何命令都可以加DEBUG前缀来查看 Vite 内部日志DEBUGvite:* astro dev # debug everything in Vite DEBUGvite:[name] astro dev # debug specific process, e.g. vite:deps or vite:transform例如vite:deps、vite:transform等命名空间可以分别观察依赖优化与模块转换阶段的细节。语言工具链贡献VS Code 扩展、Language Server 等语言工具位于 packages/language-tools/有自己独立的贡献文档 packages/language-tools/CONTRIBUTING.md涵盖扩展调试器的启动方式等涉及这部分代码时应当优先阅读该文档。运行测试从全量到单用例顶层测试命令# run this in the top-level project root to run all tests pnpm run test # run only a few tests in the astro package, great for working on a single feature # (example - pnpm run test:match cli runs tests with cli in the name) pnpm run test:match $STRING_MATCH # run tests on another package # (example - pnpm --filter astrojs/rss run test runs packages/astro-rss/test/rss.test.js) pnpm --filter $STRING_MATCH run test对应根 package.json 的实现test依次执行test:astro、test:integrations、test:language-tools其中前两者通过 scripts/turbo-run-affected.js 以--concurrency1 --only的方式仅运行受影响的包test:match则cd packages/astro pnpm run test:match。关于测试框架的迁移状态需要说明文档提到大多数测试使用 mocha正在通过自定义的astro-scripts test命令逐步迁移到node:test。从当前源码看这一迁移已基本完成——packages/astro/package.json 中的测试脚本全部经由astro-scripts test驱动node:testtest:unit: astro-scripts test \test/units/**/*.test.ts\ --strip-types --teardown ./test/units/teardown.ts, test:integration: astro-scripts test \test/*.test.ts\ --parallel --strip-types, test:match: astro-scripts test \test/**/*.test.ts\ --matchastro-scripts test的实现在 scripts/cmd/test.js它基于node:test的runAPI 与 spec reporter 封装支持--match/-m按名称过滤、--only/-o配合.only、--parallel/-p、--timeout、--setup、--teardown、--strip-types等参数其中超时默认为 CI 环境 30 分钟、本地 10 分钟。运行单个测试 / 单个用例直接借助 Node.js 测试运行器即可# run a single test file node --test test/astro-basic.test.js要运行单个it/describe用例需要给目标用例加.only后缀// test/astro-basic.test.js - describe(description, () { describe.only(description, () { - it(description, () { it.only(description, () {}) })然后传入--test-only选项node --test --test-only test/astro-basic.test.js两条容易踩坑的细节来自文档的 WARNING若存在嵌套的describe每一层都必须加.only--test-only与--test必须放在文件路径之前否则会退化为运行全部文件。对应文件在当前仓库为 TypeScript 测试例如 packages/astro/test/astro-basic.test.ts。CI 中排查测试超时偶尔某些测试会在 CI 上因超时失败而 Node.js 测试运行器的行为加上仓库架构使得很难判断是哪个文件卡住。文档给出的诊断技巧是临时给包的test脚本加--parallel{ - test: astro-scripts test \test/**/*.test.js\, test: astro-scripts test --parallel \test/**/*.test.js\, }保存后推送到 PRCI 会慢一些但能暴露出超时的具体文件问题解决后必须回滚该改动再推送。这与 scripts/cmd/test.js 中--parallel选项直接透传给node:test并发执行的语义一致。E2E 测试PlaywrightHMR、客户端水合等必须在浏览器中验证的特性使用 Playwright 对 dev server 做端到端测试# run this in the top-level project root to run all E2E tests pnpm run test:e2e # run only a few tests, great for working on a single feature # (example - pnpm run test:e2e:match Tailwind CSS runs tailwindcss.test.js) pnpm run test:e2e:match $STRING_MATCH从根 package.json 可见test:e2e:astro会先执行pnpm playwright install firefox再进入packages/astro运行 Playwrightpackages/astro/e2e/ 目录下有数十个用例如 hmr.test.ts、hydration-race.test.ts与 playwright.config.js 及 Firefox 专用配置配合工作。何时该写 E2E文档的原则是凡是验证astro build产物的测试都应走普通单测比启动astro preview更快只有需要验证页面在浏览器中加载之后的行为如astro dev下 HMR 是否生效、组件是否水合并可交互才使用 E2E。编写测试的要点创建新测试时最佳实践是参考现有测试文件的搭建方式。文档特别强调了一个高频坑复用 fixture 时使用不同配置的话必须配置唯一的outDir、build.client、build.server否则构建产物会在 ESM 层被缓存并在测试间共享await loadFixture({ root: ./fixtures/some-fixture, outDir: ./dist/some-folder, });如果测试开始无缘无故失败第一嫌疑就是outDir配置导致的构建缓存串味。格式化、Lint 与 Changeset两个可选的日常命令CI 中有对应的自动化兜底# auto-format the entire project # (optional - a GitHub Action formats every commit after a PR is merged) pnpm run format# lint the project # (optional - our linter creates helpful warnings, but not errors.) pnpm run lint对照根 package.jsonformat是biome formatprettier的组合format:imports由 biome 负责导入排序lint则是biome lint knip eslint --cache --concurrencyauto的三层组合knip 用于检测无用代码/依赖。提交 Pull Request 时只要 Astro 有任何变更就必须附一个 changesetexamples/*等非发布包不需要pnpm exec changeset仓库中的 .changeset/config.json 展示了 changesets 的接入方式changelog 由changesets/changelog-github生成baseBranch为origin/main进入预发布模式时会被改为next见下文。.changeset/目录下的*.md文件即为待发布的变更描述。运行性能基准测试仓库在 benchmark/ 目录维护了一套性能基准套件并暴露了astro-benchmarkCLI入口为 benchmark/index.js说明见 benchmark/README.md。从项目根目录顺序运行全部基准pnpm run benchmark对应根 package.json 中benchmark: astro-benchmark。只运行某一个基准把名字放在命令后即可。从 benchmark/index.js 的源码可以确认当前可用的四个基准memory构建内存与速度、render渲染速度、server-stress服务器压力、cli-startupCLI 启动速度另有--project选择基准项目、--output结果输出文件两个选项pnpm run benchmark memory pnpm run benchmark --help在 GitHub PR 上运行评论!bench即可触发基准会分别在 PR 分支与main分支上执行结果以新评论形式贴出只跑某一个则写!bench memory。维护者指南Issue 分诊与优先级体系CONTRIBUTING.md 的 For maintainers 一节为 monorepo 维护者提供了分诊规范。Issue 分诊流程文档用一张 mermaid 流程图定义了判断链是否遵循模板否则关闭并要求补模板→ 是否重复是则关闭并指向重复 issue→ 是否有可复现步骤无则打needs repro标签3 天无更新由 bot 自动关闭→ 是否真的是 bug否则判断是否为功能请求/预期行为分别引导到 roadmap 或解释关闭→ 确认是 bug后移除needs triage标签、按特性加功能标签如feat: ssr并进入优先级判定。五级优先级定义p5最高p1最低级别定义p5影响绝大多数 Astro 项目、无 workaround、使 Astro 不可用/不稳定。典型例子dev server 崩溃、build 中断无法完成、巨大的性能回退。通常不会给非astro主包分配但可能变化p4影响很多项目、无 workaround但 Astro 整体仍稳定可用p3不属于 p4/p5 的所有 bug。若文档未覆盖用户报告的场景建议用needs discussion标签发起讨论征求 OP 与其他维护者意见p2所有有 workaround的 bugp1极轻微、影响面小的 bug常是边缘情况且容易修复很适合指派给首次贡献者配套的操作纪律有三条打p2必须附一条解释 workaround 的评论若没有现成 workaround要 ping 打标签的人补上Astro 特性多但影响力不等dev server、build 命令、HMR、明显的性能回退属于高影响力面应作为定级的重要参照优先级不是刻在石头上的issue 点赞增长、讨论暴露新信息时都可以调整优先级但调整者应当给出解释。分诊本身是自愿的、尽力而为的——不确定时可以把 issue 留给上下文更多的人处理。PR Preview 发布可以随时给某个 PR 打上pr preview标签触发预览发布workflow 完成后会附一条评论说明如何安装该预览版。每个存在待发布 changeset 的包都会生成预览产物。若同一 PR 需要多次触发把标签移除后再重新加上即可。TypeScript Project References增量类型检查的仓库级设计仓库整体采用 TypeScript project references使tsc -b只重建真正变化过的包及其依赖者同时让编辑器的跳转到定义可以跨包直达源码——即使还没有跑过pnpm build。日常命令pnpm typecheck # 即 tsc -b增量类型检查 pnpm typecheck --clean # 清除构建缓存后全量检查根 package.json 中确认了typecheck: tsc -b。共享配置configs/共享配置集中在仓库根的 configs/ 目录configs/tsconfig.base.json所有其他 tsconfig 的基座。从源码看它开启了composite、declaration、emitDeclarationOnly、strict、nodenext模块解析等并把outDir指向${configDir}/node_modules/.cache/...——这个cache 目录被 git 与 ESLint 忽略用于隔离声明产物与源码configs/tsconfig.build.json构建包用include仅含src/声明输出到dist/tsBuildInfoFile放在dist/._cache/下._cache发布到 npm 时会被忽略configs/tsconfig.test.json类型检查测试用include为test/并显式排除test/fixtures/configs/tsconfig.language-tools.jsonpackages/language-tools/下的变体target commonjs。单包的三文件布局典型包以packages/astro为例实际文件见 packages/astro/tsconfig.json、tsconfig.build.json、tsconfig.test.json遵循文档给出的三层结构// packages/pkg/tsconfig.build.json // 继承共享 build 配置references 声明 workspace 依赖 // 目前需要与 package.json 的依赖保持手工同步 { extends: ../../configs/tsconfig.build.json, references: [{ path: ../dep1/tsconfig.json }, { path: ../dep2/tsconfig.json }] }// packages/pkg/tsconfig.test.json // 引用本包的 build 配置保证测试类型检查前 dist/ 已构建 { extends: ../../configs/tsconfig.test.json, references: [{ path: ./tsconfig.build.json }] }// packages/pkg/tsconfig.json // 纯 solution 文件files: [] 表示自身不含源码 // 只作为指向 build test 的入口 { extends: ../../configs/tsconfig.base.json, files: [], references: [{ path: ./tsconfig.build.json }, { path: ./tsconfig.test.json }] }以真实的packages/astro为例可以看到这套约定的落地细节其tsconfig.build.json的references精确指向../internal-helpers、../telemetry、../markdown/remark三个 workspace 依赖tsconfig.test.json则在 include 中额外列出了几个 glob 覆盖不到的 fixture 文件如multiple-jsx-renderers的渲染器并把types扩展为[vite/client, node]。仓库根的 tsconfig.json 是顶层 solution 文件references列出所有包benchmark、packages/astro、全部astrojs/*集成、language-tools 等。新增包时必须做两件事把新包加入根references列表并让其tsconfig.build.json的references与自身 workspace 依赖对齐。代码结构三种执行上下文与公共/内部 API文档指出 SSR 很容易变得复杂packages/astro的目录结构正是为了厘清不同执行系统而设计components/项目可直接使用的内置组件如import Code from astro/components/Code.astro对应 packages/astro/components/src/Astro 源码types/集中式 TypeScript 类型目的是减少循环依赖cli/astroCLI 命令的实现core/执行在**顶层作用域Node 中**的代码支撑astro build/astro dev及顶层 SSRruntime/执行在不同作用域非纯 Node 上下文的代码思维方式必须不同client/执行在浏览器中Astro 的部分水合代码在此只能用浏览器兼容 APIserver/执行在Vite 的 SSR 内部虽然仍是 Node 环境但与core/独立执行结构上可能完全不同vite-plugin-*/Astro 运行所需的各个 Vite 插件执行环境与src/runtime/server/类似但更宜视为独立模块当前属于内部实现。这个三种上下文的划分——Node.jssrc/core/→ Vite 内部src/runtime/server/→ 浏览器src/runtime/client/——也是调试时的坐标系统如果你在src/core/里工作你的代码不经过 Vite就不必去调试 Vite 的 setup而在runtime/server/里则相反。公共 API 与内部 API 的双导出表packages/astro/package.json声明了两套导出映射可对照 packages/astro/package.jsonexportsmonorepo 视角。包含全部公共入口外加仅供其他 workspace 包使用的./_internal/*子路径如./_internal/test/test-utils、./_internal/assetspublishConfig.exportsnpm 发布视角。pnpm 在发布时会用它替换exports因此_internal/*条目绝不会随包发布。跨包引用内部实现时必须走子路径而不是深层相对路径// Do this import { loadFixture } from astro/_internal/test/test-utils; // Not this import { loadFixture } from ../../../astro/test/test-utils.js;新增导出的两条规则公共导出如./foo必须同时加入exports与publishConfig.exports两处内部导出如./_internal/foo只加入exportspublishConfig.exports保持不动。一致性由自动化测试守护packages/astro/test/units/exports.test.ts 会在剥掉./_internal/*键后断言两份映射深度相等任何漂移都会让测试失败。让代码可测试业务逻辑与基础设施解耦文档用createKey的例子系统阐述了仓库的测试哲学基础设施依赖外部系统/特殊环境数据库、文件系统、随机性等与业务逻辑任何地方都能跑的纯逻辑要分离手段是把外部依赖显式化为参数。演进路径分三步初版函数同时依赖全局 logger、crypto全局、第三方编码包与工具函数几乎无法测试第一步重构把generateKey与logger收进Options参数函数体变为纯逻辑进一步抽象定义KeyGenerator接口CryptoKeyGenerator类封装crypto.subtle.generateKeyAES-GCM 256 位 Base64 编码的具体实现main.ts负责注入实例。由此单元测试可以干净地 mock 每个抽象SpyLoggerFakeKeyGenerator断言日志类型、标签与ASTRO_KEYFOO消息格式。文档给出的两条记忆点尽量测试所有实现如果某实现只是对 NPM 包的薄封装可以直接信任包自身测试永远测试业务逻辑。仓库的 reference/unit-testing.md 与test/units/目录packages/astro/test/units/exports.test.ts 等用例均采用node:test 依赖注入模式印证了这一约定在仓库内的落地。分支模型与版本发布main 与 latest 分支main活跃开发分支永远反映最新代码。特定时期main会进入预发布prerelease状态latest稳定版所在分支每次发布稳定非预发布版本后自动更新。create-astro与 astro.new 默认指向该分支。Changesets 自动发布发布完全自动化仓库接入了 changesets 的 GitHub action 与 bot。发布新版本的操作就是找到[ci] releasePR、阅读并合并它。发布脚本链路在根 package.json 中可见release: pnpm run build changeset publish而version脚本在changeset version之后还会执行 scripts/deps/update-example-versions.js 同步示例项目版本再重装依赖并格式化。PR 快照snapshot发布changesets 支持从 PR 或自定义分支发布snapshot——临时性的 npm 产物用于让社区在合并前试用 PR 并反馈。本地运行changeset version需要一个 GitHub personal access token 并设置为GITHUB_TOKEN。快照发布四步# Notes: # - YYY 是识别本次发布的关键词如 --snapshot routing 与 --tag next--routing # - 用 npm/npx 而不是 pnpm因为 npm 处理 registry 登录、认证与发布 # - 把 GITHUB_TOKEN 写在命令里会进 bash history务必设置短过期时间 # 1: Tag 新版本 GITHUB_TOKENXXX npx changeset version --snapshot YYY # 2: 审查 diff确认没有发布超出需要的包 git checkout -- examples/ # 3: 发布 npm run release --tag next--YYY # 4: 确认无误后丢弃所有本地变更 git reset --hard默认会发布所有带 changeset 的包若想只发子集可以清空.changeset/目录并手写一个只包含目标包的 changeset——但切勿把该操作提交或推送到main否则会毁掉你本来还要发布的其他 changeset。预发布模式prerelease mode预发布模式下常规发布流程会走nextdist-tag 而不是latest——这是大特性面向全体用户之前先小范围验证的机制。进入预发布模式需核心贡献者授权项目根目录执行pnpm exec changeset pre enter next将 .changeset/config.json 的baseBranch改为next便于后续创建 changeset用命令产生的变更开 PR评审合并后即进入预发布模式成功后若存在[ci] releasePR 会改名为[ci] release (next)。退出预发布模式当实验性版本可以从npm install astronext转正式时同样仅核心贡献者操作执行pnpm exec changeset pre exit把baseBranch改回main开 PR 并评审合并成功后[ci] release (next)恢复为[ci] release。预发布期间手动发布 latest预发布模式下自动发布只发astronextlatest需手动以下以0.X代指目标版本若不存在新建release/0.X分支将其指向v0.X版本的最新 commit按需从maingit cherry-pick提交确保新发布所需 changeset 齐全必要时用pnpm exec changeset手工创建pnpm exec changeset version生成新版本pnpm exec release发布git push git push --tags推送到 GitHubgit push release/0.X:latest把发布分支推到latest在仓库 releases 页面创建 releasechangelog 从latest分支的 packages/astro/CHANGELOG.md 复制视需要在社区公告频道发布说明。深度参考文档reference/目录收录了各子系统的深度参考文档CONTRIBUTING.md 明确列出了两篇reference/optimize-deps.md —— Vite 依赖优化器dep optimizer的调试手册reference/handlers.md —— Handler 管线、PipelineFeatures以及如何在src/app.ts中为新功能添加特性检查。此外仓库中还有 reference/unit-testing.md 可配合本文的测试章节阅读。文档贡献最后CONTRIBUTING.md 也鼓励不写代码的贡献方式帮助打磨官方文档的准确性与易用性。文档托管在独立的withastro/docs仓库中适合想参与开源但不想深入本仓库工程细节的开发者作为切入点。【免费下载链接】astroThe web framework for content-driven websites. ⭐️ Star to support our work!项目地址: https://gitcode.com/GitHub_Trending/as/astro创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表