123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176openPpxlibopenAst_builder.DefaultmoduleList=ListLabelstypetarget=Native|Js(* Since ppxlib doesn't provide a way to get the submodules, we need to keep track of them manually *)letmode=refNativeletshared_folder_prefix=refNoneletrepo_url="https://github.com/ml-in-barcelona/server-reason-react"letissues_url=Printf.sprintf"%s/issues"repo_urlletmatch_substringstringsubstring=tryStr.search_forward(Str.regexp_stringsubstring)string0|>ignore;truewithNot_found->false(* There's no Ppxlib.pexp_list since isn't a parsetree constructor *)letpexp_list~locxs=List.fold_left(List.revxs)~init:[%expr[]]~f:(funxsx->letloc=x.pexp_locin[%expr[%ex]::[%exs]])exceptionErrorofexpressionletraise_errorf~locfmt=Printf.ksprintf(funmsg->letexpr=pexp_extension~loc(Location.error_extensionf~loc"%s"msg)inraise(Errorexpr))fmtletlongident~loctxt={txt=Lidenttxt;loc}letident~loctxt=pexp_ident~loc(longident~loctxt)letmake_string~locstr=Ast_helper.Exp.constant~loc(Ast_helper.Const.stringstr)letreact_dot_component="react.component"letreact_dot_async_dot_component="react.async.component"letreact_dot_client_dot_component="react.client.component"letreact_dot_server_dot_function="react.server.function"(* Helper method to look up the [@react.component] attribute *)lethasAttr{attr_name;_}comparable=attr_name.txt=comparablelethasAnyReactComponentAttribute{attr_name;_}=attr_name.txt=react_dot_component||attr_name.txt=react_dot_async_dot_component||attr_name.txt=react_dot_client_dot_component(* Helper method to filter out any attribute that isn't [@react.component] *)letnonReactAttributes{attr_name;_}=attr_name.txt<>react_dot_component&&attr_name.txt<>react_dot_async_dot_component&&attr_name.txt<>react_dot_client_dot_componentlethasAttrOnBinding{pvb_attributes}comparable=List.find_opt~f:(funattr->hasAttrattrcomparable)pvb_attributes<>NoneletisReactComponentBindingvb=hasAttrOnBindingvbreact_dot_componentletisReactAsyncComponentBindingvb=hasAttrOnBindingvbreact_dot_async_dot_componentletisReactClientComponentBindingvb=hasAttrOnBindingvbreact_dot_client_dot_componentletisReactServerFunctionBindingvb=hasAttrOnBindingvbreact_dot_server_dot_functionletisClientComponentBindingvalue_bindings=letfirst_binding=List.hdvalue_bindingsinisReactClientComponentBindingfirst_bindingletcontains_client_componentstructure=List.exists~f:(funstructure_item->matchstructure_item.pstr_descwith|Pstr_value(_,value_bindings)->List.exists~f:isReactClientComponentBindingvalue_bindings|_->false)structureletrecunwrap_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]])::argsinpexp_apply~loccomponentpropsletvalidate_prop~locidname=matchDomProps.findByJsxName~tag:idnamewith|Okp->p|Error`ElementNotFound->raise_errorf~loc"jsx: HTML tag '%s' doesn't exist.\nIf this isn't correct, please open an issue at %s"idissues_url|Error`AttributeNotFound->(matchDomProps.findClosestNamenamewith|None->raise_errorf~loc"jsx: prop '%s' isn't valid on a '%s' element.\nIf 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_value=letloc=attribute_value.pexp_locinletopenDomPropsinmatch(prop,is_optional)with|Attribute{type_=DomProps.Action;name;jsxName},false->[%exprmatch([%eattribute_value]:[`Stringofstring|`Functionof'aRuntime.server_function])with|`Strings->Some(React.JSX.String([%eestring~locname],[%eestring~locjsxName],(s:string)))|`Functionf->Some(React.JSX.Action([%eestring~locname],[%eestring~locjsxName],(f:'aRuntime.server_function)))]|Attribute{type_=DomProps.Action;name;jsxName},true->[%exprmatch([%eattribute_value]:[`Stringofstring|`Functionof'aRuntime.server_function]option)with|None->None|Somev->Some(React.JSX.Action([%eestring~locname],[%eestring~locjsxName],v))]|Attribute{type_=DomProps.String;name;jsxName},false->[%exprSome(React.JSX.String([%eestring~locname],[%eestring~locjsxName],([%eattribute_value]:string)))]|Attribute{type_=DomProps.String;name;jsxName},true->[%exprmatch([%eattribute_value]:stringoption)with|None->None|Somev->Some(React.JSX.String([%eestring~locname],[%eestring~locjsxName],v))]|Attribute{type_=DomProps.Int;name;jsxName},false->[%exprSome(React.JSX.String([%eestring~locname],[%eestring~locjsxName],Stdlib.Int.to_string([%eattribute_value]:int)))]|Attribute{type_=DomProps.Int;name;jsxName},true->[%exprmatch([%eattribute_value]:intoption)with|None->None|Somev->Some(React.JSX.String([%eestring~locname],[%eestring~locjsxName],Stdlib.Int.to_stringv))]|Attribute{type_=DomProps.Bool;name;jsxName},false->[%exprSome(React.JSX.Bool([%eestring~locname],[%eestring~locjsxName],([%eattribute_value]:bool)))]|Attribute{type_=DomProps.Bool;name;jsxName},true->[%exprmatch([%eattribute_value]:booloption)with|None->None|Somev->Some(React.JSX.Bool([%eestring~locname],[%eestring~locjsxName],v))](* BooleanishString needs to transform bool into string *)|Attribute{type_=DomProps.BooleanishString;name;jsxName},false->[%exprSome(React.JSX.String([%eestring~locname],[%eestring~locjsxName],Stdlib.Bool.to_string([%eattribute_value]:bool)))]|Attribute{type_=DomProps.BooleanishString;name;jsxName},true->[%exprmatch([%eattribute_value]:booloption)with|None->None|Somev->Some(React.JSX.String([%eestring~locname],[%eestring~locjsxName],Stdlib.Bool.to_stringv))]|Attribute{type_=DomProps.Style;_},false->[%exprSome(React.JSX.Style([%eattribute_value]:ReactDOM.Style.t))]|Attribute{type_=DomProps.Style;_},true->[%exprmatch([%eattribute_value]:ReactDOM.Style.toption)withNone->None|Somev->Some(React.JSX.Stylev)]|Attribute{type_=DomProps.Ref;_},false->[%exprSome(React.JSX.Ref([%eattribute_value]:React.domRef))]|Attribute{type_=DomProps.Ref;_},true->[%exprmatch([%eattribute_value]:React.domRefoption)withNone->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]withNone->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|_->falseletget_label=functionNolabel->""|Optionalname|Labelledname->namelettransform_labelled~loc~tag_name(prop_label,(runtime_value:expression))props=matchprop_labelwith|Nolabel->props|Optionalname|Labelledname->letis_optional=is_optionalprop_labelinletprop=validate_prop~loctag_namenameinletnew_prop=make_prop~is_optional~propruntime_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_mapStdlib.Fun.id[%elist_of_attributes]])letrewrite_lowercase~loc:exprLoctag_nameargschildren=letloc=exprLocinletdom_node_name=estring~loc:exprLoctag_nameinletkey=args|>List.find_opt~f:(fun(label,_)->get_labellabel="key")|>Option.map(fun(_,value)->value)inletprops=transform_lowercase_props~loc:exprLoc~tag_nameargsinmatch(key,children)with|Somekey,Somechildren->letchildrens=pexp_list~locchildrenin[%exprReact.createElementWithKey~key:[%ekey][%edom_node_name][%eprops][%echildrens]]|None,Somechildren->letchildrens=pexp_list~locchildrenin[%exprReact.createElement[%edom_node_name][%eprops][%echildrens]]|Somekey,None->[%exprReact.createElementWithKey~key:[%ekey][%edom_node_name][%eprops][]]|None,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(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(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 are a few unsupported features inside of blocks - Pexp_letmodule , Pexp_letexception , Pexp_ifthenelse *)letadd_unit_at_the_last_argumentexpression=letloc=expression.pexp_locinletrecinnerexpression=matchexpression.pexp_descwith(* let make = (~prop) => ... with no final unit *)|Pexp_fun(((Labelled_|Optional_)aslabel),default,pattern,({pexp_desc=Pexp_fun_}asinternalExpression))->pexp_fun~loc:expression.pexp_loclabeldefaultpattern(innerinternalExpression)(* let make = (()) => ... *)(* let make = (_) => ... *)|Pexp_fun(Nolabel,_,{ppat_desc=Ppat_construct({txt=Lident"()"},_)|Ppat_any},_)->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_fun~loc:expression.pexp_locNolabelNone[%pat?()]internalExpression);}(* let make = {let foo = bar in (~prop) => ...} *)|Pexp_let(recursive,vbs,internalExpression)->pexp_let~loc:expression.pexp_locrecursivevbs(innerinternalExpression)(* let make = React.forwardRef((~prop) => ...) *)|Pexp_apply(_,[(Nolabel,internalExpression)])->innerinternalExpression(* let make = React.memoCustomCompareProps((~prop) => ..., (prevPros, nextProps) => true) *)|Pexp_apply(_,[(Nolabel,internalExpression);((Nolabel,{pexp_desc=Pexp_fun_})as_compareProps)])->innerinternalExpression|Pexp_sequence(wrapperExpression,internalExpression)->pexp_sequence~loc:expression.pexp_locwrapperExpression(innerinternalExpression)|_->expressionininnerexpressionlettransform_fun_body_expressionexprfn=letrecinnerexpr=matchexpr.pexp_descwith|Pexp_fun(label,def,patt,expression)->pexp_fun~loc:expr.pexp_loclabeldefpatt(innerexpression)|_->fnexprininnerexprlettransform_fun_argumentsexprfn=letrecinnerexpr=matchexpr.pexp_descwith|Pexp_fun(label,def,patt,expression)->pexp_fun~loc:expr.pexp_loclabeldef(fnpatt)(innerexpression)|_->exprininnerexprlettransform_labelled_arguments_type(core_type:core_type)fn=letrecinnercore_type=matchcore_type.ptyp_descwith|Ptyp_arrow(label,core_type_1,core_type_2)->ptyp_arrow~loc:core_type.ptyp_loclabel(fncore_type_1)(innercore_type_2)|_->core_typeininnercore_typeletexpand_make_bindingbindingreact_element_variant_wrapping=letattributers=binding.pvb_attributes|>List.filter~f:nonReactAttributesinletloc=binding.pvb_locinletghost_loc={binding.pvb_locwithloc_ghost=true}inletbinding_with_unit=add_unit_at_the_last_argumentbinding.pvb_exprinletbinding_expr=transform_fun_body_expressionbinding_with_unitreact_element_variant_wrappingin(* Builds an AST node for the modified `make` function *)letname=ppat_var~loc:ghost_loc{txt=get_function_namebinding;loc=ghost_loc}inletkey_arg=Optional"key"inletdefault_value=(* default_value = None means there's no default *)Noneinletunderscore=ppat_var~loc:ghost_loc{txt="_";loc}inletcore_type=[%type:stringoption]inletkey_pattern=ppat_constraint~locunderscorecore_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) *)letfunction_body=pexp_fun~loc:ghost_lockey_argdefault_valuekey_patternbinding_exprin(* Since expand_make_binding is called on both native and js contexts, we need to keep the attributes *){(value_binding~loc:ghost_loc~pat:name~expr:function_body)withpvb_attributes=attributers}letget_argumentspvb_expr=letrecgoacc=function|Pexp_fun(label,default,patt,expr)->go((label,default,patt)::acc)expr.pexp_desc|_->accingo[]pvb_expr.pexp_descletmake_of_json~loc(core_type:core_type)prop=matchcore_typewith(* QUESTION: How do we handle especial types on props,
like `("someProp"), `List([React.element, string]).
We already support it, but not with the ppx.
Checkout the test_RSC_model.ml for more details. packages/reactDom/test/test_RSC_html.ml *)(* QUESTION: How can we handle optionals and others? Need a [@deriving rsc] for them? We currently encode None's as React.Model.Json `Null, should be enought *)|[%type:React.element]->[%expr([%eprop]:React.element)]|[%type:React.elementoption]->[%expr([%eprop]:React.elementoption)](* TODO: Add promise caching? When is it needed? *)(* | [%type: [%t? t] Js.Promise.t] ->
[%expr
let promise = [%e prop] in
let promise' = (Obj.magic promise : [%t t] Js.Promise.t Js.Dict.t) in
match Js.Dict.get promise' "__promise" with
| Some promise -> promise
| None ->
let promise =
Promise.(
let* json = (Obj.magic (Js.Promise.resolve promise) : Realm.Json.t Promise.t) in
let data = [%of_json: [%t t]] json in
return data)
in
Js.Dict.set promise' "__promise" promise;
promise] *)|[%type:[%t?t]Js.Promise.t]->[%expr([%eprop]:[%tt]Js.Promise.t)]|[%type:[%t?t]Runtime.server_function]->[%expr([%eprop]:[%tt]Runtime.server_function)]|[%type:[%t?inner_type]option]astype_->(matchinner_type.ptyp_descwith|Ptyp_arrow(_,_,_)->[%expr([%eprop]:[%ttype_])]|_->[%expr[%of_json:[%ttype_]][%eprop]])|type_->(matchtype_.ptyp_descwith|Ptyp_arrow(_,_,_)->[%expr([%eprop]:[%ttype_])]|_->[%expr[%of_json:[%ttype_]][%eprop]])letprops_of_model~loc(props:(arg_label*expressionoption*pattern)list):(longidentloc*expression)list=List.filter_map~f:(fun(arg_label,default,pattern)->matchpattern.ppat_descwith|Ppat_construct({txt=Lident"()";_},None)->None|Ppat_constraint(_,core_type)->(matcharg_labelwith|Nolabel->(* This error is raised by reason-react-ppx as well *)letloc=pattern.ppat_locinSome(longident~loc"error",[%expr[%ocaml.error"props need to be labelled arguments"]])|Labelledlabel|Optionallabel->letcore_type=matchdefaultwithSome_->[%type:[%tcore_type]option]|None->core_typeinletprop=[%exprprops##[%eident~loclabel]]inletvalue=make_of_json~loccore_typepropinSome(longident~loclabel,value))|_->letloc=pattern.ppat_locinletexpr=matcharg_labelwith|Nolabel->[%expr[%ocaml.error"server-reason-react: client components need type annotations"]]|Labelledlabel|Optionallabel->letmsg=Printf.sprintf"server-reason-react: client components need type annotations. Missing annotation for '%s'"labelinletmsg_expr=estring~locmsgin[%expr[%ocaml.error[%emsg_expr]]]inSome(longident~loc"error",expr))propsletreact_component_attribute~loc={attr_name={txt="react.component";loc};attr_payload=PStr[];attr_loc=loc}letmel_obj~locfields=matchfieldswith(* QUESTION: Maybe unit would work here best, for correctness? *)|[]->[%exprJs.Obj.empty()]|_->letrecord=pexp_record~locfieldsNoneinletstri=pstr_eval~locrecord[]in[%expr[%mel.obj[%%istri]]]letexpand_make_binding_to_clientbinding=letloc=binding.pvb_locinletghost_loc={binding.pvb_locwithloc_ghost=true}inletarguments=get_argumentsbinding.pvb_exprinletprops_as_object_with_decoders=mel_obj~loc(props_of_model~locarguments)inletmake_argument=[(Nolabel,props_as_object_with_decoders)]inletmake_call=pexp_apply~loc:ghost_loc[%exprmake]make_argumentinletname=ppat_var~loc:ghost_loc{txt="make_client";loc=ghost_loc}inletclient_single_argument=ppat_var~loc:ghost_loc{txt="props";loc}inletfunction_body=pexp_fun~loc:ghost_locNolabelNoneclient_single_argumentmake_callinvalue_binding~loc:ghost_loc~pat:name~expr:function_bodyletrecadd_unit_at_the_last_argument_in_core_typecore_type=matchcore_type.ptyp_descwith|Ptyp_arrow(arg_label,core_type_1,core_type_2)->{core_typewithptyp_desc=Ptyp_arrow(arg_label,core_type_1,add_unit_at_the_last_argument_in_core_typecore_type_2);}|Ptyp_constr_->letloc=core_type.ptyp_locin{core_typewithptyp_desc=Ptyp_arrow(Nolabel,[%type:unit],core_type)}|_->core_typeletrewrite_signature_itemsignature_item=(* Removes 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->(letnew_ptyp_desc=matchpval_type.ptyp_descwith|Ptyp_arrow(arg_label,core_type_1,core_type_2)->letloc=pval_type.ptyp_locinletoriginal_core_type={pval_typewithptyp_desc=Ptyp_arrow(arg_label,core_type_1,core_type_2)}inletnew_core_type=add_unit_at_the_last_argument_in_core_typeoriginal_core_typeinPtyp_arrow(Optional"key",[%type:string],new_core_type)|ptyp_desc->ptyp_descinletnew_core_type={pval_typewithptyp_desc=new_ptyp_desc}inmatchList.filter~f:hasAnyReactComponentAttributepval_attributeswith|[]->signature_item|[_]->{psigwithpsig_desc=Psig_value{psig_descwithpval_type=new_core_type;pval_attributes=List.filter~f:nonReactAttributespval_attributes;};}|_->letloc=signature_item.psig_locin[%sigi:[%%ocaml.error"server-reason-react: there's seems to be an error in the signature of the component."]])|_->signature_itemletmake_to_json~loc(core_type:core_type)prop=matchcore_typewith|[%type:React.element]->[%exprReact.Model.Element([%eprop]:React.element)]|[%type:React.elementoption]->[%exprmatch[%eprop]withSomeprop->React.Model.Element(prop:React.element)|None->React.Model.Json`Null]|[%type:[%t?inner_type]Js.Promise.t]->letjson=[%expr[%to_json:[%tinner_type]]]in[%exprReact.Model.Promise([%eprop],[%ejson])]|[%type:[%t?inner_type]Js.Promise.toption]->letjson=[%expr[%to_json:[%tinner_type]]]in[%exprmatch[%eprop]with|Someprop->[%exprReact.Model.Promise([%eprop],[%ejson])]|None->React.Model.Json`Null]|{ptyp_desc=Ptyp_arrow(_,_,_)}->letloc=core_type.ptyp_locin[%expr[%ocaml.error"server-reason-react: you can't pass functions into client components. Functions aren't serialisable to JSON."]]|[%type:[%t?_]Runtime.server_function]->[%exprReact.Model.Function[%eprop]]|type_->letjson=[%expr[%to_json:[%ttype_]][%eprop]]in[%exprReact.Model.Json[%ejson]]letprops_to_model~loc(props:(arg_label*expressionoption*pattern)list)=List.fold_left~init:[%expr[]]~f:(funacc(arg_label,_default,pattern)->matchpattern.ppat_descwith|Ppat_construct({txt=Lident"()";_},None)->acc|Ppat_constraint(_,core_type)->(matcharg_labelwith|Nolabel->(* This error is raised by reason-react-ppx as well *)letloc=pattern.ppat_locin[%expr[%ocaml.error"props need to be labelled arguments"]::[%eacc]]|Labelledlabel|Optionallabel->letprop=ident~loclabelinletvalue=make_to_json~loccore_typepropinletname=estring~loclabelin[%expr([%ename],[%evalue])::[%eacc]])(* TODO: Add all ppat_desc possibilities *)|_->letloc=pattern.ppat_locinletexpr=matcharg_labelwith|Nolabel->[%expr[%ocaml.error"server-reason-react: client components need type annotations"]]|Labelledlabel|Optionallabel->letmsg=Printf.sprintf"server-reason-react: client components need type annotations. Missing annotation for '%s'"labelinletmsg_expr=estring~locmsgin[%expr[%ocaml.error[%emsg_expr]]]in[%expr[%eexpr]::[%eacc]])propsmoduleServerFunction=structletreclast_expr_to_fn~locexprfn=matchexpr.pexp_descwith|Pexp_constraint(expr,_)->last_expr_to_fn~locexprfn|Pexp_fun(arg_label,arg_expression,fun_pattern,expression)->pexp_fun~locarg_labelarg_expressionfun_pattern(last_expr_to_fn~locexpressionfn)|_->fnletgenerate_id~locname=letfile_path=loc.loc_start.pos_fnameinletreplacement=matchshared_folder_prefix.contentswith|Somex->ifmatch_substringfile_pathxthenxelseraise_errorf~loc"Prefix doesn't match the file path. Provide a prefix that matches the file path."|None->raise_errorf~loc"Found a server.function without --shared-folder-prefix argument. Provide one."in(* We need to add a nasty hack here, since have different files for native and melange.Assume that the file structure is native/lib and js, and replace the name directly. This is supposed to be temporal, until dune implements https://github.com/ocaml/dune/issues/10630 *)letfile_path=Str.replace_first(Str.regexpreplacement)""file_pathinlethash=Printf.sprintf"%s_%s_%d"namefile_pathloc.loc_start.pos_lnum|>Hashtbl.hash|>string_of_intinhashletget_arg_details(arg:arg_label*expressionoption*pattern)=letarg_label,default,pattern=arginletloc=pattern.ppat_locinmatchpattern.ppat_descwith|Ppat_construct({txt=Lident"()";loc},None)->Ok(Nolabel,None,[%type:unit])|Ppat_constraint(pattern,core_type)->(letloc=pattern.ppat_locinletcore_type=matchdefaultwithSome_->[%type:[%tcore_type]option]|None->core_typeinmatchpattern.ppat_descwith|Ppat_var{txt=label;_}->Ok(arg_label,Somelabel,core_type)|_->Error(loc,"server-reason-react: server function arguments must have a name"))|_->Error(loc,"server-reason-react: server function arguments must have type annotations")letget_response_typeexpr=letrecauxexpracc=matchexpr.pexp_descwith|Pexp_fun(_,_,_,body)->auxbodyacc|Pexp_constraint(expr,core_type)->auxexpr(Somecore_type)|_->accinauxexprNoneletresponse_to_json~loccore_typeresponse=matchcore_typewith|Some[%type:[%t?core_type]Js.Promise.t]->letjson=[%expr[%to_json:[%tcore_type]][%eresponse]]in[%exprReact.Model.Json[%ejson]]|Some_->[%expr[%ocaml.error"server-reason-react: server functions must return a promise"]]|_->[%expr[%ocaml.error"server-reason-react: server functions must have a return type annotation (Js.Promise.t)"]]letmap_arguments_to_expressions~locargs=List.map~f:(funarg->matchargwith|Ok(arg_label,Somearg_name,_)->(arg_label,[%expr[%eevar~locarg_name]])|Ok(arg_label,_,[%type:unit])->(arg_label,[%expr()])|Ok_->(Nolabel,[%expr[%ocaml.error"server-reason-react: invalid argument, it must have a argument with name and type annotation"]])|Error(loc,msg)->(Nolabel,[%expr[%ocaml.error[%eestring~locmsg]]]))argsletencode_function_response~loc~response_expr~core_type=[%exprtry[%eresponse_expr]|>Lwt.map(funresponse->[%eresponse_to_json~loccore_type[%exprresponse]])withe->Lwt.faile]letdecode_arguments_vb~locargs_to_decode=args_to_decode|>List.mapi~f:(funi(_,label,core_type)->letstring_of_core_typex=letf=Format.str_formatterinAstlib.Pprintast.core_typefx;Format.flush_str_formatter()inletcore_type_string=string_of_core_typecore_typeinletof_json=make_of_json~loccore_type[%exprStdlib.Array.unsafe_getargs[%eeint~loci]]invalue_binding~loc~pat:[%pat?[%pppat_var~loc{txt=label;loc}]]~expr:[%exprtry[%eof_json]with_->Stdlib.raise(Invalid_argument(Stdlib.Printf.sprintf"server-reason-react: error on decoding argument '%s'. EXPECTED: %s, RECEIVED: %s"[%eestring~loclabel][%eestring~loccore_type_string](Stdlib.Array.unsafe_getargs[%eeint~loci]|>Yojson.Basic.to_string)))])letcreate_function_reference_registration~loc~id~function_name~args~core_type=letapply_args=map_arguments_to_expressions~locargsinletresponse_expr=pexp_apply~loc[%expr[%eevar~locfunction_name].call]apply_argsinletencoded_response_expr=encode_function_response~loc~response_expr~core_typeinletargs_to_decode=List.filter_map~f:(funarg->matchargwith|Ok(_,_,[%type:Js.FormData.t])->None|Ok(arg_label,Somearg_name,core_type)->Some(arg_label,arg_name,core_type)|Ok_->None|Error_->None)argsinletargs,formData=List.partition_map~f:(funarg->matchargwithOk(_,_,[%type:Js.FormData.t])->Rightarg|Ok_->Leftarg|Error_->Leftarg)argsinletbody_expr=matchargs_to_decodewith|[]->encoded_response_expr|args_to_decode->letdecoded_expr=decode_arguments_vb~locargs_to_decodeinpexp_let~locNonrecursivedecoded_exprencoded_response_exprinmatch(formData,args)with|[],_->[%striFunctionReferences.register[%eestring~locid](Body(funargs->[%ebody_expr]))]|[_],[]->[%striFunctionReferences.register[%eestring~locid](FormData(fun_formData->[%ebody_expr]))]|_,[]->[%stri[%ocaml.error"server-reason-react: server functions with form data must have at only one argument"]]|_->[%striFunctionReferences.register[%eestring~locid](FormData(funargsformData->[%ebody_expr]))]letcreate_server_function_record~locidexpression=[%expr{Runtime.id=[%eestring~locid];call=[%eexpression]}]letrewrite_native_function~vb~rec_flagstructure_item=letloc=structure_item.pstr_locinletfunction_name=get_function_namevbinletargs=get_argumentsvb.pvb_expr|>List.map~f:get_arg_details|>List.revinletbase_fn=vb.pvb_exprinletreturn_core_type=get_response_typebase_fninletid=generate_id~loc:vb.pvb_locfunction_nameinletserver_function_record_vb=value_binding~loc:vb.pvb_loc~pat:vb.pvb_pat~expr:(create_server_function_record~loc:vb.pvb_locidbase_fn)inletstri=[%striincludestruct[%%ipstr_value~locrec_flag[server_function_record_vb]][%%icreate_function_reference_registration~loc~id~function_name~args~core_type:return_core_type]end]instriletresponse_of_json~loccore_typeresponse=matchcore_typewith|Some[%type:[%t?core_type]Js.Promise.t]->[%expr[%of_json:[%tcore_type]][%eresponse]]|Some_->[%expr[%ocaml.error"server-reason-react: server functions must return a promise"]]|_->[%expr[%ocaml.error"server-reason-react: server functions must have a return type annotation (Js.Promise.t)"]]letcreate_client_function~loc~return_core_typeidargs=letdecode_response=response_of_json~locreturn_core_typeinletapply_args=map_arguments_to_expressions~locargs|>List.map~f:(fun(_,expr)->(Nolabel,expr))inletfn=[%exprletaction=ReactServerDOMEsbuild.createServerReference[%eestring~locid]in([%epexp_apply~loc[%expraction]apply_args][@u])|>Js.Promise.then_(funresponse->Js.Promise.resolve[%edecode_response[%exprresponse]])]infnletrewrite_client_function~nested_module_names~vb~rec_flagstructure_item=letloc=structure_item.pstr_locinletfunction_name=get_function_namevbinletargs=get_argumentsvb.pvb_expr|>List.map~f:get_arg_details|>List.revinletbase_fn=vb.pvb_exprinletreturn_core_type=get_response_typebase_fninletid=generate_id~loc:vb.pvb_locfunction_nameinletserver_function_record_vb=value_binding~loc:vb.pvb_loc~pat:vb.pvb_pat~expr:(create_server_function_record~loc:vb.pvb_locid(last_expr_to_fn~locbase_fn(create_client_function~loc~return_core_typeidargs)))inletloc=structure_item.pstr_locinletmodule_name=String.concat"."nested_module_namesinlet_,formData=List.partition_map~f:(funarg->matchargwithOk(_,_,[%type:Js.FormData.t])->Rightarg|Ok_->Leftarg|Error_->Leftarg)argsinletfunctionToCall=matchformDatawith[]->function_name|_->Printf.sprintf"%s.call"function_nameinletcomment=Printf.sprintf"// extract-server-function %s %s %s"idfunctionToCallmodule_nameinletraw=estring~loccommentinletextract_client_raw=[%stri[%%raw[%eraw]]]in[%striincludestruct[%%iextract_client_raw][%%ipstr_value~loc:structure_item.pstr_locrec_flag[server_function_record_vb]]end]endletrewrite_structure_item~nested_module_namesstructure_item=matchstructure_item.pstr_descwith(* external *)|Pstr_primitive({pval_name={txt=_fnName};pval_attributes;pval_type=_}as_value_description)->(matchList.filter~f:(funattr->hasAttrattrreact_dot_component||hasAttrattrreact_dot_async_dot_component)pval_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 make = ... *)|Pstr_value(rec_flag,value_bindings)whenisReactServerFunctionBinding(List.hdvalue_bindings)->letvb=List.hdvalue_bindingsinletloc=structure_item.pstr_locinifList.lengthvalue_bindings>1then[%stri[%%ocaml.error"server-reason-react: server functions don't support recursive bindings yet. If you need it, please open an \
issue on https://github.com/reasonml-community/server-reason-react/issues"]]elseServerFunction.rewrite_native_function~vb~rec_flagstructure_item|Pstr_value(rec_flag,value_bindings)->letmap_value_bindingvb=ifisReactClientComponentBindingvbthenexpand_make_bindingvb(funexpr->letloc=expr.pexp_locinletfileName=expr.pexp_loc.loc_start.pos_fnameinletreplacement=matchshared_folder_prefix.contentswith|Someprefix->ifmatch_substringfileNameprefixthenprefixelseraise_errorf~loc"Prefix doesn't match the file path. Provide a prefix that matches the file path."|None->raise_errorf~loc"Found a react.client.component without --shared-folder-prefix argument. Provide one."inletfile=fileName|>Str.replace_first(Str.regexpreplacement)""|>estring~locinletimport_module=matchnested_module_nameswith|[]->file|_->letsubmodule=estring~loc(String.concat"."nested_module_names)in[%exprPrintf.sprintf"%s#%s"[%efile][%esubmodule]]inletarguments=get_argumentsvb.pvb_exprin(* We transform the arguments from the value binding into React.client_props *)letprops=props_to_model~locargumentsin[%exprReact.Client_component{import_module=[%eimport_module];import_name="";props=[%eprops];client=React.Upper_case_component(Stdlib.__FUNCTION__,fun()->[%eexpr]);}])elseifisReactComponentBindingvbthenexpand_make_bindingvb(funexpr->letloc=expr.pexp_locin[%exprReact.Upper_case_component(Stdlib.__FUNCTION__,fun()->[%eexpr])])elseifisReactAsyncComponentBindingvbthenexpand_make_bindingvb(funexpr->letloc=expr.pexp_locin[%exprReact.Async_component(Stdlib.__FUNCTION__,fun()->[%eexpr])])elsevbinletbindings=List.map~f:map_value_bindingvalue_bindingsinpstr_value~loc:structure_item.pstr_locrec_flagbindings|_->structure_itemletrewrite_structure_item_for_js~nested_module_namesctxstructure_item=matchstructure_item.pstr_descwith(* external *)|Pstr_primitive({pval_name={txt=_fnName};pval_attributes;pval_type=_}as_value_description)->(matchList.filter~f:(funattr->hasAttrattrreact_dot_client_dot_component)pval_attributeswith|[]->structure_item|_->letloc=structure_item.pstr_locin[%stri[%%ocaml.error"server-reason-react: externals aren't supported on client components yet"]])|Pstr_value(rec_flag,value_bindings)whenisReactServerFunctionBinding(List.hdvalue_bindings)->letvb=List.hdvalue_bindingsinServerFunction.rewrite_client_function~nested_module_names~vb~rec_flagstructure_item(* let make = ... *)|Pstr_value(rec_flag,value_bindings)whenisClientComponentBindingvalue_bindings->letfirst_value_binding=List.hdvalue_bindingsinletmake_client=expand_make_binding_to_clientfirst_value_bindinginletmake_client_binding=pstr_value~loc:structure_item.pstr_locrec_flag[make_client]inletoriginal_value_binding={first_value_bindingwithpvb_attributes=[react_component_attribute~loc:first_value_binding.pvb_loc]}inletloc=structure_item.pstr_locinletcode_path=Expansion_context.Base.code_pathctxinletfileName=Code_path.file_pathcode_pathin(* We need to add a nasty hack here, since have different files for native and melange.Assume that the file structure is /native/shared/ and js, and replace the name directly. This is supposed to be temporal, until dune implements https://github.com/ocaml/dune/issues/10630 *)letreplacement=matchshared_folder_prefix.contentswith|Someprefix->ifmatch_substringfileNameprefixthenprefixelseraise_errorf~loc"Prefix doesn't match the file path. Provide a prefix that matches the file path."|None->raise_errorf~loc"Found a react.client.component without --shared-folder-prefix argument. Provide one."inletfileName=Str.replace_first(Str.regexpreplacement)""fileNameinletcomment=matchnested_module_nameswith|[]->estring~loc(Printf.sprintf"// extract-client %s"fileName)|_->estring~loc(Printf.sprintf"// extract-client %s %s"fileName(String.concat"."nested_module_names))in[%striincludestruct[%%i[%stri[%%raw[%ecomment]]]][%%ipstr_value~loc:structure_item.pstr_locrec_flag[original_value_binding]][%%imake_client_binding]end]|_->structure_itemletvalidate_tag_childrentagchildrenattributes:(unit,string)result=matchHtml.is_self_closing_tagtagwith|truewhenOption.fold~none:false~some:(funchildren->List.lengthchildren>0)children->Error(Printf.sprintf{|"%s" is a self-closing tag and must not have "children".\n|}tag)|truewhenList.exists~f:(fun(arg_label,_)->matcharg_labelwith|Labelled"dangerouslySetInnerHTML"|Optional"dangerouslySetInnerHTML"->true|_->false)attributes->Error(Printf.sprintf{|server-reason-react: "%s" is a self-closing tag and must not have "children".\n|}tag)|false->Ok()|true->Ok()lettraverse=object(_)inherit[Expansion_context.Base.t]Ast_traverse.map_with_contextassupervalmutablenested_module_names=[]method!module_bindingctxtmodule_binding=(matchmodule_binding.pmb_name.txtwith|None->()|Somename->nested_module_names<-nested_module_names@[name]);letmapped=super#module_bindingctxtmodule_bindinginletrecremove_lastl=matchlwith[]->[]|[_]->[]|hd::tl->hd::remove_lasttlinnested_module_names<-remove_lastnested_module_names;mappedmethod!structure_itemctxstructure_item=matchmode.contentswith|Native->rewrite_structure_item~nested_module_names(super#structure_itemctxstructure_item)|Js->rewrite_structure_item_for_js~nested_module_namesctx(super#structure_itemctxstructure_item)method!signature_itemctxsignature_item=matchmode.contentswith|Native->rewrite_signature_item(super#signature_itemctxsignature_item)|Js->super#signature_itemctxsignature_itemmethod!expressionctxexpr=letexpr=super#expressionctxexprinletattributes=expr.pexp_attributesinmatchmode.contentswith|Js->((* In the case of expressions, it's the only transformation that needs to be done for JS. This expansion from "styles" prop into "className" and "style" props is a feature by styled-ppx. The existence of this here, is because dune/ppxlib doesn't allow more than one preprocess_impl and even that, the combination of styled-ppx and server-reason-react.ppx doesn't compose properly. *)trymatchexpr.pexp_descwith|Pexp_apply(({pexp_desc=Pexp_ident_;pexp_loc=loc;_}astag),args)whenhas_jsx_attrexpr.pexp_attributes->letnew_args=Expand_styles_attribute.make~locargsin{(pexp_apply~loc(super#expressionctxtag)new_args)withpexp_attributes=attributes}|_->exprwithErrorerr->[%expr[%eerr]])|Native->(trymatchexpr.pexp_descwith|Pexp_apply(({pexp_desc=Pexp_ident_;pexp_loc=loc;_}astag),args)whenhas_jsx_attrexpr.pexp_attributes->(letchildren,rest_of_args=split_argsargsinmatchvalidate_tag_children(Pprintast.string_of_expressiontag)childrenrest_of_argswith|Errorerr->[%expr[%ocaml.error[%eestring~loc:expr.pexp_locerr]]]|Ok()->(matchtag.pexp_descwith(* div() [@JSX] *)|Pexp_ident{txt=Lidentname;loc=_name_loc}->(* This expansion from "styles" prop into "className" and "style" props is a feature by styled-ppx. The existence of this here, is because dune/ppxlib doesn't allow more than one preprocess_impl and even that, the combination of styled-ppx and server-reason-react.ppx doesn't compose properly. *)letnew_args=Expand_styles_attribute.make~locrest_of_argsinrewrite_lowercase~loc:expr.pexp_locnamenew_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"(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()=Driver.add_arg"-melange"(Unit(fun()->mode:=Js))~doc:"preprocess for js build";Driver.add_arg"-shared-folder-prefix"(String(funstr->letcomponents=String.split_on_char'/'str|>List.filter~f:(funx->x<>"")inletprefix=String.concat"/"componentsinletprefix=ifprefix=""then""elseprefix^"/"inshared_folder_prefix:=Someprefix))~doc:"prefix of shared folder, used to replace the it in the file path";Ppxlib.Driver.V2.register_transformation"server-reason-react.ppx"~preprocess_impl:traverse#structure~preprocess_intf:traverse#signature