知海

贡献指南

vite-main开发与贡献

Vite 贡献指南

你好!我们非常高兴你对为 Vite 做出贡献感兴趣!在提交你的贡献之前,请仔细阅读以下指南。我们还建议你阅读我们文档中的项目哲学

仓库搭建

要在本地进行开发,请先 Fork Vite 仓库并将其克隆到你的本地机器。Vite 仓库是一个使用 pnpm workspaces 的 monorepo。用于安装和链接依赖的包管理器必须是 pnpm。你可以在 package.jsonpackageManager 字段中找到所需的 pnpm 版本。

要开发和测试核心的 vite 包:

  1. 在 Vite 的根文件夹中运行 pnpm i

  2. 在 Vite 的根文件夹中运行 pnpm run build

  3. 如果你正在开发 Vite 本身,你可以进入 packages/vite 并运行 pnpm run dev,这样每当你修改其代码时,Vite 都会自动重新构建。

如果你需要在多个使用不同 pnpm 版本的项目上工作,建议通过运行 corepack enable 来启用 Corepack

在 Windows 上克隆仓库

在 Windows 上,你可能需要激活开发者模式来解决非管理员用户创建符号链接的问题。此外,你可能需要将 git core.symlinks 设置为 true 来解决 git 中的符号链接问题

运行 git blame 时忽略特定提交

我们有一个 .git-blame-ignore-revs 文件用于忽略格式化更改。要让 git blame 使用此文件,你需要运行以下命令。

sh 复制代码
git config --local blame.ignoreRevsFile .git-blame-ignore-revs

文档

要开发 docs/ 站点:

  1. 在 Vite 的根文件夹中运行 pnpm run build。这将生成类型,以便 twoslash 在代码示例中正常工作。如果类型不可用,第 2 步会记录错误,但不会阻止站点正常工作。

  2. 在 Vite 的根文件夹中运行 pnpm run docs

文档翻译贡献

要为 Vite 文档添加新语言,请参阅 vite-docs-template

关于依赖的注意事项

Vite 的目标是保持轻量,这包括关注 npm 依赖的数量及其大小。

我们使用 Rolldown 在发布前预打包大多数依赖!因此,大多数依赖,即使是运行时源代码中使用的依赖,默认也应添加到 devDependencies 下。这也带来了我们在代码库中需要注意的以下约束。

require() 的使用

在某些情况下,我们有意使用懒加载依赖来提升启动性能。但是,请注意我们不能使用简单的 require('somedep') 调用,因为这些调用在 ESM 文件中会被忽略,导致依赖不会被打包进最终产物,而且由于这些依赖在 devDependencies 中,发布时实际的依赖甚至不存在。

相反,请使用 (await import('somedep')).default

添加依赖前三思

