123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898openPpxlibmoduleBuilder=Ast_builder.DefaultmoduleMel_module=structtypebundler=Webpack|Esbuildletbundler=refWebpackletprefix=ref"/"letis_melange_attr{attr_name={txt=attr}}="mel.module"=attrlethas_attrattrs=List.existsis_melange_attrattrsletasset_payloadattrs=letattr=(* we use `find` directly even if it can raise, assuming `has_attr` has been called before *)List.findis_melange_attrattrsinmatchattr.attr_payloadwith|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_string(str,_,_))},_);};]whenString.length(Filename.extensionstr)>0->Somestr|_->NonemoduleEsbuild=struct(* This code is adapted from Esbuild hashing algorithm:
base32: https://github.com/evanw/esbuild/blob/efa3dd2d8e895f7f9a9bef0d588560bbae7d776e/internal/bundler/bundler.go#L1174
sum function: https://github.com/evanw/esbuild/blob/efa3dd2d8e895f7f9a9bef0d588560bbae7d776e/internal/xxhash/xxhash.go#L104
the internal xxhash that esbuild uses is adapted from https://github.com/cespare/xxhash
*)lethash_for_filenamebytes=String.sub(Base32.encode_string(Bytes.to_stringbytes))08letsumhex_str=(* Convert hexadecimal string to Int64 *)letint64_value=Int64.of_string("0x"^hex_str)in(* Create an 8-byte buffer *)letbytes=Bytes.create8in(* Fill the buffer with the bytes of the Int64 value *)fori=0to7doletbyte=Int64.(to_int(shift_right_logicalint64_value(8*(7-i))))land0xFFinBytes.setbytesi(char_of_intbyte)done;byteslethashcontent=lethash=XXH64.hashcontentinletb=sum(XXH64.to_hexhash)inhash_for_filenamebletfilename~basecontent=Filename.(chop_extensionbase^"-"^hashcontent^extensionbase)end(*
(* For now, rspack doesn't support real content hashes, see https://github.com/web-infra-dev/rspack/issues/6606 *)
module Rspack = struct
(* This code is adapted from Rspack hashing algorithm:
https://github.com/web-infra-dev/rspack/blob/0a5cf0ddf38d41c2cad58c95ee9c1d3bd95e377f/crates/rspack_hash/src/lib.rs
*)
let hex_to_little_endian hex_str =
(* Split the hex string into byte pairs *)
let rec split_into_bytes acc i =
if i >= String.length hex_str then List.rev acc
else
let byte = String.sub hex_str i 2 in
split_into_bytes (byte :: acc) (i + 2)
in
(* Join byte pairs into a single string *)
let join_bytes bytes = String.concat "" bytes in
(* Perform the transformation *)
let bytes = split_into_bytes [] 0 in
let reversed_bytes = List.rev bytes in
join_bytes reversed_bytes
let hash content =
let open XXHash in
let hash = XXH3_64.hash content in
hex_to_little_endian (XXH3_64.to_hex hash)
end *)moduleWebpack=struct(* Needs following config in webpack.config.js, see https://webpack.js.org/configuration/output/#outputhashfunction
```
module.exports = {
//...
output: {
hashFunction: 'xxhash64',
},
};
```
Also needs to set `realContentHash` for it to work in dev mode (see https://webpack.js.org/configuration/optimization/#optimizationrealcontenthash):
```
module.exports = {
//...
optimization: {
realContentHash: false,
},
};
```
*)lethashcontent=lethash=XXH64.hashcontentinXXH64.to_hexhashletfilename~basecontent=hashcontent^Filename.extensionbaseendendmoduleString_interpolation=struct(* https://github.com/melange-re/melange/blob/fb1466fed7d6e5aafd3ee266bbd4ec70c8fb857a/ppx/string_interp.ml *)moduleUtf8_string=structtypebyte=Singleofint|Contofint|Leadingofint*int|Invalid(** [classify chr] returns the {!byte} corresponding to [chr] *)letclassifychr=letc=int_of_charchrin(* Classify byte according to leftmost 0 bit *)ifcland0b1000_0000=0thenSinglecelseif(* c 0b0____*)cland0b0100_0000=0thenCont(cland0b0011_1111)elseif(* c 0b10___*)cland0b0010_0000=0thenLeading(1,cland0b0001_1111)elseif(* c 0b110__*)cland0b0001_0000=0thenLeading(2,cland0b0000_1111)elseif(* c 0b1110_ *)cland0b0000_1000=0thenLeading(3,cland0b0000_0111)elseif(* c 0b1111_0___*)cland0b0000_0100=0thenLeading(4,cland0b0000_0011)elseif(* c 0b1111_10__*)cland0b0000_0010=0thenLeading(5,cland0b0000_0001)(* c 0b1111_110__ *)elseInvalidendtypeerror=|Invalid_code_point|Unterminated_backslash|Unterminated_variable|Unmatched_paren|Invalid_syntax_of_varofstringtypekind=String|Varofint*int(* [Var (loffset, roffset)]
For parens it used to be (2,-1)
for non-parens it used to be (1,0) *)(* Note the position is about code point *)typepos={lnum:int;offset:int;byte_bol:int;(* Note it actually needs to be in sync with OCaml's lexing semantics *)}typesegment={start:pos;finish:pos;kind:kind;content:string}typecxt={mutablesegment_start:pos;buf:Buffer.t;s_len:int;mutablesegments:segmentlist;pos_bol:int;(* record the abs position of current beginning line *)byte_bol:int;pos_lnum:int;(* record the line number *)}exceptionErrorofpos*pos*errorletpp_errorfmterr=Format.pp_print_stringfmt@@matcherrwith|Invalid_code_point->"Invalid code point"|Unterminated_backslash->"\\ ended unexpectedly"|Unterminated_variable->"$ unterminated"|Unmatched_paren->"Unmatched paren"|Invalid_syntax_of_vars->"`"^s^"' is not a valid syntax of interpolated identifer"letvalid_lead_identifier_charx=matchxwith'a'..'z'|'_'->true|_->falseletvalid_identifier_charx=matchxwith|'a'..'z'|'A'..'Z'|'0'..'9'|'_'|'\''->true|_->false(* Invariant: [valid_lead_identifier] has to be [valid_identifier] *)letvalid_identifier=letfor_all_from=letrecunsafe_for_all_ranges~start~finishp=start>finish||p(String.unsafe_getsstart)&&unsafe_for_all_ranges~start:(start+1)~finishpinfunsstartp->letlen=String.lengthsinifstart<0theninvalid_arg"for_all_from"elseunsafe_for_all_ranges~start~finish:(len-1)pinfuns->lets_len=String.lengthsinifs_len=0thenfalseelsevalid_lead_identifier_chars.[0]&&for_all_froms1valid_identifier_char(* FIXME: multiple line offset
if there is no line offset. Note {|{j||} border will never trigger a new
line *)letupdate_positionborder{lnum;offset;byte_bol}(pos:Lexing.position)=iflnum=0then{poswithpos_cnum=pos.pos_cnum+border+offset}(* When no newline, the column number is [border + offset] *)else{poswithpos_lnum=pos.pos_lnum+lnum;pos_bol=pos.pos_cnum+border+byte_bol;pos_cnum=pos.pos_cnum+border+byte_bol+offset;(* when newline, the column number is [offset] *)}letupdateborderstartfinish(loc:Location.t)=letstart_pos=loc.loc_startin{locwithloc_start=update_positionborderstartstart_pos;loc_end=update_positionborderfinishstart_pos;}letpos_errorcxt~locerror=raise(Error(cxt.segment_start,{lnum=cxt.pos_lnum;offset=loc-cxt.pos_bol;byte_bol=cxt.byte_bol;},error))letadd_var_segmentcxtlocloffsetroffset=letcontent=Buffer.contentscxt.bufinBuffer.clearcxt.buf;letnext_loc={lnum=cxt.pos_lnum;offset=loc-cxt.pos_bol;byte_bol=cxt.byte_bol;}inifvalid_identifiercontentthen(cxt.segments<-{start=cxt.segment_start;finish=next_loc;kind=Var(loffset,roffset);content;}::cxt.segments;cxt.segment_start<-next_loc)elseletcxt=matchString.trimcontentwith|""->(* Move the position back 2 characters "$(" if this is the empty
interpolation. *){cxtwithsegment_start={cxt.segment_startwithoffset=(matchcxt.segment_start.offsetwith0->0|n->n-3);byte_bol=(matchcxt.segment_start.byte_bolwith|0->0|n->n-3);};pos_bol=cxt.pos_bol+3;byte_bol=cxt.byte_bol+3;}|_->cxtinpos_errorcxt~loc(Invalid_syntax_of_varcontent)letadd_str_segmentcxtloc=letcontent=Buffer.contentscxt.bufinBuffer.clearcxt.buf;letnext_loc={lnum=cxt.pos_lnum;offset=loc-cxt.pos_bol;byte_bol=cxt.byte_bol;}incxt.segments<-{start=cxt.segment_start;finish=next_loc;kind=String;content}::cxt.segments;cxt.segment_start<-next_locletreccheck_and_transformlocsbyte_offset({s_len;buf;_}ascxt)=ifbyte_offset=s_lenthenadd_str_segmentcxtlocelseletcurrent_char=s.[byte_offset]inmatchUtf8_string.classifycurrent_charwith|Single92(* '\\' *)->letloc=loc+1inletoffset=byte_offset+1inifoffset>=s_lenthenpos_errorcxt~locUnterminated_backslashelseBuffer.add_charbuf'\\';letcur_char=s.[offset]inBuffer.add_charbufcur_char;check_and_transform(loc+1)s(offset+1)cxt|Single36->(* $ *)add_str_segmentcxtloc;letoffset=byte_offset+1inifoffset>=s_lenthenpos_error~loccxtUnterminated_variableelseletcur_char=s.[offset]inifcur_char='('thenexpect_var_paren(loc+2)s(offset+1)cxtelseexpect_simple_var(loc+1)soffsetcxt|Single_|Leading_|Cont_->Buffer.add_charbufcurrent_char;check_and_transform(loc+1)s(byte_offset+1)cxt|Invalid->pos_error~loccxtInvalid_code_point(* Lets keep identifier simple, so that we could generating a function easier
in the future for example
let f = [%fn{| $x + $y = $x_add_y |}] *)andexpect_simple_varlocsoffset({buf;s_len;_}ascxt)=letv=refoffsetinifnot(offset<s_len&&valid_lead_identifier_chars.[offset])thenpos_errorcxt~loc(Invalid_syntax_of_varString.empty)else(while!v<s_len&&valid_identifier_chars.[!v]do(* TODO *)letcur_char=s.[!v]inBuffer.add_charbufcur_char;incrvdone;letadded_length=!v-offsetinletloc=added_length+locinadd_var_segmentcxtloc10;check_and_transformlocs(added_length+offset)cxt)andexpect_var_parenlocsoffset({buf;s_len;_}ascxt)=letv=refoffsetinwhile!v<s_len&&s.[!v]<>')'doletcur_char=s.[!v]inBuffer.add_charbufcur_char;incrvdone;letadded_length=!v-offsetinletloc=added_length+1+locinif!v<s_len&&s.[!v]=')'then(add_var_segmentcxtloc2(-1);check_and_transformlocs(added_length+1+offset)cxt)elsepos_errorcxt~locUnmatched_paren(* TODO: Allow identifers x.A.y *)letborder=String.length"{j|"letrechandle_segments=letmoduleExp=Ast_helper.Expinletconcat_ident:Longident.t=Ldot(Lident"Stdlib","^")inletescaped_js_delimiter=(* syntax not allowed at the user level *)letunescaped_js_delimiter="js"inSomeunescaped_js_delimiterinletmerge_loc(l:Location.t)(r:Location.t)=ifl.loc_ghostthenrelseifr.loc_ghostthenlelsematch(l,r)with|{loc_start;_},{loc_end;_}(* TODO: improve*)->{loc_start;loc_end;loc_ghost=false}inletauxlocsegment=matchsegmentwith|{start;finish;kind;content}->(matchkindwith|String->letloc=updateborderstartfinishlocinExp.constant(Pconst_string(content,loc,escaped_js_delimiter))|Var(soffset,foffset)->letloc={locwithloc_start=update_position(soffset+border)startloc.loc_start;loc_end=update_position(foffset+border)finishloc.loc_start;}inExp.ident~loc{loc;txt=Lidentcontent})inletconcat_expa_locx~(lhs:expression)=letloc=merge_loca_loclhs.pexp_locinExp.apply(Exp.ident{txt=concat_ident;loc})[(Nolabel,lhs);(Nolabel,auxlocx)]infunlocrev_segments->matchrev_segmentswith|[]->Exp.constant(Pconst_string("",loc,escaped_js_delimiter))|[segment]->auxlocsegment(* string literal *)|{content="";_}::rest->handle_segmentslocrest|a::rest->concat_exploca~lhs:(handle_segmentslocrest)lettransform=lettransform(e:expression)s=lets_len=String.lengthsinletbuf=Buffer.create(s_len*2)inletcxt={segment_start={lnum=0;offset=0;byte_bol=0};buf;s_len;segments=[];pos_lnum=0;byte_bol=0;pos_bol=0;}incheck_and_transform0s0cxt;handle_segmentse.pexp_loccxt.segmentsinfun~locexprs->trytransformexprswithError(start,pos,error)->letloc=updateborderstartposlocinLocation.raise_errorf~loc"%a"pp_errorerrorendletis_send_pipepval_attributes=List.exists(fun{attr_name={txt=attr}}->String.equalattr"mel.send.pipe")pval_attributesletget_function_namepattern=letrecgopattern=matchpatternwith|Ppat_var{txt=name;_}->Somename|Ppat_constraint(pattern,_)->gopattern.ppat_desc|_->Noneingopatternletget_label=function|Ptyp_constr({txt=Lidentlabel;_},_)->Somelabel|_->None(* Extract the `t` from [@mel.send.pipe: t] *)letget_send_pipepval_attributes=ifis_send_pipepval_attributesthenletfirst_attribute=List.hdpval_attributesinmatchfirst_attribute.attr_payloadwith|PTypcore_type->Somecore_type|_->NoneelseNonelethas_ptyp_attributeptyp_attributesattribute=List.exists(fun{attr_name={txt=attr}}->attr=attribute)ptyp_attributesletis_mel_ascore_type=matchcore_typewith|{ptyp_desc=Ptyp_any;ptyp_attributes;_}->has_ptyp_attributeptyp_attributes"mel.as"|_->falseletextract_args_labels_typesaccpval_type=letrecgoacc=function(* In case of being mel.as, ignore those *)|{ptyp_desc=Ptyp_arrow(_label,t1,_t2);_}whenis_mel_ast1->acc|{ptyp_desc=Ptyp_arrow(_label,_t1,t2);_}whenis_mel_ast2->acc|{ptyp_desc=Ptyp_arrow(_label,t1,t2);_}whenis_mel_ast1&&is_mel_ast2->acc|{ptyp_desc=Ptyp_arrow(label,t1,t2);_}->letpattern=Builder.ppat_var~loc:t1.ptyp_loc{loc=t1.ptyp_loc;txt="_"}ingo((label,pattern,t1)::acc)t2|_->accingoaccpval_type(* Insert send_pipe_core_type as a last argument of the function, but not the return type *)letconstruct_pval_with_send_pipesend_pipe_core_typepval_type=letrecinsert_core_type_in_arrowcore_type=matchcore_typewith(* Handle only ptyp and constr.
Missing `| Ptyp_any | Ptyp_var | Ptyp_arrow | Ptyp_tuple | Ptyp_constr
| Ptyp_object | Ptyp_class | Ptyp_alias | Ptyp_variant
| Ptyp_poly | Ptyp_package | Ptyp_extension`
The aren't used in most bindings.
*)|{ptyp_desc=Ptyp_arrow(label,t1,t2);_}->(match(t1.ptyp_desc,t2.ptyp_desc)with(* `constr -> arrow (constr -> constr)` gets transformed into
`constr -> constr -> t -> constr` *)|Ptyp_constr_,Ptyp_arrow(_inner_label,_p1,_p2)->Builder.ptyp_arrow~loc:t1.ptyp_loclabelt1(insert_core_type_in_arrowt2)(* `constr -> constr` gets transformed into `constr -> t -> constr` *)(* `arrow (constr -> constr) -> constr` gets transformed into,
`arrow (constr -> constr) -> t -> constr` *)|_,_->Builder.ptyp_arrow~loc:t2.ptyp_loclabelt1(Builder.ptyp_arrow~loc:t2.ptyp_locNolabelsend_pipe_core_typet2))(* In case of being a single ptyp_* turn into ptyp_* -> t *)|{ptyp_desc=Ptyp_constr({txt=_;loc},_);_}|{ptyp_desc=Ptyp_var_;ptyp_loc=loc;_}->Builder.ptyp_arrow~locNolabelcore_typesend_pipe_core_type(* Here we ignore the Ptyp_any *)|_->core_typeininsert_core_type_in_arrowpval_typeletinject_send_pipe_as_last_argumentpipe_typeargs_labels=matchpipe_typewith|None->args_labels|Somepipe_core_type->pipe_core_type::args_labelsletis_mel_rawexpr=matchexprwith|Pexp_extension({txt="mel.raw";_},_)->true|_->falseletcapture_payloadexpr=matchexprwith|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_string(payload,_,_));_},_);_;};]->payload|_->"..."letget_payload_from_mel_rawexpr=letrecgoexpr=matchexprwith|Pexp_extension({txt="mel.raw";_},pstr)->capture_payloadpstr|Pexp_constraint(expr,_)->goexpr.pexp_desc|Pexp_fun(_,_,_,expr)->goexpr.pexp_desc|_->"..."ingoexprletexpression_has_mel_rawexpr=letrecgoexpr=matchexprwith|Pexp_extension({txt="mel.raw";_},_)aspexp_desc->is_mel_rawpexp_desc|Pexp_constraint(expr,_)->is_mel_rawexpr.pexp_desc|Pexp_fun(_,_,_,expr)->goexpr.pexp_desc|_->falseingoexprletraise_failure~locname=[%exprlet()=Printf.printf{|
There is a Melange's external (for example: [@mel.get]) call from native code.
Melange externals are bindings to JavaScript code, which can't run on the server and should be wrapped with browser_only ppx or only run it only on the client side. If there's any issue, try wrapping the expression with a try/catch as a workaround.
|}inraise(Runtime.fail_impossible_action_in_ssr[%eBuilder.pexp_constant~loc(Pconst_string(name,loc,None))])]letmel_raw_found_in_native_message~locpayload=letmsg=Printf.sprintf"[server-reason-react.melange_ppx] There's a [%%mel.raw \"%s\"] \
expression in native, which should only happen in JavaScript. You need \
to conditionally run it via let%%browser_only or switch%%platform. More \
info at \
https://ml-in-barcelona.github.io/server-reason-react/local/server-reason-react/browser_only.html"payloadinBuilder.pexp_constant~loc(Pconst_string(msg,loc,None))letmel_module_found_in_native_message~loc=letmsg=Printf.sprintf"[server-reason-react.melange_ppx] There's an external with \
[%%mel.module \"...\"] in native, which should only happen in \
JavaScript. You need to conditionally run it, either by not including \
it on native or via let%%browser_only/switch%%platform. More info at \
https://ml-in-barcelona.github.io/server-reason-react/local/server-reason-react/browser_only.html"inBuilder.pexp_constant~loc(Pconst_string(msg,loc,None))letexternal_found_in_native_message~loc=letmsg=Printf.sprintf"[server-reason-react.melange_ppx] There's an external in native, which \
should only happen in JavaScript. You need to conditionally run it, \
either by not including it on native or via \
let%%browser_only/switch%%platform. More info at \
https://ml-in-barcelona.github.io/server-reason-react/local/server-reason-react/browser_only.html"inBuilder.pexp_constant~loc(Pconst_string(msg,loc,None))letget_function_aritypattern=letrecgoarity=function|Pexp_fun(_,_,_,expr)->go(arity+1)expr.pexp_desc|_->arityingo0patternlettransform_external_arrow~locpval_namepval_attributespval_type=letpipe_type=matchget_send_pipepval_attributeswith|Somecore_type->letpattern=Builder.ppat_var~loc:core_type.ptyp_loc{loc=core_type.ptyp_loc;txt="_"}inSome(Nolabel,pattern,core_type)|None->Noneinletargs_labels_types=extract_args_labels_types[]pval_typeinletfunction_core_type=Builder.ppat_var~loc:pval_name.loc{loc=pval_name.loc;txt=pval_name.txt}inletpval_type_piped=matchpipe_typewith|None->pval_type|Some(_,_,pipe_type)->construct_pval_with_send_pipepipe_typepval_typeinletpat=Builder.ppat_constraint~loc:pval_type.ptyp_locfunction_core_type(Builder.ptyp_poly~loc:pval_type.ptyp_loc[]pval_type_piped)inletarg_labels=inject_send_pipe_as_last_argumentpipe_typeargs_labels_typesinletfunction_expression=List.fold_left(funacc(label,arg_pat,arg_type)->Builder.pexp_fun~loc:arg_type.ptyp_loclabelNonearg_patacc)(raise_failure~loc:pval_type.ptyp_locpval_name.txt)arg_labelsinletvb=Builder.value_binding~loc~pat~expr:function_expressioninAst_helper.Str.valueNonrecursive[vb]letptyp_humanize=function|Ptyp_tuple_->"Tuples"|Ptyp_object_->"Objects"|Ptyp_class_->"Classes"|Ptyp_variant_->"Variants"|Ptyp_extension_->"Extensions"|Ptyp_alias_->"Alias"|Ptyp_poly_->"Polyvariants"|Ptyp_package_->"Packages"|Ptyp_any->"Any"|Ptyp_var_->"Var"|Ptyp_arrow_->"Arrow"|Ptyp_constr_->"Constr"lettransform_external~module_pathpval_namepval_attributespval_locpval_type=letloc=pval_locinmatchpval_type.ptyp_descwith|Ptyp_arrow_->transform_external_arrow~locpval_namepval_attributespval_type|Ptyp_var_|Ptyp_any|Ptyp_constr_->(* When mel.send.pipe is used, it's treated as a funcion *)ifOption.is_some(get_send_pipepval_attributes)thentransform_external_arrow~locpval_namepval_attributespval_typeelseifMel_module.has_attrpval_attributesthenmatchMel_module.asset_payloadpval_attributeswith|None->(* If it doesn't have asset payload, we error out as it must be some .js module or package being imported *)[%stri[%%ocaml.error[%emel_module_found_in_native_message~loc]]]|Somestr->(* If it has asset payload (file with extension), calculate hash and replace external *)letname=Builder.pvar~loc:pval_name.locpval_name.txtinletpath=letasset_path=Filename.(concat(dirnamemodule_path)str)inlets=In_channel.with_open_binasset_pathIn_channel.input_allinletfilename_fn=match!Mel_module.bundlerwith|Webpack->Mel_module.Webpack.filename|Esbuild->Mel_module.Esbuild.filenameinletprefix=!Mel_module.prefixinBuilder.estring~locFilename.(concatprefix(filename_fn~base:(Filename.basenamestr)s))in[%strilet[%pname]=[%epath]]else[%stri[%%ocaml.error[%eexternal_found_in_native_message~loc]]]|_->[%stri[%%ocaml.error"[server-reason-react.melange_ppx] %s are not supported in native \
externals the same way as melange.ppx support them."(ptyp_humanizepval_type.ptyp_desc)]]lettranform_record_to_object~locrecord=letfields=List.map(fun(label,expression)->Builder.pcf_method~loc(Builder.Located.mklabel~loc,Public,Cfk_concrete(Fresh,expression)))recordinBuilder.pexp_object~loc(Builder.class_structure~self:(Builder.ppat_any~loc)~fields)letvalidate_record_labels~locrecord=List.fold_left(funacc(longident,expression)->matchaccwith|Error_aserror->error|Okacc->(matchlongident.txtwith|Lidentlabel->Ok((label,expression)::acc)|Ldot_|Lapply_->Error(Location.error_extensionf~loc"[server-reason-react.melange_ppx] Js.t objects only \
support labels as keys")))(Ok[])recordclassraise_exception_mapper(module_path:string)=object(_self)inheritAst_traverse.mapassupermethod!expressionexpr=matchexpr.pexp_descwith|Pexp_extension({txt="mel.obj";_},PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_record(record,None);pexp_loc},_);_;};])->(matchvalidate_record_labels~loc:pexp_locrecordwith|Okrecord->tranform_record_to_object~loc:pexp_locrecord|Errorextension->Builder.pexp_extension~loc:pexp_locextension)|Pexp_extension({txt="mel.obj";loc},_)->Builder.pexp_extension~loc(Location.error_extensionf~loc:expr.pexp_loc"[server-reason-react.melange_ppx] Js.t objects requires a \
record literal")|Pexp_constant(Pconst_string(s,loc,Some"j"))->String_interpolation.transform~locexprs|_->super#expressionexprmethod!structure_itemitem=matchitem.pstr_descwith(* [%%mel.raw ...] *)|Pstr_extension(({txt="mel.raw";_},pstr),_)->letloc=item.pstr_locinletpayload=capture_payloadpstrin[%stri[%%ocaml.error[%emel_raw_found_in_native_message~locpayload]]](* let a _ = [%mel.raw ...] *)|Pstr_value(Nonrecursive,[{pvb_expr={pexp_desc=Pexp_fun(_arg_label,_arg_expression,_fun_pattern,expression);};pvb_pat={ppat_desc=Ppat_var{txt=_function_name;_}};pvb_attributes=_;pvb_loc;};])whenexpression_has_mel_rawexpression.pexp_desc->letloc=item.pstr_locinletpayload=get_payload_from_mel_rawexpression.pexp_descin[%stri[%error[%emel_raw_found_in_native_message~loc:pvb_locpayload]]](* let a = [%mel.raw ...] *)|Pstr_value(Nonrecursive,[{pvb_expr=expression;pvb_pat={ppat_desc=Ppat_var{txt=_function_name;_}};pvb_attributes=_;pvb_loc;};])whenexpression_has_mel_rawexpression.pexp_desc->letloc=item.pstr_locinletpayload=get_payload_from_mel_rawexpression.pexp_descin[%stri[%error[%emel_raw_found_in_native_message~loc:pvb_locpayload]]](* let a: t = [%mel.raw ...] *)|Pstr_value(Nonrecursive,[{pvb_expr=expression;pvb_pat={ppat_desc=_};pvb_attributes=_;pvb_loc;};])whenexpression_has_mel_rawexpression.pexp_desc->letloc=item.pstr_locinletpayload=get_payload_from_mel_rawexpression.pexp_descin[%stri[%error[%emel_raw_found_in_native_message~loc:pvb_locpayload]]](* %mel. *)(* external foo: t = "{{JavaScript}}" *)|Pstr_primitive{pval_name;pval_attributes;pval_loc;pval_type}->transform_external~module_pathpval_namepval_attributespval_locpval_type|_->super#structure_itemitemendletstructure_mapperctxts=letmodule_path=Code_path.file_path(Expansion_context.Base.code_pathctxt)in(newraise_exception_mappermodule_path)#structuresmoduleDebug=structletrule=letextractor=Ast_pattern.(__')inlethandler~ctxt:_{loc}=[%expr()]inContext_free.Rule.extension(Extension.V3.declare"debug"Extension.Context.expressionextractorhandler)endlet()=Driver.add_arg"-bundler"(String(funstr->matchstrwith|"webpack"->Mel_module.bundler:=Webpack|"esbuild"->Mel_module.bundler:=Esbuild|_->failwith(Printf.sprintf{|Unknown value %S passed as -bundler flag in melange.ppx, valid values: "webpack", "esbuild"|}str)))~doc:"generate paths to assets in mel.module using the file name scheme of \
the bundler of choice";Driver.add_arg"-prefix"(String(funstr->Mel_module.prefix:=str))~doc:"the paths to the generated assets will include the given prefix before \
the filename (default: \"/\")";Driver.V2.register_transformation~impl:structure_mapper~rules:[Pipe_first.rule;Regex.rule;Double_hash.rule;Debug.rule]"melange-native-ppx"