@@ -13,6 +13,8 @@ pub enum ParseResult {
1313
1414#[ derive( Debug , PartialEq , Eq , Serialize , Deserializable , Default , Clone ) ]
1515pub struct ConfigV1 {
16+ #[ serde( rename = "$schema" , skip) ]
17+ schema : Option < String > ,
1618 version : Option < String > ,
1719 applications : BTreeMap < String , Application > ,
1820 options : Option < Options > ,
@@ -47,10 +49,16 @@ pub struct PathGroup {
4749}
4850
4951#[ derive( Debug , PartialEq , Eq , Serialize , Deserializable , Default , Clone ) ]
50- struct ProductionConfig { }
52+ struct ProductionConfig {
53+ protocol : Option < String > ,
54+ host : Option < String > ,
55+ }
5156
5257#[ derive( Debug , PartialEq , Eq , Serialize , Deserializable , Default , Clone ) ]
53- struct VercelConfig { }
58+ struct VercelConfig {
59+ #[ serde( rename = "projectId" ) ]
60+ project_id : Option < String > ,
61+ }
5462
5563#[ derive( Debug , PartialEq , Eq , Serialize , Deserializable , Default , Clone ) ]
5664struct Development {
@@ -152,6 +160,10 @@ impl ConfigV1 {
152160 . consume ( ) ;
153161
154162 if let Some ( config) = config {
163+ // Only accept the config if there were no errors during parsing
164+ if !errs. is_empty ( ) {
165+ return Err ( Error :: biome_error ( errs) ) ;
166+ }
155167 // Accept any version. This allows the Turborepo proxy to work with
156168 // configurations that have different version numbers than expected,
157169 // as long as the structure is compatible with what Turborepo needs
@@ -200,6 +212,7 @@ impl ConfigV1 {
200212 local_proxy_port : Some ( port) ,
201213 disable_overrides : None ,
202214 } ) ,
215+ schema : None ,
203216 }
204217 }
205218
@@ -585,4 +598,73 @@ mod test {
585598 ParseResult :: Reference ( _) => panic ! ( "expected to get main config" ) ,
586599 }
587600 }
601+
602+ #[ test]
603+ fn test_malformed_json_unclosed_bracket ( ) {
604+ let input = r#"{"applications": {"web": {"development": {"local": 3000}}"# ;
605+ let config = ConfigV1 :: from_str ( input, "microfrontends.json" ) ;
606+ assert ! (
607+ config. is_err( ) ,
608+ "Parser should reject JSON with unclosed bracket"
609+ ) ;
610+ }
611+
612+ #[ test]
613+ fn test_malformed_json_trailing_comma ( ) {
614+ let input = r#"{"applications": {"web": {"development": {"local": 3000,}}}}"# ;
615+ let config = ConfigV1 :: from_str ( input, "microfrontends.json" ) ;
616+ assert ! (
617+ config. is_err( ) ,
618+ "Parser should reject JSON with trailing comma"
619+ ) ;
620+ }
621+
622+ #[ test]
623+ fn test_missing_required_applications ( ) {
624+ // Even though applications has defaults, if JSON structure is invalid it should
625+ // fail
626+ let input = r#"{"applications": {, "web": {}}}"# ;
627+ let config = ConfigV1 :: from_str ( input, "microfrontends.json" ) ;
628+ assert ! (
629+ config. is_err( ) ,
630+ "Parser should reject JSON with syntax errors"
631+ ) ;
632+ }
633+
634+ #[ test]
635+ fn test_invalid_routing_structure ( ) {
636+ let input = r#"{
637+ "applications": {
638+ "docs": {
639+ "routing": "invalid"
640+ }
641+ }
642+ }"# ;
643+ let config = ConfigV1 :: from_str ( input, "microfrontends.json" ) ;
644+ assert ! (
645+ config. is_err( ) ,
646+ "Parser should reject routing that is not an array"
647+ ) ;
648+ }
649+
650+ #[ test]
651+ fn test_invalid_path_group_structure ( ) {
652+ let input = r#"{
653+ "applications": {
654+ "docs": {
655+ "routing": [
656+ {
657+ "group": "docs",
658+ "paths": "should_be_array"
659+ }
660+ ]
661+ }
662+ }
663+ }"# ;
664+ let config = ConfigV1 :: from_str ( input, "microfrontends.json" ) ;
665+ assert ! (
666+ config. is_err( ) ,
667+ "Parser should reject paths that is not an array"
668+ ) ;
669+ }
588670}
0 commit comments