1
1
use std:: collections:: { HashMap , HashSet } ;
2
2
3
+ use itertools:: Itertools ;
3
4
use tracing:: warn;
4
5
use turbopath:: AbsoluteSystemPath ;
5
- use turborepo_micro_frontend:: { Config as MFEConfig , Error , DEFAULT_MICRO_FRONTENDS_CONFIG } ;
6
- use turborepo_repository:: package_graph:: PackageGraph ;
6
+ use turborepo_micro_frontend:: {
7
+ Config as MFEConfig , Error , DEFAULT_MICRO_FRONTENDS_CONFIG , MICRO_FRONTENDS_PACKAGES ,
8
+ } ;
9
+ use turborepo_repository:: package_graph:: { PackageGraph , PackageName } ;
7
10
8
- use crate :: run:: task_id:: TaskId ;
11
+ use crate :: {
12
+ config,
13
+ run:: task_id:: { TaskId , TaskName } ,
14
+ turbo_json:: TurboJson ,
15
+ } ;
9
16
10
17
#[ derive( Debug , Clone ) ]
11
18
pub struct MicroFrontendsConfigs {
12
19
configs : HashMap < String , HashSet < TaskId < ' static > > > ,
20
+ mfe_package : Option < & ' static str > ,
13
21
}
14
22
15
23
impl MicroFrontendsConfigs {
@@ -44,7 +52,23 @@ impl MicroFrontendsConfigs {
44
52
configs. insert ( package_name. to_string ( ) , tasks) ;
45
53
}
46
54
47
- Ok ( ( !configs. is_empty ( ) ) . then_some ( Self { configs } ) )
55
+ let mfe_package = package_graph
56
+ . packages ( )
57
+ . map ( |( pkg, _) | pkg. as_str ( ) )
58
+ . sorted ( )
59
+ // We use `find_map` here instead of a simple `find` so we get the &'static str
60
+ // instead of the &str tied to the lifetime of the package graph.
61
+ . find_map ( |pkg| {
62
+ MICRO_FRONTENDS_PACKAGES
63
+ . iter ( )
64
+ . find ( |static_pkg| pkg == * * static_pkg)
65
+ } )
66
+ . copied ( ) ;
67
+
68
+ Ok ( ( !configs. is_empty ( ) ) . then_some ( Self {
69
+ configs,
70
+ mfe_package,
71
+ } ) )
48
72
}
49
73
50
74
pub fn contains_package ( & self , package_name : & str ) -> bool {
@@ -64,4 +88,178 @@ impl MicroFrontendsConfigs {
64
88
. values ( )
65
89
. any ( |dev_tasks| dev_tasks. contains ( task_id) )
66
90
}
91
+
92
+ pub fn update_turbo_json (
93
+ & self ,
94
+ package_name : & PackageName ,
95
+ turbo_json : Result < TurboJson , config:: Error > ,
96
+ ) -> Result < TurboJson , config:: Error > {
97
+ // If package either
98
+ // - contains the proxy task
99
+ // - a member of one of the microfrontends
100
+ // then we need to modify its task definitions
101
+ if let Some ( FindResult { dev, proxy } ) = self . package_turbo_json_update ( package_name) {
102
+ // We need to modify turbo.json, use default one if there isn't one present
103
+ let mut turbo_json = turbo_json. or_else ( |err| match err {
104
+ config:: Error :: NoTurboJSON => Ok ( TurboJson :: default ( ) ) ,
105
+ err => Err ( err) ,
106
+ } ) ?;
107
+
108
+ // If the current package contains the proxy task, then add that definition
109
+ if proxy. package ( ) == package_name. as_str ( ) {
110
+ turbo_json. with_proxy ( self . mfe_package ) ;
111
+ }
112
+
113
+ if let Some ( dev) = dev {
114
+ // If this package has a dev task that's part of the MFE, then we make sure the
115
+ // proxy gets included in the task graph.
116
+ turbo_json. with_sibling (
117
+ TaskName :: from ( dev. task ( ) ) . into_owned ( ) ,
118
+ & proxy. as_task_name ( ) ,
119
+ ) ;
120
+ }
121
+
122
+ Ok ( turbo_json)
123
+ } else {
124
+ turbo_json
125
+ }
126
+ }
127
+
128
+ fn package_turbo_json_update < ' a > (
129
+ & ' a self ,
130
+ package_name : & PackageName ,
131
+ ) -> Option < FindResult < ' a > > {
132
+ self . configs ( ) . find_map ( |( config, tasks) | {
133
+ let dev_task = tasks. iter ( ) . find_map ( |task| {
134
+ ( task. package ( ) == package_name. as_str ( ) ) . then ( || FindResult {
135
+ dev : Some ( task. as_borrowed ( ) ) ,
136
+ proxy : TaskId :: new ( config, "proxy" ) ,
137
+ } )
138
+ } ) ;
139
+ let proxy_owner = ( config. as_str ( ) == package_name. as_str ( ) ) . then ( || FindResult {
140
+ dev : None ,
141
+ proxy : TaskId :: new ( config, "proxy" ) ,
142
+ } ) ;
143
+ dev_task. or ( proxy_owner)
144
+ } )
145
+ }
146
+ }
147
+
148
+ #[ derive( Debug , PartialEq , Eq ) ]
149
+ struct FindResult < ' a > {
150
+ dev : Option < TaskId < ' a > > ,
151
+ proxy : TaskId < ' a > ,
152
+ }
153
+
154
+ #[ cfg( test) ]
155
+ mod test {
156
+ use test_case:: test_case;
157
+
158
+ use super :: * ;
159
+
160
+ macro_rules! mfe_configs {
161
+ { $( $config_owner: expr => $dev_tasks: expr) ,+} => {
162
+ {
163
+ let mut _map = std:: collections:: HashMap :: new( ) ;
164
+ $(
165
+ let mut _dev_tasks = std:: collections:: HashSet :: new( ) ;
166
+ for _dev_task in $dev_tasks. as_slice( ) {
167
+ _dev_tasks. insert( crate :: run:: task_id:: TaskName :: from( * _dev_task) . task_id( ) . unwrap( ) . into_owned( ) ) ;
168
+ }
169
+ _map. insert( $config_owner. to_string( ) , _dev_tasks) ;
170
+ ) +
171
+ _map
172
+ }
173
+ } ;
174
+ }
175
+
176
+ struct PackageUpdateTest {
177
+ package_name : & ' static str ,
178
+ result : Option < TestFindResult > ,
179
+ }
180
+
181
+ struct TestFindResult {
182
+ dev : Option < & ' static str > ,
183
+ proxy : & ' static str ,
184
+ }
185
+
186
+ impl PackageUpdateTest {
187
+ pub const fn new ( package_name : & ' static str ) -> Self {
188
+ Self {
189
+ package_name,
190
+ result : None ,
191
+ }
192
+ }
193
+
194
+ pub const fn dev ( mut self , dev : & ' static str , proxy : & ' static str ) -> Self {
195
+ self . result = Some ( TestFindResult {
196
+ dev : Some ( dev) ,
197
+ proxy,
198
+ } ) ;
199
+ self
200
+ }
201
+
202
+ pub const fn proxy_only ( mut self , proxy : & ' static str ) -> Self {
203
+ self . result = Some ( TestFindResult { dev : None , proxy } ) ;
204
+ self
205
+ }
206
+
207
+ pub fn package_name ( & self ) -> PackageName {
208
+ PackageName :: from ( self . package_name )
209
+ }
210
+
211
+ pub fn expected ( & self ) -> Option < FindResult > {
212
+ match self . result {
213
+ Some ( TestFindResult {
214
+ dev : Some ( dev) ,
215
+ proxy,
216
+ } ) => Some ( FindResult {
217
+ dev : Some ( Self :: str_to_task ( dev) ) ,
218
+ proxy : Self :: str_to_task ( proxy) ,
219
+ } ) ,
220
+ Some ( TestFindResult { dev : None , proxy } ) => Some ( FindResult {
221
+ dev : None ,
222
+ proxy : Self :: str_to_task ( proxy) ,
223
+ } ) ,
224
+ None => None ,
225
+ }
226
+ }
227
+
228
+ fn str_to_task ( s : & str ) -> TaskId < ' static > {
229
+ crate :: run:: task_id:: TaskName :: from ( s)
230
+ . task_id ( )
231
+ . unwrap ( )
232
+ . into_owned ( )
233
+ }
234
+ }
235
+
236
+ const NON_MFE_PKG : PackageUpdateTest = PackageUpdateTest :: new ( "other-pkg" ) ;
237
+ const MFE_CONFIG_PKG : PackageUpdateTest =
238
+ PackageUpdateTest :: new ( "mfe-config-pkg" ) . proxy_only ( "mfe-config-pkg#proxy" ) ;
239
+ const MFE_CONFIG_PKG_DEV_TASK : PackageUpdateTest =
240
+ PackageUpdateTest :: new ( "web" ) . dev ( "web#dev" , "mfe-config-pkg#proxy" ) ;
241
+ const DEFAULT_APP_PROXY : PackageUpdateTest =
242
+ PackageUpdateTest :: new ( "mfe-docs" ) . dev ( "mfe-docs#serve" , "mfe-web#proxy" ) ;
243
+ const DEFAULT_APP_PROXY_AND_DEV : PackageUpdateTest =
244
+ PackageUpdateTest :: new ( "mfe-web" ) . dev ( "mfe-web#dev" , "mfe-web#proxy" ) ;
245
+
246
+ #[ test_case( NON_MFE_PKG ) ]
247
+ #[ test_case( MFE_CONFIG_PKG ) ]
248
+ #[ test_case( MFE_CONFIG_PKG_DEV_TASK ) ]
249
+ #[ test_case( DEFAULT_APP_PROXY ) ]
250
+ #[ test_case( DEFAULT_APP_PROXY_AND_DEV ) ]
251
+ fn test_package_turbo_json_update ( test : PackageUpdateTest ) {
252
+ let configs = mfe_configs ! (
253
+ "mfe-config-pkg" => [ "web#dev" , "docs#dev" ] ,
254
+ "mfe-web" => [ "mfe-web#dev" , "mfe-docs#serve" ]
255
+ ) ;
256
+ let mfe = MicroFrontendsConfigs {
257
+ configs,
258
+ mfe_package : None ,
259
+ } ;
260
+ assert_eq ! (
261
+ mfe. package_turbo_json_update( & test. package_name( ) ) ,
262
+ test. expected( )
263
+ ) ;
264
+ }
67
265
}
0 commit comments