审查 Renovate 或 Dependabot PR
机器人在一个上午开了十五个 PR,大多数是例行操作,但其中一个悄悄修改了脚本,或收紧了 peerDependency,或升级了 engines 字段导致 CI 镜像失效。PR 标题写着 "chore(deps): bump foo from 4.1.0 to 4.2.1",您习惯性地信任并合并。将前后两个 package.json 粘贴到 diff 中,五秒内即可确认没有其他内容变动。这是 JS 工程师使用清单 diff 最常见的原因。
将旧版 package.json 粘贴到左侧,新版粘贴到右侧,即可查看依赖、脚本和引擎的具体变更。在浏览器中运行,不上传任何内容。
一个免费的浏览器内工具,用于并排比较两个 npm package.json 文件。将旧清单粘贴到左侧,新清单粘贴到右侧,差异会逐字符高亮显示。文本不会离开您的设备,这一点很重要,尤其是当清单属于私有仓库或未发布产品时。
它专为以下场景而设计:当 Renovate 或 Dependabot 的 PR 落地,您需要了解除版本升级之外还发生了什么变化。是否有人悄悄修改了脚本?engines 字段是否从 Node 18 升级到了 Node 20?某个 peerDependency 是否被收紧了?GitHub 的 diff 视图可以回答部分问题,但当您需要逐一扫描多个 PR 或对比尚未推送的分支时,将两个清单粘贴到干净的双栏视图中更为便捷。
底层使用的是与我们 compare-json 页面相同的 JSON 感知 diff 引擎,针对 npm 和 yarn 工作流进行了定制。两个清单在 diff 运行前都会作为 JSON 解析并格式化,因此空白字符的差异不会污染高亮结果。如果需要对非 JSON 清单格式进行纯文本 diff,请使用我们的 compare-text 工具;pnpm-lock.yaml 则可使用 compare-yaml。
每个面板都会被解析为 JSON。如果解析成功,清单会以统一的两空格缩进重新格式化,并保留原有的键顺序。如果解析失败(多余的逗号、缺少括号、复制粘贴时截断了内容),工具会回退到纯文本模式并告知您出错的行。当两侧均为有效 JSON 后,diff 会逐字符运行,然后通过语义清理将变更分组为可读的块,例如将版本从 ^18.2.0 升级到 ^19.0.0 会显示为一次编辑,而不是九次字符编辑。
右侧面板的插入内容以绿色显示,左侧面板的删除内容以红色显示。两个面板会同步滚动,因此当您在一侧的 devDependencies 深处找到某处变更时,另一侧会跳转到相同的键。由于 diff 在格式化后是文本级别的,它以人类可读的方式呈现结构变化:删除的依赖以一行的形式消失,版本范围的变化在值内部高亮,新增的脚本出现在您指定的位置。
这个工具有意不做的事:它不是依赖解析器。它不会告诉您某个 caret 范围的升级会拉入哪些传递依赖,某个 peer 是否满足,或新版本是否引入了安全公告。为此,请在本地运行 npm install 后执行 npm audit,或使用 npm 配合 Snyk 或 GitHub Dependabot 警报等感知 lockfile 的服务。这个页面告诉您清单文本说了什么,解析工作由您的包管理器负责。
两个文本面板,一次 diff。无需 CLI 参数,无需安装,无需阅读 patch 格式。
从编辑器、git show main:package.json 或团队成员处复制 package.json 的前一个版本,粘贴到左侧面板。工具会将其解析为 JSON 并格式化;如果 JSON 无效,编辑器会显示解析错误,您可以在 diff 之前修复片段。
对更新后的版本执行相同操作。如果您正在审查 Renovate 或 Dependabot 的 PR,最方便的来源是 PR 分支中的原始文件。两个文件都会以统一的缩进格式化,因此空白字符的编辑不会在 diff 中显示为虚假变更。
左侧以红色删除线显示删除内容,右侧以绿色显示插入内容。先扫描依赖块,再检查脚本区域是否有命令修改,然后检查 engines 和 peerDependencies 字段。单个值内的版本升级会高亮显示变更的字符,一眼便能区分 patch 升级和 major 升级。
机器人在一个上午开了十五个 PR,大多数是例行操作,但其中一个悄悄修改了脚本,或收紧了 peerDependency,或升级了 engines 字段导致 CI 镜像失效。PR 标题写着 "chore(deps): bump foo from 4.1.0 to 4.2.1",您习惯性地信任并合并。将前后两个 package.json 粘贴到 diff 中,五秒内即可确认没有其他内容变动。这是 JS 工程师使用清单 diff 最常见的原因。
您和同事在各自的特性分支上都修改了 package.json。由于编辑位于不同块中,Git 可以干净合并,但干净合并不等于正确合并。将两个分支的清单粘贴到 diff 中,找出某个分支删掉的脚本、某个分支降级的依赖,或两个分支以不同版本添加了同一个包的冲突。这比在 CI 对合并结果运行 npm ci 后才发现问题要便宜得多。
编辑 package.json 只是变更的一半。另一半是 package-lock.json 中记录的已解析依赖树。运行 npm install 后,将旧 lockfile 和新 lockfile 并排粘贴,查看哪些传递依赖被引入。当 caret 范围拉入深层嵌套依赖的新 minor 版本时,意外新增包的情况很常见。对于原始 lockfile 检查,我们的 compare-json 页面能更好地处理较大的文件;本页面最适合用于清单本身。
某个版本发布后出现 bug,二分查找后怀疑问题在依赖树中某处。将上一个正常版本的 package.json 粘贴到当前版本旁边,在脑海中略过未变更的块,聚焦于高亮的版本范围。修复方法通常是将某个库降级到上一次绿色构建中固定的版本。找到嫌疑对象后,用精确版本(不带 caret 或 tilde)锁定它,直到上游问题解决。
两位工程师,一次棘手的合并,rebase 结束后出现两个不同的 package.json。lockfile 更是一团糟。将两个清单并排粘贴,查看哪些键存在分歧,然后有意识地解决它们,而不是盲目接受 git merge -X theirs 的结果。当新贡献者的 npm install 拉取了与您不同的依赖树,您怀疑存在清单漂移时,这也是正确的工具。
在运行 npm publish 发布库之前,需要关注的清单字段与应用不同:main、module、types、exports、files、peerDependencies、engines。将即将发布的清单与上一次发布的清单进行 diff。丢失的 peerDependencies 条目、变更的 exports 条件或收紧的 engines 范围,可能以 npm 本身无法警告您的方式破坏消费者。Node.js 包文档 是这些字段含义的参考资料。
真实 package.json diff 中出现的字段及其含义。在批准 Renovate PR 或合并两个都修改了清单的分支之前,值得快速浏览一下。
| Topic | What this tool does |
|---|---|
| dependencies vs devDependencies vs peerDependencies | dependencies 随您的包一起发布,任何消费者安装时都会安装。devDependencies 只有在项目根目录运行 npm install 时才会安装,下游消费者不会安装。peerDependencies 是您的库期望宿主提供的包(通常是 React、测试框架、打包工具),npm 7+ 会自动安装它们,除非存在冲突。在这些块之间移动包会改变谁承担安装成本。 |
| Caret (^) 与 tilde (~) semver 范围 | Caret ^1.2.3 允许任何低于 2.0.0 的版本,是 npm 的默认值,也是最宽松的常见范围。Tilde ~1.2.3 只允许 patch 更新,低于 1.3.0。1.2.x 与 tilde 等效。* 表示任意版本(生产环境不要使用)。latest 在安装时解析,会产生不可重现的构建,应避免使用。 |
| 精确锁定(无范围前缀) | 写 "react": "18.2.0" 不带 caret 或 tilde,会锁定到精确版本。结合 lockfile,这能提供最可重现的安装,代价是永远不会自动接收安全补丁。有些团队锁定所有依赖;有些则依赖 caret+lockfile 的组合。两者没有绝对的对错之分,权衡在于更确定的构建与更可能过时的依赖之间。 |
| package-lock.json 的确定性 | package-lock.json 记录了依赖树中每个包(包括传递依赖)的精确解析版本。npm ci 从 lockfile 安装且不允许修改它;npm install 在范围允许更新版本时可能会修改它。CI 环境始终使用 npm ci;开发环境使用 npm install 没问题,只要您提交产生的 lockfile 变更。 |
| monorepo 的 workspaces 字段 | workspaces 数组告诉 npm 将兄弟目录视为 monorepo 中的链接包。在根目录运行 npm install 会安装所有工作区,并创建一个单一提升的 node_modules 树。Yarn 和 pnpm 支持各自约定的工作区,pnpm 的提升策略更为保守,能在安装时捕获更多依赖泄露问题。 |
| engines 字段 | engines.node 声明您的包支持的 Node.js 版本。默认情况下,npm 只在宿主违反此条件时发出警告;在 .npmrc 中设置 engine-strict=true 则会将其变为硬错误。升级 engines 字段对仍在使用旧版 Node 的消费者来说是破坏性变更,而这类变更往往隐藏在 Renovate 的 "chore" PR 中。务必仔细阅读 engines diff。 |
| ES module 的 exports 字段 | exports 字段控制消费者可以从您的包中导入哪些路径,以及哪些条件(import、require、types、node、browser)解析到哪些文件。添加或移除条目对下游代码来说是破坏性变更。Node.js 包文档 详细介绍了解析规则;除非您明确添加新入口点,否则应将任何 exports diff 视为 major 版本级别的编辑。 |
| package.json 内部的字段顺序 | package.json 中顶层键没有强制顺序。按照惯例,大多数项目以 name、version、description 开头,然后是 scripts,再是依赖。在依赖块内部,按字母顺序排列键是事实标准,大多数包管理器在保存时会对块进行排序。显示条目被重新排列但内容完全相同的 diff,通常是工具差异,而非真正的变更。 |
npm diff 是 npm 的内置命令,用于比较 registry 上某个包的两个已发布版本(包括 tarball 和源代码)。npm-check-updates(ncu)报告清单中哪些依赖有更新版本可用。两者都不能显示您磁盘上或两个分支中两个任意 package.json 文件之间的差异。本工具做的正是这件事。用 ncu 了解应该升级什么,用 npm diff 查看 registry 端两个版本之间的变更,用本页面在并排视图中阅读您自己的前后清单。
不会。本页面只对文本进行 diff,不查询 npm registry、GitHub Advisory Database 或任何漏洞服务。安全审计请对已安装的依赖树运行 npm audit,使用 npm audit signatures 验证包来源,或依赖 Snyk、Socket 或 Dependabot 警报。本工具适合用于了解清单发生了什么变化,而不适合判断该变化是否安全可发布。
仅凭清单无法做到。package.json 只列出直接依赖及其请求的版本范围。完整的已解析依赖树(包括传递依赖)存在于 package-lock.json(或 yarn.lock 或 pnpm-lock.yaml)中。要比较已解析的依赖树,请将两个 lockfile 粘贴进行 diff。由于 lockfile 较大,我们的 compare-json 页面比本页面更适合处理它们。对于 pnpm-lock.yaml,请使用 compare-yaml。本页面针对清单本身进行了优化。
像粘贴清单一样将两个 lockfile 粘贴到面板中。工具会将它们解析为 JSON、格式化并进行 diff。请注意,lockfile 可能有数千行,高亮的 diff 可能会很长。先关注顶层 packages 条目,再关注 version 字段。对于超过约五千行的文件,我们的 compare-json 页面更适合,因为它专门处理大型 JSON 内容。
两者都是 semver 范围,允许在不手动编辑清单的情况下进行更新。caret ^1.2.3 允许任何不改变最左侧非零数字的版本,即接受 1.2.3 到 1.999.999,但不接受 2.0.0。tilde ~1.2.3 更严格:只允许 patch 更新,接受 1.2.3 到 1.2.999,但不接受 1.3.0。caret 是 npm 的默认值,范围更宽松;当某个库的 minor 版本有破坏性变更历史时,可使用 tilde。
实际上有。diff 在浏览器中运行,因此非常大的输入(例如来自 monorepo 的两万行 lockfile)可能会根据内存情况导致页面变慢或标签页卡顿。对于典型的应用清单和每侧几千行以内的 lockfile,diff 几乎瞬间完成。对于更大的文件,我们的 compare-json 页面是更好的入口。如果您需要频繁比较超大 lockfile,可考虑在本地运行 git diff package-lock.json 并通过分页器查看,这种方式比任何浏览器工具都更具扩展性。
粘贴两个清单并点击格式化,使两侧的键以相同顺序排列。版本升级随后会在同一行显示为红绿配对高亮:左侧划去旧的 ^18.2.0,右侧以绿色显示新的 ^19.0.0。字符级高亮落在值内部,一眼即可区分 patch 升级和 major 升级。^ 和 ~ 前缀也会高亮,当范围放宽或收紧而数字本身未移动时,这一点很重要。
本工具比较的是您剪贴板中的两个 package.json 文件,无需克隆仓库,无需调用 registry,不上传任何内容。GitHub 的 diff 需要两个版本都已提交并推送,当您比较本地分支与团队成员的粘贴内容或未推送的进行中工作时,这种方式就不够用了。npm diff 则完全是另一回事:它比较 registry 上两个已发布包的 tarball,而不是磁盘上的两个清单。当两个文件就摆在您面前时,使用本工具即可。
可以。两者都只是文本,而 package-lock.json 是 JSON,因此可以像任何清单一样粘贴并进行 diff。它们体积较大,所以在阅读之前先点击格式化以统一缩进。lockfile diff 本质上会有很多噪音,因为单个依赖升级会在数百行中重写已解析的依赖树和哈希值。真正有用的信号是版本号以及 resolved 和 integrity 行;略过周围的噪音即可。对于超过几千行的 lockfile,我们的 compare-json 页面更适合处理其大小。
安全。diff 完全在您的浏览器中运行,文本不会被上传、记录或发送到任何地方。这一点很重要,因为 package.json 可能泄露的信息超出预期:publishConfig 中的私有 registry URL、内部 scoped 包名(如 @yourcompany/auth)、description 中未发布的产品名称。在大多数工作场所政策下,将这些内容粘贴到服务器端 diff 网站将是真正的数据处理事件。想要验证?打开 DevTools,粘贴时观察 Network 标签,您会看到没有任何出站请求。
免费,无需创建账户。没有试用期,没有席位限制,没有邮件门槛。粘贴两个清单,阅读 diff,关闭标签页。每个按钮——格式化、上传、示例——在首次访问时无需登录即可使用。这是一个专用工具,作为免费工具提供,而非付费漏斗,因此没有隐藏在锁后面的功能层。
您的清单不会离开浏览器。diff、JSON 解析、高亮和渲染都在您的设备上运行。我们不会上传文本,不会记录,也不会将其发送给任何第三方服务。对于专有代码而言,这一点尤为重要:在云端 diff 服务中粘贴来自未发布库的 package.json 或私有仓库的 lockfile,本身就可能违反雇主的数据处理政策,特别是当清单包含内部 scoped 包、私有 registry 主机或开发中的产品名称时。验证这一点很简单:打开浏览器 DevTools,切换到 Network 标签,粘贴两个清单并观察。比较时没有任何出站请求。同样的隐私模型适用于我们的其他工具,包括 compare-json、用于 pnpm-lock.yaml 的 compare-yaml 以及用于通用代码审查的 git-diff-online。相关规范请参阅 Yarn 配置参考 和 npm package.json 文档。