123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534openPpxlibmoduleBuilder=Ast_builder.Defaulttypetarget=Native|Jsletmode=refNativeletbrowser_ppx="browser_ppx"letplatform_tag="platform"letis_platform_tagstr=String.equalstrbrowser_ppx||String.equalstrplatform_tagmodulePlatform=structletpattern=Ast_pattern.(__')letcollect_expressions~locfirstsecond=match(first.pc_lhs.ppat_desc,second.pc_lhs.ppat_desc)with|(Ppat_construct({txt=Lident"Server"|Ldot(Lident"Runtime","Server");_},_),Ppat_construct({txt=Lident"Client"|Ldot(Lident"Runtime","Client");_},_))->Ok(first.pc_rhs,second.pc_rhs)|(Ppat_construct({txt=Lident"Client"|Ldot(Lident"Runtime","Client");_},_),Ppat_construct({txt=Lident"Server"|Ldot(Lident"Runtime","Server");_},_))->Ok(second.pc_rhs,first.pc_rhs)|_->Error[%expr[%ocaml.error"[browser_only] switch%%platform requires 2 cases: `Server` and \
`Client`"]]letswitch_platform_requires_a_match~loc=[%expr[%ocaml.error"[browser_ppx] switch%%platform requires a match expression"]]lethandler~ctxt:_{txt=payload;loc}=matchpayloadwith|PStr[{pstr_desc=Pstr_eval(expression,_);_}]->(matchexpression.pexp_descwith|Pexp_match(_expression,cases)->(matchcaseswith|[first;second]->(matchcollect_expressions~locfirstsecondwith|Ok(server_expr,client_expr)->(match!modewith(* When it's -js keep the client_expr *)|Js->client_expr(* When it's isn't -js keep the server_expr *)|Native->server_expr)|Errorerror_msg_expr->error_msg_expr)|_->switch_platform_requires_a_match~loc)|_->switch_platform_requires_a_match~loc)|_->switch_platform_requires_a_match~locletrule=Context_free.Rule.extension(Extension.V3.declare"platform"Extension.Context.expressionpatternhandler)endletremove_type_constraintpattern=matchpatternwith|{ppat_desc=Ppat_constraint(pattern,_);_}->pattern|_->patternletreclast_expr_to_raise_impossible~locoriginal_nameexpr=matchexpr.pexp_descwith|Pexp_constraint(expr,_)->last_expr_to_raise_impossible~locoriginal_nameexpr|Pexp_fun(arg_label,_arg_expression,fun_pattern,expression)->letnew_fun_pattern=remove_type_constraintfun_patterninletfn=Builder.pexp_fun~locarg_labelNonenew_fun_pattern(last_expr_to_raise_impossible~locoriginal_nameexpression)in{fnwithpexp_attributes=expr.pexp_attributes}|_->[%exprRuntime.fail_impossible_action_in_ssr[%eBuilder.estring~locoriginal_name]]moduleBrowser_only=structletget_function_namepattern=matchpatternwithPpat_var{txt=name;_}->name|_->"<unkwnown>"leterror_only_works_on~loc=[%expr[%ocaml.error"[browser_ppx] browser_only works on function definitions. For other \
cases, use switch%platform or feel free to open an issue in \
https://github.com/ml-in-barcelona/server-reason-react."]]letremove_alert_browser_only~loc=Builder.attribute~loc~name:{txt="alert";loc}~payload:(PStr[[%stri"-browser_only"]])letbrowser_only_fun~locarg_labelpatternexpression=letstringified=Ppxlib.Pprintast.string_of_expressionexpressioninletmessage=Builder.estring~locstringifiedinletfn=Builder.pexp_fun~locarg_labelNonepattern[%exprRuntime.fail_impossible_action_in_ssr[%emessage]]in{fnwithpexp_attributes=expression.pexp_attributes}letbrowser_only_value_bindingpatternexpression=letloc=pattern.ppat_locinmatchpatternwith|[%pat?()]->Builder.value_binding~loc~pat:pattern~expr:[%expr()]|_->(matchexpression.pexp_descwith|Pexp_constraint({pexp_desc=Pexp_fun(_arg_label,_arg_expression,_fun_pattern,_expr);_;},_type_constraint)->letfunction_name=get_function_namepattern.ppat_descinletexpr=last_expr_to_raise_impossible~locfunction_nameexpressioninletvb=Builder.value_binding~loc~pat:pattern~exprin{vbwithpvb_attributes=[remove_alert_browser_only~loc]}|Pexp_fun(_arg_label,_arg_expression,_fun_pattern,_expr)->letfunction_name=get_function_namepattern.ppat_descinletexpr=last_expr_to_raise_impossible~locfunction_nameexpressioninletvb=Builder.value_binding~loc~pat:pattern~exprin{vbwithpvb_attributes=[remove_alert_browser_only~loc]}|_->Builder.value_binding~loc~pat:pattern~expr:(error_only_works_on~loc))letextractor_single_payload=Ast_pattern.(single_expr_payload__)letexpression_handler~ctxtpayload=letreplace_fun_body_with_raise_impossible~locpexp_desc=matchpexp_descwith|Pexp_constraint({pexp_desc=Pexp_fun(arg_label,_arg_expression,pattern,expression);},type_constraint)->letfn=browser_only_fun~locarg_labelpatternexpressioninBuilder.pexp_constraint~loc{fnwithpexp_attributes=expression.pexp_attributes}type_constraint|Pexp_fun(arg_label,_arg_expression,pattern,expr)->letfunction_name=get_function_namepattern.ppat_descinletnew_fun_pattern=remove_type_constraintpatterninBuilder.pexp_fun~locarg_labelNonenew_fun_pattern(last_expr_to_raise_impossible~locfunction_nameexpr)|Pexp_let(rec_flag,value_bindings,expression)->letpexp_let=Builder.pexp_let~locrec_flag(List.map(funbinding->browser_only_value_bindingbinding.pvb_patbinding.pvb_expr)value_bindings)expressionin[%expr[%epexp_let]]|_->error_only_works_on~locinmatch!modewith|Js->payload|Native->letloc=Expansion_context.Extension.extension_point_locctxtinreplace_fun_body_with_raise_impossible~locpayload.pexp_descletexpression_rule=Context_free.Rule.extension(Extension.V3.declare"browser_only"Extension.Context.expressionextractor_single_payloadexpression_handler)(* Generates a structure_item with a value binding with a pattern and an expression with all the alerts and warnings *)letmake_vb_with_browser_only~loc?type_constraintpatternexpression=matchtype_constraintwith|Sometype_constraint->[%strilet[@warning"-27-32"]([%ppattern]:([%ttype_constraint][@alertbrowser_only"This expression is marked to only run \
on the browser where JavaScript can \
run. You can only use it inside a \
let%browser_only function."]))=([%eexpression][@alert"-browser_only"])]|None->[%strilet[@warning"-27-32"]([%ppattern][@alertbrowser_only"This expression is marked to only run on the browser where \
JavaScript can run. You can only use it inside a \
let%browser_only function."])=([%eexpression][@alert"-browser_only"])]letextractor_vb=letopenAst_patterninletextractor_in_let=pstr_value__(value_binding~pat:__~expr:__^::nil)inpstr@@extractor_in_let^::nilletstructure_item_handler~ctxtrec_flagpatternexpression=letloc=Expansion_context.Extension.extension_point_locctxtinletdo_nothingrec_flag=matchrec_flagwith|Recursive->[%striletrec[%ppattern]=[%eexpression]]|Nonrecursive->[%strilet[%ppattern]=[%eexpression]]inletadd_browser_only_alertexpression=matchexpression.pexp_descwith|Pexp_constraint({pexp_desc=Pexp_fun(arg_label,_arg_expression,fun_pattern,expr);_;},type_constraint)->letoriginal_function_name=get_function_namepattern.ppat_descinletnew_fun_pattern=remove_type_constraintfun_patterninletfn=Builder.pexp_fun~locarg_labelNonenew_fun_pattern(last_expr_to_raise_impossible~locoriginal_function_nameexpr)inletitem={fnwithpexp_attributes=expr.pexp_attributes}inmake_vb_with_browser_only~loc~type_constraintpatternitem|Pexp_fun(arg_label,_arg_expression,fun_pattern,expr)->letoriginal_function_name=get_function_namepattern.ppat_descinletnew_fun_pattern=remove_type_constraintfun_patterninletfn=Builder.pexp_fun~locarg_labelNonenew_fun_pattern(last_expr_to_raise_impossible~locoriginal_function_nameexpr)inletitem={fnwithpexp_attributes=expr.pexp_attributes}inmake_vb_with_browser_only~locpatternitem|Pexp_function_cases->(* Because pexp_function doesn't have a pattern, neither a label, we construct an empty pattern and use it to generate the vb *)letoriginal_function_name=get_function_namepattern.ppat_descinletfn=Builder.pexp_fun~locNolabelNone[%pat?_](last_expr_to_raise_impossible~locoriginal_function_nameexpression)inletitem={fnwithpexp_attributes=expression.pexp_attributes}inmake_vb_with_browser_only~locpatternitem|Pexp_ident{txt=_longident;loc}->letitem=[%exprObj.magic()]inmake_vb_with_browser_only~locpatternitem|Pexp_newtype(name,expr)->letoriginal_function_name=name.txtinletitem=last_expr_to_raise_impossible~locoriginal_function_nameexprinmake_vb_with_browser_only~locpatternitem|_expr->do_nothingrec_flaginmatch!modewith(* When it's -js, keep item as it is *)|Js->do_nothingrec_flag|Native->add_browser_only_alertexpressionletstructure_item_rule=Context_free.Rule.extension(Extension.V3.declare"browser_only"Extension.Context.structure_itemextractor_vbstructure_item_handler)lethas_browser_only_attributeexpr=matchexpr.pexp_descwith|Pexp_extension({txt="browser_only"},_)->true|_->falseletuse_effect(expr:expression)=letadd_browser_only_extensionexpr=matchexpr.pexp_descwith|Pexp_apply(_,[(Nolabel,effect_body)])whenhas_browser_only_attributeeffect_body->None|Pexp_apply(apply_expr,[(Nolabel,effect_body);_])|Pexp_apply(apply_expr,[(Nolabel,effect_body)])->letloc=expr.pexp_locinletnew_effect_body=[%expr[%browser_only[%eeffect_body]]]inletnew_effect_fun=Builder.pexp_apply~locapply_expr[(Nolabel,new_effect_body)]inSomenew_effect_fun|_->Noneinmatch!modewith(* When it's -js, keep item as it is *)|Js->None|Native->add_browser_only_extensionexprletuse_effects=[(* useEffect *)Context_free.Rule.special_function"React.useEffect"use_effect;Context_free.Rule.special_function"React.useEffect0"use_effect;Context_free.Rule.special_function"React.useEffect1"use_effect;Context_free.Rule.special_function"React.useEffect2"use_effect;Context_free.Rule.special_function"React.useEffect3"use_effect;Context_free.Rule.special_function"React.useEffect4"use_effect;Context_free.Rule.special_function"React.useEffect5"use_effect;Context_free.Rule.special_function"React.useEffect6"use_effect;Context_free.Rule.special_function"React.useEffect7"use_effect;(* useLayoutEffect *)Context_free.Rule.special_function"React.useLayoutEffect"use_effect;Context_free.Rule.special_function"React.useLayoutEffect0"use_effect;Context_free.Rule.special_function"React.useLayoutEffect1"use_effect;Context_free.Rule.special_function"React.useLayoutEffect2"use_effect;Context_free.Rule.special_function"React.useLayoutEffect3"use_effect;Context_free.Rule.special_function"React.useLayoutEffect4"use_effect;Context_free.Rule.special_function"React.useLayoutEffect5"use_effect;Context_free.Rule.special_function"React.useLayoutEffect6"use_effect;Context_free.Rule.special_function"React.useLayoutEffect7"use_effect;]endmodulePreprocess=struct(* This module is heavily based on leostera `config.ml` PPX:
https://github.com/ocaml-sys/config.ml/blob/d248987cc1795de99d3735c06635dbd355d4d642/config/cfg_ppx.ml*)leteval_attrattr=ifnot(is_platform_tagattr.attr_name.txt)then`keepelsematch(attr.attr_payload,!mode)with|(PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_ident{txt=Lident"js"}},[]);_;};],Native)|(PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_ident{txt=Lident"native"}},[]);_;};],Js)->`drop|_->`keepletrecshould_keepattrs=matchattrswith|[]->`keep|attr::attrs->ifeval_attrattr=`dropthen`dropelseshould_keepattrsletrecshould_keep_manylistfn=matchlistwith|[]->`keep|item::list->ifshould_keep(fnitem)=`dropthen`dropelseshould_keep_manylistfnletapply_config_on_types(tds:type_declarationlist)=List.filter_map(funtd->matchtdwith|{ptype_kind=Ptype_abstract;ptype_manifest=Some({ptyp_desc=Ptyp_variant(rows,closed_flag,labels);_}asmanifest);_;}->letrows=List.filter_map(funrow->ifshould_keeprow.prf_attributes=`keepthenSomerowelseNone)rowsinifrows=[]thenNoneelseSome{tdwithptype_manifest=Some{manifestwithptyp_desc=Ptyp_variant(rows,closed_flag,labels);};}|{ptype_kind=Ptype_variantcstrs;_}->letcstrs=List.filter_map(funcstr->ifshould_keepcstr.pcd_attributes=`keepthenSomecstrelseNone)cstrsinifcstrs=[]thenNoneelseSome{tdwithptype_kind=Ptype_variantcstrs}|{ptype_kind=Ptype_recordlabels;_}->letlabels=List.filter_map(funlabel->ifshould_keeplabel.pld_attributes=`keepthenSomelabelelseNone)labelsiniflabels=[]thenNoneelseSome{tdwithptype_kind=Ptype_recordlabels}|_->Sometd)tdsletapply_config_on_structure_itemstri=matchstri.pstr_descwith|Pstr_typext{ptyext_attributes=attrs;_}|Pstr_modtype{pmtd_attributes=attrs;_}|Pstr_open{popen_attributes=attrs;_}|Pstr_include{pincl_attributes=attrs;_}|Pstr_exception{ptyexn_attributes=attrs;_}|Pstr_primitive{pval_attributes=attrs;_}|Pstr_eval(_,attrs)|Pstr_module{pmb_attributes=attrs;_}->ifshould_keepattrs=`keepthenSomestrielseNone|Pstr_value(_,vbs)->ifshould_keep_manyvbs(funvb->vb.pvb_attributes)=`keepthenSomestrielseNone|Pstr_type(recflag,tds)->ifshould_keep_manytds(funtd->td.ptype_attributes)=`keepthenlettds=apply_config_on_typestdsinSome{striwithpstr_desc=Pstr_type(recflag,tds)}elseNone|Pstr_recmodulemd->ifshould_keep_manymd(funmd->md.pmb_attributes)=`keepthenSomestrielseNone|Pstr_classcds->ifshould_keep_manycds(funcd->cd.pci_attributes)=`keepthenSomestrielseNone|Pstr_class_typectds->ifshould_keep_manyctds(functd->ctd.pci_attributes)=`keepthenSomestrielseNone|Pstr_extension_|Pstr_attribute_->Somestriletapply_config_on_signature_itemsigi=matchsigi.psig_descwith|Psig_typext{ptyext_attributes=attrs;_}|Psig_modtype{pmtd_attributes=attrs;_}|Psig_open{popen_attributes=attrs;_}|Psig_include{pincl_attributes=attrs;_}|Psig_exception{ptyexn_attributes=attrs;_}|Psig_value{pval_attributes=attrs;_}|Psig_modtypesubst{pmtd_attributes=attrs;_}|Psig_modsubst{pms_attributes=attrs;_}|Psig_module{pmd_attributes=attrs;_}->ifshould_keepattrs=`keepthenSomesigielseNone|Psig_typesubsttds->ifshould_keep_manytds(funtd->td.ptype_attributes)=`keepthenlettds=apply_config_on_typestdsinSome{sigiwithpsig_desc=Psig_typesubsttds}elseNone|Psig_type(recflag,tds)->ifshould_keep_manytds(funtd->td.ptype_attributes)=`keepthenlettds=apply_config_on_typestdsinSome{sigiwithpsig_desc=Psig_type(recflag,tds)}elseNone|Psig_recmodulemd->ifshould_keep_manymd(funmd->md.pmd_attributes)=`keepthenSomesigielseNone|Psig_classcds->ifshould_keep_manycds(funcd->cd.pci_attributes)=`keepthenSomesigielseNone|Psig_class_typectds->ifshould_keep_manyctds(functd->ctd.pci_attributes)=`keepthenSomesigielseNone|Psig_extension_|Psig_attribute_->Somesigiletpreprocess_impl_exp_ctxtstr=matchstrwith|{pstr_desc=Pstr_attributeattr;_}::restwhenis_platform_tagattr.attr_name.txt->ifeval_attrattr=`keepthenrestelse[]|_->List.filter_mapapply_config_on_structure_itemstrletpreprocess_intf_exp_ctxtsigi=matchsigiwith|{psig_desc=Psig_attributeattr;_}::restwhenis_platform_tagattr.attr_name.txt->ifeval_attrattr=`keepthenrestelse[]|_->List.filter_mapapply_config_on_signature_itemsigiendlet()=Driver.add_arg"-js"(Unit(fun()->mode:=Js))~doc:"preprocess for js build";letrules=[Browser_only.expression_rule;Browser_only.structure_item_rule;Platform.rule;]@Browser_only.use_effectsinDriver.V2.register_transformationbrowser_ppx~rules~preprocess_impl:Preprocess.preprocess_impl~preprocess_intf:Preprocess.preprocess_intf