Skip to content

Commit

Permalink
refactor(query): make immediate dependencies default (#9114)
Browse files Browse the repository at this point in the history
### Description

We have a `dependencies` field on our `Package` type in `turbo query`.
This returned all the transitive dependencies, i.e. the dependencies of
the package's dependencies and their dependencies, and so on. This is a
little confusing, so now we have explicit `directDependencies`,
`indirectDependencies` and `allDependencies` fields.

### Testing Instructions

Tests in `command-query.t` are updated.
  • Loading branch information
NicholasLYang authored Sep 16, 2024
1 parent c2a51d8 commit d0f4a0a
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 45 deletions.
176 changes: 136 additions & 40 deletions crates/turborepo-lib/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,40 +44,56 @@ struct Package {
}

impl Package {
fn immediate_dependents_count(&self) -> usize {
fn direct_dependents_count(&self) -> usize {
self.run
.pkg_dep_graph()
.immediate_ancestors(&PackageNode::Workspace(self.name.clone()))
.map_or(0, |pkgs| pkgs.len())
}

fn immediate_dependencies_count(&self) -> usize {
fn direct_dependencies_count(&self) -> usize {
self.run
.pkg_dep_graph()
.immediate_dependencies(&PackageNode::Workspace(self.name.clone()))
.map_or(0, |pkgs| pkgs.len())
}

fn dependent_count(&self) -> usize {
fn indirect_dependents_count(&self) -> usize {
let node: PackageNode = PackageNode::Workspace(self.name.clone());

self.run.pkg_dep_graph().ancestors(&node).len()
self.run.pkg_dep_graph().ancestors(&node).len() - self.direct_dependents_count()
}

fn dependency_count(&self) -> usize {
fn indirect_dependencies_count(&self) -> usize {
let node: PackageNode = PackageNode::Workspace(self.name.clone());

self.run.pkg_dep_graph().dependencies(&node).len()
self.run.pkg_dep_graph().dependencies(&node).len() - self.direct_dependencies_count()
}

fn all_dependents_count(&self) -> usize {
self.run
.pkg_dep_graph()
.ancestors(&PackageNode::Workspace(self.name.clone()))
.len()
}

fn all_dependencies_count(&self) -> usize {
self.run
.pkg_dep_graph()
.dependencies(&PackageNode::Workspace(self.name.clone()))
.len()
}
}

#[derive(Enum, Copy, Clone, Eq, PartialEq, Debug)]
enum PackageFields {
Name,
DependencyCount,
DependentCount,
ImmediateDependentCount,
ImmediateDependencyCount,
DirectDependencyCount,
DirectDependentCount,
IndirectDependentCount,
IndirectDependencyCount,
AllDependentCount,
AllDependencyCount,
}

#[derive(InputObject)]
Expand Down Expand Up @@ -107,89 +123,125 @@ impl PackagePredicate {
fn check_equals(pkg: &Package, field: &PackageFields, value: &Any) -> bool {
match (field, &value.0) {
(PackageFields::Name, Value::String(name)) => pkg.name.as_ref() == name,
(PackageFields::DependencyCount, Value::Number(n)) => {
(PackageFields::DirectDependencyCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.dependency_count() == n as usize
pkg.direct_dependencies_count() == n as usize
}
(PackageFields::DependentCount, Value::Number(n)) => {
(PackageFields::DirectDependentCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.dependent_count() == n as usize
pkg.direct_dependents_count() == n as usize
}
(PackageFields::ImmediateDependentCount, Value::Number(n)) => {
(PackageFields::IndirectDependentCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.immediate_dependents_count() == n as usize
pkg.indirect_dependents_count() == n as usize
}
(PackageFields::ImmediateDependencyCount, Value::Number(n)) => {
(PackageFields::IndirectDependencyCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.immediate_dependencies_count() == n as usize
pkg.indirect_dependencies_count() == n as usize
}
(PackageFields::AllDependentCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.all_dependents_count() == n as usize
}
(PackageFields::AllDependencyCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.all_dependencies_count() == n as usize
}
_ => false,
}
}

fn check_greater_than(pkg: &Package, field: &PackageFields, value: &Any) -> bool {
match (field, &value.0) {
(PackageFields::DependencyCount, Value::Number(n)) => {
(PackageFields::DirectDependencyCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.direct_dependencies_count() > n as usize
}
(PackageFields::DirectDependentCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.dependency_count() > n as usize
pkg.direct_dependents_count() > n as usize
}
(PackageFields::DependentCount, Value::Number(n)) => {
(PackageFields::IndirectDependentCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.dependent_count() > n as usize
pkg.indirect_dependents_count() > n as usize
}
(PackageFields::ImmediateDependentCount, Value::Number(n)) => {
(PackageFields::IndirectDependencyCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.immediate_dependents_count() > n as usize
pkg.indirect_dependencies_count() > n as usize
}
(PackageFields::ImmediateDependencyCount, Value::Number(n)) => {
(PackageFields::AllDependentCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.immediate_dependencies_count() > n as usize
pkg.all_dependents_count() > n as usize
}
(PackageFields::AllDependencyCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.all_dependencies_count() > n as usize
}
_ => false,
}
}

fn check_less_than(pkg: &Package, field: &PackageFields, value: &Any) -> bool {
match (field, &value.0) {
(PackageFields::DependencyCount, Value::Number(n)) => {
(PackageFields::DirectDependencyCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.dependency_count() < n as usize
pkg.direct_dependencies_count() < n as usize
}
(PackageFields::DependentCount, Value::Number(n)) => {
(PackageFields::DirectDependentCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.dependent_count() < n as usize
pkg.direct_dependents_count() < n as usize
}
(PackageFields::ImmediateDependentCount, Value::Number(n)) => {
(PackageFields::IndirectDependentCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.immediate_dependents_count() < n as usize
pkg.indirect_dependents_count() < n as usize
}
(PackageFields::ImmediateDependencyCount, Value::Number(n)) => {
(PackageFields::IndirectDependencyCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.immediate_dependencies_count() < n as usize
pkg.indirect_dependencies_count() < n as usize
}
(PackageFields::AllDependentCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.all_dependents_count() < n as usize
}
(PackageFields::AllDependencyCount, Value::Number(n)) => {
let Some(n) = n.as_u64() else {
return false;
};
pkg.all_dependencies_count() < n as usize
}
_ => false,
}
Expand Down Expand Up @@ -317,7 +369,7 @@ impl Package {
}

/// The upstream packages that have this package as a direct dependency
async fn immediate_dependents(&self) -> Result<Vec<Package>, Error> {
async fn direct_dependents(&self) -> Result<Vec<Package>, Error> {
let node: PackageNode = PackageNode::Workspace(self.name.clone());
Ok(self
.run
Expand All @@ -329,11 +381,12 @@ impl Package {
run: self.run.clone(),
name: package.as_package_name().clone(),
})
.sorted_by(|a, b| a.name.cmp(&b.name))
.collect())
}

/// The downstream packages that directly depend on this package
async fn immediate_dependencies(&self) -> Result<Vec<Package>, Error> {
async fn direct_dependencies(&self) -> Result<Vec<Package>, Error> {
let node: PackageNode = PackageNode::Workspace(self.name.clone());
Ok(self
.run
Expand All @@ -345,18 +398,55 @@ impl Package {
run: self.run.clone(),
name: package.as_package_name().clone(),
})
.sorted_by(|a, b| a.name.cmp(&b.name))
.collect())
}

async fn all_dependents(&self) -> Result<Vec<Package>, Error> {
let node: PackageNode = PackageNode::Workspace(self.name.clone());
Ok(self
.run
.pkg_dep_graph()
.ancestors(&node)
.iter()
.map(|package| Package {
run: self.run.clone(),
name: package.as_package_name().clone(),
})
.sorted_by(|a, b| a.name.cmp(&b.name))
.collect())
}

async fn all_dependencies(&self) -> Result<Vec<Package>, Error> {
let node: PackageNode = PackageNode::Workspace(self.name.clone());
Ok(self
.run
.pkg_dep_graph()
.dependencies(&node)
.iter()
.map(|package| Package {
run: self.run.clone(),
name: package.as_package_name().clone(),
})
.sorted_by(|a, b| a.name.cmp(&b.name))
.collect())
}

/// The downstream packages that depend on this package, transitively
async fn dependents(&self) -> Result<Vec<Package>, Error> {
/// The downstream packages that depend on this package, indirectly
async fn indirect_dependents(&self) -> Result<Vec<Package>, Error> {
let node: PackageNode = PackageNode::Workspace(self.name.clone());
let immediate_dependents = self
.run
.pkg_dep_graph()
.immediate_ancestors(&node)
.ok_or_else(|| Error::PackageNotFound(self.name.clone()))?;

Ok(self
.run
.pkg_dep_graph()
.ancestors(&node)
.iter()
.filter(|package| !immediate_dependents.contains(*package))
.map(|package| Package {
run: self.run.clone(),
name: package.as_package_name().clone(),
Expand All @@ -365,15 +455,21 @@ impl Package {
.collect())
}

/// The upstream packages that this package depends on, transitively
async fn dependencies(&self) -> Result<Vec<Package>, Error> {
/// The upstream packages that this package depends on, indirectly
async fn indirect_dependencies(&self) -> Result<Vec<Package>, Error> {
let node: PackageNode = PackageNode::Workspace(self.name.clone());
let immediate_dependencies = self
.run
.pkg_dep_graph()
.immediate_dependencies(&node)
.ok_or_else(|| Error::PackageNotFound(self.name.clone()))?;

Ok(self
.run
.pkg_dep_graph()
.dependencies(&node)
.iter()
.filter(|package| !immediate_dependencies.contains(*package))
.map(|package| Package {
run: self.run.clone(),
name: package.as_package_name().clone(),
Expand Down
44 changes: 39 additions & 5 deletions turborepo-tests/integration/tests/command-query.t
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Query packages with equals filter
}
Query packages that have at least one dependent package
$ ${TURBO} query "query { packages(filter: { greaterThan: { field: DEPENDENT_COUNT, value: 0 } }) { name } }" | jq
$ ${TURBO} query "query { packages(filter: { greaterThan: { field: DIRECT_DEPENDENT_COUNT, value: 0 } }) { name } }" | jq
WARNING query command is experimental and may change in the future
{
"data": {
Expand All @@ -50,13 +50,13 @@ Query packages that have at least one dependent package
}
Get dependents of `util`
$ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"util\" } }) { dependents { name } } }" | jq
$ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"util\" } }) { directDependents { name } } }" | jq
WARNING query command is experimental and may change in the future
{
"data": {
"packages": [
{
"dependents": [
"directDependents": [
{
"name": "my-app"
}
Expand All @@ -67,13 +67,47 @@ Get dependents of `util`
}
Get dependencies of `my-app`
$ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { dependencies { name } } }" | jq
$ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { directDependencies { name } } }" | jq
WARNING query command is experimental and may change in the future
{
"data": {
"packages": [
{
"dependencies": [
"directDependencies": [
{
"name": "util"
}
]
}
]
}
}
Get the indirect dependencies of `my-app`
$ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { indirectDependencies { name } } }" | jq
WARNING query command is experimental and may change in the future
{
"data": {
"packages": [
{
"indirectDependencies": [
{
"name": "//"
}
]
}
]
}
}
Get all dependencies of `my-app`
$ ${TURBO} query "query { packages(filter: { equal: { field: NAME, value: \"my-app\" } }) { allDependencies { name } } }" | jq
WARNING query command is experimental and may change in the future
{
"data": {
"packages": [
{
"allDependencies": [
{
"name": "//"
},
Expand Down

0 comments on commit d0f4a0a

Please sign in to comment.