Skip to content

Commit

Permalink
fix: support yarn4 semver resolutions (#7442)
Browse files Browse the repository at this point in the history
### Description

Fixes #7414

Crux of the issue is that Yarn4 started encoding the default `npm:`
protocol in `yarn.lock`, but not in other places where it is assumed
such as [`resolutions` in
`package.json`](https://yarnpkg.com/configuration/manifest#resolutions).
We work around this by now applying overrides on packages that would
match a `resolution` entry if the `npm:` protocol was added.

### Testing Instructions

Added unit test, existing unit tests.

Verified repro provided in repo now produces a pruned lockfile that is
`yarn install --immutable`


Closes TURBO-2404
  • Loading branch information
chris-olszewski committed Feb 21, 2024
1 parent ae69ebc commit b417e36
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 2 deletions.
47 changes: 47 additions & 0 deletions crates/turborepo-lockfiles/fixtures/yarn4-resolution.lock
@@ -0,0 +1,47 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!

__metadata:
version: 8
cacheKey: 10

"@reproduce/something@workspace:packages/something":
version: 0.0.0-use.local
resolution: "@reproduce/something@workspace:packages/something"
dependencies:
react: "npm:^18.2.0"
languageName: unknown
linkType: soft

"js-tokens@npm:^3.0.0 || ^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
checksum: 10/af37d0d913fb56aec6dc0074c163cc71cd23c0b8aad5c2350747b6721d37ba118af35abdd8b33c47ec2800de07dedb16a527ca9c530ee004093e04958bd0cbf2
languageName: node
linkType: hard

"loose-envify@npm:^1.1.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
dependencies:
js-tokens: "npm:^3.0.0 || ^4.0.0"
bin:
loose-envify: cli.js
checksum: 10/6517e24e0cad87ec9888f500c5b5947032cdfe6ef65e1c1936a0c48a524b81e65542c9c3edc91c97d5bddc806ee2a985dbc79be89215d613b1de5db6d1cfe6f4
languageName: node
linkType: hard

"react@npm:18.1.0":
version: 18.1.0
resolution: "react@npm:18.1.0"
dependencies:
loose-envify: "npm:^1.1.0"
checksum: 10/d1ec025276096aa8b87cc4ee95f911612f21c4d54e08a3fdf5a9d3d2242f825142eef300b13f26e17a14a94adcb640f4278ae5b4fe0de3a912adc325434b9a2a
languageName: node
linkType: hard

"reproduce@workspace:.":
version: 0.0.0-use.local
resolution: "reproduce@workspace:."
languageName: unknown
linkType: soft
45 changes: 44 additions & 1 deletion crates/turborepo-lockfiles/src/berry/mod.rs
Expand Up @@ -809,7 +809,7 @@ mod test {
key: "debug@npm:1.0.0".into(),
version: "1.0.0".into()
}
)
);
}

#[test]
Expand Down Expand Up @@ -895,6 +895,49 @@ mod test {
}));
}

#[test]
fn test_nonexistent_resolutions_dependencies() {
let data: LockfileData =
serde_yaml::from_str(include_str!("../../fixtures/yarn4-resolution.lock")).unwrap();
let manifest = BerryManifest {
resolutions: Some(
[("react@^18.2.0".to_string(), "18.1.0".to_string())]
.iter()
.cloned()
.collect(),
),
};
let lockfile = BerryLockfile::new(data, Some(manifest)).unwrap();

let actual = lockfile
.resolve_package("packages/something", "react", "^18.2.0")
.unwrap()
.unwrap();
let expected = Package {
key: "react@npm:18.1.0".into(),
version: "18.1.0".into(),
};
assert_eq!(actual, expected,);

let pruned = lockfile
.subgraph(
&["packages/something".into()],
&[
"react@npm:18.1.0".into(),
"loose-envify@npm:1.4.0".into(),
"js-tokens@npm:4.0.0".into(),
],
)
.unwrap();
assert_eq!(
pruned
.resolve_package("packages/something", "react", "^18.2.0")
.unwrap()
.unwrap(),
expected
);
}

#[test]
fn test_workspace_collision() {
let data = LockfileData::from_bytes(include_bytes!(
Expand Down
6 changes: 5 additions & 1 deletion crates/turborepo-lockfiles/src/berry/resolution.rs
Expand Up @@ -130,7 +130,11 @@ impl Resolution {
}

if let Some(resolution_range) = &self.descriptor.description {
if resolution_range != &dependency.range {
if resolution_range != &dependency.range
// Yarn4 encodes the default npm protocol in yarn.lock, but not in resolutions field of package.json
// We check if the ranges match when we add `npm:` to range coming from resolutions.
&& !Self::eq_with_protocol(&dependency.range, resolution_range, "npm:")
{
return None;
}
}
Expand Down

0 comments on commit b417e36

Please sign in to comment.