11use itertools:: Itertools ;
22use lazy_static:: lazy_static;
3- use serde_json:: { Result , Value } ;
3+ use serde_json:: Value ;
44use std:: collections:: HashMap ;
55use warp_completion_metadata:: {
66 CommandBuilder , CommandSignatureGenerators , Generator , GeneratorResults ,
77 GeneratorResultsCollector , Suggestion ,
88} ;
99
1010lazy_static ! {
11- /// Command that computes the `nx` workspace targets by executing `nx graph --file`.
12- /// `nx graph` supports printing to stdout, but older versions of `nx` (before 19.20) had a bug
13- /// where the output of `nx graph` could be truncated when printing to stdout (see https://github.com/nrwl/nx/issues/18689
14- /// for more details).
15- /// The workaround here is to write the output to a tmpfile and then `cat` that tmpfile. We execute
16- /// this within a sh shell to ensure we are running in an environment where we can run POSIX-shell
17- /// compliant commands to generate the output, even if the user is running a non-POSIX compliant
18- /// shell (such as fish).
19- static ref NX_WORKSPACE_TARGETS_COMMAND : CommandBuilder = CommandBuilder :: and(
20- CommandBuilder :: single_command_and_ignore_stderr(
21- "sh -c 'temp=$(mktemp -u).json && nx graph --file $temp"
22- ) ,
23- CommandBuilder :: single_command( "cat $temp && rm $temp'" )
11+ /// Command that retrieves the Nx project graph with target information.
12+ ///
13+ /// Uses `nx graph --print` (Nx 19.20+) which outputs JSON to stdout.
14+ /// Falls back to `nx graph --file` with a tmpfile for older Nx versions
15+ /// (avoids a truncation bug in stdout output, see
16+ /// https://github.com/nrwl/nx/issues/18689).
17+ static ref NX_WORKSPACE_TARGETS_COMMAND : CommandBuilder = CommandBuilder :: single_command(
18+ "sh -c \" nx graph --print 2>/dev/null || { temp=\\ $(mktemp -u).json && nx graph --file \\ $temp 2>/dev/null && cat \\ $temp && rm -f \\ $temp; }\" "
2419 ) ;
2520}
2621
27- #[ derive( Debug , serde:: Deserialize ) ]
28- #[ serde( rename_all = "camelCase" ) ]
29- struct NxOutput {
30- projects : HashMap < String , NxProject > ,
31- }
32-
33- #[ derive( Debug , serde:: Deserialize ) ]
34- #[ serde( rename_all = "camelCase" ) ]
35- struct NxProject {
36- project_type : String ,
37- }
38-
22+ /// Parsed output from `nx graph --print` / `nx graph --file`.
3923#[ derive( Debug , serde:: Deserialize ) ]
4024#[ serde( rename_all = "camelCase" ) ]
4125struct NXGraphFile {
@@ -62,61 +46,71 @@ struct NXData {
6246 targets : HashMap < String , Value > ,
6347}
6448
65- fn process_workspace_json (
66- output : & str ,
67- filter_fn : fn ( project : & ( String , NxProject ) ) -> bool ,
68- ) -> GeneratorResults {
69- let json_output: Result < NxOutput > = serde_json:: from_str ( output) ;
70- match json_output {
71- Ok ( output) => output
72- . projects
73- . into_iter ( )
74- . filter ( filter_fn)
75- . map ( |( name, _) | Suggestion :: new ( name) )
76- . collect_unordered_results ( ) ,
77- Err ( e) => {
78- log:: info!( "Unable to deserialize nx output: {:?}" , e) ;
79- GeneratorResults :: default ( )
80- }
81- }
82- }
83-
8449fn process_generators ( output : & str ) -> GeneratorResults {
8550 output
8651 . split ( '\n' )
8752 . filter_map ( |line| line. split ( ' ' ) . next ( ) . map ( Suggestion :: new) )
8853 . collect_unordered_results ( )
8954}
9055
56+ /// Parse the output of `nx show projects` (one project name per line) into suggestions.
57+ fn process_project_list ( output : & str ) -> GeneratorResults {
58+ output
59+ . trim ( )
60+ . lines ( )
61+ . filter ( |line| !line. is_empty ( ) )
62+ . map ( |line| Suggestion :: new ( line. trim ( ) ) )
63+ . collect_unordered_results ( )
64+ }
65+
66+ /// Parse the project graph output into `project:target` suggestions.
67+ fn process_workspace_targets ( output : & str ) -> GeneratorResults {
68+ let Ok ( parsed_output) = serde_json:: from_str :: < NXGraphFile > ( output) else {
69+ return GeneratorResults :: default ( ) ;
70+ } ;
71+
72+ let suggestions = parsed_output
73+ . graph
74+ . nodes
75+ . into_values ( )
76+ . flat_map ( |node| {
77+ node. data . targets . into_keys ( ) . map ( move |target| {
78+ let name = format ! ( "{}:{target}" , node. name) ;
79+ Suggestion :: with_description ( name, "nx target" )
80+ } )
81+ } )
82+ . unique ( ) ;
83+ GeneratorResults {
84+ suggestions : suggestions. collect ( ) ,
85+ is_ordered : false ,
86+ }
87+ }
88+
9189pub fn generator ( ) -> CommandSignatureGenerators {
9290 CommandSignatureGenerators :: new ( "nx" )
9391 . add_generator (
9492 "apps" ,
9593 Generator :: script (
96- CommandBuilder :: single_command ( "cat workspace.json" ) ,
97- |output| {
98- process_workspace_json ( output, |( name, project) | {
99- project. project_type == "application" && !name. ends_with ( "-e2e" )
100- } )
101- } ,
94+ CommandBuilder :: single_command (
95+ "sh -c \" nx show projects --type app 2>/dev/null | grep -v -- '-e2e\\ $'\" " ,
96+ ) ,
97+ process_project_list,
10298 ) ,
10399 )
104100 . add_generator (
105101 "e2e_apps" ,
106102 Generator :: script (
107- CommandBuilder :: single_command ( "cat workspace.json" ) ,
108- |output| {
109- process_workspace_json ( output, |( name, project) | {
110- project. project_type == "application" && name. ends_with ( "-e2e" )
111- } )
112- } ,
103+ CommandBuilder :: single_command (
104+ "sh -c \" nx show projects --type e2e 2>/dev/null || nx show projects --type app 2>/dev/null | grep -- '-e2e\\ $'\" " ,
105+ ) ,
106+ process_project_list,
113107 ) ,
114108 )
115109 . add_generator (
116110 "apps_and_libs" ,
117111 Generator :: script (
118- CommandBuilder :: single_command ( "cat workspace.json " ) ,
119- |output| process_workspace_json ( output , |_| true ) ,
112+ CommandBuilder :: single_command ( "nx show projects " ) ,
113+ process_project_list ,
120114 ) ,
121115 )
122116 . add_generator (
@@ -135,27 +129,10 @@ pub fn generator() -> CommandSignatureGenerators {
135129 )
136130 . add_generator (
137131 "workspace_targets" ,
138- Generator :: script ( NX_WORKSPACE_TARGETS_COMMAND . clone ( ) , |output| {
139- let Ok ( parsed_output) = serde_json:: from_str :: < NXGraphFile > ( output) else {
140- return GeneratorResults :: default ( ) ;
141- } ;
142-
143- let suggestions = parsed_output
144- . graph
145- . nodes
146- . into_values ( )
147- . flat_map ( |node| {
148- node. data . targets . into_keys ( ) . map ( move |target| {
149- let name = format ! ( "{}:{target}" , node. name) ;
150- Suggestion :: with_description ( name, "nx target" )
151- } )
152- } )
153- . unique ( ) ;
154- GeneratorResults {
155- suggestions : suggestions. collect ( ) ,
156- is_ordered : false ,
157- }
158- } ) ,
132+ Generator :: script (
133+ NX_WORKSPACE_TARGETS_COMMAND . clone ( ) ,
134+ process_workspace_targets,
135+ ) ,
159136 )
160137 . add_generator (
161138 "installed_plugins" ,
0 commit comments