Material UI (MUI Core) 贡献指南:从本地环境搭建到 PR 通过全部 CI 检查
【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Google's Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-ui
本篇指南基于 MUI 官方仓库根目录的 CONTRIBUTING.md,完整覆盖 Material UI 与 MUI System 的贡献流程:如何搭建本地文档站与 playground 开发环境、如何规范地提交 PR、如何逐项修复 CI 检查,以及如何生成组件 API 文档、为文档新增演示示例和使用尚未发布的包。读完后你将能够独立完成一次从修改源码到合并入master分支的完整贡献闭环。
一、贡献体系概览:代码只是其中一部分
MUI 项目采用 Contributor Covenant 作为行为准则,并强调贡献的“宽光谱”:编写代码只是贡献的一种形式,文档改进与代码变更同等重要。官方建议的流程是——先开 issue 讨论,再动手写 PR,尤其是较大的改动。
针对第一次贡献的开发者,官方提供了两类入口:
- good first issue:范围有限、且已有可用方案的讨论,适合新开发者或新接触该库的贡献者。可在 issue 列表中使用标签检索
is:issue is:open label:"good first issue"。 - ready to take:已经在讨论中至少部分解决、下一步方向明确的问题,适合希望降低“踩坑”成本的开发者,检索式
is:issue is:open label:"ready to take"。
认领 issue 的约定(CONTRIBUTING.md):
- 先查看评论线程,确认是否已有人在修;
- 若无人处理,留言说明你已开始着手,避免重复劳动;
- 若某人认领后超过一周无后续,可以接手,但仍需留言说明;issue 若 7~14 天无活动,可视为无人处理。
二、标准 PR 流程:从 Fork 到 Push
PR 应当保持小步快跑:一个 PR 不要捆绑多个 feature 或 bug fix,与其做一个大 PR,不如拆成两个小 PR。标准流程(CONTRIBUTING.md):
# 1. Fork 仓库后,克隆你的 fork 并添加 upstream 远程 git clone https://github.com/<your username>/material-ui.git cd material-ui git remote add upstream https://github.com/mui/material-ui.git # 2. 将本地 master 与上游同步 git checkout master git pull upstream master # 3. 用 pnpm 安装依赖(不支持 yarn / npm) pnpm install # 4. 创建主题分支 git checkout -b my-topic-branch # 5. 修改、提交并推送到你的 fork git push -u origin HEAD关于“只支持 pnpm”这一点,仓库源码中有硬性保障:根 package.json 中定义了"preinstall": "npx only-allow pnpm",如果尝试用 npm 或 yarn 安装,安装过程会直接失败。
推送后在仓库中发起 PR,核心团队会持续监控新 PR,并以“合并 / 要求修改 / 关闭并说明原因”三种结果回应。
分支目标与 master 策略
PR 应指向master分支。master的代码必须与最新稳定版保持兼容:可以有新 feature,但不允许破坏性变更——原则上任何时候都应当能从master的最新提交直接发布一个新的 minor 版本(CONTRIBUTING.md)。
三、本地验证:文档站与 Playground 两套环境
3.1 文档站(维护者日常开发环境)
文档站本身就用 Material UI 构建,包含所有组件的示例,是实验改动效果的最佳场所:
pnpm start对应地,根 package.json 中该脚本实际执行pnpm install && pnpm docs:dev,其中docs:dev由 docs/package.json 中的next dev --turbopack驱动。启动后访问http://localhost:3000,对文档的修改会热更新。
3.2 Playground 隔离环境
在文档站上直接改现有 demo 有三大痛点(CONTRIBUTING.md):
- 修改现有 demo 无法让你隔离地调试单个组件实例;
- 清空页面做隔离实验会让
git diff变得很“吵”; - 静态检查器可能报告你并不关心的既有问题。
官方方案是 playground:
pnpm docs:create-playground && pnpm start访问http://localhost:3000/playground/。从源码看,根 package.json 的docs:create-playground会过滤到 docs 包执行其 create-playground 脚本:cpy --cwd=scripts playground.template.tsx ../pages/playground/ --rename=index.tsx,即把 playground 模板文件 复制为docs/pages/playground/index.tsx。要创建更多隔离页面,只需把index.tsx复制一份改名为<file_name>.tsx,新页面即可通过http://localhost:3000/playground/<file_name>访问。
四、提高 PR 被接收概率:合并前自检清单
CI 会在 PR 打开时自动运行一组检查。若不确定能否通过,可以先开 PR,由界面展示结果汇总,再对照下文的修复方法。官方列出的合并前提(CONTRIBUTING.md):
分支层面
- 分支目标为
master,所有测试通过,且符合“随时可发 minor 版本”的兼容性要求; - 分支不能落后于目标分支,需要保持与
master同步。
功能层面
- 新增 feature 时:若该能力用核心库现有 API 已经可以实现,你需要解释为什么必须进核心库;若是常见用法,要在文档中补充示例;
- 新增或修改功能必须附带测试(测试体系详见 test/README.md);
- 新增 prop 或修改 prop 类型时,必须同步更新 TypeScript 声明;
- 提交新组件时,组件先加入 packages/mui-lab(实验室包),而非直接进核心包。
代码层面
- 代码已格式化:改动过代码则运行
pnpm prettier; - 代码已通过 lint:运行
pnpm eslint; - 类型安全:改动过 TypeScript 源码或声明时运行
pnpm typescript; - API 文档为最新:改动过 API 时运行
pnpm proptypes && pnpm docs:api; - Demo 为最新:改动过 demo 时运行
pnpm docs:typescript:formatted; - PR 标题遵循
[product-name][Component] Imperative commit message格式,例如[material-ui][Button] Fix loading state handling。
这些脚本在仓库中的真实定义(根 package.json):
| 脚本 | 实际命令 |
|---|---|
pnpm prettier | pretty-quick --ignore-path .lintignore --branch master |
pnpm eslint | eslint . --cache --report-unused-disable-directives --max-warnings 0 |
pnpm typescript | lerna run --no-bail typescript |
pnpm proptypes | tsx ./scripts/generateProptypes.ts |
pnpm docs:api | 清理旧 api-docs 后执行tsx ./scripts/buildApiDocs/index.ts |
pnpm docs:typescript:formatted | tsx ./docs/scripts/formattedTSDemos.mjs |
pnpm test:unit | cross-env TZ=UTC vitest |
pnpm test:browser | cross-env TEST_SCOPE=browser pnpm test:unit |
另外两点约定:若 PR 解决了某个 issue,务必在 PR 描述中使用受支持的 GitHub 关键词(如Fixes #xxx)关联 issue,这样 PR 合并后 issue 会自动关闭;若某个步骤遗漏了不要担心,CI 会运行完整测试集,维护者也会协助排查。
五、CI 检查逐项解析与本地修复方法
检查失败时点击Details查看构建日志(CONTRIBUTING.md 对各检查的说明如下,本文补充了对应的本地命令)。
checkout
依赖与 lockfile 的预检。运行pnpm install和pnpm deduplicate可修复大多数问题。
test_static
检查代码格式并对整个仓库做 lint,同时会执行一些会生成或修改文件的命令(如pnpm docs:api)。因此这类失败最常见的修复方式是:本地重跑失败的命令,把生成结果提交进 PR。
test_unit-1
在jsdom环境中跑单元测试。失败时本地pnpm test:unit通常也会失败,可用pnpm test:unit --grep ComponentName缩小范围。若本地通过而 CI 失败,要考虑可访问性树排除这一差异:测试中 a11y 树校验默认在本地被禁用、在 CI 中启用(见 test/README.md),这往往是 “Unable to find an accessible element with the role” 这类报错的根源;本地可设置环境变量CI=true来对齐行为。
test_browser
通过 Playwright 在多个浏览器中跑单元测试。失败日志会列出是哪个浏览器失败:Chrome 失败时本地pnpm test:browser也会失败;其他浏览器可用VITEST_BROWSERS=firefox,webkit pnpm test:browser调试。这与 test/README.md 中记录的“vitest browser mode + 无头 Chrome/Firefox/WebKit”三端测试方案一致。
test_regressions
构建回归 fixture 应用(Vite)并在真实浏览器中跑 Playwright,做两类回归:截图比对(视觉回归)与 axe-core(可访问性回归)。从根 package.json 可见其实现链:test:regressions:build(vite build)+test:regressions:run(vitest)+test:regressions:server(vite preview,5001 端口)并发执行,最后还会对docs/data/material/components/**/*.a11y.json跑一次 prettier。若可访问性结果过期,本地重跑pnpm test:regressions并提交更新后的文件即可。
test_types / test_bundle_size_monitor
前者对整个仓库做类型检查,日志会列出具体问题(本地对应pnpm typescript);后者监控 bundle size,仅在超过阈值时报错,失败通常意味着包或文档的构建方式出了问题。
Continuous Releases / argos / deploy/netlify / codecov
- Continuous Releases:把每个 PR 的包发布为 pkg.pr.new 预览包,理论上不应单独失败,可用于测试复杂场景;
- argos:评估
test/regressions/tests截到的截图,发现差异即失败——但这不必然意味着 PR 会被拒绝,变化可能是预期的,点开 Details 查看差异即可; - deploy/netlify:成功后渲染一个包含你改动的文档预览;失败时本地
pnpm docs:build通常也会失败。从源码看,文档构建(docs/package.json 的build脚本)会执行next build+ 构建 service worker + 断链检查(reportBrokenLinks.mts),这正是 CI 中“Netlify 其他完整性检查”的来源; - codecov/project:监控测试覆盖率,覆盖率下降不是致命问题,但若能提升总是受欢迎的。
六、组件 API 文档的自动生成机制
组件 API 文档(即文档站各组件页的 API 表格)不是手写的,而是从 TypeScript 声明文件中的 JSDoc 自动生成。流程:
- 更新对应
.d.ts文件中的文档,例如<Button>的 packages/mui-material/src/Button/Button.d.ts——每个 prop 的 JSDoc 描述、@default标注都会成为 API 文档内容; - 运行
pnpm proptypes && pnpm docs:api。
pnpm proptypes的入口是 scripts/generateProptypes.ts,其工作机制值得了解(L250-L299):
- 它扫描各包源码目录中“目录名与文件名一致”的大驼峰组件文件(如
Button/Button.d.ts,即 generateProptypes.ts 中的 glob 过滤逻辑),支持--pattern参数只对匹配的文件做处理; - 通过 TypeScript 语言服务(getPropTypesFromFile)解析出组件的 props 与 JSDoc,并按
shouldInclude规则决定哪些 prop 进入文档——外部继承的 prop 默认不展开,除非列入了 useExternalDocumentation 白名单(如Button的disableRipple、InputBase系列共享的 22 个 prop); - 结果被
injectPropTypesInFile注入回组件的 JS/TS 源文件,并附上醒目注释(L220-L227):
┌────────────────────────────── Warning ──────────────────────────────┐ │ These PropTypes are generated from the TypeScript type definitions. │ │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ └─────────────────────────────────────────────────────────────────────┘这解释了“修改 prop 必须同步更新 TypeScript 声明”这条规则的底层原因:运行时propTypes与 API 文档同源于类型定义。而pnpm docs:api(scripts/buildApiDocs/index.ts)则负责把这些数据整理为文档站消费的 api-docs 文件,因此两个命令必须配合使用。
七、为文档新增一个演示(Demo)
以 Button 组件为例,完整步骤(CONTRIBUTING.md):
1. 添加新的组件文件
把新文件放进该组件的演示目录。需要说明的是:原文档给出的路径是docs/src/pages/components/buttons/,而从当前仓库结构看,演示文件实际已迁移到产品化目录 docs/data/material/components/buttons/(系统演示在docs/data/system/等对应目录下),以SuperButtons.tsx为例命名。
2. 编写 demo 代码
官方要求演示以 TypeScript(.tsx)编写;创建后运行pnpm docs:typescript:formatted自动生成同样必需的 JavaScript 版本。从 docs/scripts/formattedTSDemos.mjs 的注释可确认其职责:“Transpiles TypeScript demos to formatted JavaScript. Can be used to verify that JS and TS demos are equivalent. No introduced change would indicate equivalence.”——即它既负责生成,也充当 JS/TS 双版本一致性校验。不熟悉 TypeScript 的贡献者可以先用 JS 写,再由核心成员协助迁移。
一个真实示例,BasicButtons.tsx 演示了 Button 的三种基础变体:
import Stack from '@mui/material/Stack'; import Button from '@mui/material/Button'; export default function BasicButtons() { return ( <Stack spacing={2} direction="row"> <Button variant="text">Text</Button> <Button variant="contained">Contained</Button> <Button variant="outlined">Outlined</Button> </Stack> ); }3. 编辑页面的 Markdown 文件
组件目录下的 Markdown 文件(当前仓库中为 docs/data/material/components/buttons/buttons.md)是文档内容的唯一来源,改动会直接反映到站点上。为新 demo 添加小节、描述与{{"demo": ...}}注入语句,例如:
+### Super buttons + +To create a super button for a specific use case, add the `super` prop: + +{{"demo": "pages/components/buttons/SuperButtons.js"}}真实页面中的写法可对照 buttons.md:## Basic button小节紧随{{"demo": "BasicButtons.js"}}注入语句——注意 demo 引用的是JS 文件,与“TS 为主、JS 由脚本生成”的规则一致。
4. 提交 PR
按第二节的流程开 PR。文档类 PR 都会经过编辑审阅,计划长期贡献的开发者建议先熟悉官方的写作风格指南,可以加快编辑流程。找文档任务可以用 issue 检索is:issue is:open label:docs label:"ready to take"。
八、如何使用尚未发布的改动
三种方式(CONTRIBUTING.md):
方式一:pkg.pr.new 预览包。每个 PR 都会通过 Continuous Releases 任务发布预览版本,从该状态中取得 URL 后,把依赖指向它:
diff --git a/package.json b/package.json index 791a7da1f4..a5db13b414 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "dependencies": { "@babel/runtime": "^7.4.4", "@mui/styled-engine": "^5.0.0-alpha.16", - "@mui/material": "^5.0.0-alpha.15", + "@mui/material": "https://pkg.pr.new/mui/material-ui/@mui/material@b0f26aa", "@mui/system": "^5.0.0-alpha.16",方式二:Netlify 文档预览。打开文档的 Netlify 预览,把任意 demo 打开到 CodeSandbox 或 StackBlitz,文档会自动配置好预览包依赖。
方式三:本地打包。以@mui/material为例(任何 npm 包同理):
$> cd packages/mui-material # 或任意其他 mui 包的路径 $packages/mui-material> pnpm build $packages/mui-material> cd ./build $packages/mui-material> pnpm pack到该包的 build 目录找到mui-material-x.x.x.tar.gz,拷贝到目标测试项目中安装:
$test-project> npm i ./path-to-file/mui-material-x.x.x.tar.gz注意:如果该包此前已安装,重新安装不会反映你的改动。一个快捷修法是运行
pnpm build前先在package.json中临时提高版本号。
九、路线图与许可证
项目未来方向见 Material UI 官方文档站上的 Roadmap 页面(可在仓库文档 docs/data/material/discover-more/ 中找到对应页面源文件)。最后,向仓库提交代码即表示同意你的贡献遵循 MIT 许可证。
附:贡献者命令速查
| 目的 | 命令 |
|---|---|
| 安装依赖(仅 pnpm) | pnpm install |
| 启动文档站(http://localhost:3000) | pnpm start |
| 创建 playground | pnpm docs:create-playground && pnpm start |
| 单元测试(jsdom) | pnpm test:unit(可加--grep ComponentName缩小范围) |
| 浏览器端测试 | pnpm test:browser,VITEST_BROWSERS=firefox,webkit pnpm test:browser调试指定浏览器 |
| 视觉/可访问性回归 | pnpm test:regressions(开发模式pnpm test:regressions:dev+pnpm test:regressions:run) |
| 格式化 / lint / 类型检查 | pnpm prettier/pnpm eslint/pnpm typescript |
| 重新生成 propTypes 与 API 文档 | pnpm proptypes && pnpm docs:api |
| 生成 TS demo 的 JS 版本 | pnpm docs:typescript:formatted |
| 本地构建单个包 | cd packages/mui-material && pnpm build && pnpm pack |
(适用前提:以上均以当前仓库的 pnpm workspace + Lerna + Vitest/Playwright 工具链为准,命令定义见根 package.json;根目录另有 AGENTS.md 与 CLAUDE.md 供 AI 辅助开发参考。)
【免费下载链接】material-uiMaterial UI: Comprehensive React component library that implements Google's Material Design. Free forever.项目地址: https://gitcode.com/GitHub_Trending/ma/material-ui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考