Skip to content

Commit 5fe5f12

Browse files
committed
Initial transform of Pexp_jsx_unary_element in automatic mode
1 parent bf1b411 commit 5fe5f12

File tree

2 files changed

+191
-49
lines changed

2 files changed

+191
-49
lines changed

compiler/syntax/src/jsx_v4.ml

Lines changed: 183 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,7 @@ let transform_signature_item ~config item =
14961496
"Only one JSX component call can exist on a component at one time")
14971497
| _ -> [item]
14981498

1499-
let transform_jsx_call ~config mapper call_expression call_arguments
1499+
let _transform_jsx_call ~config mapper call_expression call_arguments
15001500
jsx_expr_loc attrs =
15011501
match call_expression.pexp_desc with
15021502
| Pexp_ident caller -> (
@@ -1530,58 +1530,163 @@ let transform_jsx_call ~config mapper call_expression call_arguments
15301530
"JSX: `createElement` should be preceeded by a simple, direct module \
15311531
name."
15321532

1533-
let mkChildrenProps (config : Jsx_common.jsx_config) mapper
1534-
(children : jsx_children) =
1535-
let record_of_children children =
1536-
Exp.record [(Location.mknoloc (Lident "children"), children, false)] None
1537-
in
1538-
let apply_jsx_array expr =
1539-
Exp.apply
1540-
(Exp.ident {txt = module_access_name config "array"; loc = Location.none})
1541-
[(Nolabel, expr)]
1542-
in
1543-
match children with
1544-
| JSXChildrenItems [] -> empty_record ~loc:Location.none
1545-
| JSXChildrenItems [child] | JSXChildrenSpreading child ->
1546-
record_of_children (mapper.expr mapper child)
1547-
| JSXChildrenItems xs -> (
1548-
match config.mode with
1549-
| "automatic" ->
1533+
let starts_with_lowercase s =
1534+
if String.length s = 0 then false
1535+
else
1536+
let c = s.[0] in
1537+
Char.lowercase_ascii c = c
1538+
1539+
let _starts_with_uppercase s =
1540+
if String.length s = 0 then false
1541+
else
1542+
let c = s.[0] in
1543+
Char.uppercase_ascii c = c
1544+
1545+
module AutomaticExpr = struct
1546+
let mk_children_props (config : Jsx_common.jsx_config) mapper
1547+
(children : jsx_children) =
1548+
let record_of_children children =
1549+
Exp.record [(Location.mknoloc (Lident "children"), children, false)] None
1550+
in
1551+
let apply_jsx_array expr =
1552+
Exp.apply
1553+
(Exp.ident
1554+
{txt = module_access_name config "array"; loc = Location.none})
1555+
[(Nolabel, expr)]
1556+
in
1557+
match children with
1558+
| JSXChildrenItems [] -> empty_record ~loc:Location.none
1559+
| JSXChildrenItems [child] | JSXChildrenSpreading child ->
1560+
record_of_children (mapper.expr mapper child)
1561+
| JSXChildrenItems xs ->
15501562
record_of_children
15511563
@@ apply_jsx_array (Exp.array (List.map (mapper.expr mapper) xs))
1552-
| "classic" | _ -> empty_record ~loc:Location.none)
15531564

1554-
let mkReactCreateElement (config : Jsx_common.jsx_config) mapper loc attrs
1555-
(elementTag : expression) (children : jsx_children) : expression =
1556-
let more_than_one_children =
1557-
match children with
1558-
| JSXChildrenSpreading _ -> false
1559-
| JSXChildrenItems xs -> List.length xs > 1
1560-
in
1561-
let children_props = mkChildrenProps config mapper children in
1562-
let args =
1563-
(nolabel, elementTag) :: (nolabel, children_props)
1564-
::
1565-
(match (config.mode, children) with
1566-
| "classic", JSXChildrenItems xs when more_than_one_children ->
1567-
[(nolabel, Exp.array (List.map (mapper.expr mapper) xs))]
1568-
| _ -> [])
1569-
in
1570-
Exp.apply ~loc ~attrs
1571-
(* ReactDOM.createElement *)
1572-
(match config.mode with
1573-
| "automatic" ->
1574-
if more_than_one_children then
1575-
Exp.ident ~loc {loc; txt = module_access_name config "jsxs"}
1576-
else Exp.ident ~loc {loc; txt = module_access_name config "jsx"}
1577-
| "classic" | _ ->
1578-
if more_than_one_children then
1579-
Exp.ident ~loc
1580-
{loc; txt = Ldot (Lident "React", "createElementVariadic")}
1581-
else Exp.ident ~loc {loc; txt = Ldot (Lident "React", "createElement")})
1582-
args
1565+
let mk_react_jsx (config : Jsx_common.jsx_config) mapper loc attrs
1566+
(elementTag : expression) (children : jsx_children) : expression =
1567+
let more_than_one_children =
1568+
match children with
1569+
| JSXChildrenSpreading _ -> false
1570+
| JSXChildrenItems xs -> List.length xs > 1
1571+
in
1572+
let children_props = mk_children_props config mapper children in
1573+
let args = [(nolabel, elementTag); (nolabel, children_props)] in
1574+
Exp.apply ~loc ~attrs (* ReactDOM.jsx *)
1575+
(if more_than_one_children then
1576+
Exp.ident ~loc {loc; txt = module_access_name config "jsxs"}
1577+
else Exp.ident ~loc {loc; txt = module_access_name config "jsx"})
1578+
args
1579+
1580+
let try_find_key_prop (props : jsx_props) : (arg_label * expression) option =
1581+
props
1582+
|> List.find_map (function
1583+
| JSXPropPunning (_, ({txt = "key"} as name)) ->
1584+
Some (Labelled name, Exp.ident {txt = Lident "key"; loc = name.loc})
1585+
| JSXPropValue (({txt = "key"} as name), is_optional, expr) ->
1586+
let arg_label =
1587+
if is_optional then Optional name else Labelled name
1588+
in
1589+
Some (arg_label, expr)
1590+
| _ -> None)
1591+
1592+
let expr ~(config : Jsx_common.jsx_config) mapper expression =
1593+
match expression with
1594+
| {
1595+
pexp_desc = Pexp_jsx_fragment (_, children, _);
1596+
pexp_loc = loc;
1597+
pexp_attributes = attrs;
1598+
} ->
1599+
let loc = {loc with loc_ghost = true} in
1600+
let fragment =
1601+
Exp.ident ~loc {loc; txt = module_access_name config "jsxFragment"}
1602+
in
1603+
mk_react_jsx config mapper loc attrs fragment children
1604+
| {
1605+
pexp_desc =
1606+
Pexp_jsx_unary_element
1607+
{jsx_unary_element_tag_name = name; jsx_unary_element_props = props};
1608+
pexp_loc = loc;
1609+
pexp_attributes = attrs;
1610+
} -> (
1611+
match name.txt with
1612+
| Longident.Lident elementName when starts_with_lowercase elementName ->
1613+
(* For example 'input' *)
1614+
let component_name_expr = constant_string ~loc:name.loc elementName in
1615+
let element_binding =
1616+
match config.module_ |> String.lowercase_ascii with
1617+
| "react" -> Lident "ReactDOM"
1618+
| _generic -> module_access_name config "Elements"
1619+
in
1620+
let jsx_expr, key_and_unit =
1621+
match try_find_key_prop props with
1622+
| None ->
1623+
( Exp.ident
1624+
{loc = Location.none; txt = Ldot (element_binding, "jsx")},
1625+
[] )
1626+
| Some key_prop ->
1627+
( Exp.ident
1628+
{loc = Location.none; txt = Ldot (element_binding, "jsxKeyed")},
1629+
[key_prop; (nolabel, unit_expr ~loc:Location.none)] )
1630+
in
1631+
(* TODO *)
1632+
let props = empty_record ~loc in
1633+
1634+
Exp.apply ~loc ~attrs jsx_expr
1635+
([(nolabel, component_name_expr); (nolabel, props)] @ key_and_unit)
1636+
| _ ->
1637+
Jsx_common.raise_error ~loc
1638+
"JSX: element name is neither upper- or lowercase, got \"%s\""
1639+
(Longident.flatten name.txt |> String.concat "."))
1640+
| e -> default_mapper.expr mapper e
1641+
end
1642+
1643+
module ClassicExpr = struct
1644+
let mk_react_create_element mapper loc attrs (elementTag : expression)
1645+
(children : jsx_children) : expression =
1646+
let more_than_one_children =
1647+
match children with
1648+
| JSXChildrenSpreading _ -> false
1649+
| JSXChildrenItems xs -> List.length xs > 1
1650+
in
1651+
(* children are a special prop are special in React.createElement *)
1652+
let children_props = empty_record ~loc:Location.none in
1653+
let args =
1654+
(nolabel, elementTag) :: (nolabel, children_props)
1655+
::
1656+
(match children with
1657+
| JSXChildrenItems xs when more_than_one_children ->
1658+
[(nolabel, Exp.array (List.map (mapper.expr mapper) xs))]
1659+
| _ -> [])
1660+
in
1661+
Exp.apply ~loc ~attrs
1662+
(* ReactDOM.createElement *)
1663+
(if more_than_one_children then
1664+
Exp.ident ~loc
1665+
{loc; txt = Ldot (Lident "React", "createElementVariadic")}
1666+
else Exp.ident ~loc {loc; txt = Ldot (Lident "React", "createElement")})
1667+
args
1668+
1669+
let expr (_config : Jsx_common.jsx_config) mapper expression =
1670+
match expression with
1671+
| {
1672+
pexp_desc = Pexp_jsx_fragment (_, children, _);
1673+
pexp_loc = loc;
1674+
pexp_attributes = attrs;
1675+
} ->
1676+
let loc = {loc with loc_ghost = true} in
1677+
let fragment =
1678+
Exp.ident ~loc {loc; txt = Ldot (Lident "React", "fragment")}
1679+
in
1680+
mk_react_create_element mapper loc attrs fragment children
1681+
| e -> default_mapper.expr mapper e
1682+
end
15831683

15841684
let expr ~(config : Jsx_common.jsx_config) mapper expression =
1685+
match config.mode with
1686+
| "automatic" -> AutomaticExpr.expr ~config mapper expression
1687+
| "classic" -> ClassicExpr.expr config mapper expression
1688+
| _ -> default_mapper.expr mapper expression
1689+
(*
15851690
match expression with
15861691
| {
15871692
pexp_desc = Pexp_jsx_fragment (_, children, _);
@@ -1596,7 +1701,35 @@ let expr ~(config : Jsx_common.jsx_config) mapper expression =
15961701
| "classic" | _ ->
15971702
Exp.ident ~loc {loc; txt = Ldot (Lident "React", "fragment")}
15981703
in
1599-
mkReactCreateElement config mapper loc attrs fragment children
1704+
mk_react_create_element config mapper loc attrs fragment children
1705+
| {
1706+
pexp_desc =
1707+
Pexp_jsx_unary_element
1708+
{jsx_unary_element_tag_name = name; jsx_unary_element_props = props};
1709+
pexp_loc = loc;
1710+
pexp_attributes = attrs;
1711+
} as e ->
1712+
let elementTag =
1713+
match name.txt with
1714+
| Longident.Lident elementName when starts_with_lowercase elementName -> (
1715+
(* For example 'input' *)
1716+
let component_name_expr = constant_string ~loc:name.loc elementName in
1717+
match config.mode with
1718+
| "automatic" ->
1719+
let element_binding =
1720+
match config.module_ |> String.lowercase_ascii with
1721+
| "react" -> Lident "ReactDOM"
1722+
| _generic -> module_access_name config "Elements"
1723+
in
1724+
Exp.ident ~loc {loc; txt = Lident elementName}
1725+
| "classic" | _ -> ()
1726+
)
1727+
| _ ->
1728+
Jsx_common.raise_error ~loc
1729+
"JSX: element name is neither upper- or lowercase, got \"%s\""
1730+
(Longident.flatten name.txt |> String.concat ".")
1731+
in
1732+
e
16001733
(* Does the function application have the @JSX attribute? *)
16011734
| {
16021735
pexp_desc = Pexp_apply {funct = call_expression; args = call_arguments};
@@ -1616,6 +1749,7 @@ let expr ~(config : Jsx_common.jsx_config) mapper expression =
16161749
non_jsx_attributes)
16171750
(* Delegate to the default mapper, a deep identity traversal *)
16181751
| e -> default_mapper.expr mapper e
1752+
*)
16191753

16201754
let module_binding ~(config : Jsx_common.jsx_config) mapper module_binding =
16211755
config.nested_modules <- module_binding.pmb_name.txt :: config.nested_modules;

compiler/syntax/src/jsx_v4.mli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
open Parsetree
2+
3+
val jsx_mapper :
4+
config:Jsx_common.jsx_config ->
5+
(Ast_mapper.mapper -> expression -> expression)
6+
* (Ast_mapper.mapper -> module_binding -> module_binding)
7+
* (signature_item -> signature_item list)
8+
* (structure_item -> structure_item list)

0 commit comments

Comments
 (0)