Skip to content

Commit f9520fb

Browse files
committed
Merge branch 'main' into npm-packument-in-lockfile
2 parents ba5f05e + 54be772 commit f9520fb

10 files changed

+891
-30
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[package]
44
name = "deno_lockfile"
5-
version = "0.24.0"
5+
version = "0.25.0"
66
edition = "2021"
77
license = "MIT"
88
description = "An implementation of a lockfile used in Deno"

src/graphs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ enum LockfileGraphPackage {
7474
struct LockfileNpmGraphPackage {
7575
/// Root ids that transitively reference this package.
7676
root_ids: HashSet<LockfilePkgId>,
77-
integrity: String,
77+
integrity: Option<String>,
7878
dependencies: BTreeMap<StackString, LockfileNpmPackageId>,
7979
optional_dependencies: BTreeMap<StackString, LockfileNpmPackageId>,
8080
os: Vec<SmallStackString>,

src/lib.rs

+61-8
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub struct SetWorkspaceConfigOptions {
5353
pub struct WorkspaceConfig {
5454
pub root: WorkspaceMemberConfig,
5555
pub members: HashMap<String, WorkspaceMemberConfig>,
56+
pub patches: HashMap<String, LockfilePatchContent>,
5657
}
5758

5859
#[derive(Default, Debug, Clone, PartialEq, Eq)]
@@ -64,7 +65,8 @@ pub struct WorkspaceMemberConfig {
6465
#[derive(Debug, Clone, PartialEq, Eq)]
6566
pub struct NpmPackageLockfileInfo {
6667
pub serialized_id: StackString,
67-
pub integrity: String,
68+
/// Will be `None` for patch packages.
69+
pub integrity: Option<String>,
6870
pub dependencies: Vec<NpmPackageDependencyLockfileInfo>,
6971
pub optional_dependencies: Vec<NpmPackageDependencyLockfileInfo>,
7072
pub os: Vec<SmallStackString>,
@@ -80,7 +82,8 @@ pub struct NpmPackageDependencyLockfileInfo {
8082

8183
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
8284
pub struct NpmPackageInfo {
83-
pub integrity: String,
85+
/// Will be `None` for patch packages.
86+
pub integrity: Option<String>,
8487
#[serde(default)]
8588
pub dependencies: BTreeMap<StackString, StackString>,
8689
#[serde(default)]
@@ -180,18 +183,34 @@ impl WorkspaceMemberConfigContent {
180183
}
181184
}
182185

186+
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)]
187+
#[serde(rename_all = "camelCase")]
188+
pub struct LockfilePatchContent {
189+
#[serde(default)]
190+
#[serde(skip_serializing_if = "Vec::is_empty")]
191+
pub dependencies: HashSet<JsrDepPackageReq>,
192+
#[serde(default)]
193+
#[serde(skip_serializing_if = "Vec::is_empty")]
194+
pub peer_dependencies: HashSet<JsrDepPackageReq>,
195+
#[serde(default)]
196+
#[serde(skip_serializing_if = "HashMap::is_empty")]
197+
pub peer_dependencies_meta: HashMap<String, serde_json::Value>,
198+
}
199+
183200
#[derive(Debug, Default, Clone, Deserialize)]
184201
#[serde(rename_all = "camelCase")]
185202
pub(crate) struct WorkspaceConfigContent {
186203
#[serde(default, flatten)]
187204
pub root: WorkspaceMemberConfigContent,
188205
#[serde(default)]
189206
pub members: HashMap<String, WorkspaceMemberConfigContent>,
207+
#[serde(default)]
208+
pub patches: HashMap<String, LockfilePatchContent>,
190209
}
191210

192211
impl WorkspaceConfigContent {
193212
pub fn is_empty(&self) -> bool {
194-
self.root.is_empty() && self.members.is_empty()
213+
self.root.is_empty() && self.members.is_empty() && self.patches.is_empty()
195214
}
196215

197216
fn get_all_dep_reqs(&self) -> impl Iterator<Item = &JsrDepPackageReq> {
@@ -265,7 +284,7 @@ impl LockfileContent {
265284
#[derive(Debug, Deserialize)]
266285
#[serde(rename_all = "camelCase")]
267286
struct RawNpmPackageInfo {
268-
pub integrity: String,
287+
pub integrity: Option<String>,
269288
#[serde(default)]
270289
pub dependencies: Vec<StackString>,
271290
#[serde(default)]
@@ -699,6 +718,40 @@ impl Lockfile {
699718
// to !self.has_content_changed after populating it with this information
700719
let allow_content_changed =
701720
self.has_content_changed || !self.content.is_empty();
721+
722+
let has_any_patch_changed = options.config.patches.len()
723+
!= self.content.workspace.patches.len()
724+
|| !options.config.patches.is_empty()
725+
&& options.config.patches.iter().all(|(patch, new)| {
726+
let Some(existing) = self.content.workspace.patches.get_mut(patch)
727+
else {
728+
return true;
729+
};
730+
new != existing
731+
});
732+
733+
// if a patch changes, it's quite complicated to figure out how to get it to redo
734+
// npm resolution just for that part, so for now, clear out all the npm dependencies
735+
// if any patch changes
736+
if has_any_patch_changed {
737+
self.has_content_changed = true;
738+
self.content.packages.npm.clear();
739+
self
740+
.content
741+
.packages
742+
.specifiers
743+
.retain(|k, _| match k.kind {
744+
deno_semver::package::PackageKind::Jsr => true,
745+
deno_semver::package::PackageKind::Npm => false,
746+
});
747+
self.content.workspace.patches.clear();
748+
self
749+
.content
750+
.workspace
751+
.patches
752+
.extend(options.config.patches);
753+
}
754+
702755
let old_deps = self
703756
.content
704757
.workspace
@@ -1215,7 +1268,7 @@ mod tests {
12151268
// already in lockfile
12161269
let npm_package = NpmPackageLockfileInfo {
12171270
serialized_id: "[email protected]".into(),
1218-
integrity: "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==".to_string(),
1271+
integrity: Some("sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==".to_string()),
12191272
dependencies: vec![],
12201273
optional_dependencies: vec![],
12211274
os: vec![],
@@ -1228,7 +1281,7 @@ mod tests {
12281281
// insert package that exists already, but has slightly different properties
12291282
let npm_package = NpmPackageLockfileInfo {
12301283
serialized_id: "[email protected]".into(),
1231-
integrity: "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==".to_string(),
1284+
integrity: Some("sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==".to_string()),
12321285
dependencies: vec![],
12331286
optional_dependencies: vec![],
12341287
os: vec![],
@@ -1241,7 +1294,7 @@ mod tests {
12411294
lockfile.has_content_changed = false;
12421295
let npm_package = NpmPackageLockfileInfo {
12431296
serialized_id: "[email protected]".into(),
1244-
integrity: "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==".to_string(),
1297+
integrity: Some("sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==".to_string()),
12451298
dependencies: vec![],
12461299
optional_dependencies: vec![],
12471300
os: vec![],
@@ -1259,7 +1312,7 @@ mod tests {
12591312

12601313
let npm_package = NpmPackageLockfileInfo {
12611314
serialized_id: "[email protected]".into(),
1262-
integrity: "sha512-foobar".to_string(),
1315+
integrity: Some("sha512-foobar".to_string()),
12631316
dependencies: vec![],
12641317
optional_dependencies: vec![],
12651318
os: vec![],

src/printer.rs

+58-19
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::borrow::Cow;
44
use std::collections::BTreeMap;
55
use std::collections::HashMap;
6+
use std::collections::HashSet;
67

78
use deno_semver::jsr::JsrDepPackageReq;
89
use deno_semver::package::PackageNv;
@@ -13,6 +14,7 @@ use serde::Serialize;
1314
use crate::JsrPackageInfo;
1415
use crate::LockfileContent;
1516
use crate::LockfilePackageJsonContent;
17+
use crate::LockfilePatchContent;
1618
use crate::NpmPackageInfo;
1719
use crate::WorkspaceConfigContent;
1820
use crate::WorkspaceMemberConfigContent;
@@ -27,7 +29,9 @@ struct SerializedJsrPkg<'a> {
2729
#[derive(Serialize)]
2830
#[serde(rename_all = "camelCase")]
2931
struct SerializedNpmPkg<'a> {
30-
integrity: &'a str,
32+
/// Will be `None` for patch packages.
33+
#[serde(skip_serializing_if = "Option::is_none")]
34+
integrity: Option<&'a str>,
3135
#[serde(skip_serializing_if = "Vec::is_empty")]
3236
dependencies: Vec<Cow<'a, str>>,
3337
#[serde(skip_serializing_if = "Vec::is_empty")]
@@ -68,6 +72,20 @@ impl SerializedLockfilePackageJsonContent {
6872
}
6973
}
7074

75+
#[derive(Debug, Default, Serialize)]
76+
#[serde(rename_all = "camelCase")]
77+
struct SerializedLockfilePatchContent {
78+
#[serde(default)]
79+
#[serde(skip_serializing_if = "Vec::is_empty")]
80+
pub dependencies: Vec<SerializedJsrDepPackageReq>,
81+
#[serde(default)]
82+
#[serde(skip_serializing_if = "Vec::is_empty")]
83+
pub peer_dependencies: Vec<SerializedJsrDepPackageReq>,
84+
#[serde(default)]
85+
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
86+
pub peer_dependencies_meta: BTreeMap<String, serde_json::Value>,
87+
}
88+
7189
#[derive(Debug, Default, Serialize)]
7290
#[serde(rename_all = "camelCase")]
7391
struct SerializedWorkspaceMemberConfigContent {
@@ -95,11 +113,14 @@ struct SerializedWorkspaceConfigContent<'a> {
95113
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
96114
#[serde(default)]
97115
pub members: BTreeMap<&'a str, SerializedWorkspaceMemberConfigContent>,
116+
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
117+
#[serde(default)]
118+
pub patches: BTreeMap<&'a str, SerializedLockfilePatchContent>,
98119
}
99120

100121
impl SerializedWorkspaceConfigContent<'_> {
101122
pub fn is_empty(&self) -> bool {
102-
self.root.is_empty() && self.members.is_empty()
123+
self.root.is_empty() && self.members.is_empty() && self.patches.is_empty()
103124
}
104125
}
105126

@@ -240,7 +261,7 @@ pub fn print_v5_content(content: &LockfileContent) -> String {
240261
(
241262
key.as_str(),
242263
SerializedNpmPkg {
243-
integrity: &value.integrity,
264+
integrity: value.integrity.as_deref(),
244265
dependencies,
245266
optional_dependencies,
246267
os: value.os.clone(),
@@ -255,32 +276,45 @@ pub fn print_v5_content(content: &LockfileContent) -> String {
255276
fn handle_pkg_json_content(
256277
content: &LockfilePackageJsonContent,
257278
) -> SerializedLockfilePackageJsonContent {
258-
let mut dependencies = content
259-
.dependencies
260-
.iter()
261-
.map(SerializedJsrDepPackageReq::new)
262-
.collect::<Vec<_>>();
263-
dependencies.sort();
264-
SerializedLockfilePackageJsonContent { dependencies }
279+
SerializedLockfilePackageJsonContent {
280+
dependencies: sort_deps(&content.dependencies),
281+
}
265282
}
266283

267284
fn handle_workspace_member(
268285
member: &WorkspaceMemberConfigContent,
269286
) -> SerializedWorkspaceMemberConfigContent {
270287
SerializedWorkspaceMemberConfigContent {
271-
dependencies: {
272-
let mut member = member
273-
.dependencies
274-
.iter()
275-
.map(SerializedJsrDepPackageReq::new)
276-
.collect::<Vec<_>>();
277-
member.sort();
278-
member
279-
},
288+
dependencies: sort_deps(&member.dependencies),
280289
package_json: handle_pkg_json_content(&member.package_json),
281290
}
282291
}
283292

293+
fn handle_patch_content(
294+
content: &LockfilePatchContent,
295+
) -> SerializedLockfilePatchContent {
296+
SerializedLockfilePatchContent {
297+
dependencies: sort_deps(&content.dependencies),
298+
peer_dependencies: sort_deps(&content.peer_dependencies),
299+
peer_dependencies_meta: content
300+
.peer_dependencies_meta
301+
.iter()
302+
.map(|(k, v)| (k.clone(), v.clone()))
303+
.collect(),
304+
}
305+
}
306+
307+
fn sort_deps(
308+
deps: &HashSet<JsrDepPackageReq>,
309+
) -> Vec<SerializedJsrDepPackageReq> {
310+
let mut dependencies = deps
311+
.iter()
312+
.map(SerializedJsrDepPackageReq::new)
313+
.collect::<Vec<_>>();
314+
dependencies.sort();
315+
dependencies
316+
}
317+
284318
fn handle_workspace(
285319
content: &WorkspaceConfigContent,
286320
) -> SerializedWorkspaceConfigContent {
@@ -291,6 +325,11 @@ pub fn print_v5_content(content: &LockfileContent) -> String {
291325
.iter()
292326
.map(|(key, value)| (key.as_str(), handle_workspace_member(value)))
293327
.collect(),
328+
patches: content
329+
.patches
330+
.iter()
331+
.map(|(key, value)| (key.as_str(), handle_patch_content(value)))
332+
.collect(),
294333
}
295334
}
296335

tests/integration_test.rs

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ fn adding_workspace_does_not_cause_content_changes() {
2929
package_json_deps: Default::default(),
3030
},
3131
members: Default::default(),
32+
patches: Default::default(),
3233
},
3334
});
3435
assert!(!lockfile.has_content_changed); // should not have changed
@@ -50,6 +51,7 @@ fn adding_workspace_does_not_cause_content_changes() {
5051
package_json_deps: Default::default(),
5152
},
5253
members: Default::default(),
54+
patches: Default::default(),
5355
},
5456
});
5557
assert!(lockfile.has_content_changed);
@@ -76,6 +78,7 @@ fn adding_workspace_does_not_cause_content_changes() {
7678
package_json_deps: Default::default(),
7779
},
7880
members: Default::default(),
81+
patches: Default::default(),
7982
},
8083
});
8184
assert!(lockfile.has_content_changed); // should have changed since lockfile was not empty

0 commit comments

Comments
 (0)