12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052openPpxlibmoduleBuilder=Ast_builder.DefaultmodulePrivate=structmoduleTypemod_hide=structletno_type_defined(x:structure_item)=matchx.pstr_descwith|Pstr_eval_|Pstr_value_|Pstr_primitive_|Pstr_typext_|Pstr_exception_(* | Pstr_module {pmb_expr = {pmod_desc = Pmod_ident _} } *)->true|Pstr_include{pincl_mod={pmod_desc=Pmod_constraint({pmod_desc=Pmod_structure[{pstr_desc=Pstr_primitive_;_}];_},_);_;};_;}->true(* FIX https://github.com/rescript-lang/rescript/issues/4881
generated code from:
{[
external %private x : int -> int = "x"
[@@mel.module "./x"]
]}
*)|_->falseletcheck(x:structure)=List.iter(funx->ifnot(no_type_definedx)thenLocation.raise_errorf~loc:x.pstr_loc"the structure is not supported in local extension")xendletrule=letexpand(stru:structure)=Typemod_hide.checkstru;letlast_loc=(List.hdstru).pstr_locinletfirst_loc=(List.hdstru).pstr_locinletloc={first_locwithloc_end=last_loc.loc_end}inAst_helper.[Str.open_(Opn.mk~override:Override(Mod.structure~locstru))]|>List.hdinletrulelabel=letextractor=Ast_pattern.__'inlethandler~ctxt:_{txt=payload;loc}=matchpayloadwith|PStrwork->expandwork|PSig_|PTyp_|PPat_->Location.raise_errorf~loc"private extension is not support"inletextender=Extension.V3.declarelabelStructure_itemextractorhandlerinContext_free.Rule.extensionextenderinrule"private"endmoduleMel_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)~finishp)infunsstartp->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_bolwith0->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_attributeslethas_browser_ppx_attributeattrs=List.exists(fun{attr_name={txt=attr};attr_payload;_}->match(attr,attr_payload)with|"browser_only",_->true|"platform",PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_ident{txt=Lident"js"}},_);_}]->true|_->false)attrslethas_attributeattrsname=List.exists(fun{attr_name={txt;_};_}->String.equaltxtname)attrs(* Keep this keyword list and translate_mel_obj_label in sync with Melange's
Lam_methname.translate implementation:
https://github.com/melange-re/melange/blob/main/jscomp/common/lam_methname.ml *)letmel_obj_keywords=["and";"as";"assert";"begin";"class";"constraint";"do";"done";"downto";"else";"end";"exception";"external";"false";"for";"fun";"function";"functor";"if";"in";"include";"inherit";"initializer";"lazy";"let";"match";"method";"module";"mutable";"new";"nonrec";"object";"of";"open";"or";"private";"rec";"sig";"struct";"then";"to";"true";"try";"type";"val";"virtual";"when";"while";"with";"mod";"land";"lor";"lxor";"lsl";"lsr";"asr";]letfind_double_underscorename=letrecgoindex=ifindex<0then-1elseifindex+1<String.lengthname&&name.[index]='_'&&name.[index+1]='_'thenindexelsego(index-1)ingo(String.lengthname-2)lettranslate_mel_obj_labelname=letvalid_start_char=function'_'|'a'..'z'->true|_->falseinletdouble_underscore_index=find_double_underscorenameinifdouble_underscore_index=0thennameelseifdouble_underscore_index>0thenString.subname0double_underscore_indexelsematchname.[0]with|'_'whenString.lengthname>1->letcandidate=String.subname1(String.lengthname-1)inif(not(valid_start_charcandidate.[0]))||List.memcandidatemel_obj_keywordsthencandidateelsename|_->nametypejs_object_field={method_name:string;js_name:string;present_expr:expression;value_expr:expression}letoption_is_some_expr~locexpr=[%exprmatch[%eexpr]withNone->false|Some_->true]letjs_obj_internal_expression~locname=Builder.pexp_ident~loc{txt=Ldot(Ldot(Ldot(Lident"Js","Obj"),"Internal"),name);loc}letis_literal_bool_exprexpr=matchexpr.pexp_descwithPexp_construct({txt=Lident("true"|"false");_},None)->true|_->false(* Emits a registered [Js.t] object. Field values live in plain refs read by
the object methods; the registry entry is a single [Deferred] thunk that
builds the per-field [entry] records (a record plus two boxing closures
each) only if [Js.Obj.keys]/[assign]/[merge] ever inspects the object.
This keeps object construction — every [makeProps] call and [%mel.obj]
literal — free of per-field entry allocation. *)letbuild_registered_js_object_expression?as_type?(register_name="register_deferred")~locfields=letgenerated_fields=List.mapi(funindex{method_name;js_name;present_expr;value_expr}->letcell_name=Printf.sprintf"__js_obj_cell_%d"indexinletcell_binding=Builder.value_binding~loc~pat:(Builder.ppat_var~loc{loc;txt=cell_name})~expr:[%exprStdlib.ref[%evalue_expr]]in(* [present] must be evaluated at construction time (it reads the
original argument), but literal booleans can be inlined into the
thunk directly. *)letpresent_bindings,present_in_thunk=ifis_literal_bool_exprpresent_exprthen([],present_expr)elseletpresent_name=Printf.sprintf"__js_obj_present_%d"indexin([Builder.value_binding~loc~pat:(Builder.ppat_var~loc{loc;txt=present_name})~expr:present_expr],Builder.evar~locpresent_name)inletentry_expr=Builder.pexp_apply~loc(js_obj_internal_expression~loc"deferred_entry")[(Labelled"method_name",Builder.estring~locmethod_name);(Labelled"js_name",Builder.estring~locjs_name);(Labelled"present",present_in_thunk);(Nolabel,Builder.evar~loccell_name);]inletmethod_body=Builder.pexp_apply~loc(Builder.evar~loc"!")[(Nolabel,Builder.evar~loccell_name)]inletmethod_=Builder.pcf_method~loc(Builder.Located.mkmethod_name~loc,Public,Cfk_concrete(Fresh,method_body))in(cell_binding::present_bindings,entry_expr,method_))fieldsinletfield_bindings,entry_exprs,methods=List.fold_right(fun(bindings,entry_expr,method_)(field_bindings,entry_exprs,methods)->(bindings@field_bindings,entry_expr::entry_exprs,method_::methods))generated_fields([],[],[])inletobject_name="__js_obj"inletobject_binding=Builder.value_binding~loc~pat:(Builder.ppat_var~loc{loc;txt=object_name})~expr:(Builder.pexp_object~loc(Builder.class_structure~self:(Builder.ppat_any~loc)~fields:methods))inletentries_thunk=[%exprfun()->[%eBuilder.elist~locentry_exprs]]inletregister_call=Builder.pexp_apply~loc(js_obj_internal_expression~locregister_name)[(Nolabel,Builder.evar~locobject_name);(Nolabel,entries_thunk)]inletregister_call=matchas_typewithNone->register_call|Somecore_type->Builder.pexp_constraint~locregister_callcore_typeinList.fold_right(funbindingacc->Builder.pexp_let~locNonrecursive[binding]acc)(field_bindings@[object_binding])register_callletrecget_return_core_type=function|{ptyp_desc=Ptyp_arrow(_,_,rest);_}->get_return_core_typerest|core_type->core_typeletget_function_namepattern=letrecgopattern=matchpatternwith|Ppat_var{txt=name;_}->Somename|Ppat_constraint(pattern,_)->gopattern.ppat_desc|_->Noneingopatternletget_label=functionPtyp_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_payloadwithPTypcore_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_typewithNone->args_labels|Somepipe_core_type->pipe_core_type::args_labelsletis_mel_rawexpr=matchexprwithPexp_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_function(_,_,Pfunction_bodyexpr)->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_function(_,_,Pfunction_bodyexpr)->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))])]letvalidate_mel_obj_primitive~locpval_prim=matchpval_primwith|[]->()|primswhenList.for_all(String.equal"")prims->()|_->Location.raise_errorf~loc"[server-reason-react.melange_ppx] [@@mel.obj] requires its external payload to be the empty string"lettransform_external_obj~locpval_namepval_type=letfunction_core_type=Builder.ppat_var~loc:pval_name.loc{loc=pval_name.loc;txt=pval_name.txt}inletpat=Builder.ppat_constraint~loc:pval_type.ptyp_locfunction_core_type(Builder.ptyp_poly~loc:pval_type.ptyp_loc[]pval_type)inletreccollect_argumentsfunction_argsfields=function|{ptyp_desc=Ptyp_arrow(label,core_type,rest);_}->(ifis_mel_ascore_typethenLocation.raise_errorf~loc:core_type.ptyp_loc"[server-reason-react.melange_ppx] [@mel.as] is not supported in native [@@mel.obj] externals yet";matchlabelwith|Nolabel->letpattern=matchcore_type.ptyp_descwith|Ptyp_constr({txt=Lident"unit";_},[])->Builder.ppat_any~loc:core_type.ptyp_loc|_->Location.raise_errorf~loc:core_type.ptyp_loc"[server-reason-react.melange_ppx] [@@mel.obj] externals in native only support labelled \
arguments, optionally labelled arguments, and a final unit argument"incollect_arguments((label,pattern,core_type.ptyp_loc)::function_args)fieldsrest|Labelledname|Optionalname->letident={loc=core_type.ptyp_loc;txt=name}inletpattern=Builder.ppat_var~loc:core_type.ptyp_locidentinletvalue=Builder.pexp_ident~loc:core_type.ptyp_loc{loc=core_type.ptyp_loc;txt=Lidentname}inletpresent_expr=matchlabelwith|Labelled_->Builder.ebool~loc:core_type.ptyp_loctrue|Optional_->option_is_some_expr~loc:core_type.ptyp_locvalue|Nolabel->assertfalseinletfield={method_name=name;js_name=translate_mel_obj_labelname;present_expr;value_expr=value}incollect_arguments((label,pattern,core_type.ptyp_loc)::function_args)(field::fields)rest)|_->(List.revfunction_args,List.revfields)inletfunction_args,fields=collect_arguments[][]pval_typeinletobject_expression=build_registered_js_object_expression~loc~register_name:"register_deferred_abstract"~as_type:(get_return_core_typepval_type)fieldsinletfunction_expression=List.fold_right(fun(label,arg_pat,arg_loc)acc->Builder.pexp_fun~loc:arg_loclabelNonearg_patacc)function_argsobject_expressioninletvb=Builder.value_binding~loc~pat~expr:function_expressioninAst_helper.Str.valueNonrecursive[vb]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/server-reason-react/browser_ppx.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 discard it from the native build, either by moving the external \
in a module only available in native, or annotating it with [@platform js]. More info at \
https://ml-in-barcelona.github.io/server-reason-react/server-reason-react/browser_ppx.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 discard it from the native build, either by moving the external in a module only \
available in native, or annotating it with [@platform js]. More info at \
https://ml-in-barcelona.github.io/server-reason-react/server-reason-react/browser_ppx.html"inBuilder.pexp_constant~loc(Pconst_string(msg,loc,None))letget_function_aritypattern=letrecgoarityparamsbody=matchparamswith|_::rest->go(arity+1)restbody|[]->(matchbodywith|Pfunction_body{pexp_desc=Pexp_function(more_params,_,inner_body);_}->goaritymore_paramsinner_body|_->arity)inmatchpatternwithPexp_function(params,_,body)->go0paramsbody|_->0lettransform_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"|Ptyp_open_->"Open"lettransform_external~module_pathpval_namepval_attributespval_locpval_typepval_prim=letloc=pval_locinmatchpval_type.ptyp_descwith|Ptyp_arrow_->ifhas_attributepval_attributes"mel.obj"then(validate_mel_obj_primitive~locpval_prim;transform_external_obj~locpval_namepval_type)elsetransform_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)]]letvalidate_record_labels~locrecord=List.fold_left(funacc(longident,expression)->matchaccwith|Error_aserror->error|Okacc->(matchlongident.txtwith|Lidentlabel->Ok({method_name=label;js_name=translate_mel_obj_labellabel;present_expr=Builder.ebool~loc:expression.pexp_loctrue;value_expr=expression;}::acc)|Ldot_|Lapply_->Error(Location.error_extensionf~loc"[server-reason-react.melange_ppx] Js.t objects only support labels as keys")))(Ok[])record|>Result.mapList.revclassraise_exception_mapper(module_path:string)=object(_self)inheritAst_traverse.mapassupermethod!expressionexpr=letexpr=super#expressionexprinmatchexpr.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->build_registered_js_object_expression~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|_->exprmethod!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_function(_::_,_,Pfunction_bodyexpression);_};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;pval_prim}->(* Detects [@browser_only] or [@platform js] attributes. When present on an external, we pass it through unchanged so browser_ppx can filter it out in native mode. *)ifhas_browser_ppx_attributepval_attributesthenitemelsetransform_external~module_pathpval_namepval_attributespval_locpval_typepval_prim|_->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;Private.rule]"melange-native-ppx"