大多数依赖应该添加到 devDependencies,即使它们在运行时是必需的。一些例外情况包括:

  • 类型包。例如:@types/*
  • 由于二进制文件而无法正确打包的依赖。例如:esbuild
  • 自带类型且这些类型被 Vite 的公共类型所使用的依赖。例如:rollup

避免使用具有大量传递依赖的依赖,这些依赖会导致与提供的功能相比体积臃肿。例如,http-proxy 本身大约 380kB,但 http-proxy-middleware 会引入大量依赖,使其达到 3MB(!),而在 http-proxy 之上实现一个最小的自定义中间件只需几行代码。

确保类型支持

Vite 的目标是作为依赖完全可用于 TypeScript 项目(例如,它应该为 VitePress 提供适当的类型),并且也可用于 vite.config.ts。这意味着从技术上讲,暴露了类型的依赖需要成为 dependencies 的一部分,而不是 devDependencies。然而,这也意味着我们无法对其进行打包。

为了解决这个问题,我们将其中一些依赖的类型内联在 packages/vite/src/types 中。这样,我们仍然可以暴露类型,但可以打包依赖的源代码。

使用 pnpm run build-types-check 来检查打包后的类型是否依赖于 devDependencies 中的类型。

对于客户端和 Node 之间共享的类型,它们应该被添加到 packages/vite/types。这些类型不会被打包,并按原样发布(尽管它们仍被视为内部类型)。

添加新选项前三思

我们已经有了许多配置选项,我们应该避免通过增加又一个选项来解决问题。在添加选项之前,请考虑该问题:

  • 是否真的值得解决
  • 是否可以通过更智能的默认值来解决
  • 是否有使用现有选项的变通方法
  • 是否可以通过插件来解决

调试

要使用断点并探索代码执行流程,你可以使用 VS Code 的 “运行和调试” 功能。

  1. 在你想要暂停代码执行的位置添加 debugger 语句。

  2. 点击编辑器活动栏中的“运行和调试”图标,打开 “运行和调试”视图

  3. 在“运行和调试”视图中点击“JavaScript 调试终端”按钮,这会在 VS Code 中打开一个终端。

  4. 在该终端中,进入 playground/xxx,并运行 pnpm run dev

  5. 执行将在 debugger 语句处暂停,你可以使用 调试工具栏 来继续、单步执行和重启进程……

使用 Playwright(Chromium)调试 Vitest 测试中的错误

由于 Vitest、Playwright 和 Chromium 引入的抽象层和沙箱特性,某些错误会被掩盖和隐藏。为了在这些情况下查看实际出错的原因以及 devtools 控制台的内容,请按照以下步骤操作:

  1. playground/vitestSetup.ts -> afterAll 钩子中添加一个 debugger 语句。这将在测试退出且 Playwright 浏览器实例退出之前暂停执行。

  2. 使用 debug-serve 脚本命令运行测试,该命令将启用远程调试:pnpm run debug-serve resolve

  3. 等待检查器 devtools 在你的浏览器中打开并附加调试器。

  4. 在右侧的“源代码”面板中,点击播放按钮恢复执行,并允许测试运行,这将打开一个 Chromium 实例。

  5. 聚焦 Chromium 实例,你可以打开浏览器 devtools 并检查那里的控制台以找到潜在问题。

  6. 要关闭所有内容,只需在你的终端中停止测试进程。

调试日志

你可以设置 --debug 选项来开启调试日志(例如 vite --debug resolve)。要查看所有调试日志,你可以设置 vite --debug *,但请注意这会非常嘈杂。你可以运行 grep -r "createDebugger('vite:" packages/vite/src/ 来查看可用的调试范围列表。

禁用 Source Map

当 Vite 位于 node_modules 之外时,Vite 源代码的 source map 默认是启用的,以便你可以轻松地进行调试。在监听模式下打包 Vite 时,会生成 source map。

然而,当你在开发 source map 相关功能时,这种行为可能不是期望的。在这种情况下,你可以在运行 Vite 时通过将 DEBUG_DISABLE_SOURCE_MAP 环境变量设置为 1 来禁用 source map(例如 DEBUG_DISABLE_SOURCE_MAP=1 vite)。此环境变量也可用于禁用 source map 生成。

针对外部包测试 Vite

你可能希望将本地修改的 Vite 副本与另一个使用 Vite 构建的包进行测试。对于 pnpm,在构建 Vite 之后,你可以使用 overrides 来做到这一点。在 pnpm v10.5+ 中,overrides 应该指定在根目录的 pnpm-workspace.yaml 中,并且你必须在根目录的 package.json 中将该包列为依赖:

yaml 复制代码
# pnpm-workspace.yaml
overrides:
  vite: link:../path/to/vite/packages/vite

然后重新运行 pnpm install 来链接该包。

运行测试

集成测试

playground/ 下的每个包都包含一个 __tests__ 目录。测试使用 Vitest + Playwright 以及自定义集成来运行,以使编写测试变得简单。详细设置位于 vitest.config.e2e.tsplayground/vitest*.ts 文件中。

一些 playground 定义了变体,以使用不同的配置设置来运行同一个应用。按照惯例,当在 __tests__ 中的嵌套文件夹中运行测试规范文件时,设置将尝试在 playground 的根目录使用名为 vite.config-{folderName}.js 的配置文件。你可以在 assets playground 中查看变体的示例。

在运行测试之前,请确保 Vite 已构建

每个集成测试都可以在 dev server 模式或 build 模式下运行。

  • pnpm test 默认在 serve 和 build 两种模式下运行每个集成测试,同时也运行单元测试。

  • pnpm run test-serve 仅在 serve 模式下运行测试。

  • pnpm run test-build 仅在 build 模式下运行测试。

  • pnpm run test-serve-bundled 在启用 experimental.bundledDev 的 serve 模式下运行测试(无需单独配置)。尚未在此模式下通过的规范文件列在 vitest.config.e2e.tsbundledDevExclude 中——一旦某个文件通过,就将其从该列表中移除。当一个文件中只有少数用例失败时,请将该文件保留在该列表之外,并使用 ~utils 中的 isBundledDev 标志用 test.skipIf(isBundledDev)(或 describe.skipIf(isBundledDev))标记这些用例——它们仍然会在正常的 serve 和 build 模式下运行。

pnpm run test-serve [match]pnpm run test-build [match] 在匹配给定过滤器的特定包中运行测试。例如,pnpm run test-serve assets 会在 serve 模式下为 playground/assetsplayground/assets-sanitize 运行测试。请注意,pnpm test 脚本不支持包匹配,它总是运行所有测试。

单元测试

除了 playground/ 下的集成测试外,包可能在其 __tests__ 目录下包含单元测试。单元测试由 Vitest 驱动。详细配置在 vitest.config.ts 文件中。

  • pnpm run test-unit 运行每个包下的单元测试。

pnpm run test-unit [match] 运行匹配给定过滤器的特定包中的测试。

测试环境和辅助函数

在 playground 测试中,你可以从 ~utils 导入 page 对象,这是一个 Playwright Page 实例,它已经导航到当前 playground 的服务页面。因此,编写一个测试非常简单:

js 复制代码
import { page } from '~utils'

test('should work', async () => {
  expect(await page.textContent('.foo')).toMatch('foo')
})

一些常见的测试辅助函数(例如 testDirisBuildeditFile)也可在 utils 中使用。源代码位于 playground/test-utils.ts

[!NOTE]
测试期间,dev server 的文件监听器以轮询模式运行。轮询(chokidar)仅在文件大小不同或 mtime 严格增加时才将文件注册为已更改。在某些平台上,快速的原地重写可能不会报告更新的 mtime,因此保持完全相同字节长度的编辑可能会被遗漏,导致预期的 HMR 更新或重建不会触发(造成不稳定的超时)。为了强制执行这一点,如果你的替换操作使文件的字节长度保持不变,editFile 会抛出错误;请确保编辑改变了文件大小(例如通过添加一个不影响测试语义的尾随空格或额外字符)。如果你通过其他方式触发监听的更改,请确保编辑会更改文件的字节长度。

注意:测试构建环境使用一组不同的默认 Vite 配置,以在测试期间跳过转译来加快速度。这可能会产生与默认生产构建不同的结果。

扩展测试套件

要添加新测试,你应该找到一个与修复或功能相关的 playground(或创建一个新的)。例如,静态资源加载在 assets playground 中测试。在这个 Vite 应用中,有一个针对 ?raw 导入的测试,index.html 中为其定义了一个部分

html 复制代码
<h2>?raw import</h2>
<code class="raw"></code>

这将被文件导入的结果修改:

js 复制代码
import rawSvg from './nested/fragment.svg?raw'
text('.raw', rawSvg)

...其中 text 工具函数定义为:

js 复制代码
function text(el, text) {
  document.querySelector(el).textContent = text
}

规范测试中,上面列出的对 DOM 的修改被用于测试此功能:

js 复制代码
test('?raw import', async () => {
  expect(await page.textContent('.raw')).toMatch('SVG')
})

关于测试依赖的注意事项

在许多测试用例中,我们需要使用 link:file: 协议来模拟依赖。pnpmlink: 视为符号链接,将 file: 视为硬链接。要测试依赖就像它们被复制到 node_modules 中一样,请使用 file: 协议。否则,请使用 link: 协议。

对于模拟依赖,请确保为包名添加 @vitejs/test- 前缀。这将避免可能出现的问题,比如误报。

Pull Request 指南

[!NOTE]
你无需请求许可即可处理一个已开启的问题。你可以直接开始调查或开启一个 PR。如果其他人先提交了修复,你仍然可以通过审查或验证解决方案来提供帮助。

  • 从基础分支(例如 main)检出一个主题分支,并合并回该分支。

  • 如果是添加新功能:

    • 添加相应的测试用例。
    • 提供有说服力的理由来添加此功能。理想情况下,你应该先开启一个建议 issue,并在开始处理之前获得批准。
  • 如果是修复 bug:

    • 如果你正在解决一个特殊问题,请在 PR 标题中添加 (fix #xxxx[,#xxxx])(#xxxx 是 issue id),以获得更好的发布日志(例如 fix: update entities encoding/decoding (fix #3899))。
    • 在 PR 中提供 bug 的详细描述。最好有在线演示。
    • 添加适当的测试覆盖。如果不适用,请在 PR 描述中解释为什么没有包含测试。
  • 如果是杂务(chore):

    • 对于错别字和注释更改,请尽量将多个更改合并到一个 PR 中。
    • 请注意,我们不鼓励贡献者提交主要是风格上的代码重构。 代码重构只有在提升性能或客观改善代码质量时才会被接受(例如,使相关的 bug 修复或功能实现更容易,并且作为一个独立的 PR 提交以改善 git 历史)。
      • 原因是代码可读性是主观的。这个项目的维护者已经根据他们的偏好选择了当前的代码风格,我们不想花时间解释我们的风格偏好。贡献者在贡献代码时应该尊重已建立的约定。另一个方面是,大规模的风格更改会产生影响多个文件的巨大差异,给 git 历史增加噪音,并使跨提交追踪行为变更变得更加困难。
  • 在处理 PR 时,有多个小提交是可以的。GitHub 可以在合并前自动压缩它们。

  • 确保测试通过!

  • 只要你安装了开发依赖,就无需担心代码风格。修改的文件会在提交时自动使用 Oxfmt 格式化(通过 simple-git-hooks 调用 Git Hooks)。

  • PR 标题必须遵循提交消息约定,以便自动生成变更日志。

维护指南

以下部分主要面向拥有提交权限的维护者,但如果你打算对代码库做出非平凡的贡献,浏览一下也会有所帮助。

Issue 分类工作流

flowchart TD start{遵循了 issue<br>模板吗?} start --否--> close1["关闭并要求<br>遵循模板"] start --是--> dupe{是重复的吗?} dupe --是--> close2[关闭并指向<br>重复的 issue] dupe --否--> repro{有正确的<br>复现吗?} repro --否--> close3[标记:'needs reproduction'<br>机器人将在 3 天内<br>无更新时自动关闭] repro --是--> real{这真的是 bug 吗?} real --否--> intended{这是预期<br>行为吗?} intended --是--> explain[解释并关闭<br>如果需要指向文档] intended --否--> open[保持开启以供讨论<br>移除 'pending triage' 标签] real --是--> real2["① 移除 'pending triage' 标签<br>② 添加相关的功能标签(如适用)<br>(例如 'feat: ssr')<br>③ 添加优先级和元标签(见下文)"] real2 --> unusable{这个 bug 是否使<br>Vite 无法使用?} unusable --是--> maj{这个 bug 是否影响<br>大多数 Vite 用户?} maj --是--> p5[p5:紧急] maj --否--> p4[p4:重要] unusable --否--> workarounds{这个 bug 有<br>变通方法吗?} workarounds --否--> p3[p3:次要 bug] workarounds --是--> p2[p2:边缘情况<br>有变通方法]

Pull Request 审查工作流

flowchart TD start{Bug 修复<br>还是<br>功能} start --BUG 修复--> strict_bug{"这是一个'严格修复'吗?<br>即修复一个没有副作用的明显疏忽"} start --功能--> feature[• 讨论功能的必要性<br>• 是否有更好的方式满足需求?<br>• 审查代码质量<br>• 添加标签<br>• 添加到里程碑<br>• 添加到团队看板] feature -.-> approve_non_strict[• 如果需要运行 vite-ecosystem-ci<br>• 如果你强烈认为 PR 是需要的,则批准并添加到里程碑] strict_bug --是--> strict[• 在本地验证修复<br>• 审查代码质量<br>• 如果适用要求添加测试用例<br>• 如有需要请求更改<br>• 添加标签] strict_bug --否--> non_strict[讨论修复的潜在副作用,例如<br>• 是否会在其他情况下引入隐式行为更改?<br>• 是否引入了太多更改?<br>• 添加标签<br>• 添加到团队看板] non_strict -.-> approve_non_strict strict --> approve_strict[如果准备合并则批准] approve_strict --> merge_strict[如果获得 2 名或更多团队成员批准则合并] approve_non_strict -.-> merge_non_strict[如果获得 2 名或更多团队成员批准且 PR 已在团队会议上讨论过则合并] merge_non_strict -.-> merge_extra merge_strict --> merge_extra["• 使用'Squash and Merge'<br>• 编辑提交消息以符合约定<br>• 在提交消息正文中,列出正在修复的相关问题,例如 'fix #1234, fix #1235'"]

发布

如果你有发布权限,以下步骤说明了如何为包进行发布。发布步骤分为两个阶段:“Release”和“Publish”。

“Release”在本地完成,用于生成变更日志和 git 标签:

  1. 确保 https://github.com/vitejs/vite 的 git remote 被设置为 origin
  2. vite 项目根目录的 main 分支上,运行 git pullpnpm i 使其保持最新。然后运行 pnpm build
  3. 运行 pnpm release 并按照提示为某个包进行发布。它将生成变更日志、git 发布标签,并将它们推送到 origin。你可以使用 --dry 标志进行测试运行。
  4. 当命令完成时,它将提供一个指向 https://github.com/vitejs/vite/actions/workflows/publish.yml 的链接。
  5. 点击该链接访问页面,并按照下面的后续步骤操作。

“Publish”在 GitHub Actions 上完成,用于将包发布到 npm:

  1. 稍后在工作流页面中,会为发布的包出现一个新的工作流,等待批准发布到 npm。
  2. 点击该工作流以打开其页面。
  3. 点击黄色框中的“Review deployments”按钮,会弹出一个弹窗。
  4. 勾选“Release”并点击“Approve and deploy”。
  5. 包将开始发布到 npm。

要了解更多关于 Vite 如何以及何时发布的信息,请查看发布文档。

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。