mirror of
https://github.com/denoland/deno.git
synced 2024-11-28 16:20:57 -05:00
chore: remove lockfile/ (#18618)
New home at https://github.com/denoland/deno_lockfile.
This commit is contained in:
parent
d07aa4a072
commit
0dca0c5196
6 changed files with 3 additions and 562 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -1071,11 +1071,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "deno_lockfile"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88b89dc19bc7b0c28297c9fde36dc999a04c19b6d01ff061ae30dc9119488c8"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"test_util",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ members = [
|
|||
"ext/websocket",
|
||||
"ext/webstorage",
|
||||
"ext/napi",
|
||||
"lockfile",
|
||||
]
|
||||
exclude = ["test_util/std/hash/_wasm"]
|
||||
|
||||
|
@ -52,7 +51,7 @@ deno_runtime = { version = "0.105.0", path = "./runtime" }
|
|||
napi_sym = { version = "0.27.0", path = "./cli/napi/sym" }
|
||||
deno_bench_util = { version = "0.91.0", path = "./bench_util" }
|
||||
test_util = { path = "./test_util" }
|
||||
deno_lockfile = { version = "0.13.0", path = "./lockfile" }
|
||||
deno_lockfile = "0.13.0"
|
||||
|
||||
# exts
|
||||
deno_broadcast_channel = { version = "0.91.0", path = "./ext/broadcast_channel" }
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
# Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
[package]
|
||||
name = "deno_lockfile"
|
||||
version = "0.13.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = "An implementation of a lockfile used in Deno"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
ring.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
test_util.workspace = true
|
|
@ -1,3 +0,0 @@
|
|||
# `deno_lockfile`
|
||||
|
||||
This crate implements the lockfile format used by Deno.
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LockfileError {
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Unable to read lockfile: \"{0}\"")]
|
||||
ReadError(String),
|
||||
|
||||
#[error("Unable to parse contents of lockfile: \"{0}\"")]
|
||||
ParseError(String),
|
||||
}
|
521
lockfile/lib.rs
521
lockfile/lib.rs
|
@ -1,521 +0,0 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
mod error;
|
||||
pub use error::LockfileError as Error;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::Write;
|
||||
|
||||
use ring::digest;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct NpmPackageLockfileInfo {
|
||||
pub display_id: String,
|
||||
pub serialized_id: String,
|
||||
pub integrity: String,
|
||||
pub dependencies: Vec<NpmPackageDependencyLockfileInfo>,
|
||||
}
|
||||
|
||||
pub struct NpmPackageDependencyLockfileInfo {
|
||||
pub name: String,
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
fn gen_checksum(v: &[impl AsRef<[u8]>]) -> String {
|
||||
let mut ctx = digest::Context::new(&digest::SHA256);
|
||||
for src in v {
|
||||
ctx.update(src.as_ref());
|
||||
}
|
||||
let digest = ctx.finish();
|
||||
let out: Vec<String> = digest
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|byte| format!("{byte:02x}"))
|
||||
.collect();
|
||||
out.join("")
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LockfileError(String);
|
||||
|
||||
impl std::fmt::Display for LockfileError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LockfileError {}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NpmPackageInfo {
|
||||
pub integrity: String,
|
||||
pub dependencies: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct NpmContent {
|
||||
/// Mapping between requests for npm packages and resolved packages, eg.
|
||||
/// {
|
||||
/// "chalk": "chalk@5.0.0"
|
||||
/// "react@17": "react@17.0.1"
|
||||
/// "foo@latest": "foo@1.0.0"
|
||||
/// }
|
||||
pub specifiers: BTreeMap<String, String>,
|
||||
/// Mapping between resolved npm specifiers and their associated info, eg.
|
||||
/// {
|
||||
/// "chalk@5.0.0": {
|
||||
/// "integrity": "sha512-...",
|
||||
/// "dependencies": {
|
||||
/// "ansi-styles": "ansi-styles@4.1.0",
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
pub packages: BTreeMap<String, NpmPackageInfo>,
|
||||
}
|
||||
|
||||
impl NpmContent {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.specifiers.is_empty() && self.packages.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LockfileContent {
|
||||
version: String,
|
||||
// Mapping between URLs and their checksums for "http:" and "https:" deps
|
||||
remote: BTreeMap<String, String>,
|
||||
#[serde(skip_serializing_if = "NpmContent::is_empty")]
|
||||
#[serde(default)]
|
||||
pub npm: NpmContent,
|
||||
}
|
||||
|
||||
impl LockfileContent {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
version: "2".to_string(),
|
||||
remote: BTreeMap::new(),
|
||||
npm: NpmContent::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Lockfile {
|
||||
pub overwrite: bool,
|
||||
pub has_content_changed: bool,
|
||||
pub content: LockfileContent,
|
||||
pub filename: PathBuf,
|
||||
}
|
||||
|
||||
impl Lockfile {
|
||||
pub fn new(filename: PathBuf, overwrite: bool) -> Result<Lockfile, Error> {
|
||||
// Writing a lock file always uses the new format.
|
||||
if overwrite {
|
||||
return Ok(Lockfile {
|
||||
overwrite,
|
||||
has_content_changed: false,
|
||||
content: LockfileContent::empty(),
|
||||
filename,
|
||||
});
|
||||
}
|
||||
|
||||
let result = match std::fs::read_to_string(&filename) {
|
||||
Ok(content) => Ok(content),
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
return Ok(Lockfile {
|
||||
overwrite,
|
||||
has_content_changed: false,
|
||||
content: LockfileContent::empty(),
|
||||
filename,
|
||||
});
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let s =
|
||||
result.map_err(|_| Error::ReadError(filename.display().to_string()))?;
|
||||
let value: serde_json::Value = serde_json::from_str(&s)
|
||||
.map_err(|_| Error::ParseError(filename.display().to_string()))?;
|
||||
let version = value.get("version").and_then(|v| v.as_str());
|
||||
let content = if version == Some("2") {
|
||||
serde_json::from_value::<LockfileContent>(value)
|
||||
.map_err(|_| Error::ParseError(filename.display().to_string()))?
|
||||
} else {
|
||||
// If there's no version field, we assume that user is using the old
|
||||
// version of the lockfile. We'll migrate it in-place into v2 and it
|
||||
// will be written in v2 if user uses `--lock-write` flag.
|
||||
let remote: BTreeMap<String, String> = serde_json::from_value(value)
|
||||
.map_err(|_| Error::ParseError(filename.display().to_string()))?;
|
||||
LockfileContent {
|
||||
version: "2".to_string(),
|
||||
remote,
|
||||
npm: NpmContent::default(),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Lockfile {
|
||||
overwrite,
|
||||
has_content_changed: false,
|
||||
content,
|
||||
filename,
|
||||
})
|
||||
}
|
||||
|
||||
// Synchronize lock file to disk - noop if --lock-write file is not specified.
|
||||
pub fn write(&self) -> Result<(), Error> {
|
||||
if !self.has_content_changed && !self.overwrite {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut json_string = serde_json::to_string_pretty(&self.content).unwrap();
|
||||
json_string.push('\n'); // trailing newline in file
|
||||
let mut f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&self.filename)?;
|
||||
f.write_all(json_string.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO(bartlomieju): this function should return an error instead of a bool,
|
||||
// but it requires changes to `deno_graph`'s `Locker`.
|
||||
pub fn check_or_insert_remote(
|
||||
&mut self,
|
||||
specifier: &str,
|
||||
code: &str,
|
||||
) -> bool {
|
||||
if !(specifier.starts_with("http:") || specifier.starts_with("https:")) {
|
||||
return true;
|
||||
}
|
||||
if self.overwrite {
|
||||
// In case --lock-write is specified check always passes
|
||||
self.insert(specifier, code);
|
||||
true
|
||||
} else {
|
||||
self.check_or_insert(specifier, code)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_or_insert_npm_package(
|
||||
&mut self,
|
||||
package_info: NpmPackageLockfileInfo,
|
||||
) -> Result<(), LockfileError> {
|
||||
if self.overwrite {
|
||||
// In case --lock-write is specified check always passes
|
||||
self.insert_npm(package_info);
|
||||
Ok(())
|
||||
} else {
|
||||
self.check_or_insert_npm(package_info)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the given module is included, if so verify the checksum. If module
|
||||
/// is not included, insert it.
|
||||
fn check_or_insert(&mut self, specifier: &str, code: &str) -> bool {
|
||||
if let Some(lockfile_checksum) = self.content.remote.get(specifier) {
|
||||
let compiled_checksum = gen_checksum(&[code.as_bytes()]);
|
||||
lockfile_checksum == &compiled_checksum
|
||||
} else {
|
||||
self.insert(specifier, code);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, specifier: &str, code: &str) {
|
||||
let checksum = gen_checksum(&[code.as_bytes()]);
|
||||
self.content.remote.insert(specifier.to_string(), checksum);
|
||||
self.has_content_changed = true;
|
||||
}
|
||||
|
||||
fn check_or_insert_npm(
|
||||
&mut self,
|
||||
package: NpmPackageLockfileInfo,
|
||||
) -> Result<(), LockfileError> {
|
||||
if let Some(package_info) =
|
||||
self.content.npm.packages.get(&package.serialized_id)
|
||||
{
|
||||
if package_info.integrity.as_str() != package.integrity {
|
||||
return Err(LockfileError(format!(
|
||||
"Integrity check failed for npm package: \"{}\". Unable to verify that the package
|
||||
is the same as when the lockfile was generated.
|
||||
|
||||
This could be caused by:
|
||||
* the lock file may be corrupt
|
||||
* the source itself may be corrupt
|
||||
|
||||
Use \"--lock-write\" flag to regenerate the lockfile at \"{}\".",
|
||||
package.display_id, self.filename.display()
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
self.insert_npm(package);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_npm(&mut self, package_info: NpmPackageLockfileInfo) {
|
||||
let dependencies = package_info
|
||||
.dependencies
|
||||
.iter()
|
||||
.map(|dep| (dep.name.to_string(), dep.id.to_string()))
|
||||
.collect::<BTreeMap<String, String>>();
|
||||
|
||||
self.content.npm.packages.insert(
|
||||
package_info.serialized_id.to_string(),
|
||||
NpmPackageInfo {
|
||||
integrity: package_info.integrity,
|
||||
dependencies,
|
||||
},
|
||||
);
|
||||
self.has_content_changed = true;
|
||||
}
|
||||
|
||||
pub fn insert_npm_specifier(
|
||||
&mut self,
|
||||
serialized_package_req: String,
|
||||
serialized_package_id: String,
|
||||
) {
|
||||
let maybe_prev = self.content.npm.specifiers.get(&serialized_package_req);
|
||||
|
||||
if maybe_prev.is_none() || maybe_prev != Some(&serialized_package_id) {
|
||||
self.has_content_changed = true;
|
||||
}
|
||||
|
||||
self
|
||||
.content
|
||||
.npm
|
||||
.specifiers
|
||||
.insert(serialized_package_req, serialized_package_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::Write;
|
||||
use test_util::TempDir;
|
||||
|
||||
fn setup(temp_dir: &TempDir) -> PathBuf {
|
||||
let file_path = temp_dir.path().join("valid_lockfile.json");
|
||||
let mut file = File::create(file_path).expect("write file fail");
|
||||
|
||||
let value: serde_json::Value = json!({
|
||||
"version": "2",
|
||||
"remote": {
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts": "3118d7a42c03c242c5a49c2ad91c8396110e14acca1324e7aaefd31a999b71a4",
|
||||
"https://deno.land/std@0.71.0/async/delay.ts": "35957d585a6e3dd87706858fb1d6b551cb278271b03f52c5a2cb70e65e00c26a"
|
||||
},
|
||||
"npm": {
|
||||
"specifiers": {},
|
||||
"packages": {
|
||||
"nanoid@3.3.4": {
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"picocolors@1.0.0": {
|
||||
"integrity": "sha512-foobar",
|
||||
"dependencies": {}
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
file.write_all(value.to_string().as_bytes()).unwrap();
|
||||
|
||||
temp_dir.path().join("valid_lockfile.json")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_lockfile_for_nonexistent_path() {
|
||||
let file_path = PathBuf::from("nonexistent_lock_file.json");
|
||||
assert!(Lockfile::new(file_path, false).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_valid_lockfile() {
|
||||
let temp_dir = TempDir::new();
|
||||
let file_path = setup(&temp_dir);
|
||||
|
||||
let result = Lockfile::new(file_path, false).unwrap();
|
||||
|
||||
let remote = result.content.remote;
|
||||
let keys: Vec<String> = remote.keys().cloned().collect();
|
||||
let expected_keys = vec![
|
||||
String::from("https://deno.land/std@0.71.0/async/delay.ts"),
|
||||
String::from("https://deno.land/std@0.71.0/textproto/mod.ts"),
|
||||
];
|
||||
|
||||
assert_eq!(keys.len(), 2);
|
||||
assert_eq!(keys, expected_keys);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_lockfile_from_file_and_insert() {
|
||||
let temp_dir = TempDir::new();
|
||||
let file_path = setup(&temp_dir);
|
||||
|
||||
let mut lockfile = Lockfile::new(file_path, false).unwrap();
|
||||
|
||||
lockfile.insert(
|
||||
"https://deno.land/std@0.71.0/io/util.ts",
|
||||
"Here is some source code",
|
||||
);
|
||||
|
||||
let remote = lockfile.content.remote;
|
||||
let keys: Vec<String> = remote.keys().cloned().collect();
|
||||
let expected_keys = vec![
|
||||
String::from("https://deno.land/std@0.71.0/async/delay.ts"),
|
||||
String::from("https://deno.land/std@0.71.0/io/util.ts"),
|
||||
String::from("https://deno.land/std@0.71.0/textproto/mod.ts"),
|
||||
];
|
||||
assert_eq!(keys.len(), 3);
|
||||
assert_eq!(keys, expected_keys);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_lockfile_and_write() {
|
||||
let temp_dir = TempDir::new();
|
||||
let file_path = setup(&temp_dir);
|
||||
|
||||
let mut lockfile = Lockfile::new(file_path, true).unwrap();
|
||||
|
||||
lockfile.insert(
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts",
|
||||
"Here is some source code",
|
||||
);
|
||||
lockfile.insert(
|
||||
"https://deno.land/std@0.71.0/io/util.ts",
|
||||
"more source code here",
|
||||
);
|
||||
lockfile.insert(
|
||||
"https://deno.land/std@0.71.0/async/delay.ts",
|
||||
"this source is really exciting",
|
||||
);
|
||||
|
||||
lockfile.write().expect("unable to write");
|
||||
|
||||
let file_path_buf = temp_dir.path().join("valid_lockfile.json");
|
||||
let file_path = file_path_buf.to_str().expect("file path fail").to_string();
|
||||
|
||||
// read the file contents back into a string and check
|
||||
let mut checkfile = File::open(file_path).expect("Unable to open the file");
|
||||
let mut contents = String::new();
|
||||
checkfile
|
||||
.read_to_string(&mut contents)
|
||||
.expect("Unable to read the file");
|
||||
|
||||
let contents_json =
|
||||
serde_json::from_str::<serde_json::Value>(&contents).unwrap();
|
||||
let object = contents_json["remote"].as_object().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
object
|
||||
.get("https://deno.land/std@0.71.0/textproto/mod.ts")
|
||||
.and_then(|v| v.as_str()),
|
||||
// sha-256 hash of the source 'Here is some source code'
|
||||
Some("fedebba9bb82cce293196f54b21875b649e457f0eaf55556f1e318204947a28f")
|
||||
);
|
||||
|
||||
// confirm that keys are sorted alphabetically
|
||||
let mut keys = object.keys().map(|k| k.as_str());
|
||||
assert_eq!(
|
||||
keys.next(),
|
||||
Some("https://deno.land/std@0.71.0/async/delay.ts")
|
||||
);
|
||||
assert_eq!(keys.next(), Some("https://deno.land/std@0.71.0/io/util.ts"));
|
||||
assert_eq!(
|
||||
keys.next(),
|
||||
Some("https://deno.land/std@0.71.0/textproto/mod.ts")
|
||||
);
|
||||
assert!(keys.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_or_insert_lockfile() {
|
||||
let temp_dir = TempDir::new();
|
||||
let file_path = setup(&temp_dir);
|
||||
|
||||
let mut lockfile = Lockfile::new(file_path, false).unwrap();
|
||||
|
||||
lockfile.insert(
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts",
|
||||
"Here is some source code",
|
||||
);
|
||||
|
||||
let check_true = lockfile.check_or_insert_remote(
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts",
|
||||
"Here is some source code",
|
||||
);
|
||||
assert!(check_true);
|
||||
|
||||
let check_false = lockfile.check_or_insert_remote(
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts",
|
||||
"Here is some NEW source code",
|
||||
);
|
||||
assert!(!check_false);
|
||||
|
||||
// Not present in lockfile yet, should be inserted and check passed.
|
||||
let check_true = lockfile.check_or_insert_remote(
|
||||
"https://deno.land/std@0.71.0/http/file_server.ts",
|
||||
"This is new Source code",
|
||||
);
|
||||
assert!(check_true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_or_insert_lockfile_npm() {
|
||||
let temp_dir = TempDir::new();
|
||||
let file_path = setup(&temp_dir);
|
||||
|
||||
let mut lockfile = Lockfile::new(file_path, false).unwrap();
|
||||
|
||||
let npm_package = NpmPackageLockfileInfo {
|
||||
display_id: "nanoid@3.3.4".to_string(),
|
||||
serialized_id: "nanoid@3.3.4".to_string(),
|
||||
integrity: "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==".to_string(),
|
||||
dependencies: vec![],
|
||||
};
|
||||
let check_ok = lockfile.check_or_insert_npm_package(npm_package);
|
||||
assert!(check_ok.is_ok());
|
||||
|
||||
let npm_package = NpmPackageLockfileInfo {
|
||||
display_id: "picocolors@1.0.0".to_string(),
|
||||
serialized_id: "picocolors@1.0.0".to_string(),
|
||||
integrity: "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==".to_string(),
|
||||
dependencies: vec![],
|
||||
};
|
||||
// Integrity is borked in the loaded lockfile
|
||||
let check_err = lockfile.check_or_insert_npm_package(npm_package);
|
||||
assert!(check_err.is_err());
|
||||
|
||||
let npm_package = NpmPackageLockfileInfo {
|
||||
display_id: "source-map-js@1.0.2".to_string(),
|
||||
serialized_id: "source-map-js@1.0.2".to_string(),
|
||||
integrity: "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==".to_string(),
|
||||
dependencies: vec![],
|
||||
};
|
||||
// Not present in lockfile yet, should be inserted and check passed.
|
||||
let check_ok = lockfile.check_or_insert_npm_package(npm_package);
|
||||
assert!(check_ok.is_ok());
|
||||
|
||||
let npm_package = NpmPackageLockfileInfo {
|
||||
display_id: "source-map-js@1.0.2".to_string(),
|
||||
serialized_id: "source-map-js@1.0.2".to_string(),
|
||||
integrity: "sha512-foobar".to_string(),
|
||||
dependencies: vec![],
|
||||
};
|
||||
// Now present in lockfile, should file due to borked integrity
|
||||
let check_err = lockfile.check_or_insert_npm_package(npm_package);
|
||||
assert!(check_err.is_err());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue