123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731openPpxlibopenAst_builder.DefaultmoduleList=ListLabelsletrepo_url="https://github.com/ml-in-barcelona/server-reason-react"letissues_url=repo_url|>Printf.sprintf"%s/issues"(* There's no pexp_list on Ppxlib since isn't a constructor of the Parsetree *)letpexp_list~locxs=List.fold_left(List.revxs)~init:[%expr[]]~f:(funxsx->letloc=x.pexp_locin[%expr[%ex]::[%exs]])exceptionErrorofexpressionletraise_errorf~locfmt=letopenAst_builder.DefaultinPrintf.ksprintf(funmsg->letexpr=pexp_extension~loc(Location.error_extensionf~loc"%s"msg)inraise(Errorexpr))fmtletmake_string~locstr=letopenAst_helperinAst_helper.Exp.constant~loc(Const.stringstr)(* Helper method to look up the [@react.component] attribute *)lethasAttr{attr_name;_}=attr_name.txt="react.component"(* Helper method to filter out any attribute that isn't [@react.component] *)letotherAttrsPure{attr_name;_}=attr_name.txt<>"react.component"lethasAttrOnBinding{pvb_attributes}=List.find_opt~f:hasAttrpvb_attributes<>Noneletrecunwrap_childrenchildren=function|{pexp_desc=Pexp_construct({txt=Lident"[]";_},None);_}->List.revchildren|{pexp_desc=Pexp_construct({txt=Lident"::";_},Some{pexp_desc=Pexp_tuple[child;next];_});_;}->unwrap_children(child::children)next|e->raise_errorf~loc:e.pexp_loc"jsx: children prop should be a list"letis_jsx=function|{attr_name={txt="JSX";_};_}->true|_->falselethas_jsx_attrattrs=List.exists~f:is_jsxattrsletrewrite_component~loctagargschildren=letcomponent=pexp_ident~loctaginletprops=matchchildrenwith|None->args|Some[children]->(Labelled"children",children)::args|Somechildren->(Labelled"children",[%exprReact.list[%epexp_list~locchildren]])::argsin[%exprReact.Upper_case_component(fun()->[%epexp_apply~loccomponentprops])]letvalidate_prop~locidname=matchDomProps.findByNameidnamewith|Okp->p|Error`ElementNotFound->raise_errorf~loc"jsx: HTML tag '%s' doesn't exist.\n\
If this isn't correct, please open an issue at %s"idissues_url|Error`AttributeNotFound->(matchDomProps.find_closest_namenamewith|None->raise_errorf~loc"jsx: prop '%s' isn't valid on a '%s' element.\n\
If this isn't correct, please open an issue at %s."nameidissues_url|Somesuggestion->raise_errorf~loc"jsx: prop '%s' isn't valid on a '%s' element.\n\
Hint: Maybe you mean '%s'?\n\n\
If this isn't correct, please open an issue at %s."nameidsuggestionissues_url)letmake_prop~is_optional~propattribute_nameattribute_value=letloc=attribute_value.pexp_locinletopenDomPropsinmatch(prop,is_optional)with|Attribute{type_=DomProps.String;_},false->[%exprSome(React.JSX.String([%eattribute_name],([%eattribute_value]:string)))]|Attribute{type_=DomProps.String;_},true->[%exprmatch([%eattribute_value]:stringoption)with|None->None|Somev->Some(React.JSX.String([%eattribute_name],v))]|Attribute{type_=DomProps.Int;_},false->[%exprSome(React.JSX.String([%eattribute_name],string_of_int([%eattribute_value]:int)))]|Attribute{type_=DomProps.Int;_},true->[%exprmatch([%eattribute_value]:intoption)with|None->None|Somev->Some(React.JSX.String([%eattribute_name],string_of_intv))]|Attribute{type_=DomProps.Bool;_},false->[%exprSome(React.JSX.Bool([%eattribute_name],([%eattribute_value]:bool)))]|Attribute{type_=DomProps.Bool;_},true->[%exprmatch([%eattribute_value]:booloption)with|None->None|Somev->Some(React.JSX.Bool([%eattribute_name],v))](* BooleanishString needs to transform bool into string *)|Attribute{type_=DomProps.BooleanishString;_},false->[%exprSome(React.JSX.String([%eattribute_name],string_of_bool([%eattribute_value]:bool)))]|Attribute{type_=DomProps.BooleanishString;_},true->[%exprmatch([%eattribute_value]:booloption)with|None->None|Somev->Some(React.JSX.String([%eattribute_name],string_of_boolv))]|Attribute{type_=DomProps.Style;_},false->[%exprSome(React.JSX.Style(ReactDOM.Style.to_string([%eattribute_value]:ReactDOM.Style.t)))]|Attribute{type_=DomProps.Style;_},true->[%exprmatch([%eattribute_value]:ReactDOM.Style.toption)with|None->None|Somev->Some(React.JSX.Style(ReactDOM.Style.to_stringv))]|Attribute{type_=DomProps.Ref;_},false->[%exprSome(React.JSX.Ref([%eattribute_value]:React.domRef))]|Attribute{type_=DomProps.Ref;_},true->[%exprmatch([%eattribute_value]:React.domRefoption)with|None->None|Somev->Some(React.JSX.Refv)]|Attribute{type_=DomProps.InnerHtml;_},false->[%exprSome(React.JSX.dangerouslyInnerHtml[%eattribute_value])]|Attribute{type_=DomProps.InnerHtml;_},true->[%exprmatch[%eattribute_value]with|None->None|Somev->Some(React.JSX.dangerouslyInnerHtmlv)]|Event{type_=Mouse;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Mouse([%eattribute_value]:React.Event.Mouse.t->unit)))]|Event{type_=Mouse;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Mouse.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Mousev))]|Event{type_=Selection;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Selection([%eattribute_value]:React.Event.Mouse.t->unit)))]|Event{type_=Selection;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Selection.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Selectionv))]|Event{type_=Touch;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Touch([%eattribute_value]:React.Event.Touch.t->unit)))]|Event{type_=Touch;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Touch.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Touchv))]|Event{type_=UI;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.UI([%eattribute_value]:React.Event.UI.t->unit)))]|Event{type_=UI;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.UI.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.UIv))]|Event{type_=Wheel;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Wheel([%eattribute_value]:React.Event.Wheel.t->unit)))]|Event{type_=Wheel;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Wheel.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Wheelv))]|Event{type_=Clipboard;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Clipboard([%eattribute_value]:React.Event.Clipboard.t->unit)))]|Event{type_=Clipboard;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Clipboard.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Clipboardv))]|Event{type_=Composition;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Composition([%eattribute_value]:React.Event.Composition.t->unit)))]|Event{type_=Composition;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Composition.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Compositionv))]|Event{type_=Keyboard;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Keyboard([%eattribute_value]:React.Event.Keyboard.t->unit)))]|Event{type_=Keyboard;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Keyboard.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Keyboardv))]|Event{type_=Focus;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Focus([%eattribute_value]:React.Event.Focus.t->unit)))]|Event{type_=Focus;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Focus.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Focusv))]|Event{type_=Form;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Form([%eattribute_value]:React.Event.Form.t->unit)))]|Event{type_=Form;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Form.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Formv))]|Event{type_=Media;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Media([%eattribute_value]:React.Event.Media.t->unit)))]|Event{type_=Media;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Media.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Mediav))]|Event{type_=Inline;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Inline([%eattribute_value]:string)))]|Event{type_=Inline;jsxName},true->[%exprmatch([%eattribute_value]:stringoption)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Inlinev))]|Event{type_=Image;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Image([%eattribute_value]:(React.Event.Image.t->unit)option)))]|Event{type_=Image;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Image.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Imagev))]|Event{type_=Animation;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Animation([%eattribute_value]:React.Event.Animation.t->unit)))]|Event{type_=Animation;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Animation.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Animationv))]|Event{type_=Transition;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Transition([%eattribute_value]:React.Event.Transition.t->unit)))]|Event{type_=Transition;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Transition.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Transitionv))]|Event{type_=Pointer;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Pointer([%eattribute_value]:React.Event.Pointer.t->unit)))]|Event{type_=Pointer;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Pointer.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Pointerv))]|Event{type_=Drag;jsxName},false->[%exprSome(React.JSX.Event([%emake_string~locjsxName],React.JSX.Drag([%eattribute_value]:React.Event.Drag.t->unit)))]|Event{type_=Drag;jsxName},true->[%exprmatch([%eattribute_value]:(React.Event.Drag.t->unit)option)with|None->None|Somev->Some(React.JSX.Event([%emake_string~locjsxName],React.JSX.Dragv))]letis_optional=functionOptional_->true|_->falselettransform_labelled~loc~tag_name(prop_label,(runtime_value:expression))props=matchprop_labelwith|Nolabel->props|Optionalname|Labelledname->letis_optional=is_optionalprop_labelinletprop=validate_prop~loctag_namenameinletname=estring~loc(DomProps.getNameprop)inletnew_prop=make_prop~is_optional~propnameruntime_valuein[%expr[%enew_prop]::[%eprops]]lettransform_lowercase_props~loc~tag_nameargs=matchargswith|[]->[%expr[]]|attrs->(letlist_of_attributes=attrs|>List.fold_right~f:(transform_labelled~loc~tag_name)~init:[%expr[]]inmatchlist_of_attributeswith|[%expr[]]->[%expr[]]|_->(* We need to filter attributes since optionals are represented as None *)[%exprStdlib.List.filter_mapFun.id[%elist_of_attributes]])letrewrite_lowercase~loc:exprLoctag_nameargschildren=letloc=exprLocinletdom_node_name=estring~loc:exprLoctag_nameinletprops=transform_lowercase_props~loc:exprLoc~tag_nameargsinmatchchildrenwith|Somechildren->letchildrens=pexp_list~locchildrenin[%exprReact.createElement[%edom_node_name][%eprops][%echildrens]]|None->[%exprReact.createElement[%edom_node_name][%eprops][]]letsplit_argsargs=letchildren=ref(Location.none,[])inletrest=List.filter_mapargs~f:(function|Labelled"children",children_expression->letchildren'=unwrap_children[]children_expressioninchildren:=(children_expression.pexp_loc,children');None|arg_label,e->Some(arg_label,e))inletchildren_prop=match!childrenwith_loc,[]->None|_loc,children->Somechildrenin(children_prop,rest)letreverse_pexp_list~locexpr=letrecgoacc=function|[%expr[]]->acc|[%expr[%e?hd]::[%e?tl]]->go[%expr[%ehd]::[%eacc]]tl|expr->expringo[%expr[]]exprletlist_have_tailexpr=matchexprwith|Pexp_construct({txt=Lident"::";_},Some{pexp_desc=Pexp_tuple_;_})|Pexp_construct({txt=Lident"[]";_},None)->false|_->truelettransform_items_of_list~locchildren=letrecrun_mapperchildrenaccum=matchchildrenwith|[%expr[]]->reverse_pexp_list~locaccum|[%expr[%e?v]::[%e?acc]]whenlist_have_tailacc.pexp_desc->[%expr[%ev]]|[%expr[%e?v]::[%e?acc]]->run_mapperacc[%expr[%ev]::[%eaccum]]|notAList->notAListinrun_mapperchildren[%expr[]]letremove_warning_16_optional_argument_cannot_be_erased~loc=letopenAst_helperin{attr_name={txt="warning";loc};attr_payload=PStr[Str.eval(Ast_helper.Exp.constant(Const.string"-16"))];attr_loc=loc;}letremove_warning_27_unused_var_strict~loc=letopenAst_helperin{attr_name={txt="warning";loc};attr_payload=PStr[Str.eval(Ast_helper.Exp.constant(Const.string"-27"))];attr_loc=loc;}(* Finds the name of the variable the binding is assigned to, otherwise raises *)letget_function_namebinding=matchbindingwith|{pvb_pat={ppat_desc=Ppat_var{txt}}}->txt|_->raise_errorf~loc:binding.pvb_loc"react.component calls cannot be destructured."(* TODO: there is a long-tail of unsupported features inside of blocks - Pexp_letmodule , Pexp_letexception , Pexp_ifthenelse *)letrectransform_function_with_warningexpression=letloc=expression.pexp_locinmatchexpression.pexp_descwith(* let make = (~prop) => ... with no final unit *)|Pexp_fun(((Labelled_|Optional_)aslabel),default,pattern,({pexp_desc=Pexp_fun_}asinternalExpression))->letexp=transform_function_with_warninginternalExpressionin{expressionwithpexp_desc=Pexp_fun(label,default,pattern,exp)}(* let make = (()) => ... *)(* let make = (_) => ... *)|Pexp_fun(Nolabel,_default,{ppat_desc=Ppat_construct({txt=Lident"()"},_)|Ppat_any},_internalExpression)->expression(* let make = (~prop) => ... *)|Pexp_fun(label,default,pattern,internalExpression)->{expressionwithpexp_attributes=remove_warning_16_optional_argument_cannot_be_erased~loc::expression.pexp_attributes;pexp_desc=Pexp_fun(label,default,pattern,{pexp_loc=expression.pexp_loc;pexp_desc=Pexp_fun(Nolabel,None,[%pat?()],internalExpression);pexp_loc_stack=[];pexp_attributes=[];});}(* let make = {let foo = bar in (~prop) => ...} *)|Pexp_let(recursive,vbs,internalExpression)->(* here's where we spelunk! *)letexp=transform_function_with_warninginternalExpressionin{expressionwithpexp_desc=Pexp_let(recursive,vbs,exp)}(* let make = React.forwardRef((~prop) => ...) *)|Pexp_apply(_wrapperExpression,[(Nolabel,internalExpression)])->transform_function_with_warninginternalExpression(* let make = React.memoCustomCompareProps((~prop) => ..., (prevPros, nextProps) => true) *)|Pexp_apply(_wrapperExpression,[(Nolabel,internalExpression);((Nolabel,{pexp_desc=Pexp_fun_})as_compareProps);])->transform_function_with_warninginternalExpression|Pexp_sequence(wrapperExpression,internalExpression)->letexp=transform_function_with_warninginternalExpressionin{expressionwithpexp_desc=Pexp_sequence(wrapperExpression,exp)}|_->expressionletmake_value_bindingbinding=letloc=binding.pvb_locinletghost_loc={binding.pvb_locwithloc_ghost=true}inletbinding_expr=transform_function_with_warningbinding.pvb_exprin(* Builds an AST node for the modified `make` function *)letname=Ast_helper.Pat.mk~loc:ghost_loc(Ppat_var{txt=get_function_namebinding;loc=ghost_loc})inletkey_arg=Optional"key"in(* default_value = None means there's no default *)letdefault_value=Noneinletkey_renamed_to_underscore=ppat_var~loc:ghost_loc{txt="_";loc}inletcore_type=[%type:stringoption]inletkey_pattern=ppat_constraint~lockey_renamed_to_underscorecore_typein(* Append key argument since we want to allow users of this component to set key
(and assign it to _ since it shouldn't be used) *)letbody_expression=pexp_fun~loc:ghost_lockey_argdefault_valuekey_patternbinding_exprinAst_helper.Vb.mk~locnamebody_expressionletrewrite_signature_itemsignature_item=(* Remove the [@react.component] from the AST *)matchsignature_itemwith|{psig_loc=_;psig_desc=Psig_value({pval_name={txt=_fnName};pval_attributes;pval_type}aspsig_desc);}aspsig->(matchList.filter~f:hasAttrpval_attributeswith|[]->signature_item|[_]->{psigwithpsig_desc=Psig_value{psig_descwithpval_type;pval_attributes=List.filter~f:otherAttrsPurepval_attributes;};}|_->letloc=signature_item.psig_locin[%sigi:[%%ocaml.error"externals aren't supported on server-reason-react. externals are \
used to bind to React components defined in JavaScript, in the \
server, that doesn't make sense. If you need to render this on \
the server, implement a placeholder or an empty element"]])|_signature_item->signature_itemletrewrite_structure_itemstructure_item=matchstructure_item.pstr_descwith(* external *)|Pstr_primitive({pval_name={txt=_fnName};pval_attributes;pval_type=_}as_value_description)->(matchList.filter~f:hasAttrpval_attributeswith|[]->structure_item|_->letloc=structure_item.pstr_locin[%stri[%%ocaml.error"externals aren't supported on server-reason-react. externals are \
used to bind to React components defined in JavaScript, in the \
server, that doesn't make sense. If you need to render this on \
the server, implement a placeholder or an empty element"]])(* let component = ... *)|Pstr_value(rec_flag,value_bindings)->letmap_value_bindingvb=ifnot(hasAttrOnBindingvb)thenvbelsemake_value_bindingvbinletbindings=List.map~f:map_value_bindingvalue_bindingsinpstr_value~loc:structure_item.pstr_locrec_flagbindings|_->structure_itemletrewrite_jsx=object(_:Ast_traverse.map)inheritAst_traverse.mapassupermethod!signature_itemsignature_item=rewrite_signature_item(super#signature_itemsignature_item)method!structure_itemstructure_item=rewrite_structure_item(super#structure_itemstructure_item)method!expressionexpr=letexpr=super#expressionexprintrymatchexpr.pexp_descwith|Pexp_apply(({pexp_desc=Pexp_ident_;_}astag),args)whenhas_jsx_attrexpr.pexp_attributes->(letchildren,rest_of_args=split_argsargsinmatchtag.pexp_descwith(* div() [@JSX] *)|Pexp_ident{txt=Lidentname;loc=_name_loc}->rewrite_lowercase~loc:expr.pexp_locnamerest_of_argschildren(* Reason adds `createElement` as default when an uppercase is found,
we change it back to make *)(* Foo.createElement() [@JSX] *)|Pexp_ident{txt=Ldot(modulePath,("createElement"|"make"));loc}->letid={loc;txt=Ldot(modulePath,"make")}inrewrite_component~loc:expr.pexp_locidrest_of_argschildren(* local_function() [@JSX] *)|Pexp_identid->rewrite_component~loc:expr.pexp_locidrest_of_argschildren|_->assertfalse)(* div() [@JSX] *)|Pexp_apply(tag,_props)whenhas_jsx_attrexpr.pexp_attributes->raise_errorf~loc:expr.pexp_loc"jsx: %s should be an identifier, not an expression"(Ppxlib_ast.Pprintast.string_of_expressiontag)(* <> </> is represented as a list in the Parsetree with [@JSX] *)|Pexp_construct({txt=Lident"::";loc},Some{pexp_desc=Pexp_tuple_;_})|Pexp_construct({txt=Lident"[]";loc},None)->(letjsx_attr,rest_attributes=List.partition~f:is_jsxexpr.pexp_attributesinmatch(jsx_attr,rest_attributes)with|[],_->expr|_,rest_attributes->letchildren=transform_items_of_list~locexprinletnew_expr=[%exprReact.fragment(React.list[%echildren])]in{new_exprwithpexp_attributes=rest_attributes})|_->exprwithErrorerr->[%expr[%eerr]]endlet()=Ppxlib.Driver.register_transformation"server-reason-react.ppx"~impl:rewrite_jsx#structure