@@ -20,7 +20,8 @@ pub struct MicrofrontendsConfigs {
20
20
21
21
#[ derive( Debug , Clone , Default , PartialEq ) ]
22
22
struct ConfigInfo {
23
- tasks : HashSet < TaskId < ' static > > ,
23
+ // A map from tasks declared in the configuration to the application that they belong to
24
+ tasks : HashMap < TaskId < ' static > , String > ,
24
25
ports : HashMap < TaskId < ' static > , u16 > ,
25
26
version : & ' static str ,
26
27
path : Option < RelativeUnixPathBuf > ,
@@ -32,24 +33,33 @@ impl MicrofrontendsConfigs {
32
33
repo_root : & AbsoluteSystemPath ,
33
34
package_graph : & PackageGraph ,
34
35
) -> Result < Option < Self > , Error > {
35
- Self :: from_configs ( package_graph. packages ( ) . map ( |( name, info) | {
36
- (
37
- name. as_str ( ) ,
38
- MFEConfig :: load_from_dir ( repo_root, info. package_path ( ) ) ,
39
- )
40
- } ) )
36
+ let package_names = package_graph
37
+ . packages ( )
38
+ . map ( |( name, _) | name. as_str ( ) )
39
+ . collect ( ) ;
40
+ Self :: from_configs (
41
+ package_names,
42
+ package_graph. packages ( ) . map ( |( name, info) | {
43
+ (
44
+ name. as_str ( ) ,
45
+ MFEConfig :: load_from_dir ( repo_root, info. package_path ( ) ) ,
46
+ )
47
+ } ) ,
48
+ )
41
49
}
42
50
43
51
/// Constructs a collection of configurations from a list of configurations
44
52
pub fn from_configs < ' a > (
53
+ package_names : HashSet < & str > ,
45
54
configs : impl Iterator < Item = ( & ' a str , Result < Option < MFEConfig > , Error > ) > ,
46
55
) -> Result < Option < Self > , Error > {
47
56
let PackageGraphResult {
48
57
configs,
49
58
missing_default_apps,
59
+ missing_applications,
50
60
unsupported_version,
51
61
mfe_package,
52
- } = PackageGraphResult :: new ( configs) ?;
62
+ } = PackageGraphResult :: new ( package_names , configs) ?;
53
63
54
64
for ( package, err) in unsupported_version {
55
65
warn ! ( "Ignoring {package}: {err}" ) ;
@@ -62,29 +72,34 @@ impl MicrofrontendsConfigs {
62
72
) ;
63
73
}
64
74
75
+ if !missing_applications. is_empty ( ) {
76
+ warn ! (
77
+ "Unable to find packages referenced in 'microfrontends.json' in workspace. Local \
78
+ proxy will not route to the following applications if they are running locally: \
79
+ {}",
80
+ missing_applications. join( ", " )
81
+ ) ;
82
+ }
83
+
65
84
Ok ( ( !configs. is_empty ( ) ) . then_some ( Self {
66
85
configs,
67
86
mfe_package,
68
87
} ) )
69
88
}
70
89
71
- pub fn contains_package ( & self , package_name : & str ) -> bool {
72
- self . configs . contains_key ( package_name)
73
- }
74
-
75
- pub fn configs ( & self ) -> impl Iterator < Item = ( & String , & HashSet < TaskId < ' static > > ) > {
90
+ pub fn configs ( & self ) -> impl Iterator < Item = ( & String , & HashMap < TaskId < ' static > , String > ) > {
76
91
self . configs . iter ( ) . map ( |( pkg, info) | ( pkg, & info. tasks ) )
77
92
}
78
93
79
- pub fn get ( & self , package_name : & str ) -> Option < & HashSet < TaskId < ' static > > > {
94
+ pub fn get ( & self , package_name : & str ) -> Option < & HashMap < TaskId < ' static > , String > > {
80
95
let info = self . configs . get ( package_name) ?;
81
96
Some ( & info. tasks )
82
97
}
83
98
84
99
pub fn task_has_mfe_proxy ( & self , task_id : & TaskId ) -> bool {
85
100
self . configs
86
101
. values ( )
87
- . any ( |info| info. tasks . contains ( task_id) )
102
+ . any ( |info| info. tasks . contains_key ( task_id) )
88
103
}
89
104
90
105
pub fn config_filename ( & self , package_name : & str ) -> Option < & RelativeUnixPath > {
@@ -152,7 +167,7 @@ impl MicrofrontendsConfigs {
152
167
package_name : & PackageName ,
153
168
) -> Option < FindResult < ' a > > {
154
169
let results = self . configs . iter ( ) . filter_map ( |( config, info) | {
155
- let dev_task = info. tasks . iter ( ) . find_map ( |task| {
170
+ let dev_task = info. tasks . iter ( ) . find_map ( |( task, _ ) | {
156
171
( task. package ( ) == package_name. as_str ( ) ) . then ( || FindResult {
157
172
dev : Some ( task. as_borrowed ( ) ) ,
158
173
proxy : TaskId :: new ( config, "proxy" ) ,
@@ -186,16 +201,19 @@ impl MicrofrontendsConfigs {
186
201
struct PackageGraphResult {
187
202
configs : HashMap < String , ConfigInfo > ,
188
203
missing_default_apps : Vec < String > ,
204
+ missing_applications : Vec < String > ,
189
205
unsupported_version : Vec < ( String , String ) > ,
190
206
mfe_package : Option < & ' static str > ,
191
207
}
192
208
193
209
impl PackageGraphResult {
194
210
fn new < ' a > (
211
+ packages_in_graph : HashSet < & str > ,
195
212
packages : impl Iterator < Item = ( & ' a str , Result < Option < MFEConfig > , Error > ) > ,
196
213
) -> Result < Self , Error > {
197
214
let mut configs = HashMap :: new ( ) ;
198
215
let mut referenced_default_apps = HashSet :: new ( ) ;
216
+ let mut referenced_packages = HashSet :: new ( ) ;
199
217
let mut unsupported_version = Vec :: new ( ) ;
200
218
let mut mfe_package = None ;
201
219
// We sort packages to ensure deterministic behavior
@@ -223,6 +241,8 @@ impl PackageGraphResult {
223
241
if let Some ( path) = config. path ( ) {
224
242
info. path = Some ( path. to_unix ( ) ) ;
225
243
}
244
+ referenced_packages. insert ( package_name. to_string ( ) ) ;
245
+ referenced_packages. extend ( info. tasks . keys ( ) . map ( |task| task. package ( ) . to_string ( ) ) ) ;
226
246
configs. insert ( package_name. to_string ( ) , info) ;
227
247
}
228
248
let default_apps_found = configs. keys ( ) . cloned ( ) . collect ( ) ;
@@ -231,9 +251,15 @@ impl PackageGraphResult {
231
251
. cloned ( )
232
252
. collect :: < Vec < _ > > ( ) ;
233
253
missing_default_apps. sort ( ) ;
254
+ let mut missing_applications = referenced_packages
255
+ . into_iter ( )
256
+ . filter ( |package| !packages_in_graph. contains ( package. as_str ( ) ) )
257
+ . collect :: < Vec < _ > > ( ) ;
258
+ missing_applications. sort ( ) ;
234
259
Ok ( Self {
235
260
configs,
236
261
missing_default_apps,
262
+ missing_applications,
237
263
unsupported_version,
238
264
mfe_package,
239
265
} )
@@ -250,13 +276,13 @@ struct FindResult<'a> {
250
276
impl ConfigInfo {
251
277
fn new ( config : & MFEConfig ) -> Self {
252
278
let mut ports = HashMap :: new ( ) ;
253
- let mut tasks = HashSet :: new ( ) ;
254
- for ( application , dev_task) in config. development_tasks ( ) {
255
- let task = TaskId :: new ( application , dev_task. unwrap_or ( "dev" ) ) . into_owned ( ) ;
256
- if let Some ( port) = config. port ( application ) {
279
+ let mut tasks = HashMap :: new ( ) ;
280
+ for dev_task in config. development_tasks ( ) {
281
+ let task = TaskId :: new ( dev_task . package , dev_task. task . unwrap_or ( "dev" ) ) . into_owned ( ) ;
282
+ if let Some ( port) = config. port ( dev_task . application_name ) {
257
283
ports. insert ( task. clone ( ) , port) ;
258
284
}
259
- tasks. insert ( task) ;
285
+ tasks. insert ( task, dev_task . application_name . to_owned ( ) ) ;
260
286
}
261
287
let version = config. version ( ) ;
262
288
@@ -281,9 +307,11 @@ mod test {
281
307
{
282
308
let mut _map = std:: collections:: HashMap :: new( ) ;
283
309
$(
284
- let mut _dev_tasks = std:: collections:: HashSet :: new( ) ;
310
+ let mut _dev_tasks = std:: collections:: HashMap :: new( ) ;
285
311
for _dev_task in $dev_tasks. as_slice( ) {
286
- _dev_tasks. insert( crate :: run:: task_id:: TaskName :: from( * _dev_task) . task_id( ) . unwrap( ) . into_owned( ) ) ;
312
+ let _dev_task_id = crate :: run:: task_id:: TaskName :: from( * _dev_task) . task_id( ) . unwrap( ) . into_owned( ) ;
313
+ let _dev_application = _dev_task_id. package( ) . to_owned( ) ;
314
+ _dev_tasks. insert( _dev_task_id, _dev_application) ;
287
315
}
288
316
_map. insert( $config_owner. to_string( ) , ConfigInfo { tasks: _dev_tasks, version: "1" , path: None , ports: std:: collections:: HashMap :: new( ) } ) ;
289
317
) +
@@ -363,22 +391,28 @@ mod test {
363
391
364
392
#[ test]
365
393
fn test_mfe_package_is_found ( ) {
366
- let result =
367
- PackageGraphResult :: new ( vec ! [ ( MICROFRONTENDS_PACKAGE , Ok ( None ) ) ] . into_iter ( ) ) . unwrap ( ) ;
394
+ let result = PackageGraphResult :: new (
395
+ HashSet :: default ( ) ,
396
+ vec ! [ ( MICROFRONTENDS_PACKAGE , Ok ( None ) ) ] . into_iter ( ) ,
397
+ )
398
+ . unwrap ( ) ;
368
399
assert_eq ! ( result. mfe_package, Some ( MICROFRONTENDS_PACKAGE ) ) ;
369
400
}
370
401
371
402
#[ test]
372
403
fn test_no_mfe_package ( ) {
373
- let result =
374
- PackageGraphResult :: new ( vec ! [ ( "foo" , Ok ( None ) ) , ( "bar" , Ok ( None ) ) ] . into_iter ( ) )
375
- . unwrap ( ) ;
404
+ let result = PackageGraphResult :: new (
405
+ HashSet :: default ( ) ,
406
+ vec ! [ ( "foo" , Ok ( None ) ) , ( "bar" , Ok ( None ) ) ] . into_iter ( ) ,
407
+ )
408
+ . unwrap ( ) ;
376
409
assert_eq ! ( result. mfe_package, None ) ;
377
410
}
378
411
379
412
#[ test]
380
413
fn test_unsupported_versions_ignored ( ) {
381
414
let result = PackageGraphResult :: new (
415
+ HashSet :: default ( ) ,
382
416
vec ! [ ( "foo" , Err ( Error :: UnsupportedVersion ( "bad version" . into( ) ) ) ) ] . into_iter ( ) ,
383
417
)
384
418
. unwrap ( ) ;
@@ -388,6 +422,7 @@ mod test {
388
422
#[ test]
389
423
fn test_child_configs_with_missing_default ( ) {
390
424
let result = PackageGraphResult :: new (
425
+ HashSet :: default ( ) ,
391
426
vec ! [ (
392
427
"child" ,
393
428
Err ( Error :: ChildConfig {
@@ -404,6 +439,7 @@ mod test {
404
439
#[ test]
405
440
fn test_io_err_stops_traversal ( ) {
406
441
let result = PackageGraphResult :: new (
442
+ HashSet :: default ( ) ,
407
443
vec ! [
408
444
(
409
445
"a" ,
@@ -442,8 +478,11 @@ mod test {
442
478
"something.txt" ,
443
479
)
444
480
. unwrap ( ) ;
445
- let mut result =
446
- PackageGraphResult :: new ( vec ! [ ( "web" , Ok ( Some ( config) ) ) ] . into_iter ( ) ) . unwrap ( ) ;
481
+ let mut result = PackageGraphResult :: new (
482
+ HashSet :: default ( ) ,
483
+ vec ! [ ( "web" , Ok ( Some ( config) ) ) ] . into_iter ( ) ,
484
+ )
485
+ . unwrap ( ) ;
447
486
result
448
487
. configs
449
488
. values_mut ( )
@@ -456,6 +495,42 @@ mod test {
456
495
)
457
496
}
458
497
498
+ #[ test]
499
+ fn test_missing_packages ( ) {
500
+ let config = MFEConfig :: from_str (
501
+ & serde_json:: to_string_pretty ( & json ! ( {
502
+ "version" : "1" ,
503
+ "applications" : {
504
+ "web" : { } ,
505
+ "docs" : {
506
+ "development" : {
507
+ "task" : "serve"
508
+ }
509
+ }
510
+ }
511
+ } ) )
512
+ . unwrap ( ) ,
513
+ "something.txt" ,
514
+ )
515
+ . unwrap ( ) ;
516
+ let missing_result = PackageGraphResult :: new (
517
+ HashSet :: default ( ) ,
518
+ vec ! [ ( "web" , Ok ( Some ( config. clone( ) ) ) ) ] . into_iter ( ) ,
519
+ )
520
+ . unwrap ( ) ;
521
+ assert_eq ! ( missing_result. missing_applications, vec![ "docs" , "web" ] ) ;
522
+ let found_result = PackageGraphResult :: new (
523
+ HashSet :: from_iter ( [ "docs" , "web" ] . iter ( ) . copied ( ) ) ,
524
+ vec ! [ ( "web" , Ok ( Some ( config) ) ) ] . into_iter ( ) ,
525
+ )
526
+ . unwrap ( ) ;
527
+ assert ! (
528
+ found_result. missing_applications. is_empty( ) ,
529
+ "Expected no missing applications: {:?}" ,
530
+ found_result. missing_applications
531
+ ) ;
532
+ }
533
+
459
534
#[ test]
460
535
fn test_port_collection ( ) {
461
536
let config = MFEConfig :: from_str (
@@ -477,7 +552,11 @@ mod test {
477
552
"something.txt" ,
478
553
)
479
554
. unwrap ( ) ;
480
- let result = PackageGraphResult :: new ( vec ! [ ( "web" , Ok ( Some ( config) ) ) ] . into_iter ( ) ) . unwrap ( ) ;
555
+ let result = PackageGraphResult :: new (
556
+ HashSet :: default ( ) ,
557
+ vec ! [ ( "web" , Ok ( Some ( config) ) ) ] . into_iter ( ) ,
558
+ )
559
+ . unwrap ( ) ;
481
560
let web_ports = result. configs [ "web" ] . ports . clone ( ) ;
482
561
assert_eq ! (
483
562
web_ports. get( & TaskId :: new( "docs" , "serve" ) ) . copied( ) ,
0 commit comments