From a98522550d26998374c8d01d9af565c7f07fcb3b Mon Sep 17 00:00:00 2001 From: Chris Olszewski Date: Fri, 20 Dec 2024 09:31:36 -0500 Subject: [PATCH 1/3] feat(lockfile): add human_name method --- crates/turborepo-lockfiles/src/berry/mod.rs | 8 ++++++++ crates/turborepo-lockfiles/src/bun/mod.rs | 7 +++++++ crates/turborepo-lockfiles/src/lib.rs | 6 ++++++ crates/turborepo-lockfiles/src/npm.rs | 7 +++++++ crates/turborepo-lockfiles/src/pnpm/data.rs | 10 ++++++++++ crates/turborepo-lockfiles/src/yarn1/mod.rs | 7 +++++++ 6 files changed, 45 insertions(+) diff --git a/crates/turborepo-lockfiles/src/berry/mod.rs b/crates/turborepo-lockfiles/src/berry/mod.rs index c2f4ebf081d2c..37cd2b8c13f41 100644 --- a/crates/turborepo-lockfiles/src/berry/mod.rs +++ b/crates/turborepo-lockfiles/src/berry/mod.rs @@ -531,6 +531,14 @@ impl Lockfile for BerryLockfile { let entry = self.locator_package.get(key)?; Some(entry.version.clone()) } + + fn human_name(&self, package: &crate::Package) -> Option { + let locator = Locator::try_from(package.key.as_str()).ok()?; + let berry_package = self.locator_package.get(&locator)?; + let name = locator.ident.to_string(); + let version = &berry_package.version; + Some(format!("{name}@{version}")) + } } impl LockfileData { diff --git a/crates/turborepo-lockfiles/src/bun/mod.rs b/crates/turborepo-lockfiles/src/bun/mod.rs index 363d443fe3705..a49bd341abe05 100644 --- a/crates/turborepo-lockfiles/src/bun/mod.rs +++ b/crates/turborepo-lockfiles/src/bun/mod.rs @@ -122,6 +122,13 @@ impl Lockfile for BunLockfile { fn turbo_version(&self) -> Option { None } + + fn human_name(&self, package: &crate::Package) -> Option { + let entry = self.inner.get(&package.key)?; + let name = entry.name.as_deref()?; + let version = &entry.version; + Some(format!("{name}@{version}")) + } } impl Entry { diff --git a/crates/turborepo-lockfiles/src/lib.rs b/crates/turborepo-lockfiles/src/lib.rs index c8fbb9d3d2f45..ad56239ea6cd2 100644 --- a/crates/turborepo-lockfiles/src/lib.rs +++ b/crates/turborepo-lockfiles/src/lib.rs @@ -65,6 +65,12 @@ pub trait Lockfile: Send + Sync + Any + std::fmt::Debug { /// Return any turbo version found in the lockfile fn turbo_version(&self) -> Option; + + /// A human friendly version of a lockfile key. + /// Usually of the form `package@version`, but version might include + /// additional information to convey difference from other packages in + /// the lockfile e.g. differing peer dependencies. + fn human_name(&self, package: &Package) -> Option; } /// Takes a lockfile, and a map of workspace directory paths -> (package name, diff --git a/crates/turborepo-lockfiles/src/npm.rs b/crates/turborepo-lockfiles/src/npm.rs index 2422a190e8ff8..55508052e6d1f 100644 --- a/crates/turborepo-lockfiles/src/npm.rs +++ b/crates/turborepo-lockfiles/src/npm.rs @@ -152,6 +152,13 @@ impl Lockfile for NpmLockfile { let turbo_entry = self.packages.get("node_modules/turbo")?; turbo_entry.version.clone() } + + fn human_name(&self, package: &Package) -> Option { + let npm_package = self.packages.get(&package.key)?; + let version = npm_package.version.as_deref()?; + let name = package.key.split("node_modules/").last()?; + Some(format!("{name}@{version}")) + } } impl NpmLockfile { diff --git a/crates/turborepo-lockfiles/src/pnpm/data.rs b/crates/turborepo-lockfiles/src/pnpm/data.rs index 7ce68449251b0..324f3b3c9016a 100644 --- a/crates/turborepo-lockfiles/src/pnpm/data.rs +++ b/crates/turborepo-lockfiles/src/pnpm/data.rs @@ -524,6 +524,16 @@ impl crate::Lockfile for PnpmLockfile { .find_map(|project| project.dependencies.turbo_version())?; Some(turbo_version.to_owned()) } + + fn human_name(&self, package: &crate::Package) -> Option { + if matches!(self.version(), SupportedLockfileVersion::V7AndV9) { + Some(package.key.clone()) + } else { + // TODO: this is really hacky and doesn't properly handle v5 as it uses `/` as + // the delimiter between name and version + Some(package.key.strip_prefix('/')?.to_owned()) + } + } } impl DependencyInfo { diff --git a/crates/turborepo-lockfiles/src/yarn1/mod.rs b/crates/turborepo-lockfiles/src/yarn1/mod.rs index f93f1c6d837c4..7c58f2e000781 100644 --- a/crates/turborepo-lockfiles/src/yarn1/mod.rs +++ b/crates/turborepo-lockfiles/src/yarn1/mod.rs @@ -127,6 +127,13 @@ impl Lockfile for Yarn1Lockfile { let entry = self.inner.get(key)?; Some(entry.version.clone()) } + + fn human_name(&self, package: &crate::Package) -> Option { + let entry = self.inner.get(&package.key)?; + let name = entry.name.as_deref()?; + let version = &entry.version; + Some(format!("{name}@{version}")) + } } pub fn yarn_subgraph(contents: &[u8], packages: &[String]) -> Result, crate::Error> { From 02a812c499ee4623b863bede2731f2fd23bb6cbe Mon Sep 17 00:00:00 2001 From: Chris Olszewski Date: Fri, 20 Dec 2024 09:32:19 -0500 Subject: [PATCH 2/3] feat(query): add additional lockfile change info --- .../src/query/external_package.rs | 33 ++++ crates/turborepo-lib/src/query/mod.rs | 156 +++++++++--------- .../src/run/scope/change_detector.rs | 10 +- .../src/change_mapper/mod.rs | 33 ++-- .../src/package_graph/mod.rs | 59 +++++-- 5 files changed, 192 insertions(+), 99 deletions(-) create mode 100644 crates/turborepo-lib/src/query/external_package.rs diff --git a/crates/turborepo-lib/src/query/external_package.rs b/crates/turborepo-lib/src/query/external_package.rs new file mode 100644 index 0000000000000..f4cd438a45a86 --- /dev/null +++ b/crates/turborepo-lib/src/query/external_package.rs @@ -0,0 +1,33 @@ +use std::sync::Arc; + +use async_graphql::Object; + +use crate::run::Run; + +#[derive(Clone)] +pub struct ExternalPackage { + run: Arc, + package: turborepo_lockfiles::Package, +} + +impl ExternalPackage { + pub fn new(run: Arc, package: turborepo_lockfiles::Package) -> Self { + Self { run, package } + } + + /// Converts the lockfile key to a human friendly name + pub fn human_name(&self) -> String { + self.run + .pkg_dep_graph() + .lockfile() + .and_then(|lockfile| lockfile.human_name(&self.package)) + .unwrap_or_else(|| self.package.key.clone()) + } +} + +#[Object] +impl ExternalPackage { + async fn name(&self) -> String { + self.human_name().to_string() + } +} diff --git a/crates/turborepo-lib/src/query/mod.rs b/crates/turborepo-lib/src/query/mod.rs index 97e02e5208ee4..9529bffeb8336 100644 --- a/crates/turborepo-lib/src/query/mod.rs +++ b/crates/turborepo-lib/src/query/mod.rs @@ -1,3 +1,4 @@ +mod external_package; mod file; mod package; mod server; @@ -11,6 +12,7 @@ use std::{ use async_graphql::{http::GraphiQLSource, *}; use axum::{response, response::IntoResponse}; +use external_package::ExternalPackage; use miette::Diagnostic; use package::Package; pub use server::run_server; @@ -64,6 +66,82 @@ impl RepositoryQuery { pub fn new(run: Arc) -> Self { Self { run } } + + fn convert_change_reason( + &self, + reason: turborepo_repository::change_mapper::PackageInclusionReason, + ) -> PackageChangeReason { + match reason { + turborepo_repository::change_mapper::PackageInclusionReason::All( + AllPackageChangeReason::GlobalDepsChanged { file }, + ) => PackageChangeReason::GlobalDepsChanged(GlobalDepsChanged { + file_path: file.to_string(), + }), + turborepo_repository::change_mapper::PackageInclusionReason::All( + AllPackageChangeReason::DefaultGlobalFileChanged { file }, + ) => PackageChangeReason::DefaultGlobalFileChanged(DefaultGlobalFileChanged { + file_path: file.to_string(), + }), + turborepo_repository::change_mapper::PackageInclusionReason::All( + AllPackageChangeReason::LockfileChangeDetectionFailed, + ) => { + PackageChangeReason::LockfileChangeDetectionFailed(LockfileChangeDetectionFailed { + empty: false, + }) + } + turborepo_repository::change_mapper::PackageInclusionReason::All( + AllPackageChangeReason::GitRefNotFound { from_ref, to_ref }, + ) => PackageChangeReason::GitRefNotFound(GitRefNotFound { from_ref, to_ref }), + turborepo_repository::change_mapper::PackageInclusionReason::All( + AllPackageChangeReason::LockfileChangedWithoutDetails, + ) => { + PackageChangeReason::LockfileChangedWithoutDetails(LockfileChangedWithoutDetails { + empty: false, + }) + } + turborepo_repository::change_mapper::PackageInclusionReason::All( + AllPackageChangeReason::RootInternalDepChanged { root_internal_dep }, + ) => PackageChangeReason::RootInternalDepChanged(RootInternalDepChanged { + root_internal_dep: root_internal_dep.to_string(), + }), + turborepo_repository::change_mapper::PackageInclusionReason::RootTask { task } => { + PackageChangeReason::RootTask(RootTask { + task_name: task.to_string(), + }) + } + turborepo_repository::change_mapper::PackageInclusionReason::ConservativeRootLockfileChanged => { + PackageChangeReason::ConservativeRootLockfileChanged(ConservativeRootLockfileChanged { empty: false }) + } + turborepo_repository::change_mapper::PackageInclusionReason::LockfileChanged { removed, added } => { + let removed = removed.into_iter().map(|package| ExternalPackage::new(self.run.clone(), package)).collect::>(); + let added = added.into_iter().map(|package| ExternalPackage::new(self.run.clone(), package)).collect::>(); + PackageChangeReason::LockfileChanged(LockfileChanged { empty: false, removed, added }) + } + turborepo_repository::change_mapper::PackageInclusionReason::DependencyChanged { + dependency, + } => PackageChangeReason::DependencyChanged(DependencyChanged { + dependency_name: dependency.to_string(), + }), + turborepo_repository::change_mapper::PackageInclusionReason::DependentChanged { + dependent, + } => PackageChangeReason::DependentChanged(DependentChanged { + dependent_name: dependent.to_string(), + }), + turborepo_repository::change_mapper::PackageInclusionReason::FileChanged { file } => { + PackageChangeReason::FileChanged(FileChanged { + file_path: file.to_string(), + }) + } + turborepo_repository::change_mapper::PackageInclusionReason::InFilteredDirectory { + directory, + } => PackageChangeReason::InFilteredDirectory(InFilteredDirectory { + directory_path: directory.to_string(), + }), + turborepo_repository::change_mapper::PackageInclusionReason::IncludedByFilter { + filters, + } => PackageChangeReason::IncludedByFilter(IncludedByFilter { filters }), + } + } } #[derive(Debug, SimpleObject)] @@ -72,6 +150,7 @@ impl RepositoryQuery { #[graphql(concrete(name = "ChangedPackages", params(ChangedPackage)))] #[graphql(concrete(name = "Files", params(File)))] #[graphql(concrete(name = "TraceErrors", params(file::TraceError)))] +#[graphql(concrete(name = "ExternalPackages", params(ExternalPackage)))] pub struct Array { items: Vec, length: usize, @@ -375,6 +454,8 @@ struct ConservativeRootLockfileChanged { struct LockfileChanged { /// This is a nothing field empty: bool, + removed: Array, + added: Array, } #[derive(SimpleObject)] @@ -416,79 +497,6 @@ enum PackageChangeReason { InFilteredDirectory(InFilteredDirectory), } -impl From for PackageChangeReason { - fn from(value: turborepo_repository::change_mapper::PackageInclusionReason) -> Self { - match value { - turborepo_repository::change_mapper::PackageInclusionReason::All( - AllPackageChangeReason::GlobalDepsChanged { file }, - ) => PackageChangeReason::GlobalDepsChanged(GlobalDepsChanged { - file_path: file.to_string(), - }), - turborepo_repository::change_mapper::PackageInclusionReason::All( - AllPackageChangeReason::DefaultGlobalFileChanged { file }, - ) => PackageChangeReason::DefaultGlobalFileChanged(DefaultGlobalFileChanged { - file_path: file.to_string(), - }), - turborepo_repository::change_mapper::PackageInclusionReason::All( - AllPackageChangeReason::LockfileChangeDetectionFailed, - ) => { - PackageChangeReason::LockfileChangeDetectionFailed(LockfileChangeDetectionFailed { - empty: false, - }) - } - turborepo_repository::change_mapper::PackageInclusionReason::All( - AllPackageChangeReason::GitRefNotFound { from_ref, to_ref }, - ) => PackageChangeReason::GitRefNotFound(GitRefNotFound { from_ref, to_ref }), - turborepo_repository::change_mapper::PackageInclusionReason::All( - AllPackageChangeReason::LockfileChangedWithoutDetails, - ) => { - PackageChangeReason::LockfileChangedWithoutDetails(LockfileChangedWithoutDetails { - empty: false, - }) - } - turborepo_repository::change_mapper::PackageInclusionReason::All( - AllPackageChangeReason::RootInternalDepChanged { root_internal_dep }, - ) => PackageChangeReason::RootInternalDepChanged(RootInternalDepChanged { - root_internal_dep: root_internal_dep.to_string(), - }), - turborepo_repository::change_mapper::PackageInclusionReason::RootTask { task } => { - PackageChangeReason::RootTask(RootTask { - task_name: task.to_string(), - }) - } - turborepo_repository::change_mapper::PackageInclusionReason::ConservativeRootLockfileChanged => { - PackageChangeReason::ConservativeRootLockfileChanged(ConservativeRootLockfileChanged { empty: false }) - } - turborepo_repository::change_mapper::PackageInclusionReason::LockfileChanged => { - PackageChangeReason::LockfileChanged(LockfileChanged { empty: false }) - } - turborepo_repository::change_mapper::PackageInclusionReason::DependencyChanged { - dependency, - } => PackageChangeReason::DependencyChanged(DependencyChanged { - dependency_name: dependency.to_string(), - }), - turborepo_repository::change_mapper::PackageInclusionReason::DependentChanged { - dependent, - } => PackageChangeReason::DependentChanged(DependentChanged { - dependent_name: dependent.to_string(), - }), - turborepo_repository::change_mapper::PackageInclusionReason::FileChanged { file } => { - PackageChangeReason::FileChanged(FileChanged { - file_path: file.to_string(), - }) - } - turborepo_repository::change_mapper::PackageInclusionReason::InFilteredDirectory { - directory, - } => PackageChangeReason::InFilteredDirectory(InFilteredDirectory { - directory_path: directory.to_string(), - }), - turborepo_repository::change_mapper::PackageInclusionReason::IncludedByFilter { - filters, - } => PackageChangeReason::IncludedByFilter(IncludedByFilter { filters }), - } - } -} - #[derive(SimpleObject)] struct ChangedPackage { reason: PackageChangeReason, @@ -518,7 +526,7 @@ impl RepositoryQuery { .map(|(package, reason)| { Ok(ChangedPackage { package: Package::new(self.run.clone(), package)?, - reason: reason.into(), + reason: self.convert_change_reason(reason), }) }) .filter(|package: &Result| { diff --git a/crates/turborepo-lib/src/run/scope/change_detector.rs b/crates/turborepo-lib/src/run/scope/change_detector.rs index 3b8459e352836..991963657cc6b 100644 --- a/crates/turborepo-lib/src/run/scope/change_detector.rs +++ b/crates/turborepo-lib/src/run/scope/change_detector.rs @@ -5,7 +5,7 @@ use turbopath::{AbsoluteSystemPath, AnchoredSystemPathBuf}; use turborepo_repository::{ change_mapper::{ AllPackageChangeReason, ChangeMapper, DefaultPackageChangeMapper, Error, - GlobalDepsPackageChangeMapper, LockfileChange, PackageChanges, PackageInclusionReason, + GlobalDepsPackageChangeMapper, PackageChanges, PackageInclusionReason, }, package_graph::{PackageGraph, PackageName}, }; @@ -53,12 +53,12 @@ impl<'a> ScopeChangeDetector<'a> { /// Gets the lockfile content from SCM if it has changed. /// Does *not* error if cannot get content, instead just - /// returns an empty lockfile change + /// returns a Some(None) fn get_lockfile_contents( &self, from_ref: Option<&str>, changed_files: &HashSet, - ) -> Option { + ) -> Option>> { let lockfile_path = self .pkg_graph .package_manager() @@ -79,10 +79,10 @@ impl<'a> ScopeChangeDetector<'a> { .lockfile_path(self.turbo_root); let Ok(content) = self.scm.previous_content(from_ref, &lockfile_path) else { - return Some(LockfileChange::Empty); + return Some(None); }; - Some(LockfileChange::WithContent(content)) + Some(Some(content)) } } diff --git a/crates/turborepo-repository/src/change_mapper/mod.rs b/crates/turborepo-repository/src/change_mapper/mod.rs index 5b861802e7f9c..0138a7a503328 100644 --- a/crates/turborepo-repository/src/change_mapper/mod.rs +++ b/crates/turborepo-repository/src/change_mapper/mod.rs @@ -14,7 +14,9 @@ use tracing::debug; use turbopath::{AbsoluteSystemPath, AnchoredSystemPathBuf}; use wax::Program; -use crate::package_graph::{ChangedPackagesError, PackageGraph, PackageName, WorkspacePackage}; +use crate::package_graph::{ + ChangedPackagesError, ExternalDependencyChange, PackageGraph, PackageName, WorkspacePackage, +}; mod package; @@ -24,7 +26,7 @@ const DEFAULT_GLOBAL_DEPS: [&str; 2] = ["package.json", "turbo.json"]; // still want to be able to express a generic change. pub enum LockfileChange { Empty, - WithContent(Vec), + ChangedPackages(HashSet), } #[derive(Debug, PartialEq, Eq, Hash, Clone)] @@ -37,7 +39,10 @@ pub enum PackageInclusionReason { /// the lockfile changed. ConservativeRootLockfileChanged, /// The lockfile changed and caused this package to be invalidated - LockfileChanged, + LockfileChanged { + removed: Vec, + added: Vec, + }, /// A transitive dependency of this package changed DependencyChanged { dependency: PackageName }, /// A transitive dependent of this package changed @@ -117,7 +122,7 @@ impl<'a, PD: PackageChangeMapper> ChangeMapper<'a, PD> { pub fn changed_packages( &self, changed_files: HashSet, - lockfile_change: Option, + lockfile_change: Option>>, ) -> Result { if let Some(file) = Self::default_global_file_changed(&changed_files) { debug!("global file changed"); @@ -136,7 +141,7 @@ impl<'a, PD: PackageChangeMapper> ChangeMapper<'a, PD> { PackageChanges::Some(mut changed_pkgs) => { match lockfile_change { - Some(LockfileChange::WithContent(content)) => { + Some(Some(content)) => { // if we run into issues, don't error, just assume all packages have changed let Ok(lockfile_changes) = self.get_changed_packages_from_lockfile(&content) @@ -155,16 +160,24 @@ impl<'a, PD: PackageChangeMapper> ChangeMapper<'a, PD> { ); merge_changed_packages( &mut changed_pkgs, - lockfile_changes - .into_iter() - .map(|pkg| (pkg, PackageInclusionReason::LockfileChanged)), + lockfile_changes.into_iter().map(|change| { + let ExternalDependencyChange { + package, + added, + removed, + } = change; + ( + package, + PackageInclusionReason::LockfileChanged { added, removed }, + ) + }), ); Ok(PackageChanges::Some(changed_pkgs)) } // We don't have the actual contents, so just invalidate everything - Some(LockfileChange::Empty) => { + Some(None) => { debug!("no previous lockfile available, assuming all packages changed"); Ok(PackageChanges::All( AllPackageChangeReason::LockfileChangedWithoutDetails, @@ -226,7 +239,7 @@ impl<'a, PD: PackageChangeMapper> ChangeMapper<'a, PD> { fn get_changed_packages_from_lockfile( &self, lockfile_content: &[u8], - ) -> Result, ChangeMapError> { + ) -> Result, ChangeMapError> { let previous_lockfile = self .pkg_graph .package_manager() diff --git a/crates/turborepo-repository/src/package_graph/mod.rs b/crates/turborepo-repository/src/package_graph/mod.rs index 1d9063e754211..94615ddffa747 100644 --- a/crates/turborepo-repository/src/package_graph/mod.rs +++ b/crates/turborepo-repository/src/package_graph/mod.rs @@ -43,7 +43,7 @@ pub struct PackageGraph { /// There are other structs in this module that have "Workspace" in the name, /// but they do NOT follow the glossary, and instead mean "package" when they /// say Workspace. Some of these are labeled as such. -#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Debug, Eq, PartialEq, Hash, Clone)] pub struct WorkspacePackage { pub name: PackageName, pub path: AnchoredSystemPathBuf, @@ -132,6 +132,15 @@ impl PackageNode { } } +#[derive(Debug, Clone, PartialEq)] +pub struct ExternalDependencyChange { + pub package: WorkspacePackage, + /// Dependencies that were added to the package + pub added: Vec, + /// Dependencies that were removed from the package + pub removed: Vec, +} + impl PackageGraph { pub fn builder( repo_root: &AbsoluteSystemPath, @@ -427,7 +436,7 @@ impl PackageGraph { pub fn changed_packages_from_lockfile( &self, previous: &dyn Lockfile, - ) -> Result, ChangedPackagesError> { + ) -> Result, ChangedPackagesError> { let current = self.lockfile().ok_or(ChangedPackagesError::NoLockfile)?; let external_deps = self @@ -458,7 +467,7 @@ impl PackageGraph { } else { self.packages .iter() - .filter(|(name, info)| { + .filter_map(|(name, info)| { let previous_closure = closures.get(info.package_path().to_unix().as_str()); let not_equal = previous_closure != info.transitive_dependencies.as_ref(); if not_equal { @@ -470,30 +479,60 @@ impl PackageGraph { prev.symmetric_difference(curr) ); } + let empty_set = HashSet::default(); + let prev_deps = previous_closure.unwrap_or(&empty_set); + let curr_deps = info.transitive_dependencies.as_ref().unwrap_or(&empty_set); + // {a, b} -> {a, c} + // b was removed + // c was added + let added = curr_deps + .difference(prev_deps) + .cloned() + .sorted() + .collect::>(); + let removed = prev_deps + .difference(curr_deps) + .cloned() + .sorted() + .collect::>(); + Some((name, info, added, removed)) + } else { + None } - not_equal }) - .map(|(name, info)| match name { + .map(|(name, info, added, removed)| match name { PackageName::Other(n) => { let w_name = PackageName::Other(n.to_owned()); - Some(WorkspacePackage { + let package = WorkspacePackage { name: w_name.clone(), path: info.package_path().to_owned(), + }; + Some(ExternalDependencyChange { + package, + added, + removed, }) } // if the root package has changed, then we should report `None` // since all packages need to be revalidated PackageName::Root => None, }) - .collect::>>() + .collect::>>() }; Ok(changed.unwrap_or_else(|| { self.packages .iter() - .map(|(name, info)| WorkspacePackage { - name: name.clone(), - path: info.package_path().to_owned(), + .map(|(name, info)| { + let package = WorkspacePackage { + name: name.clone(), + path: info.package_path().to_owned(), + }; + ExternalDependencyChange { + package, + added: Vec::new(), + removed: Vec::new(), + } }) .collect() })) From c76f307d88387c8d7bf93ac266d514f629ec1384 Mon Sep 17 00:00:00 2001 From: Chris Olszewski Date: Mon, 23 Dec 2024 10:51:05 -0600 Subject: [PATCH 3/3] chore(lockfile): add default impl for human name --- crates/turborepo-lockfiles/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/turborepo-lockfiles/src/lib.rs b/crates/turborepo-lockfiles/src/lib.rs index ad56239ea6cd2..9f3dddba04f81 100644 --- a/crates/turborepo-lockfiles/src/lib.rs +++ b/crates/turborepo-lockfiles/src/lib.rs @@ -70,7 +70,10 @@ pub trait Lockfile: Send + Sync + Any + std::fmt::Debug { /// Usually of the form `package@version`, but version might include /// additional information to convey difference from other packages in /// the lockfile e.g. differing peer dependencies. - fn human_name(&self, package: &Package) -> Option; + #[allow(unused)] + fn human_name(&self, package: &Package) -> Option { + None + } } /// Takes a lockfile, and a map of workspace directory paths -> (package name,