Skip to content

refactor: simplify patchedDependencies lockfile format#10911

Merged
zkochan merged 9 commits intomainfrom
update-lockfile-patch
Mar 8, 2026
Merged

refactor: simplify patchedDependencies lockfile format#10911
zkochan merged 9 commits intomainfrom
update-lockfile-patch

Conversation

@zkochan
Copy link
Copy Markdown
Member

@zkochan zkochan commented Mar 8, 2026

Summary

  • Simplify the patchedDependencies field in the lockfile from Record<string, { path: string, hash: string }> to Record<string, string> (selector → hash)
  • Remove PatchFile type, simplify PatchInfo to { hash: string }
  • Remove unused path field — patch file paths come from user config (opts.patchedDependencies), not from the lockfile
  • Simplify calcPatchHashes to return just hashes (no longer needs lockfileDir param)

Test plan

  • Existing patch tests pass with updated assertions
  • groupPatchedDependencies tests verify new format
  • getPatchInfo tests verify new format
  • sortLockfileKeys test updated for new format
  • Deploy test updated for new format

🤖 Generated with Claude Code

…rs to hashes

Remove the `path` field from patchedDependencies in the lockfile, changing the
format from `Record<string, { path: string, hash: string }>` to
`Record<string, string>` (selector → hash). The path was never consumed from
the lockfile — patch file paths come from user config, not the lockfile.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 8, 2026 12:30
@zkochan zkochan added this to the v11.0 milestone Mar 8, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the lockfile patchedDependencies representation to store only patch hashes (selector → hash), simplifying patch-related types and hashing logic across install/resolve/link flows.

Changes:

  • Change lockfile patchedDependencies from an object ({ path, hash }) to a plain hash string per selector.
  • Simplify patching types (PatchInfo) and update patch grouping / lookup utilities and tests to the new format.
  • Update install/link/build cache key generation and multiple tests to use patch.hash and the new lockfile shape.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
releasing/plugin-commands-deploy/test/shared-lockfile.test.ts Updates deploy test expectations for new patchedDependencies format and patch hash usage.
pkg-manager/resolve-dependencies/src/resolveDependencies.ts Uses patch.hash when creating patched package IDs.
pkg-manager/headless/src/linkHoistedModules.ts Uses depNode.patch?.hash in dep state calculation.
pkg-manager/headless/src/index.ts Uses depNode.patch?.hash in dep state calculation.
pkg-manager/core/test/install/patch.ts Updates patch install tests to expect selector → hash in lockfile.
pkg-manager/core/src/install/link.ts Uses depNode.patch?.hash in dep state calculation.
pkg-manager/core/src/install/index.ts Drops path-resolution for patches and groups patched deps from hash-only map.
patching/types/src/index.ts Simplifies PatchInfo to { hash: string }.
patching/config/test/index.test.ts Updates getPatchInfo tests for hash-only patch info.
patching/config/test/groupPatchedDependencies.test.ts Updates grouping tests for selector → hash input and hash-only patch info.
patching/config/src/index.ts Stops exporting removed PatchFile type.
patching/config/src/groupPatchedDependencies.ts Changes grouping input to selector → hash and emits hash-only patch info.
lockfile/types/src/index.ts Updates lockfile type for patchedDependencies to Record<string, string>.
lockfile/settings-checker/src/getOutdatedLockfileSetting.ts Updates outdated-setting checker typing for hash-only patched deps.
lockfile/settings-checker/src/calcPatchHashes.ts Changes calcPatchHashes to return only hashes and removes lockfileDir param.
lockfile/fs/test/sortLockfileKeys.test.ts Updates sort-key test fixtures for hash-only patched deps.
exec/build-modules/src/index.ts Updates dep state calculation to use depNode.patch?.hash.
Comments suppressed due to low confidence (1)

patching/types/src/index.ts:7

  • PatchInfo no longer contains any patch file path information, but the build/install pipeline applies patches from disk and needs to know where the patch file is located. With this type change, existing code that expects patch.file.path (e.g. in build-modules) can no longer type-check, and it’s unclear how runtime patch application will locate patch files. Consider introducing a separate runtime patch type that includes both hash and an absolute path (derived from user config), while keeping the lockfile representation as selector→hash.
export interface PatchInfo {
  hash: string
}

export interface ExtendedPatchInfo extends PatchInfo {
  key: string
}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

zkochan and others added 2 commits March 8, 2026 13:37
When reading a lockfile with the old `{ path, hash }` format for
patchedDependencies, extract just the hash string. This ensures
backwards compatibility with existing lockfiles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cation

The previous commit removed `path` from the lockfile format but also
accidentally dropped it from the runtime PatchInfo type. This broke
patch application since `applyPatchToDir` needs the file path.

- Add optional `patchFilePath` to `PatchInfo` for runtime use
- Build patch groups with resolved file paths in install
- Fix `build-modules` to use `patchFilePath` instead of `file.path`
- Fix `calcPatchHashes` call site in `checkDepsStatus` (extra arg)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

zkochan and others added 3 commits March 8, 2026 14:13
- Update getPatchInfo tests to use { hash, key } instead of { file, key }
- Fix createDeployFiles to handle patchedDependencies as hash strings
- Fix configurationalDependencies test assertion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Also guard against undefined patchedDependencies entry when
ignorePackageManifest is true.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@zkochan
Copy link
Copy Markdown
Member Author

