首页
/ Vite 致谢页全解析:依赖鸣谢清单如何由 LICENSE.md 与 package.json 自动生成

Vite 致谢页全解析:依赖鸣谢清单如何由 LICENSE.md 与 package.json 自动生成

2026-09-03 15:31:47作者:盛欣凯Ernestine

Vite 官方文档中的致谢页(Acknowledgements)并不是一份人工维护的静态名单——页面上的依赖包、作者与赞助信息全部由 VitePress 数据加载器在构建时从仓库文件自动读取和聚合。本文沿这条完整的"许可证汇总 → LICENSE.md → 致谢页"流水线逐环节展开,并解释 Vite "绝大多数依赖放入 devDependencies 再预打包"的依赖策略,帮助你在阅读这份鸣谢清单的同时,理解 Vite 核心包是如何保持轻量的。

一、致谢页包含什么:人、资金与包的完整清单

致谢页的页面结构由四个板块组成,这也是本文的骨架:

  • Contributors(贡献者):Vite 由一个国际化团队开发,核心成员见团队页;同时鸣谢所有通过代码、Bug 报告、文档及文档翻译帮助改进 Vite 的贡献者。
  • Sponsors(赞助者):页面通过 VitePress 主题提供的 useSponsor 可组合函数和 VPSponsors 组件动态渲染赞助商列表,支持渠道为 GitHub Sponsors 与 Open Collective。
  • Dependencies(依赖):又分两部分——Notable Dependencies(重点依赖,以卡片形式展示作者、描述与仓库/赞助链接)和 Bundled Dependency Authors(被打包进发行产物的依赖的作者汇总表,按作者归组)。
  • Development Tools(开发工具):支撑 Vite 自身开发工作流的工具清单。
  • Past Notable Dependencies(历史重点依赖):记录 Vite 早期版本使用、如今已被替换的项目。

页面本身只是"展示层"(docs/acknowledgements.md 中大量使用 v-for 循环渲染数据数组),真正决定页面内容的是下一节的数据源文件。

二、数据源:一个构建期读取 node_modules 的 VitePress 数据加载器

致谢页所有动态数据来自 docs/_data/acknowledgements.data.ts。这个文件是一个标准的 VitePress data loader(导出 { watch, load }),构建文档时执行,其工作分三步:

