diff --git a/.gitignore b/.gitignore index b0b6ed39..9918b2c5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ tests/bin /build-installer docs/book docs/installer +dhat-*.json .idea diff --git a/Cargo.lock b/Cargo.lock index 1f1599c6..65180624 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,6 +469,22 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "dhat" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2aaf837aaf456f6706cb46386ba8dffd4013a757e36f4ea05c20dd46b209a3" +dependencies = [ + "backtrace", + "lazy_static", + "mintex", + "parking_lot", + "rustc-hash", + "serde", + "serde_json", + "thousands", +] + [[package]] name = "dialoguer" version = "0.10.4" @@ -990,6 +1006,16 @@ dependencies = [ "adler", ] +[[package]] +name = "mintex" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7c5ba1c3b5a23418d7bbf98c71c3d4946a0125002129231da8d6b723d559cb" +dependencies = [ + "once_cell", + "sys-info", +] + [[package]] name = "normalize-line-endings" version = "0.3.0" @@ -1247,6 +1273,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.37.19" @@ -1484,6 +1516,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sys-info" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "tar" version = "0.4.38" @@ -1535,6 +1577,12 @@ dependencies = [ "syn", ] +[[package]] +name = "thousands" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" + [[package]] name = "time" version = "0.1.45" @@ -1803,6 +1851,7 @@ dependencies = [ "chrono", "clap", "console", + "dhat", "dialoguer", "env_logger", "glob", diff --git a/Cargo.toml b/Cargo.toml index 181e3293..72508ac4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ binary-install = "0.1.0" cargo_metadata = "0.15.2" chrono = "0.4.23" console = "0.15.5" +dhat = { version = "0.3.2", optional = true } dialoguer = "0.10.3" env_logger = { version = "0.10.0", default-features = false } glob = "0.3.1" @@ -43,3 +44,5 @@ predicates = "3.0.3" serial_test = "2.0.0" tempfile = "3.3.0" +[features] +dhat-heap = ["dep:dhat"] diff --git a/src/build/wasm_target.rs b/src/build/wasm_target.rs index 8631086f..14bb050d 100644 --- a/src/build/wasm_target.rs +++ b/src/build/wasm_target.rs @@ -57,11 +57,7 @@ pub fn check_for_wasm32_target() -> Result<()> { PBAR.info(&msg); // Check if wasm32 target is present, otherwise bail. - match check_wasm32_target() { - Ok(ref wasm32_check) if wasm32_check.found => Ok(()), - Ok(wasm32_check) => bail!("{}", wasm32_check), - Err(err) => Err(err), - } + check_wasm32_target() } /// Get rustc's sysroot as a PathBuf @@ -97,36 +93,29 @@ fn is_wasm32_target_in_sysroot(sysroot: &Path) -> bool { } } -fn check_wasm32_target() -> Result { +fn check_wasm32_target() -> Result<()> { let sysroot = get_rustc_sysroot()?; - let rustc_path = which::which("rustc")?; // If wasm32-unknown-unknown already exists we're ok. if is_wasm32_target_in_sysroot(&sysroot) { - Ok(Wasm32Check { - rustc_path, - sysroot, - found: true, - is_rustup: false, - }) + Ok(()) // If it doesn't exist, then we need to check if we're using rustup. } else { + let rustc_path = which::which("rustc")?; // If sysroot contains "rustup", then we can assume we're using rustup // and use rustup to add the wasm32-unknown-unknown target. if sysroot.to_string_lossy().contains("rustup") { - rustup_add_wasm_target().map(|()| Wasm32Check { - rustc_path, - sysroot, - found: true, - is_rustup: true, - }) + rustup_add_wasm_target() } else { - Ok(Wasm32Check { - rustc_path, - sysroot, - found: false, - is_rustup: false, - }) + bail!( + "{}", + Wasm32Check { + rustc_path, + sysroot, + found: false, + is_rustup: false, + } + ); } } } diff --git a/src/command/build.rs b/src/command/build.rs index f807323d..c1e36217 100644 --- a/src/command/build.rs +++ b/src/command/build.rs @@ -7,7 +7,7 @@ use crate::command::utils::{create_pkg_dir, get_crate_path}; use crate::emoji; use crate::install::{self, InstallMode, Tool}; use crate::license; -use crate::lockfile::Lockfile; +use crate::lockfile; use crate::manifest; use crate::readme; use crate::wasm_opt; @@ -289,11 +289,7 @@ impl Build { match &mode { InstallMode::Force => {} _ => { - steps.extend(steps![ - step_check_rustc_version, - step_check_crate_config, - step_check_for_wasm_target, - ]); + steps.extend(steps![step_check_rustc, step_check_crate_config,]); } } @@ -316,7 +312,19 @@ impl Build { steps } - fn step_check_rustc_version(&mut self) -> Result<()> { + fn step_check_rustc(&mut self) -> Result<()> { + std::thread::scope(|s| { + for handle in [ + s.spawn(|| self.step_check_rustc_version()), + s.spawn(|| self.step_check_for_wasm_target()), + ] { + handle.join().unwrap()?; + } + Ok(()) + }) + } + + fn step_check_rustc_version(&self) -> Result<()> { info!("Checking rustc version..."); let version = build::check_rustc_version()?; let msg = format!("rustc version is {}.", version); @@ -331,7 +339,7 @@ impl Build { Ok(()) } - fn step_check_for_wasm_target(&mut self) -> Result<()> { + fn step_check_for_wasm_target(&self) -> Result<()> { info!("Checking for wasm-target..."); build::wasm_target::check_for_wasm32_target()?; info!("Checking for wasm-target was successful."); @@ -390,8 +398,8 @@ impl Build { fn step_install_wasm_bindgen(&mut self) -> Result<()> { info!("Identifying wasm-bindgen dependency..."); - let lockfile = Lockfile::new(&self.crate_data)?; - let bindgen_version = lockfile.require_wasm_bindgen()?; + let [package] = lockfile::Package::get(&self.crate_data, ["wasm-bindgen"])?; + let bindgen_version = package.require_version_or_suggest("dependencies", "0.2")?; info!("Installing wasm-bindgen-cli..."); let bindgen = install::download_prebuilt_or_cargo_install( Tool::WasmBindgen, diff --git a/src/command/test.rs b/src/command/test.rs index 30b0e5ff..dba22251 100644 --- a/src/command/test.rs +++ b/src/command/test.rs @@ -4,7 +4,7 @@ use crate::build; use crate::cache; use crate::command::utils::get_crate_path; use crate::install::{self, InstallMode, Tool}; -use crate::lockfile::Lockfile; +use crate::lockfile; use crate::manifest; use crate::test::{self, webdriver}; use anyhow::{bail, Result}; @@ -276,22 +276,16 @@ impl Test { fn step_install_wasm_bindgen(&mut self) -> Result<()> { info!("Identifying wasm-bindgen dependency..."); - let lockfile = Lockfile::new(&self.crate_data)?; - let bindgen_version = lockfile.require_wasm_bindgen()?; + let [bindgen, bindgen_test] = + lockfile::Package::get(&self.crate_data, ["wasm-bindgen", "wasm-bindgen-test"])?; + let bindgen_version = bindgen.require_version_or_suggest("dependencies", "0.2")?; // Unlike `wasm-bindgen` and `wasm-bindgen-cli`, `wasm-bindgen-test` // will work with any semver compatible `wasm-bindgen-cli`, so just make // sure that it is depended upon, so we can run tests on // `wasm32-unkown-unknown`. Don't enforce that it is the same version as // `wasm-bindgen`. - if lockfile.wasm_bindgen_test_version().is_none() { - bail!( - "Ensure that you have \"{}\" as a dependency in your Cargo.toml file:\n\ - [dev-dependencies]\n\ - wasm-bindgen-test = \"0.2\"", - style("wasm-bindgen-test").bold().dim(), - ) - } + bindgen_test.require_version_or_suggest("dev-dependencies", "0.2")?; let status = install::download_prebuilt_or_cargo_install( Tool::WasmBindgen, diff --git a/src/lockfile.rs b/src/lockfile.rs index f4a68eac..d81a9757 100644 --- a/src/lockfile.rs +++ b/src/lockfile.rs @@ -2,69 +2,164 @@ #![allow(clippy::new_ret_no_self)] +use std::borrow::Cow; use std::fs; use std::path::PathBuf; use crate::manifest::CrateData; use anyhow::{anyhow, bail, Context, Result}; use console::style; +use serde::{ + de::{DeserializeSeed, Error, IgnoredAny, Visitor}, + Deserializer, +}; use toml; -/// This struct represents the contents of `Cargo.lock`. -#[derive(Clone, Debug, Deserialize)] -pub struct Lockfile { - package: Vec, -} - /// This struct represents a single package entry in `Cargo.lock` -#[derive(Clone, Debug, Deserialize)] -struct Package { - name: String, - version: String, +pub struct Package<'a> { + name: &'a str, + version: Option, } -impl Lockfile { - /// Read the `Cargo.lock` file for the crate at the given path. - pub fn new(crate_data: &CrateData) -> Result { +impl<'a> Package<'a> { + /// Read the `Cargo.lock` file for the crate at the given path and get the versions of the named dependencies. + pub fn get( + crate_data: &CrateData, + dep_names: [&'a str; N], + ) -> Result<[Self; N]> { let lock_path = get_lockfile_path(crate_data)?; let lockfile = fs::read_to_string(&lock_path) .with_context(|| anyhow!("failed to read: {}", lock_path.display()))?; - let lockfile = toml::from_str(&lockfile) - .with_context(|| anyhow!("failed to parse: {}", lock_path.display()))?; - Ok(lockfile) + toml::Deserializer::new(&lockfile) + .deserialize_struct("Lockfile", &["package"], LockfileVisitor { dep_names }) + .with_context(|| anyhow!("failed to parse: {}", lock_path.display())) } - /// Get the version of `wasm-bindgen` dependency used in the `Cargo.lock`. - pub fn wasm_bindgen_version(&self) -> Option<&str> { - self.get_package_version("wasm-bindgen") + /// Get the version of this package used in the `Cargo.lock`. + pub fn version(&self) -> Option<&str> { + self.version.as_deref() } - /// Like `wasm_bindgen_version`, except it returns an error instead of - /// `None`. - pub fn require_wasm_bindgen(&self) -> Result<&str> { - self.wasm_bindgen_version().ok_or_else(|| { + /// Like `version`, except it returns an error instead of `None`. `suggested_version` is only used when showing an example of adding the dependency to `Cargo.toml`. + pub fn require_version_or_suggest( + &self, + section: &str, + suggested_version: &str, + ) -> Result<&str> { + self.version().ok_or_else(|| { anyhow!( "Ensure that you have \"{}\" as a dependency in your Cargo.toml file:\n\ - [dependencies]\n\ - wasm-bindgen = \"0.2\"", - style("wasm-bindgen").bold().dim(), + [{}]\n\ + {} = \"{}\"", + style(self.name).bold().dim(), + section, + self.name, + suggested_version, ) }) } +} + +struct LockfileVisitor<'a, const N: usize> { + dep_names: [&'a str; N], +} + +impl<'de, 'a, const N: usize> Visitor<'de> for LockfileVisitor<'a, N> { + type Value = [Package<'a>; N]; - /// Get the version of `wasm-bindgen` dependency used in the `Cargo.lock`. - pub fn wasm_bindgen_test_version(&self) -> Option<&str> { - self.get_package_version("wasm-bindgen-test") + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct Lockfile") } - fn get_package_version(&self, package: &str) -> Option<&str> { - self.package - .iter() - .find(|p| p.name == package) - .map(|p| &p.version[..]) + fn visit_map(self, mut map: A) -> std::result::Result + where + A: serde::de::MapAccess<'de>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "lowercase")] + enum Field { + Package, + Other(IgnoredAny), + } + + while let Some(key) = map.next_key()? { + match key { + Field::Package => { + return map.next_value_seed(PackagesSeed { + dep_names: self.dep_names, + }) + } + Field::Other(_) => { + map.next_value::()?; + } + } + } + Err(A::Error::missing_field("package")) } } +struct PackagesSeed<'a, const N: usize> { + dep_names: [&'a str; N], +} + +impl<'de, 'a, const N: usize> DeserializeSeed<'de> for PackagesSeed<'a, N> { + type Value = [Package<'a>; N]; + + fn deserialize(self, deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_seq(self) + } +} + +impl<'de, 'a, const N: usize> Visitor<'de> for PackagesSeed<'a, N> { + type Value = [Package<'a>; N]; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a sequence") + } + + fn visit_seq(self, mut seq: A) -> std::result::Result + where + A: serde::de::SeqAccess<'de>, + { + let mut packages = self.dep_names.map(|name| Package { + name, + version: None, + }); + while let Some(PackageValue { name, version }) = seq.next_element()? { + #[cfg(test)] + assert_eq!( + (true, true), + ( + matches!(name, Cow::Borrowed(_)), + matches!(version, Cow::Borrowed(_)) + ) + ); + for package in &mut packages { + if package.name == name { + package.version = Some(version.into_owned()); + if packages.iter().all(|i| i.version.is_some()) { + return Ok(packages); + } else { + break; + } + } + } + } + Ok(packages) + } +} + +#[derive(Deserialize)] +struct PackageValue<'a> { + #[serde(borrow)] + name: Cow<'a, str>, + #[serde(borrow)] + version: Cow<'a, str>, +} + /// Given the path to the crate that we are building, return a `PathBuf` /// containing the location of the lock file, by finding the workspace root. fn get_lockfile_path(crate_data: &CrateData) -> Result { diff --git a/src/main.rs b/src/main.rs index e8e0754c..a02c4e3b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,7 +44,14 @@ fn background_check_for_updates() -> mpsc::Receiver> { receiver } +#[cfg(feature = "dhat-heap")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + fn main() { + #[cfg(feature = "dhat-heap")] + let _profiler = dhat::Profiler::builder().trim_backtraces(None).build(); + env_logger::init(); setup_panic_hooks(); diff --git a/src/manifest/mod.rs b/src/manifest/mod.rs index f6fcae97..26974bc0 100644 --- a/src/manifest/mod.rs +++ b/src/manifest/mod.rs @@ -9,7 +9,7 @@ use anyhow::{anyhow, bail, Context, Result}; mod npm; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::{collections::HashMap, fs}; use self::npm::{ @@ -17,10 +17,9 @@ use self::npm::{ }; use crate::command::build::{BuildProfile, Target}; use crate::PBAR; -use cargo_metadata::Metadata; use chrono::offset; use chrono::DateTime; -use serde::{self, Deserialize}; +use serde::{self, de::IgnoredAny, Deserialize}; use serde_json; use std::collections::BTreeSet; use std::env; @@ -34,8 +33,9 @@ const WASM_PACK_REPO_URL: &str = "https://github.com/rustwasm/wasm-pack"; /// Store for metadata learned about a crate pub struct CrateData { - data: Metadata, - current_idx: usize, + package: cargo_metadata::Package, + target_directory: PathBuf, + workspace_root: PathBuf, manifest: CargoManifest, out_name: Option, } @@ -44,6 +44,9 @@ pub struct CrateData { #[derive(Deserialize)] pub struct CargoManifest { package: CargoPackage, + + #[serde(flatten, deserialize_with = "deserialize_unchecked_keys")] + _unchecked_keys: IgnoredAny, } #[derive(Deserialize)] @@ -52,6 +55,18 @@ struct CargoPackage { #[serde(default)] metadata: CargoMetadata, + + #[serde(flatten, deserialize_with = "deserialize_unchecked_keys")] + _unchecked_keys: IgnoredAny, +} + +/// This doesn't trigger the callback given to `serde_ignored::deserialize` +/// because it doesn't call `Deserializer::deserialize_ignored_any`. +fn deserialize_unchecked_keys<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + deserializer.deserialize_map(IgnoredAny) } #[derive(Default, Deserialize)] @@ -407,25 +422,27 @@ impl CrateData { let data = cargo_metadata::MetadataCommand::new() .manifest_path(&manifest_path) + .no_deps() .exec()?; let manifest_and_keys = CrateData::parse_crate_data(&manifest_path)?; CrateData::warn_for_unused_keys(&manifest_and_keys); let manifest = manifest_and_keys.manifest; - let current_idx = data + let package = data .packages - .iter() - .position(|pkg| { + .into_iter() + .find(|pkg| { pkg.name == manifest.package.name && CrateData::is_same_path(pkg.manifest_path.as_std_path(), &manifest_path) }) .ok_or_else(|| anyhow!("failed to find package in metadata"))?; Ok(CrateData { - data, + package, + workspace_root: data.workspace_root.into(), + target_directory: data.target_directory.into(), manifest, - current_idx, out_name, }) } @@ -457,9 +474,11 @@ impl CrateData { let manifest: CargoManifest = serde_ignored::deserialize(manifest, |path| { let path_string = path.to_string(); - if path_string.starts_with("package.metadata") - && (path_string.contains("wasm-pack") - || levenshtein(WASM_PACK_METADATA_KEY, &path_string) <= levenshtein_threshold) + // Check that deserialize_unchecked_keys works correctly + debug_assert!(path_string.starts_with("package.metadata")); + + if path_string.contains("wasm-pack") + || levenshtein(WASM_PACK_METADATA_KEY, &path_string) <= levenshtein_threshold { unused_keys.insert(path_string); } @@ -499,7 +518,7 @@ impl CrateData { } fn check_crate_type(&self) -> Result<()> { - let pkg = &self.data.packages[self.current_idx]; + let pkg = &self.package; let any_cdylib = pkg .targets .iter() @@ -517,7 +536,7 @@ impl CrateData { } fn pkg(&self) -> &cargo_metadata::Package { - &self.data.packages[self.current_idx] + &self.package } /// Get the crate name for the crate at the given path. @@ -557,12 +576,12 @@ impl CrateData { /// Returns the path to this project's target directory where artifacts are /// located after a cargo build. pub fn target_directory(&self) -> &Path { - Path::new(&self.data.target_directory) + Path::new(&self.target_directory) } /// Returns the path to this project's root cargo workspace directory pub fn workspace_root(&self) -> &Path { - Path::new(&self.data.workspace_root) + Path::new(&self.workspace_root) } /// Generate a package.json file inside in `./pkg`. @@ -618,7 +637,7 @@ impl CrateData { files.push(js_bg_file); } - let pkg = &self.data.packages[self.current_idx]; + let pkg = &self.package; let npm_name = match scope { Some(s) => format!("@{}/{}", s, pkg.name), None => pkg.name.clone(), @@ -677,7 +696,7 @@ impl CrateData { out_dir: &Path, ) -> NpmPackage { let data = self.npm_data(scope, false, disable_dts, out_dir); - let pkg = &self.data.packages[self.current_idx]; + let pkg = &self.package; self.check_optional_fields(); @@ -708,7 +727,7 @@ impl CrateData { out_dir: &Path, ) -> NpmPackage { let data = self.npm_data(scope, true, disable_dts, out_dir); - let pkg = &self.data.packages[self.current_idx]; + let pkg = &self.package; self.check_optional_fields(); @@ -740,7 +759,7 @@ impl CrateData { out_dir: &Path, ) -> NpmPackage { let data = self.npm_data(scope, false, disable_dts, out_dir); - let pkg = &self.data.packages[self.current_idx]; + let pkg = &self.package; self.check_optional_fields(); @@ -772,7 +791,7 @@ impl CrateData { out_dir: &Path, ) -> NpmPackage { let data = self.npm_data(scope, false, disable_dts, out_dir); - let pkg = &self.data.packages[self.current_idx]; + let pkg = &self.package; self.check_optional_fields(); diff --git a/src/npm.rs b/src/npm.rs index 9cdadaa6..adcde515 100644 --- a/src/npm.rs +++ b/src/npm.rs @@ -33,21 +33,19 @@ pub fn npm_publish(path: &str, access: Option, tag: Option) -> R /// Run the `npm login` command. pub fn npm_login(registry: &str, scope: &Option, auth_type: &Option) -> Result<()> { - let mut args = vec!["login".to_string(), format!("--registry={}", registry)]; + // Interactively ask user for npm login info. + // (child::run does not support interactive input) + let mut cmd = child::new_command("npm"); + cmd.args(["login".to_string(), format!("--registry={}", registry)]); if let Some(scope) = scope { - args.push(format!("--scope={}", scope)); + cmd.arg(format!("--scope={}", scope)); } if let Some(auth_type) = auth_type { - args.push(format!("--auth_type={}", auth_type)); + cmd.arg(format!("--auth_type={}", auth_type)); } - // Interactively ask user for npm login info. - // (child::run does not support interactive input) - let mut cmd = child::new_command("npm"); - cmd.args(args); - info!("Running {:?}", cmd); if cmd.status()?.success() { Ok(()) diff --git a/tests/all/lockfile.rs b/tests/all/lockfile.rs index 94b938b7..d0cef299 100644 --- a/tests/all/lockfile.rs +++ b/tests/all/lockfile.rs @@ -1,5 +1,5 @@ use crate::utils::fixture; -use wasm_pack::lockfile::Lockfile; +use wasm_pack::lockfile; use wasm_pack::manifest::CrateData; #[test] @@ -7,8 +7,8 @@ fn it_gets_wasm_bindgen_version() { let fixture = fixture::js_hello_world(); fixture.cargo_check(); let data = CrateData::new(&fixture.path, None).unwrap(); - let lock = Lockfile::new(&data).unwrap(); - assert_eq!(lock.wasm_bindgen_version(), Some("0.2.74"),); + let [package] = lockfile::Package::get(&data, ["wasm-bindgen"]).unwrap(); + assert_eq!(package.version(), Some("0.2.74"),); } #[test] @@ -16,8 +16,8 @@ fn it_gets_wasm_bindgen_test_version() { let fixture = fixture::wbg_test_node(); fixture.cargo_check(); let data = CrateData::new(&fixture.path, None).unwrap(); - let lock = Lockfile::new(&data).unwrap(); - assert_eq!(lock.wasm_bindgen_test_version(), Some("0.3.24"),); + let [package] = lockfile::Package::get(&data, ["wasm-bindgen-test"]).unwrap(); + assert_eq!(package.version(), Some("0.3.24"),); } #[test] @@ -61,8 +61,8 @@ fn it_gets_wasm_bindgen_version_in_crate_inside_workspace() { ); fixture.cargo_check(); let data = CrateData::new(&fixture.path.join("blah"), None).unwrap(); - let lock = Lockfile::new(&data).unwrap(); - assert_eq!(lock.wasm_bindgen_version(), Some("0.2.74"),); + let [package] = lockfile::Package::get(&data, ["wasm-bindgen"]).unwrap(); + assert_eq!(package.version(), Some("0.2.74"),); } #[test] @@ -129,6 +129,6 @@ fn it_gets_wasm_bindgen_version_from_dependencies() { ); fixture.cargo_check(); let data = CrateData::new(&fixture.path.join("parent"), None).unwrap(); - let lock = Lockfile::new(&data).unwrap(); - assert_eq!(lock.wasm_bindgen_version(), Some("0.2.74"),); + let [package] = lockfile::Package::get(&data, ["wasm-bindgen"]).unwrap(); + assert_eq!(package.version(), Some("0.2.74"),); }