@@ -883,20 +883,20 @@ fn create_host_directives_feature_arg<'a>(
883883
884884 // inputs (if any)
885885 if !hd. inputs . is_empty ( ) {
886- let inputs_map = create_string_map ( allocator, & hd. inputs ) ;
886+ let inputs_array = create_host_directive_mappings_array ( allocator, & hd. inputs ) ;
887887 entries. push ( LiteralMapEntry {
888888 key : Atom :: from ( "inputs" ) ,
889- value : inputs_map ,
889+ value : inputs_array ,
890890 quoted : false ,
891891 } ) ;
892892 }
893893
894894 // outputs (if any)
895895 if !hd. outputs . is_empty ( ) {
896- let outputs_map = create_string_map ( allocator, & hd. outputs ) ;
896+ let outputs_array = create_host_directive_mappings_array ( allocator, & hd. outputs ) ;
897897 entries. push ( LiteralMapEntry {
898898 key : Atom :: from ( "outputs" ) ,
899- value : outputs_map ,
899+ value : outputs_array ,
900900 quoted : false ,
901901 } ) ;
902902 }
@@ -913,26 +913,28 @@ fn create_host_directives_feature_arg<'a>(
913913 ) )
914914}
915915
916- /// Creates a string-to-string map expression.
917- fn create_string_map < ' a > (
916+ /// Creates a host directive mappings array.
917+ ///
918+ /// Format: `['publicName', 'internalName', 'publicName2', 'internalName2']`
919+ fn create_host_directive_mappings_array < ' a > (
918920 allocator : & ' a Allocator ,
919- pairs : & [ ( Atom < ' a > , Atom < ' a > ) ] ,
921+ mappings : & [ ( Atom < ' a > , Atom < ' a > ) ] ,
920922) -> OutputExpression < ' a > {
921923 let mut entries = Vec :: new_in ( allocator) ;
922924
923- for ( key , value ) in pairs {
924- entries. push ( LiteralMapEntry {
925- key : key . clone ( ) ,
926- value : OutputExpression :: Literal ( Box :: new_in (
927- LiteralExpr { value : LiteralValue :: String ( value . clone ( ) ) , source_span : None } ,
928- allocator ,
929- ) ) ,
930- quoted : false ,
931- } ) ;
925+ for ( public_name , internal_name ) in mappings {
926+ entries. push ( OutputExpression :: Literal ( Box :: new_in (
927+ LiteralExpr { value : LiteralValue :: String ( public_name . clone ( ) ) , source_span : None } ,
928+ allocator ,
929+ ) ) ) ;
930+ entries . push ( OutputExpression :: Literal ( Box :: new_in (
931+ LiteralExpr { value : LiteralValue :: String ( internal_name . clone ( ) ) , source_span : None } ,
932+ allocator ,
933+ ) ) ) ;
932934 }
933935
934- OutputExpression :: LiteralMap ( Box :: new_in (
935- LiteralMapExpr { entries, source_span : None } ,
936+ OutputExpression :: LiteralArray ( Box :: new_in (
937+ LiteralArrayExpr { entries, source_span : None } ,
936938 allocator,
937939 ) )
938940}
@@ -1419,4 +1421,127 @@ mod tests {
14191421 output
14201422 ) ;
14211423 }
1424+
1425+ #[ test]
1426+ fn test_host_directives_input_output_mappings_use_flat_array ( ) {
1427+ // Issue #67: hostDirectives input/output mappings must be flat arrays
1428+ // ["publicName", "internalName"], NOT objects {publicName: "internalName"}
1429+ let allocator = Allocator :: default ( ) ;
1430+ let type_expr = OutputExpression :: ReadVar ( Box :: new_in (
1431+ ReadVarExpr { name : Atom :: from ( "TooltipTrigger" ) , source_span : None } ,
1432+ & allocator,
1433+ ) ) ;
1434+
1435+ let directive_expr = OutputExpression :: ReadVar ( Box :: new_in (
1436+ ReadVarExpr { name : Atom :: from ( "BrnTooltipTrigger" ) , source_span : None } ,
1437+ & allocator,
1438+ ) ) ;
1439+
1440+ let mut host_directive_inputs = Vec :: new_in ( & allocator) ;
1441+ host_directive_inputs. push ( ( Atom :: from ( "uTooltip" ) , Atom :: from ( "brnTooltipTrigger" ) ) ) ;
1442+
1443+ let mut host_directives = Vec :: new_in ( & allocator) ;
1444+ host_directives. push ( R3HostDirectiveMetadata {
1445+ directive : directive_expr,
1446+ is_forward_reference : false ,
1447+ inputs : host_directive_inputs,
1448+ outputs : Vec :: new_in ( & allocator) ,
1449+ } ) ;
1450+
1451+ let metadata = R3DirectiveMetadata {
1452+ name : Atom :: from ( "TooltipTrigger" ) ,
1453+ r#type : type_expr,
1454+ type_argument_count : 0 ,
1455+ deps : None ,
1456+ selector : Some ( Atom :: from ( "[uTooltip]" ) ) ,
1457+ queries : Vec :: new_in ( & allocator) ,
1458+ view_queries : Vec :: new_in ( & allocator) ,
1459+ host : R3HostMetadata :: new ( & allocator) ,
1460+ uses_on_changes : false ,
1461+ inputs : Vec :: new_in ( & allocator) ,
1462+ outputs : Vec :: new_in ( & allocator) ,
1463+ uses_inheritance : false ,
1464+ export_as : Vec :: new_in ( & allocator) ,
1465+ providers : None ,
1466+ is_standalone : true ,
1467+ is_signal : false ,
1468+ host_directives,
1469+ } ;
1470+
1471+ let result = compile_directive ( & allocator, & metadata, 0 ) ;
1472+ let emitter = JsEmitter :: new ( ) ;
1473+ let output = emitter. emit_expression ( & result. expression ) ;
1474+ let normalized = output. replace ( [ ' ' , '\n' , '\t' ] , "" ) ;
1475+
1476+ // Must contain flat array format: inputs:["uTooltip","brnTooltipTrigger"]
1477+ assert ! (
1478+ normalized. contains( r#"inputs:["uTooltip","brnTooltipTrigger"]"# ) ,
1479+ "Host directive inputs should be flat array [\" publicName\" ,\" internalName\" ], not object. Got:\n {}" ,
1480+ output
1481+ ) ;
1482+ // Must NOT contain object format: inputs:{uTooltip:"brnTooltipTrigger"}
1483+ assert ! (
1484+ !normalized. contains( r#"inputs:{uTooltip:"brnTooltipTrigger"}"# ) ,
1485+ "Host directive inputs should NOT be object format. Got:\n {}" ,
1486+ output
1487+ ) ;
1488+ }
1489+
1490+ #[ test]
1491+ fn test_host_directives_output_mappings_use_flat_array ( ) {
1492+ // Issue #67: output mappings must also be flat arrays
1493+ let allocator = Allocator :: default ( ) ;
1494+ let type_expr = OutputExpression :: ReadVar ( Box :: new_in (
1495+ ReadVarExpr { name : Atom :: from ( "MyDirective" ) , source_span : None } ,
1496+ & allocator,
1497+ ) ) ;
1498+
1499+ let directive_expr = OutputExpression :: ReadVar ( Box :: new_in (
1500+ ReadVarExpr { name : Atom :: from ( "ClickTracker" ) , source_span : None } ,
1501+ & allocator,
1502+ ) ) ;
1503+
1504+ let mut host_directive_outputs = Vec :: new_in ( & allocator) ;
1505+ host_directive_outputs. push ( ( Atom :: from ( "clicked" ) , Atom :: from ( "trackClick" ) ) ) ;
1506+
1507+ let mut host_directives = Vec :: new_in ( & allocator) ;
1508+ host_directives. push ( R3HostDirectiveMetadata {
1509+ directive : directive_expr,
1510+ is_forward_reference : false ,
1511+ inputs : Vec :: new_in ( & allocator) ,
1512+ outputs : host_directive_outputs,
1513+ } ) ;
1514+
1515+ let metadata = R3DirectiveMetadata {
1516+ name : Atom :: from ( "MyDirective" ) ,
1517+ r#type : type_expr,
1518+ type_argument_count : 0 ,
1519+ deps : None ,
1520+ selector : Some ( Atom :: from ( "[myDir]" ) ) ,
1521+ queries : Vec :: new_in ( & allocator) ,
1522+ view_queries : Vec :: new_in ( & allocator) ,
1523+ host : R3HostMetadata :: new ( & allocator) ,
1524+ uses_on_changes : false ,
1525+ inputs : Vec :: new_in ( & allocator) ,
1526+ outputs : Vec :: new_in ( & allocator) ,
1527+ uses_inheritance : false ,
1528+ export_as : Vec :: new_in ( & allocator) ,
1529+ providers : None ,
1530+ is_standalone : true ,
1531+ is_signal : false ,
1532+ host_directives,
1533+ } ;
1534+
1535+ let result = compile_directive ( & allocator, & metadata, 0 ) ;
1536+ let emitter = JsEmitter :: new ( ) ;
1537+ let output = emitter. emit_expression ( & result. expression ) ;
1538+ let normalized = output. replace ( [ ' ' , '\n' , '\t' ] , "" ) ;
1539+
1540+ // Must contain flat array format: outputs:["clicked","trackClick"]
1541+ assert ! (
1542+ normalized. contains( r#"outputs:["clicked","trackClick"]"# ) ,
1543+ "Host directive outputs should be flat array. Got:\n {}" ,
1544+ output
1545+ ) ;
1546+ }
14221547}
0 commit comments