1. 静态名单 + 解析 LICENSE.md 得到"被打包依赖"全集。 文件开头硬编码了三份名单(acknowledgements.data.ts#L5-L20):

// Notable dependencies to highlight (by package name)
const notableDependencies = [
  'rolldown',
  'postcss',
  'lightningcss',
  'chokidar',
  'magic-string',
]

// Dev tools used for development
const devToolNames = [
  'eslint',
  'oxfmt',
  'typescript',
  'vitest',
  'playwright-chromium',
]

而"Bundled dependencies"并不在这里写死,而是由 parseBundledDependenciesFromLicense()acknowledgements.data.ts#L113-L126)从 packages/vite/LICENSE.md 中解析:

const bundledSection = content.split('# Bundled dependencies:\n')[1]
// Match all ## headers which contain package names (comma-separated for grouped packages)
const deps = [...bundledSection.matchAll(/^## (.+)$/gm)].flatMap((m) =>
  m[1].split(',').map((n) => n.trim()),
)

注意 split(',') 这一步:LICENSE.md 中的 ## 标题可能是"pkg1, pkg2, pkg3"这样的逗号分组形式(例如当前的 ## braces, fill-range, is-number## mlly, ufo),解析器必须按逗号拆开才能还原出真实包名——这个细节与第三节 LICENSE.md 的生成逻辑严格对应。

2. 逐包读取 node_modules 中的 package.json。 readPackageInfo()acknowledgements.data.ts#L195-L219)依次尝试 packages/vite/node_modules/<name>/package.json 和仓库根 node_modules/<name>/package.json,提取 nameversiondescriptionauthorrepositoryfunding 字段。包不存在时返回 null 并被过滤——注释说明原因:某些包可能只是可选 peer 依赖,并未真正安装。配套的两个归一化函数覆盖了 npm 元数据的各种"方言":

  • normalizeRepository()acknowledgements.data.ts#L128-L159):将 git+https://...ssh://user@host.com:org/repo.gitgithub:org/repogitlab:bitbucket: 以及裸的 owner/repo 等形式统一成可用的 HTTPS 链接;
  • parseAuthor():同时支持对象形式和字符串形式("Name <email> (url)"),拆出姓名与个人主页;
  • normalizeFunding()funding 字段无论是字符串、对象还是数组,统一取第一个 URL。

3. 按作者归组,生成"Bundled Dependency Authors"表。 groupByAuthor()acknowledgements.data.ts#L221-L264)把非重点的打包依赖(bundledDependencies 中排除掉 notableDependencies 后的部分)按 author 字段分桶,包名与作者均按字母序排序。有一个展示层面的优化:若某作者名下所有包的 funding URL 完全一致,则把赞助链接提升到作者级别,包列表里就不再逐个重复显示 Sponsor 按钮。

最后,loader 通过 watch: ['../../packages/vite/LICENSE.md']acknowledgements.data.ts#L314-L319)声明了对 LICENSE.md 的监听——依赖集合变化导致 LICENSE.md 重新生成后,文档构建会自动重建该页数据,整个链路因此是自我同步的。

三、流水线上游:LICENSE.md 本身是构建时自动生成的

packages/vite/LICENSE.md 并非手写。它的生成逻辑在 packages/vite/rollupLicensePlugin.ts:一个包装 rollup-plugin-licensethirdParty 钩子的构建插件,做四件事:

  1. 读取核心许可证:以仓库根 LICENSE(MIT)作为 "Vite core license" 段落写入文件开头;
  2. 排序与分组rollupLicensePlugin.ts#L22-L43):依赖按名称排序;许可证全文相同的依赖被合并成一个 ## pkg1, pkg2, pkg3 分组标题——这正是第二节数据加载器要按逗号切分的来源。若同组依赖的许可证与作者也完全一致,License:/By:/Repositories: 元信息只打印一次;
  3. 输出汇总:在 # Licenses of bundled dependencies 段落中列出产物包含的全部许可证类型(当前为 Apache-2.0、BSD-2-Clause、CC0-1.0、ISC、MIT),随后是 # Bundled dependencies: 明细区;
  4. 落盘并提醒提交rollupLicensePlugin.ts#L108-L116):只有内容变化时才写回 LICENSE.md,并在终端打印黄色警告 "LICENSE.md updated. You should commit the updated file."。插件还覆盖了 renderChunk/generateBundle 钩子,在 watch 模式下直接跳过,避免开发期反复写文件。

该插件挂载在 packages/vite/rolldown.config.tsnodeConfig 上(rolldown.config.ts#L134-L138):

plugins: [
  shimDepsPlugin({ /* ... */ }),
  buildTimeImportMetaUrlPlugin(),
  licensePlugin(
    path.resolve(dirname, 'LICENSE.md'),
    'Vite core license',
    'Vite',
  ),
  // ...
]

这个 nodeConfig 打包 src/node/index.tssrc/node/cli.tssrc/node/internalIndex.ts 三个入口,并把 pkg.dependenciespkg.peerDependencies 的全部键名标记为 externalrolldown.config.ts#L86-L99)——也就是说真正被内联进 dist 的只有 devDependencies,而 rollup-plugin-license 在生成报告时恰好只统计"实际被打包进来"的依赖,于是 LICENSE.md 的内容就精确等于"发行产物中包含的第三方代码",致谢页也因此得名 "Bundled" dependencies。

四、为什么有这么多"Bundled Dependencies":Vite 的依赖瘦身策略

packages/vite/package.json 末尾有这样一条注释,堪称理解致谢页的前提:

"//": "READ CONTRIBUTING.md to understand what to put under deps vs. devDeps!"

CONTRIBUTING.md 的 "Notes on Dependencies" 一节给出了完整规则:

  • 目标:Vite 追求轻量,包括对 npm 依赖数量和体积的敏感;
  • 核心机制:"We use Rolldown to pre-bundle most dependencies before publishing"——发布前用 Rolldown 把大部分依赖预打包进产物,因此即使某个依赖在运行时源码中被使用,默认也应放进 devDependencies
  • 例外(必须进 dependencies 的情况):类型包(@types/*);含二进制文件无法被打包的依赖(如 rolldownlightningcss);其自带类型会出现在 Vite 公开类型中的依赖(如 rolldown);
  • 约束:由于 devDependencies 打包后从产物中"消失",源码中不能用普通 require('somedep')(ESM 中会被忽略、发布后也找不到),而要写成 (await import('somedep')).default 的懒加载形式,兼顾启动性能与打包正确性;
  • 体积纪律:CONTRIBUTING 举了一个真实案例——http-proxy 本身约 380 kB,而 http-proxy-middleware 会拖入约 3 MB 的传递依赖,相比之下在 http-proxy 之上写几行自定义中间件即可,这也是 Vite 选择自研中间件的原因。

对照 packages/vite/package.json#L71-L77 可以看到策略的实际结果——vite 的运行时 dependencies 只有 5 个:

"dependencies": {
  "lightningcss": "^1.33.0",
  "picomatch": "^4.0.7",
  "postcss": "^8.5.26",
  "rolldown": "~1.2.6",
  "tinyglobby": "^0.2.17"
}

外加 optionalDependencies 中的 fsevents(macOS 原生监听)。而 devDependencies 里则躺着 chokidarmagic-stringes-module-lexersirvconnect 等三十多个"运行时也用"的包——它们全部会被打进 dist/node,并因此出现在 LICENSE.md 与致谢页中。

类型方面还有一个配套机制:为了让 Vite 能在 TypeScript 项目中被完整引用(例如供 VitePress 使用),需要把部分依赖的类型内联到 packages/vite/src/types,然后用 pnpm run build-types-check 校验打包后的类型不依赖任何 devDependencies。此外,rolldown.config.ts 中还有一个 bundleSizeLimit(55) 插件(rolldown.config.ts#L388-L416),当 module-runner 产物超过约 55 kB 时直接令构建失败——"轻量"在这里是硬约束,不只是口号。

五、Notable Dependencies 与开发工具

数据文件中的两份静态名单对应页面两组卡片:

Notable Dependenciesrolldownpostcsslightningcsschokidarmagic-string。它们在源码中的位置可以从 import 关系直接印证:

  • rolldown 是当前 Vite 的构建与转换引擎:从 optimizer/scan.tsoptimizer/index.ts(依赖预构建)到 build.tspluginContainer.tshmr.ts 等几十个核心模块都直接从 rolldown 导入插件与类型;
  • magic-string 用于精确的源码改写,Vite 自己的构建配置就用它实现 shimDepsPluginbuildTimeImportMetaUrlPluginrolldown.config.ts#L229-L299);
  • postcss 是 CSS 处理管线核心,配合 postcss-importpostcss-load-configpostcss-modules 等(均在 devDependencies 中,打包时通过 shimDepsPlugin 剔除冗余 import,见 rolldown.config.ts#L101-L132 的注释);
  • lightningcss 用于 CSS 压缩等原生加速路径;
  • chokidar 是文件监听基础,仓库根 patches/ 目录中还维护着 chokidar@3.6.0.patch 等 pnpm patch,说明 Vite 会直接修补依赖行为而非整体替换。

Development Toolseslintoxfmttypescriptvitestplaywright-chromium,与根 package.json 的 devDependencies 一一对应。仓库脚本体现了它们的分工:pnpm lint(eslint 9 + typescript-eslint)、pnpm format(oxfmt,同时由 lint-staged 在 pre-commit 时执行)、pnpm typecheck(tsc 多项目配置)、pnpm test(vitest 单元测试 + 基于 Playwright 的 test-serve/test-build 集成测试,测试目标即 playground/ 下上百个场景目录)、pnpm docs(VitePress 构建本文所在文档站)。

六、Past Notable Dependencies:一份引擎演进的对照记录

acknowledgements.data.ts#L23-L55 中的 pastNotableDependencies 列出了 Vite 曾经依赖、如今已替换或移除的六个项目,其"replacement 备注"恰好勾勒出 Vite 底层的演进轨迹:

用途 现状(据数据文件备注与 package.json 印证)
esbuild JS/TS 打包与压缩 描述为 "now using Rolldown, Oxc, and LightningCSS";但 esbuild 仍保留为可选 peerDependency 与 devDependency(^0.27.0 || ^0.28.0 / ^0.28.2),部分转换路径仍可能需要用户安装
rollup ESM 打包器 "now using Rolldown";rollup 仍留在 devDependencies(^4.59.0),主要服务于构建工具链生态(如许可证插件 rollup-plugin-license,其类型即来自 rollup
http-proxy HTTP 代理 "now using http-proxy-3",对应 devDependencies 中的 http-proxy-3: ^1.23.3
acorn JavaScript 解析器 已从依赖中移除(解析能力由 Oxc 体系承接,可参见 plugins/oxc.ts 的存在)
fast-glob 快速 glob 匹配 "now using tinyglobby/fdir",对应运行时依赖 tinyglobby: ^0.2.17
debug 调试日志 "now using obug",对应 devDependencies 中的 obug: ^1.0.2

从源码结构看,"Rolldown 取代 esbuild/rollup"这一迁移已完成度很高:vite 的构建入口、模块图、HMR、SSR 转换等核心模块均直接面向 rolldown 的插件 API 编写,而不再经由 esbuild 或 rollup 中转。

七、给包作者的提示:你的 package.json 元数据决定你在致谢页的样子

原文档中有一个面向第三方包作者的说明,值得单独强调:

This section is automatically generated from the author and funding fields in each package's package.json. If you'd like to update how your package appears here, you can update these fields in your package.

结合第二节的解析逻辑,其含义非常具体:如果你的包出现在 vite 的 devDependencies 中、被预打包进了 dist/node(从而进入 LICENSE.md 的 # Bundled dependencies: 区),那么该包在 package.json 中声明的 author(支持对象或 "Name <email> (url)" 字符串)将决定它是否以及以谁的名义出现在 "Bundled Dependency Authors" 表中,funding 字段(字符串、对象或数组的第一个 URL)则决定作者行/包名旁是否出现 Sponsor 链接;repository 字段会被 normalizeRepository() 归一化后展示。换言之,更新你所在包的 authorfunding 字段,就是更新它在 Vite 致谢页呈现方式的唯一途径——而 Vite 侧的整条链路(构建生成 LICENSE.md → 数据加载器解析 → 页面渲染)无需任何人工介入。

小结

Vite 的致谢页看似一份静态鸣谢名单,实际是一条完整的自动化管线:构建时 rollupLicensePlugin 将预打包依赖的许可证汇总写入 LICENSE.md,文档构建时 acknowledgements.data.ts 再解析该文件并结合 node_modules 中的 package.json 元数据生成卡片与作者表。这条管线背后,是 Vite "运行时依赖仅 5 个、其余全部预打包"的依赖纪律(CONTRIBUTING.md)与对产物体积的硬约束。理解这套机制后,你可以把致谢页当作观察 Vite 依赖演进的窗口——包括 esbuild 到 Rolldown、http-proxy 到 http-proxy-3 这样已经落地的替换。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384