123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259openPpxlib.ParsetreeopenPpxlib.AsttypesopenTyxml_syntaxletis_jsxe=letf=function|{attr_name={txt="JSX"}}->true|_->falseinList.existsfe.pexp_attributesletlowercase_leads=String.mapi(funic->ifi=0thenChar.lowercase_asciicelsec)sletto_kebab_case=letopenReinletcamelPat=Posix.compile_pat"[A-Z]"inletunderscore=compile@@char'_'inletprefixes=Perl.compile_pat{|^(data_?|aria_?)(.+)|}infunname->letkebabstring=replacecamelPat~f:(fung->"-"^Group.getg0)string|>String.lowercase_ascii|>replace_stringunderscore~by:"-"inmatchexec_optprefixesnamewith|None->ifname.[0]=='_'(* need to keep the leading underscore, as that's what the syntax support keys
off of to know to use Unsafe.string_attrib *)then"_"^kebab@@String.subname1(String.lengthname-1)elsename|Someg->letprefix=String.subname04inletsuffix=kebab@@Group.getg2inprefix^(ifsuffix.[0]=='-'then""else"-")^suffixletmake_attr_namename=letname=matchnamewith|"className"->"class"|"htmlFor"->"for"|"class_"->"class"|"for_"->"for"|"type_"->"type"|"to_"->"to"|"open_"->"open"|"begin_"->"begin"|"end_"->"end"|"in_"->"in"|"method_"->"method"|name->to_kebab_casenameinnameopenCommonletrecfilter_mapf=function|[]->[]|a::q->matchfawith|None->filter_mapfq|Somea->a::filter_mapfq(** Children *)letmake_txt~loc~langs=lettxt=Common.make~loclang"txt"inletarg=Common.wraplangloc@@Common.stringlocsinPpxlib.Ast_helper.Exp.apply~loctxt[Nolabel,arg]letelement_mappertransform_expre=matchewith(* Convert string constant into Html.txt "constant" for convenience *)|{pexp_desc=Pexp_constant(Pconst_string(str,loc,_));_}->make_txt~loc~lang:Htmlstr|_->transform_expreletextract_element_listtransform_exprelements=letrecmapacce=matchewith|[%expr[]]->List.revacc|[%expr[%e?child]::[%e?rest]]->letchild=Common.value(element_mappertransform_exprchild)inmap(child::acc)rest|e->List.rev(Common.antiquot(element_mappertransform_expre)::acc)inmap[]elementsletextract_childrentransform_exprargs=matchList.find(functionLabelled"children",_->true|_->false)argswith|_,children->extract_element_listtransform_exprchildren|exceptionNot_found->[](** Attributes *)typeattr={a_name:Common.name;a_value:stringvalue;a_loc:Location.t;}letrecextract_attr_value~langa_namea_value=leta_name=make_attr_namea_nameinmatcha_valuewith|{pexp_desc=Pexp_constant(Pconst_string(attr_value,_,_));_}->((lang,a_name),Common.valueattr_value)|e->((lang,a_name),Common.antiquote)andextract_attr~lang=function(* Ignore last unit argument as tyxml api is pure *)|Nolabel,[%expr()]->None|Labelled"children",_->None|Labelledname,value->Some(extract_attr_value~langnamevalue)|Nolabel,e->errore.pexp_loc"Unexpected unlabeled jsx attribute"|Optionalname,e->errore.pexp_loc"Unexpected optional jsx attribute %s"nameletclassify_name~lochint_langlid=letannotated_lang,name=matchlidwith|Longident.Ldot(Ldot(Lidents,name),"createElement")whenString.lowercase_asciis="html"->SomeHtml,lowercase_leadname|Longident.Ldot(Lidents,name)whenString.lowercase_asciis="html"->SomeHtml,lowercase_leadname|Ldot(Ldot(Lidents,name),"createElement")whenString.lowercase_asciis="svg"->SomeSvg,lowercase_leadname|Longident.Ldot(Lidents,name)whenString.lowercase_asciis="svg"->SomeSvg,lowercase_leadname|Lidentname->hint_lang,name|_->Common.errorloc"Invalid Tyxml tag %s"(String.concat"."(Longident.flatten_exnlid))inletparent_lang,elt=matchElement.find_assembler(Html,name),Element.find_assembler(Svg,name),annotated_langwith|_,Some("svg",_),Somel->l,(Svg,name)|_,Some("svg",_),None->Svg,(Svg,name)|Some_,None,_->Html,(Html,name)|None,Some_,_->Svg,(Svg,name)|Some_,Some_,Somelang->lang,(lang,name)|Some_,Some_,None->(* In case of doubt, use Html *)Html,(Html,name)|None,None,_->Common.errorloc"Unknown namespace for the element %s"nameinparent_lang,eltletis_homemade_componentlid=matchlidwith|Longident.Ldot((Lidents|Ldot(_,s)),"createElement")->String.lowercase_asciis<>"svg"&&String.lowercase_asciis<>"Html"&&letc=s.[0]in'A'<=c&&c<='Z'|_->falseletmk_component~lang~locfattrschildren=letchildren=matchchildrenwith|[]->[]|l->[Labelled"children",Common.list_wrap_valuelanglocl]inletmk_attr((_ns,name),v)=Labelledname,matchvwith|Common.Vals->Common.stringlocs|Common.Antiquote->einletattrs=List.mapmk_attrattrsinletargs=attrs@children@[Nolabel,[%expr()]]inPpxlib.Ast_helper.Exp.apply~locfargslettraverse=object(self)inherit[Common.langoption]Ppxlib.Ast_traverse.map_with_contextassupervalmutableenabled=truemethod!structure_itemhint_langstri=matchstri.pstr_descwith|Pstr_attribute{attr_name={txt=("tyxml.jsx"|"tyxml.jsx.enable")ass};attr_payload;attr_loc;}->beginmatchattr_payloadwith|PStr[%strtrue]->enabled<-true|PStr[%strfalse]->enabled<-false|_->Common.errorattr_loc"Unexpected payload for %s. A boolean is expected."send;stri|_->super#structure_itemhint_langstrimethod!expressionhint_lange=ifnot(is_jsxe)||notenabledthensuper#expressionhint_langeelseletloc=e.pexp_locinmatchewith(* matches <> ... </>; *)|[%expr[]]|[%expr[%e?_]::[%e?_]]->letl=extract_element_list(self#expressionhint_lang)einCommon.list_wrap_valueCommon.Htmllocl(* matches <Component foo={bar}> child1 child2 </div>; *)|{pexp_desc=Pexp_apply({pexp_desc=Pexp_ident{txt};_}asf_expr,args)}whenis_homemade_componenttxt->letlang=matchhint_langwith|Somel->l|None->Common.Htmlinletattributes=filter_map(extract_attr~lang)argsinletchildren=extract_children(self#expressionhint_lang)argsinlete=mk_component~loc~langf_exprattributeschildrenine(* matches <div foo={bar}> child1 child2 </div>; *)|{pexp_desc=Pexp_apply({pexp_desc=Pexp_ident{txt};_},args)}->letparent_lang,name=classify_name~lochint_langtxtinletlang=fstnameinletattributes=filter_map(extract_attr~lang)argsinletchildren=extract_children(self#expression@@Somelang)argsinlete=Element.parse~loc~parent_lang~name~attributeschildrenine|_->super#expressionhint_langeendlet()=Ppxlib.Driver.register_transformation~impl:(traverse#structureNone)"tyxml-jsx"