From 60e3cc5cfffd5afc55757217dabe3a56344262f3 Mon Sep 17 00:00:00 2001 From: tknickman Date: Mon, 19 Aug 2024 10:48:34 -0400 Subject: [PATCH 1/7] feat(ls): support output format --- crates/turborepo-lib/src/cli/mod.rs | 26 +++++++- crates/turborepo-lib/src/commands/ls.rs | 84 ++++++++++++++++++++++--- 2 files changed, 99 insertions(+), 11 deletions(-) diff --git a/crates/turborepo-lib/src/cli/mod.rs b/crates/turborepo-lib/src/cli/mod.rs index 0495fe64f2457..19eaf5a26e3d6 100644 --- a/crates/turborepo-lib/src/cli/mod.rs +++ b/crates/turborepo-lib/src/cli/mod.rs @@ -284,6 +284,25 @@ pub enum DaemonCommand { Logs, } +#[derive(Copy, Clone, Debug, Default, ValueEnum, Serialize, Eq, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum OutputFormat { + /// Output in a human-readable format + #[default] + Pretty, + /// Output in JSON format for direct parsing + Json, +} + +impl fmt::Display for OutputFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + OutputFormat::Pretty => "pretty", + OutputFormat::Json => "json", + }) + } +} + #[derive(Subcommand, Copy, Clone, Debug, PartialEq)] pub enum TelemetryCommand { /// Enables anonymous telemetry @@ -507,6 +526,9 @@ pub enum Command { /// Get insight into a specific package, such as /// its dependencies and tasks packages: Vec, + /// Output format + #[clap(long, value_enum)] + output: Option, }, /// Link your local directory to a Vercel organization and enable remote /// caching. @@ -1165,6 +1187,7 @@ pub async fn run( affected, filter, packages, + output, } => { warn!("ls command is experimental and may change in the future"); let event = CommandEventBuilder::new("info").with_parent(&root_telemetry); @@ -1173,8 +1196,9 @@ pub async fn run( let affected = *affected; let filter = filter.clone(); let packages = packages.clone(); + let output = output.clone(); let base = CommandBase::new(cli_args, repo_root, version, color_config); - ls::run(base, packages, event, filter, affected).await?; + ls::run(base, packages, event, filter, affected, output).await?; Ok(0) } diff --git a/crates/turborepo-lib/src/commands/ls.rs b/crates/turborepo-lib/src/commands/ls.rs index f6e09a7075f0b..c33c73402199d 100644 --- a/crates/turborepo-lib/src/commands/ls.rs +++ b/crates/turborepo-lib/src/commands/ls.rs @@ -1,6 +1,7 @@ //! A command for outputting info about packages and tasks in a turborepo. use miette::Diagnostic; +use serde::{ser::SerializeStruct, Serialize, Serializer}; use thiserror::Error; use turbopath::AnchoredSystemPath; use turborepo_repository::{ @@ -12,7 +13,7 @@ use turborepo_ui::{color, cprint, cprintln, ColorConfig, BOLD, BOLD_GREEN, GREY} use crate::{ cli, - cli::{Command, ExecutionArgs}, + cli::{Command, ExecutionArgs, OutputFormat}, commands::{run::get_signal, CommandBase}, run::{builder::RunBuilder, Run}, signal::SignalHandler, @@ -30,10 +31,42 @@ struct RepositoryDetails<'a> { packages: Vec<(&'a PackageName, &'a AnchoredSystemPath)>, } +impl<'a> Serialize for RepositoryDetails<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("RepositoryDetails", 2)?; + state.serialize_field("package_manager", &self.package_manager)?; + + let packages: Vec<_> = self + .packages + .iter() + .map(|(name, path)| { + serde_json::json!({ + "name": name.to_string(), + "path": path.to_string(), + }) + }) + .collect(); + + state.serialize_field("packages", &packages)?; + state.end() + } +} + +#[derive(Serialize)] +struct PackageTask<'a> { + name: &'a str, + command: &'a str, +} + +#[derive(Serialize)] struct PackageDetails<'a> { + #[serde(skip)] color_config: ColorConfig, name: &'a str, - tasks: Vec<(&'a str, &'a str)>, + tasks: Vec>, dependencies: Vec<&'a str>, } @@ -43,6 +76,7 @@ pub async fn run( telemetry: CommandEventBuilder, filter: Vec, affected: bool, + output: Option, ) -> Result<(), cli::Error> { let signal = get_signal()?; let handler = SignalHandler::new(signal); @@ -61,11 +95,11 @@ pub async fn run( let run = run_builder.build(&handler, telemetry).await?; if packages.is_empty() { - RepositoryDetails::new(&run).print()?; + RepositoryDetails::new(&run).print(output)?; } else { for package in packages { let package_details = PackageDetails::new(&run, &package)?; - package_details.print(); + package_details.print(output); } } @@ -99,7 +133,7 @@ impl<'a> RepositoryDetails<'a> { packages, } } - fn print(&self) -> Result<(), cli::Error> { + fn pretty_print(&self) { if self.packages.len() == 1 { cprintln!(self.color_config, BOLD, "{} package\n", self.packages.len()); } else { @@ -114,6 +148,21 @@ impl<'a> RepositoryDetails<'a> { for (package_name, entry) in &self.packages { println!(" {} {}", package_name, GREY.apply_to(entry)); } + } + + fn json_print(&self) { + println!("{}", serde_json::to_string_pretty(&self).unwrap()); + } + + fn print(&self, output: Option) -> Result<(), cli::Error> { + match output { + Some(OutputFormat::Json) => { + self.json_print(); + } + Some(OutputFormat::Pretty) | None => { + self.pretty_print(); + } + } Ok(()) } @@ -153,12 +202,12 @@ impl<'a> PackageDetails<'a> { tasks: package_json .scripts .iter() - .map(|(name, command)| (name.as_str(), command.as_str())) + .map(|(name, command)| PackageTask { name, command }) .collect(), }) } - fn print(&self) { + fn pretty_print(&self) { let name = color!(self.color_config, BOLD_GREEN, "{}", self.name); let depends_on = color!(self.color_config, BOLD, "depends on"); let dependencies = if self.dependencies.is_empty() { @@ -180,13 +229,28 @@ impl<'a> PackageDetails<'a> { } else { println!(); } - for (name, command) in &self.tasks { + for task in &self.tasks { println!( " {}: {}", - name, - color!(self.color_config, GREY, "{}", command) + task.name, + color!(self.color_config, GREY, "{}", task.command) ); } println!(); } + + fn json_print(&self) { + println!("{}", serde_json::to_string_pretty(&self).unwrap()); + } + + fn print(&self, output: Option) { + match output { + Some(OutputFormat::Json) => { + self.json_print(); + } + Some(OutputFormat::Pretty) | None => { + self.pretty_print(); + } + } + } } From aa9e3c73435e7da3e442b2a66d3527f3ddf04945 Mon Sep 17 00:00:00 2001 From: tknickman Date: Mon, 19 Aug 2024 11:26:43 -0400 Subject: [PATCH 2/7] Update output format --- crates/turborepo-lib/src/commands/ls.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/turborepo-lib/src/commands/ls.rs b/crates/turborepo-lib/src/commands/ls.rs index c33c73402199d..132b68f796648 100644 --- a/crates/turborepo-lib/src/commands/ls.rs +++ b/crates/turborepo-lib/src/commands/ls.rs @@ -39,7 +39,7 @@ impl<'a> Serialize for RepositoryDetails<'a> { let mut state = serializer.serialize_struct("RepositoryDetails", 2)?; state.serialize_field("package_manager", &self.package_manager)?; - let packages: Vec<_> = self + let package_items: Vec<_> = self .packages .iter() .map(|(name, path)| { @@ -50,6 +50,11 @@ impl<'a> Serialize for RepositoryDetails<'a> { }) .collect(); + let packages = serde_json::json!({ + "count": self.packages.len(), + "items": package_items, + }); + state.serialize_field("packages", &packages)?; state.end() } From edcf9f600834c500eb59a28e3074018fefaee6e8 Mon Sep 17 00:00:00 2001 From: tknickman Date: Mon, 19 Aug 2024 12:43:01 -0400 Subject: [PATCH 3/7] error fixes and formatting --- crates/turborepo-lib/src/commands/ls.rs | 38 ++++++++++++++++--------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/crates/turborepo-lib/src/commands/ls.rs b/crates/turborepo-lib/src/commands/ls.rs index 132b68f796648..a53247753093b 100644 --- a/crates/turborepo-lib/src/commands/ls.rs +++ b/crates/turborepo-lib/src/commands/ls.rs @@ -37,7 +37,7 @@ impl<'a> Serialize for RepositoryDetails<'a> { S: Serializer, { let mut state = serializer.serialize_struct("RepositoryDetails", 2)?; - state.serialize_field("package_manager", &self.package_manager)?; + state.serialize_field("packageManager", &self.package_manager)?; let package_items: Vec<_> = self .packages @@ -139,16 +139,20 @@ impl<'a> RepositoryDetails<'a> { } } fn pretty_print(&self) { - if self.packages.len() == 1 { - cprintln!(self.color_config, BOLD, "{} package\n", self.packages.len()); - } else { - cprintln!( - self.color_config, - BOLD, - "{} packages\n", - self.packages.len() - ); - } + let package_copy = match self.packages.len() { + 0 => "no packages", + 1 => "package", + _ => "packages", + }; + + cprint!( + self.color_config, + BOLD, + "{} {} ", + self.packages.len(), + package_copy + ); + cprintln!(self.color_config, GREY, "({})\n", self.package_manager); for (package_name, entry) in &self.packages { println!(" {} {}", package_name, GREY.apply_to(entry)); @@ -156,7 +160,11 @@ impl<'a> RepositoryDetails<'a> { } fn json_print(&self) { - println!("{}", serde_json::to_string_pretty(&self).unwrap()); + let as_json = serde_json::to_string_pretty(&self); + match as_json { + Ok(json) => println!("{}", json), + Err(err) => println!(r#"{{"error": "{}"}}"#, err), + } } fn print(&self, output: Option) -> Result<(), cli::Error> { @@ -245,7 +253,11 @@ impl<'a> PackageDetails<'a> { } fn json_print(&self) { - println!("{}", serde_json::to_string_pretty(&self).unwrap()); + let as_json = serde_json::to_string_pretty(&self); + match as_json { + Ok(json) => println!("{}", json), + Err(err) => println!(r#"{{"error": "{}"}}"#, err), + } } fn print(&self, output: Option) { From c6fe3fb536948e2287490bd7b4314a45175c4425 Mon Sep 17 00:00:00 2001 From: tknickman Date: Mon, 19 Aug 2024 12:55:57 -0400 Subject: [PATCH 4/7] update serializer method --- crates/turborepo-lib/src/commands/ls.rs | 61 +++++++++++++++---------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/crates/turborepo-lib/src/commands/ls.rs b/crates/turborepo-lib/src/commands/ls.rs index a53247753093b..bea1747642306 100644 --- a/crates/turborepo-lib/src/commands/ls.rs +++ b/crates/turborepo-lib/src/commands/ls.rs @@ -1,7 +1,7 @@ //! A command for outputting info about packages and tasks in a turborepo. use miette::Diagnostic; -use serde::{ser::SerializeStruct, Serialize, Serializer}; +use serde::Serialize; use thiserror::Error; use turbopath::AnchoredSystemPath; use turborepo_repository::{ @@ -25,38 +25,49 @@ pub enum Error { PackageNotFound { package: String }, } +#[derive(Clone, Serialize)] +#[serde(into = "RepositoryDetailsDisplay<'a>")] struct RepositoryDetails<'a> { color_config: ColorConfig, package_manager: &'a PackageManager, packages: Vec<(&'a PackageName, &'a AnchoredSystemPath)>, } -impl<'a> Serialize for RepositoryDetails<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("RepositoryDetails", 2)?; - state.serialize_field("packageManager", &self.package_manager)?; - - let package_items: Vec<_> = self - .packages - .iter() - .map(|(name, path)| { - serde_json::json!({ - "name": name.to_string(), - "path": path.to_string(), - }) - }) - .collect(); +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct RepositoryDetailsDisplay<'a> { + package_manager: &'a PackageManager, + packages: PackagesDisplay, +} - let packages = serde_json::json!({ - "count": self.packages.len(), - "items": package_items, - }); +#[derive(Serialize)] +struct PackagesDisplay { + count: usize, + items: Vec, +} - state.serialize_field("packages", &packages)?; - state.end() +#[derive(Serialize)] +struct PackageDetailDisplay { + name: String, + path: String, +} + +impl<'a> Into> for RepositoryDetails<'a> { + fn into(self) -> RepositoryDetailsDisplay<'a> { + RepositoryDetailsDisplay { + package_manager: self.package_manager, + packages: PackagesDisplay { + count: self.packages.len(), + items: self + .packages + .into_iter() + .map(|(name, path)| PackageDetailDisplay { + name: name.to_string(), + path: path.to_string(), + }) + .collect(), + }, + } } } From a4de4d5c50fc56e0400d47e517aca23d8805cf32 Mon Sep 17 00:00:00 2001 From: tknickman Date: Mon, 19 Aug 2024 13:46:56 -0400 Subject: [PATCH 5/7] Use a generic type and fix clippy --- crates/turborepo-lib/src/cli/mod.rs | 2 +- crates/turborepo-lib/src/commands/ls.rs | 51 +++++++++++++++++-------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/crates/turborepo-lib/src/cli/mod.rs b/crates/turborepo-lib/src/cli/mod.rs index 19eaf5a26e3d6..7c3f30c210139 100644 --- a/crates/turborepo-lib/src/cli/mod.rs +++ b/crates/turborepo-lib/src/cli/mod.rs @@ -1194,9 +1194,9 @@ pub async fn run( event.track_call(); let affected = *affected; + let output = *output; let filter = filter.clone(); let packages = packages.clone(); - let output = output.clone(); let base = CommandBase::new(cli_args, repo_root, version, color_config); ls::run(base, packages, event, filter, affected, output).await?; diff --git a/crates/turborepo-lib/src/commands/ls.rs b/crates/turborepo-lib/src/commands/ls.rs index bea1747642306..b668621176aa2 100644 --- a/crates/turborepo-lib/src/commands/ls.rs +++ b/crates/turborepo-lib/src/commands/ls.rs @@ -25,6 +25,12 @@ pub enum Error { PackageNotFound { package: String }, } +#[derive(Serialize)] +struct ItemsWithCount { + count: usize, + items: Vec, +} + #[derive(Clone, Serialize)] #[serde(into = "RepositoryDetailsDisplay<'a>")] struct RepositoryDetails<'a> { @@ -37,13 +43,7 @@ struct RepositoryDetails<'a> { #[serde(rename_all = "camelCase")] struct RepositoryDetailsDisplay<'a> { package_manager: &'a PackageManager, - packages: PackagesDisplay, -} - -#[derive(Serialize)] -struct PackagesDisplay { - count: usize, - items: Vec, + packages: ItemsWithCount, } #[derive(Serialize)] @@ -52,13 +52,13 @@ struct PackageDetailDisplay { path: String, } -impl<'a> Into> for RepositoryDetails<'a> { - fn into(self) -> RepositoryDetailsDisplay<'a> { +impl<'a> From> for RepositoryDetailsDisplay<'a> { + fn from(val: RepositoryDetails<'a>) -> Self { RepositoryDetailsDisplay { - package_manager: self.package_manager, - packages: PackagesDisplay { - count: self.packages.len(), - items: self + package_manager: val.package_manager, + packages: ItemsWithCount { + count: val.packages.len(), + items: val .packages .into_iter() .map(|(name, path)| PackageDetailDisplay { @@ -71,13 +71,14 @@ impl<'a> Into> for RepositoryDetails<'a> { } } -#[derive(Serialize)] +#[derive(Clone, Serialize)] struct PackageTask<'a> { name: &'a str, command: &'a str, } -#[derive(Serialize)] +#[derive(Clone, Serialize)] +#[serde(into = "PackageDetailsDisplay<'a>")] struct PackageDetails<'a> { #[serde(skip)] color_config: ColorConfig, @@ -86,6 +87,26 @@ struct PackageDetails<'a> { dependencies: Vec<&'a str>, } +#[derive(Serialize)] +struct PackageDetailsDisplay<'a> { + name: &'a str, + tasks: ItemsWithCount>, + dependencies: Vec<&'a str>, +} + +impl<'a> From> for PackageDetailsDisplay<'a> { + fn from(val: PackageDetails<'a>) -> Self { + PackageDetailsDisplay { + name: val.name, + dependencies: val.dependencies, + tasks: ItemsWithCount { + count: val.tasks.len(), + items: val.tasks, + }, + } + } +} + pub async fn run( mut base: CommandBase, packages: Vec, From 978e9ef007913a747b1fb8ed1373ee9cd6bb8667 Mon Sep 17 00:00:00 2001 From: tknickman Date: Mon, 19 Aug 2024 14:16:53 -0400 Subject: [PATCH 6/7] tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test json output regex paths don’t forget the regex --- turborepo-tests/integration/tests/affected.t | 12 ++--- .../integration/tests/command-ls.t | 50 ++++++++++++++++++- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/turborepo-tests/integration/tests/affected.t b/turborepo-tests/integration/tests/affected.t index 02031a33f0196..d872b33c484c8 100644 --- a/turborepo-tests/integration/tests/affected.t +++ b/turborepo-tests/integration/tests/affected.t @@ -28,7 +28,7 @@ Validate that we only run `my-app#build` with change not committed Do the same thing with the `ls` command $ ${TURBO} ls --affected WARNING ls command is experimental and may change in the future - 1 package + 1 package (npm) my-app apps[\/\\]my-app (re) @@ -56,7 +56,7 @@ Validate that we only run `my-app#build` with change committed Do the same thing with the `ls` command $ ${TURBO} ls --affected WARNING ls command is experimental and may change in the future - 1 package + 1 package (npm) my-app apps[\/\\]my-app (re) @@ -76,7 +76,7 @@ Override the SCM base to be HEAD, so nothing runs Do the same thing with the `ls` command $ TURBO_SCM_BASE="HEAD" ${TURBO} ls --affected WARNING ls command is experimental and may change in the future - 0 packages + 0 no packages (npm) @@ -96,7 +96,7 @@ Override the SCM head to be main, so nothing runs Do the same thing with the `ls` command $ TURBO_SCM_HEAD="main" ${TURBO} ls --affected WARNING ls command is experimental and may change in the future - 0 packages + 0 no packages (npm) @@ -127,7 +127,7 @@ Run the build and expect only `my-app` to be affected, since between Do the same thing with the `ls` command $ ${TURBO} ls --affected WARNING ls command is experimental and may change in the future - 1 package + 1 package (npm) my-app apps[\/\\]my-app (re) @@ -167,7 +167,7 @@ Do the same thing with the `ls` command WARNING ls command is experimental and may change in the future WARNING unable to detect git range, assuming all files have changed: git error: fatal: main...HEAD: no merge base - 3 packages + 3 packages (npm) another packages[\/\\]another (re) my-app apps[\/\\]my-app (re) diff --git a/turborepo-tests/integration/tests/command-ls.t b/turborepo-tests/integration/tests/command-ls.t index d0ab97647f9e2..72658c0287db5 100644 --- a/turborepo-tests/integration/tests/command-ls.t +++ b/turborepo-tests/integration/tests/command-ls.t @@ -4,16 +4,40 @@ Setup Run info $ ${TURBO} ls WARNING ls command is experimental and may change in the future - 3 packages + 3 packages (npm) another packages[\/\\]another (re) my-app apps[\/\\]my-app (re) util packages[\/\\]util (re) +Run info with json output + $ ${TURBO} ls --output=json + WARNING ls command is experimental and may change in the future + { + "packageManager": "npm", + "packages": { + "count": 3, + "items": [ + { + "name": "another", + "path": "packages[\/\\]another" (re) + }, + { + "name": "my-app", + "path": "apps[\/\\]my-app" (re) + }, + { + "name": "util", + "path": "packages[\/\\]util" (re) + } + ] + } + } + Run info with filter $ ${TURBO} ls -F my-app... WARNING ls command is experimental and may change in the future - 2 packages + 2 packages (npm) my-app apps[\/\\]my-app (re) util packages[\/\\]util (re) @@ -35,3 +59,25 @@ Run info on package `my-app` build: echo building maybefails: exit 4 +Run info on package `my-app` with json output + $ ${TURBO} ls my-app --output=json + WARNING ls command is experimental and may change in the future + { + "name": "my-app", + "tasks": { + "count": 2, + "items": [ + { + "name": "build", + "command": "echo building" + }, + { + "name": "maybefails", + "command": "exit 4" + } + ] + }, + "dependencies": [ + "util" + ] + } From 941fa18edc2b0b4c91b3ef46c84e2ac4500a9417 Mon Sep 17 00:00:00 2001 From: tknickman Date: Mon, 19 Aug 2024 16:28:38 -0400 Subject: [PATCH 7/7] fix multi package --- crates/turborepo-lib/src/commands/ls.rs | 59 +++++++++---------- .../integration/tests/command-ls.t | 40 +++++++------ 2 files changed, 51 insertions(+), 48 deletions(-) diff --git a/crates/turborepo-lib/src/commands/ls.rs b/crates/turborepo-lib/src/commands/ls.rs index b668621176aa2..1d9f284ef1c5e 100644 --- a/crates/turborepo-lib/src/commands/ls.rs +++ b/crates/turborepo-lib/src/commands/ls.rs @@ -87,6 +87,11 @@ struct PackageDetails<'a> { dependencies: Vec<&'a str>, } +#[derive(Clone, Serialize)] +struct PackageDetailsList<'a> { + packages: Vec>, +} + #[derive(Serialize)] struct PackageDetailsDisplay<'a> { name: &'a str, @@ -134,9 +139,24 @@ pub async fn run( if packages.is_empty() { RepositoryDetails::new(&run).print(output)?; } else { - for package in packages { - let package_details = PackageDetails::new(&run, &package)?; - package_details.print(output); + match output { + Some(OutputFormat::Json) => { + let mut package_details_list = PackageDetailsList { packages: vec![] }; + // collect all package details + for package in &packages { + let package_details = PackageDetails::new(&run, package)?; + package_details_list.packages.push(package_details); + } + + let as_json = serde_json::to_string_pretty(&package_details_list)?; + println!("{}", as_json); + } + Some(OutputFormat::Pretty) | None => { + for package in packages { + let package_details = PackageDetails::new(&run, &package)?; + package_details.print(); + } + } } } @@ -191,18 +211,16 @@ impl<'a> RepositoryDetails<'a> { } } - fn json_print(&self) { - let as_json = serde_json::to_string_pretty(&self); - match as_json { - Ok(json) => println!("{}", json), - Err(err) => println!(r#"{{"error": "{}"}}"#, err), - } + fn json_print(&self) -> Result<(), cli::Error> { + let as_json = serde_json::to_string_pretty(&self)?; + println!("{}", as_json); + Ok(()) } fn print(&self, output: Option) -> Result<(), cli::Error> { match output { Some(OutputFormat::Json) => { - self.json_print(); + self.json_print()?; } Some(OutputFormat::Pretty) | None => { self.pretty_print(); @@ -252,7 +270,7 @@ impl<'a> PackageDetails<'a> { }) } - fn pretty_print(&self) { + fn print(&self) { let name = color!(self.color_config, BOLD_GREEN, "{}", self.name); let depends_on = color!(self.color_config, BOLD, "depends on"); let dependencies = if self.dependencies.is_empty() { @@ -283,23 +301,4 @@ impl<'a> PackageDetails<'a> { } println!(); } - - fn json_print(&self) { - let as_json = serde_json::to_string_pretty(&self); - match as_json { - Ok(json) => println!("{}", json), - Err(err) => println!(r#"{{"error": "{}"}}"#, err), - } - } - - fn print(&self, output: Option) { - match output { - Some(OutputFormat::Json) => { - self.json_print(); - } - Some(OutputFormat::Pretty) | None => { - self.pretty_print(); - } - } - } } diff --git a/turborepo-tests/integration/tests/command-ls.t b/turborepo-tests/integration/tests/command-ls.t index 72658c0287db5..ebf8c9f3042f8 100644 --- a/turborepo-tests/integration/tests/command-ls.t +++ b/turborepo-tests/integration/tests/command-ls.t @@ -20,15 +20,15 @@ Run info with json output "items": [ { "name": "another", - "path": "packages[\/\\]another" (re) + "path": "packages(\/|\\\\)another" (re) }, { "name": "my-app", - "path": "apps[\/\\]my-app" (re) + "path": "apps(\/|\\\\)my-app" (re) }, { "name": "util", - "path": "packages[\/\\]util" (re) + "path": "packages(\/|\\\\)util" (re) } ] } @@ -63,21 +63,25 @@ Run info on package `my-app` with json output $ ${TURBO} ls my-app --output=json WARNING ls command is experimental and may change in the future { - "name": "my-app", - "tasks": { - "count": 2, - "items": [ - { - "name": "build", - "command": "echo building" + "packages": [ + { + "name": "my-app", + "tasks": { + "count": 2, + "items": [ + { + "name": "build", + "command": "echo building" + }, + { + "name": "maybefails", + "command": "exit 4" + } + ] }, - { - "name": "maybefails", - "command": "exit 4" - } - ] - }, - "dependencies": [ - "util" + "dependencies": [ + "util" + ] + } ] }