zkochan commented Mar 8, 2026

Addressing Copilot review comments

All 7 Copilot review threads have been resolved. Here's what was done:

Runtime patch file path (build-modules, install/index.ts, groupPatchedDependencies)

The lockfile now only stores hashes, but patch application still needs file paths at runtime. Fixed by:

  • Added patchFilePath?: string to PatchInfo type (optional — lockfile-derived patches don't have a path)
  • groupPatchedDependencies() now accepts Record<string, string | PatchInfo>, so it can carry both plain hashes and rich patch info
  • In install/index.ts, patch file paths are resolved from opts.patchedDependencies (user config) and paired with computed hashes before building patch groups
  • In build-modules, an explicit error is thrown if depNode.patch exists but patchFilePath is missing, rather than silently skipping

calcPatchHashes call site (checkDepsStatus.ts)

Removed the extra rootDir argument and the now-unused rootDir destructuring.

createDeployFiles.ts

Updated to handle patchedDependencies as Record<string, string> (hashes). Patch file paths for the deploy manifest now come from rootProjectManifest.pnpm.patchedDependencies instead of the lockfile.

Other fixes

  • Updated getPatchInfo.test.ts — removed all file: { path, hash } references, now uses { hash, key }
  • Updated configurationalDependencies.test.ts — lockfile assertion updated to expect a hash string

opts.patchedDependencies values are already absolute paths, so
path.join(opts.lockfileDir, absolutePath) created invalid doubled
paths like /project/home/runner/work/pnpm/...

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +155 to +162
result.lockfile.patchedDependencies = { ...lockfile.patchedDependencies }
const manifestPatchedDeps = rootProjectManifest?.pnpm?.patchedDependencies
if (manifestPatchedDeps) {
result.manifest.pnpm!.patchedDependencies = {}
for (const name in manifestPatchedDeps) {
const resolvedPath = path.resolve(rootProjectManifestDir, manifestPatchedDeps[name])
const relativePath = normalizePath(path.relative(deployDir, resolvedPath))
result.manifest.pnpm!.patchedDependencies[name] = relativePath
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If lockfile.patchedDependencies is set but rootProjectManifest?.pnpm?.patchedDependencies is missing, the deploy output keeps patch hashes in the lockfile but emits no patch file paths in the manifest. Since patch paths are no longer stored in the lockfile, the deployed project may fail to install (especially with --frozen-lockfile). Consider requiring manifestPatchedDeps (and that it covers all patch keys) when copying lockfile.patchedDependencies, and otherwise throw a typed error with a hint or omit patchedDependencies from the deploy output.

Suggested change
result.lockfile.patchedDependencies = { ...lockfile.patchedDependencies }
const manifestPatchedDeps = rootProjectManifest?.pnpm?.patchedDependencies
if (manifestPatchedDeps) {
result.manifest.pnpm!.patchedDependencies = {}
for (const name in manifestPatchedDeps) {
const resolvedPath = path.resolve(rootProjectManifestDir, manifestPatchedDeps[name])
const relativePath = normalizePath(path.relative(deployDir, resolvedPath))
result.manifest.pnpm!.patchedDependencies[name] = relativePath
const lockfilePatchedDeps = lockfile.patchedDependencies
const manifestPatchedDeps = rootProjectManifest?.pnpm?.patchedDependencies
// Only propagate patchedDependencies when the manifest defines them
// and covers all patch keys present in the lockfile.
if (manifestPatchedDeps) {
const missingPatchKeys = Object.keys(lockfilePatchedDeps).filter(
(name) => manifestPatchedDeps[name] === undefined
)
if (missingPatchKeys.length === 0) {
result.lockfile.patchedDependencies = { ...lockfilePatchedDeps }
result.manifest.pnpm!.patchedDependencies = {}
for (const name in manifestPatchedDeps) {
const resolvedPath = path.resolve(rootProjectManifestDir, manifestPatchedDeps[name])
const relativePath = normalizePath(path.relative(deployDir, resolvedPath))
result.manifest.pnpm!.patchedDependencies[name] = relativePath
}

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +172
if (!depNode.patch.patchFilePath) {
throw new Error(
`Cannot apply patch for ${depPath}: patch file path is missing`
)
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing a plain Error here produces a less actionable failure and bypasses the existing patch-error handling that relies on pnpm-specific error codes. Consider throwing a PnpmError (or another existing typed error) that includes the patch selector / package and a hint to configure patchedDependencies, so users get a consistent error code and message.

Copilot uses AI. Check for mistakes.
zkochan and others added 2 commits March 8, 2026 14:55
- Use path.resolve instead of path.join to correctly handle both
  relative and absolute patch file paths
- Use PnpmError instead of plain Error for missing patch file path
- Only copy patchedDependencies to deploy output when manifest
  provides the patch file paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test was missing rootProjectManifest, so createDeployFiles could
not find the manifest's patchedDependencies to propagate to the
deploy output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@zkochan zkochan merged commit aeb06ca into main Mar 8, 2026
11 of 13 checks passed
@zkochan zkochan deleted the update-lockfile-patch branch March 8, 2026 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants