@@ -1543,6 +1543,12 @@ let starts_with_uppercase s =
15431543 Char. uppercase_ascii c = c
15441544
15451545module AutomaticExpr = struct
1546+ (* There appear to be slightly different rules of transformation whether the component is upper-, lowercase or a fragment *)
1547+ type componentDescription =
1548+ | LowercasedComponent
1549+ | UppercasedComponent
1550+ | FragmentComponent
1551+
15461552 let loc_from_prop = function
15471553 | JSXPropPunning (_ , {loc} ) -> loc
15481554 | JSXPropValue (_ , _ , {pexp_loc} ) -> pexp_loc
@@ -1608,39 +1614,55 @@ module AutomaticExpr = struct
16081614 pexp_attributes = [] ;
16091615 }
16101616
1611- let mk_children_props (config : Jsx_common.jsx_config ) mapper
1612- (children : jsx_children ) =
1613- let record_of_children children =
1614- Exp. record [(Location. mknoloc (Lident " children" ), children, false )] None
1615- in
1616- let apply_jsx_array expr =
1617- Exp. apply
1618- (Exp. ident
1619- {txt = module_access_name config " array" ; loc = Location. none})
1620- [(Nolabel , expr)]
1621- in
1617+ let append_children_prop (config : Jsx_common.jsx_config ) mapper
1618+ (component_description : componentDescription ) (props : jsx_props )
1619+ (children : jsx_children ) : jsx_props =
16221620 match children with
1623- | JSXChildrenItems [] -> empty_record ~loc: Location. none
1621+ | JSXChildrenItems [] -> props
16241622 | JSXChildrenItems [child] | JSXChildrenSpreading child ->
1625- record_of_children (mapper.expr mapper child)
1623+ let expr =
1624+ (* I don't quite know why fragment and uppercase don't do this additional ReactDOM.someElement wrapping *)
1625+ match component_description with
1626+ | FragmentComponent | UppercasedComponent -> mapper.expr mapper child
1627+ | LowercasedComponent ->
1628+ let element_binding =
1629+ match config.module_ |> String. lowercase_ascii with
1630+ | "react" -> Lident " ReactDOM"
1631+ | _generic -> module_access_name config " Elements"
1632+ in
1633+ Exp. apply
1634+ (Exp. ident
1635+ {
1636+ txt = Ldot (element_binding, " someElement" );
1637+ loc = Location. none;
1638+ })
1639+ [(Nolabel , child)]
1640+ in
1641+ let is_optional =
1642+ match component_description with
1643+ | LowercasedComponent -> true
1644+ | FragmentComponent | UppercasedComponent -> false
1645+ in
1646+ props
1647+ @ [
1648+ JSXPropValue
1649+ ({txt = " children" ; loc = Location. none}, is_optional, expr);
1650+ ]
16261651 | JSXChildrenItems xs ->
1627- record_of_children
1628- @@ apply_jsx_array (Exp. array (List. map (mapper.expr mapper) xs))
1629-
1630- let mk_react_jsx (config : Jsx_common.jsx_config ) mapper loc attrs
1631- (elementTag : expression ) (children : jsx_children ) : expression =
1632- let more_than_one_children =
1633- match children with
1634- | JSXChildrenSpreading _ -> false
1635- | JSXChildrenItems xs -> List. length xs > 1
1636- in
1637- let children_props = mk_children_props config mapper children in
1638- let args = [(nolabel, elementTag); (nolabel, children_props)] in
1639- Exp. apply ~loc ~attrs (* ReactDOM.jsx *)
1640- (if more_than_one_children then
1641- Exp. ident ~loc {loc; txt = module_access_name config " jsxs" }
1642- else Exp. ident ~loc {loc; txt = module_access_name config " jsx" })
1643- args
1652+ (* this is a hack to support react components that introspect into their children *)
1653+ props
1654+ @ [
1655+ JSXPropValue
1656+ ( {txt = " children" ; loc = Location. none},
1657+ false ,
1658+ Exp. apply
1659+ (Exp. ident
1660+ {
1661+ txt = module_access_name config " array" ;
1662+ loc = Location. none;
1663+ })
1664+ [(Nolabel , Exp. array (List. map (mapper.expr mapper) xs))] );
1665+ ]
16441666
16451667 let try_find_key_prop (props : jsx_props ) : (arg_label * expression) option =
16461668 props
@@ -1654,6 +1676,56 @@ module AutomaticExpr = struct
16541676 Some (arg_label, expr)
16551677 | _ -> None )
16561678
1679+ let mk_react_jsx (config : Jsx_common.jsx_config ) mapper loc attrs
1680+ (component_description : componentDescription ) (elementTag : expression )
1681+ (props : jsx_props ) (children : jsx_children ) : expression =
1682+ let more_than_one_children =
1683+ match children with
1684+ | JSXChildrenSpreading _ -> false
1685+ | JSXChildrenItems xs -> List. length xs > 1
1686+ in
1687+ let props_with_children =
1688+ append_children_prop config mapper component_description props children
1689+ in
1690+ let props_record = mk_record_from_props mapper loc props_with_children in
1691+ let jsx_expr, key_and_unit =
1692+ let mk_element_bind (jsx_part : string ) : Longident.t =
1693+ match component_description with
1694+ | FragmentComponent | UppercasedComponent ->
1695+ module_access_name config jsx_part
1696+ | LowercasedComponent ->
1697+ let element_binding =
1698+ match config.module_ |> String. lowercase_ascii with
1699+ | "react" -> Lident " ReactDOM"
1700+ | _generic -> module_access_name config " Elements"
1701+ in
1702+ Ldot (element_binding, jsx_part)
1703+ in
1704+ match try_find_key_prop props with
1705+ | None ->
1706+ ( Exp. ident
1707+ {
1708+ loc = Location. none;
1709+ txt =
1710+ mk_element_bind
1711+ (if more_than_one_children then " jsxs" else " jsx" );
1712+ },
1713+ [] )
1714+ | Some key_prop ->
1715+ ( Exp. ident
1716+ {
1717+ loc = Location. none;
1718+ txt =
1719+ mk_element_bind
1720+ (if more_than_one_children then " jsxsKeyed" else " jsxKeyed" );
1721+ },
1722+ [key_prop; (nolabel, unit_expr ~loc: Location. none)] )
1723+ in
1724+ let args =
1725+ [(nolabel, elementTag); (nolabel, props_record)] @ key_and_unit
1726+ in
1727+ Exp. apply ~loc ~attrs jsx_expr args
1728+
16571729 let expr ~(config : Jsx_common.jsx_config ) mapper expression =
16581730 match expression with
16591731 | {
@@ -1665,7 +1737,8 @@ module AutomaticExpr = struct
16651737 let fragment =
16661738 Exp. ident ~loc {loc; txt = module_access_name config " jsxFragment" }
16671739 in
1668- mk_react_jsx config mapper loc attrs fragment children
1740+ mk_react_jsx config mapper loc attrs FragmentComponent fragment []
1741+ children
16691742 | {
16701743 pexp_desc =
16711744 Pexp_jsx_unary_element
@@ -1681,49 +1754,16 @@ module AutomaticExpr = struct
16811754 if starts_with_lowercase name then
16821755 (* For example 'input' *)
16831756 let component_name_expr = constant_string ~loc: tag_name.loc name in
1684- let element_binding =
1685- match config.module_ |> String. lowercase_ascii with
1686- | "react" -> Lident " ReactDOM"
1687- | _generic -> module_access_name config " Elements"
1688- in
1689- let jsx_expr, key_and_unit =
1690- match try_find_key_prop props with
1691- | None ->
1692- ( Exp. ident
1693- {loc = Location. none; txt = Ldot (element_binding, " jsx" )},
1694- [] )
1695- | Some key_prop ->
1696- ( Exp. ident
1697- {loc = Location. none; txt = Ldot (element_binding, " jsxKeyed" )},
1698- [key_prop; (nolabel, unit_expr ~loc: Location. none)] )
1699- in
1700- let props = mk_record_from_props mapper loc props in
1701-
1702- Exp. apply ~loc ~attrs jsx_expr
1703- ([(nolabel, component_name_expr); (nolabel, props)] @ key_and_unit)
1757+ mk_react_jsx config mapper loc attrs LowercasedComponent
1758+ component_name_expr props (JSXChildrenItems [] )
17041759 else if starts_with_uppercase name then
17051760 (* MyModule.make *)
17061761 let make_id =
17071762 Exp. ident ~loc: tag_name.loc
17081763 {txt = Ldot (tag_name.txt, " make" ); loc = tag_name.loc}
17091764 in
1710- let jsx_expr, key_and_unit =
1711- match try_find_key_prop props with
1712- | None ->
1713- ( Exp. ident
1714- {loc = Location. none; txt = module_access_name config " jsx" },
1715- [] )
1716- | Some key_prop ->
1717- ( Exp. ident
1718- {
1719- loc = Location. none;
1720- txt = module_access_name config " jsxKeyed" ;
1721- },
1722- [key_prop; (nolabel, unit_expr ~loc: Location. none)] )
1723- in
1724- let props = mk_record_from_props mapper loc props in
1725- Exp. apply ~loc ~attrs jsx_expr
1726- ([(nolabel, make_id); (nolabel, props)] @ key_and_unit)
1765+ mk_react_jsx config mapper loc attrs UppercasedComponent make_id props
1766+ (JSXChildrenItems [] )
17271767 else
17281768 Jsx_common. raise_error ~loc
17291769 " JSX: element name is neither upper- or lowercase, got \" %s\" "
@@ -1744,151 +1784,18 @@ module AutomaticExpr = struct
17441784 (* For example: <div> <h1></h1> <br /> </div>
17451785 This has an impact if we want to use ReactDOM.jsx or ReactDOM.jsxs
17461786 *)
1747- let has_multiple_literal_children =
1748- match children with
1749- | JSXChildrenItems (_ :: _ :: _ ) -> true
1750- | _ -> false
1751- in
17521787 if starts_with_lowercase name then
17531788 let component_name_expr = constant_string ~loc: tag_name.loc name in
1754- let element_binding =
1755- match config.module_ |> String. lowercase_ascii with
1756- | "react" -> Lident " ReactDOM"
1757- | _generic -> module_access_name config " Elements"
1758- in
1759- let props_record =
1760- (* Append current props with JSXPropValue("children")
1761- This will later be transformed correctly into a record. *)
1762- let props_with_children =
1763- match children with
1764- | JSXChildrenItems [] -> props
1765- | JSXChildrenItems [expr] | JSXChildrenSpreading expr ->
1766- props
1767- @ [
1768- JSXPropValue
1769- ( {txt = " children" ; loc = Location. none},
1770- true ,
1771- Exp. apply
1772- (Exp. ident
1773- {
1774- txt = Ldot (element_binding, " someElement" );
1775- loc = Location. none;
1776- })
1777- [(Nolabel , expr)] );
1778- ]
1779- | JSXChildrenItems xs ->
1780- (* this is a hack to support react components that introspect into their children *)
1781- props
1782- @ [
1783- JSXPropValue
1784- ( {txt = " children" ; loc = Location. none},
1785- false ,
1786- Exp. apply
1787- (Exp. ident
1788- {
1789- txt = module_access_name config " array" ;
1790- loc = Location. none;
1791- })
1792- [
1793- (Nolabel , Exp. array (List. map (mapper.expr mapper) xs));
1794- ] );
1795- ]
1796- in
1797- mk_record_from_props mapper loc props_with_children
1798- in
1799- let jsx_expr, key_and_unit =
1800- match try_find_key_prop props with
1801- | None ->
1802- ( Exp. ident
1803- {
1804- loc = Location. none;
1805- txt =
1806- Ldot
1807- ( element_binding,
1808- if has_multiple_literal_children then " jsxs" else " jsx"
1809- );
1810- },
1811- [] )
1812- | Some key_prop ->
1813- ( Exp. ident
1814- {
1815- loc = Location. none;
1816- txt =
1817- Ldot
1818- ( element_binding,
1819- if has_multiple_literal_children then " jsxsKeyed"
1820- else " jsxKeyed" );
1821- },
1822- [key_prop; (nolabel, unit_expr ~loc: Location. none)] )
1823- in
1824-
1825- Exp. apply ~loc ~attrs jsx_expr
1826- ([(nolabel, component_name_expr); (nolabel, props_record)]
1827- @ key_and_unit)
1789+ mk_react_jsx config mapper loc attrs LowercasedComponent
1790+ component_name_expr props children
18281791 else if starts_with_uppercase name then
18291792 (* MyModule.make *)
18301793 let make_id =
18311794 Exp. ident ~loc: tag_name.loc
18321795 {txt = Ldot (tag_name.txt, " make" ); loc = tag_name.loc}
18331796 in
1834- let props_record =
1835- (* Append current props with JSXPropValue("children")
1836- This will later be transformed correctly into a record. *)
1837- let props_with_children =
1838- match children with
1839- | JSXChildrenItems [] -> props
1840- | JSXChildrenItems [expr] | JSXChildrenSpreading expr ->
1841- props
1842- @ [
1843- JSXPropValue
1844- ( {txt = " children" ; loc = Location. none},
1845- false ,
1846- mapper.expr mapper expr );
1847- ]
1848- | JSXChildrenItems xs ->
1849- (* this is a hack to support react components that introspect into their children *)
1850- props
1851- @ [
1852- JSXPropValue
1853- ( {txt = " children" ; loc = Location. none},
1854- false ,
1855- Exp. apply
1856- (Exp. ident
1857- {
1858- txt = module_access_name config " array" ;
1859- loc = Location. none;
1860- })
1861- [
1862- (Nolabel , Exp. array (List. map (mapper.expr mapper) xs));
1863- ] );
1864- ]
1865- in
1866- mk_record_from_props mapper loc props_with_children
1867- in
1868- let jsx_expr, key_and_unit =
1869- match try_find_key_prop props with
1870- | None ->
1871- ( Exp. ident
1872- {
1873- loc = Location. none;
1874- txt =
1875- module_access_name config
1876- (if has_multiple_literal_children then " jsxs" else " jsx" );
1877- },
1878- [] )
1879- | Some key_prop ->
1880- ( Exp. ident
1881- {
1882- loc = Location. none;
1883- txt =
1884- module_access_name config
1885- (if has_multiple_literal_children then " jsxsKeyed"
1886- else " jsxKeyed" );
1887- },
1888- [key_prop; (nolabel, unit_expr ~loc: Location. none)] )
1889- in
1890- Exp. apply ~loc ~attrs jsx_expr
1891- ([(nolabel, make_id); (nolabel, props_record)] @ key_and_unit)
1797+ mk_react_jsx config mapper loc attrs UppercasedComponent make_id props
1798+ children
18921799 else
18931800 Jsx_common. raise_error ~loc
18941801 " JSX: element name is neither upper- or lowercase, got \" %s\" "
0 commit comments