123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418(* Rewrites [ReactDOM.Style.make ~foo:a ~bar:b ()] at compile time into a
direct list of [(kebab, camel, value)] tuples, avoiding the 347-optional-arg
calling convention overhead (~1460 words/call on stock OCaml).
The PPX only rewrites calls where:
- the function is literally [ReactDOM.Style.make] (or [Style.make] in the
ReactDOM module namespace), and
- all arguments are labelled (not optional), and
- the final arg is [()].
Calls that don't fit fall through to the runtime function. *)openPpxlibopenAst_builder.Default(* CamelCase -> kebab-case mapping, kept in sync with ReactDOMStyle.ml. *)letmapping=[("azimuth","azimuth");("background","background");("backgroundAttachment","background-attachment");("backgroundColor","background-color");("backgroundImage","background-image");("backgroundPosition","background-position");("backgroundRepeat","background-repeat");("border","border");("borderCollapse","border-collapse");("borderColor","border-color");("borderSpacing","border-spacing");("borderStyle","border-style");("borderTop","border-top");("borderRight","border-right");("borderBottom","border-bottom");("borderLeft","border-left");("borderTopColor","border-top-color");("borderRightColor","border-right-color");("borderBottomColor","border-bottom-color");("borderLeftColor","border-left-color");("borderTopStyle","border-top-style");("borderRightStyle","border-right-style");("borderBottomStyle","border-bottom-style");("borderLeftStyle","border-left-style");("borderTopWidth","border-top-width");("borderRightWidth","border-right-width");("borderBottomWidth","border-bottom-width");("borderLeftWidth","border-left-width");("borderWidth","border-width");("bottom","bottom");("captionSide","caption-side");("clear","clear");("color","color");("content","content");("counterIncrement","counter-increment");("counterReset","counter-reset");("cue","cue");("cueAfter","cue-after");("cueBefore","cue-before");("cursor","cursor");("direction","direction");("display","display");("elevation","elevation");("emptyCells","empty-cells");("float","float");("font","font");("fontFamily","font-family");("fontSize","font-size");("fontSizeAdjust","font-size-adjust");("fontStretch","font-stretch");("fontStyle","font-style");("fontVariant","font-variant");("fontWeight","font-weight");("height","height");("left","left");("letterSpacing","letter-spacing");("lineHeight","line-height");("listStyle","list-style");("listStyleImage","list-style-image");("listStylePosition","list-style-position");("listStyleType","list-style-type");("margin","margin");("marginTop","margin-top");("marginRight","margin-right");("marginBottom","margin-bottom");("marginLeft","margin-left");("markerOffset","marker-offset");("marks","marks");("maxHeight","max-height");("maxWidth","max-width");("minHeight","min-height");("minWidth","min-width");("orphans","orphans");("outline","outline");("outlineColor","outline-color");("outlineStyle","outline-style");("outlineWidth","outline-width");("overflow","overflow");("overflowX","overflow-x");("overflowY","overflow-y");("padding","padding");("paddingTop","padding-top");("paddingRight","padding-right");("paddingBottom","padding-bottom");("paddingLeft","padding-left");("page","page");("pageBreakAfter","page-break-after");("pageBreakBefore","page-break-before");("pageBreakInside","page-break-inside");("pause","pause");("pauseAfter","pause-after");("pauseBefore","pause-before");("pitch","pitch");("pitchRange","pitch-range");("playDuring","play-during");("position","position");("quotes","quotes");("richness","richness");("right","right");("size","size");("speak","speak");("speakHeader","speak-header");("speakNumeral","speak-numeral");("speakPunctuation","speak-punctuation");("speechRate","speech-rate");("stress","stress");("tableLayout","table-layout");("textAlign","text-align");("textDecoration","text-decoration");("textIndent","text-indent");("textShadow","text-shadow");("textTransform","text-transform");("top","top");("unicodeBidi","unicode-bidi");("verticalAlign","vertical-align");("visibility","visibility");("voiceFamily","voice-family");("volume","volume");("whiteSpace","white-space");("widows","widows");("width","width");("wordSpacing","word-spacing");("zIndex","z-index");("opacity","opacity");("backgroundOrigin","background-origin");("backgroundSize","background-size");("backgroundClip","background-clip");("borderRadius","border-radius");("borderTopLeftRadius","border-top-left-radius");("borderTopRightRadius","border-top-right-radius");("borderBottomLeftRadius","border-bottom-left-radius");("borderBottomRightRadius","border-bottom-right-radius");("borderImage","border-image");("borderImageSource","border-image-source");("borderImageSlice","border-image-slice");("borderImageWidth","border-image-width");("borderImageOutset","border-image-outset");("borderImageRepeat","border-image-repeat");("boxShadow","box-shadow");("columns","columns");("columnCount","column-count");("columnFill","column-fill");("columnGap","column-gap");("columnRule","column-rule");("columnRuleColor","column-rule-color");("columnRuleStyle","column-rule-style");("columnRuleWidth","column-rule-width");("columnSpan","column-span");("columnWidth","column-width");("breakAfter","break-after");("breakBefore","break-before");("breakInside","break-inside");("rest","rest");("restAfter","rest-after");("restBefore","rest-before");("speakAs","speak-as");("voiceBalance","voice-balance");("voiceDuration","voice-duration");("voicePitch","voice-pitch");("voiceRange","voice-range");("voiceRate","voice-rate");("voiceStress","voice-stress");("voiceVolume","voice-volume");("objectFit","object-fit");("objectPosition","object-position");("imageResolution","image-resolution");("imageOrientation","image-orientation");("alignContent","align-content");("alignItems","align-items");("alignSelf","align-self");("flex","flex");("flexBasis","flex-basis");("flexDirection","flex-direction");("flexFlow","flex-flow");("flexGrow","flex-grow");("flexShrink","flex-shrink");("flexWrap","flex-wrap");("justifyContent","justify-content");("order","order");("textDecorationColor","text-decoration-color");("textDecorationLine","text-decoration-line");("textDecorationSkip","text-decoration-skip");("textDecorationStyle","text-decoration-style");("textEmphasis","text-emphasis");("textEmphasisColor","text-emphasis-color");("textEmphasisPosition","text-emphasis-position");("textEmphasisStyle","text-emphasis-style");("textUnderlinePosition","text-underline-position");("fontFeatureSettings","font-feature-settings");("fontKerning","font-kerning");("fontLanguageOverride","font-language-override");("fontSynthesis","font-synthesis");("forntVariantAlternates","fornt-variant-alternates");("fontVariantCaps","font-variant-caps");("fontVariantEastAsian","font-variant-east-asian");("fontVariantLigatures","font-variant-ligatures");("fontVariantNumeric","font-variant-numeric");("fontVariantPosition","font-variant-position");("all","all");("textCombineUpright","text-combine-upright");("textOrientation","text-orientation");("writingMode","writing-mode");("shapeImageThreshold","shape-image-threshold");("shapeMargin","shape-margin");("shapeOutside","shape-outside");("mask","mask");("maskBorder","mask-border");("maskBorderMode","mask-border-mode");("maskBorderOutset","mask-border-outset");("maskBorderRepeat","mask-border-repeat");("maskBorderSlice","mask-border-slice");("maskBorderSource","mask-border-source");("maskBorderWidth","mask-border-width");("maskClip","mask-clip");("maskComposite","mask-composite");("maskImage","mask-image");("maskMode","mask-mode");("maskOrigin","mask-origin");("maskPosition","mask-position");("maskRepeat","mask-repeat");("maskSize","mask-size");("maskType","mask-type");("backgroundBlendMode","background-blend-mode");("isolation","isolation");("mixBlendMode","mix-blend-mode");("boxDecorationBreak","box-decoration-break");("boxSizing","box-sizing");("caretColor","caret-color");("navDown","nav-down");("navLeft","nav-left");("navRight","nav-right");("navUp","nav-up");("outlineOffset","outline-offset");("resize","resize");("textOverflow","text-overflow");("grid","grid");("gridArea","grid-area");("gridAutoColumns","grid-auto-columns");("gridAutoFlow","grid-auto-flow");("gridAutoRows","grid-auto-rows");("gridColumn","grid-column");("gridColumnEnd","grid-column-end");("gridColumnGap","grid-column-gap");("gridColumnStart","grid-column-start");("gridGap","grid-gap");("gridRow","grid-row");("gridRowEnd","grid-row-end");("gridRowGap","grid-row-gap");("gridRowStart","grid-row-start");("gridTemplate","grid-template");("gridTemplateAreas","grid-template-areas");("gridTemplateColumns","grid-template-columns");("gridTemplateRows","grid-template-rows");("willChange","will-change");("hangingPunctuation","hanging-punctuation");("hyphens","hyphens");("lineBreak","line-break");("overflowWrap","overflow-wrap");("tabSize","tab-size");("textAlignLast","text-align-last");("textJustify","text-justify");("wordBreak","word-break");("wordWrap","word-wrap");("animation","animation");("animationDelay","animation-delay");("animationDirection","animation-direction");("animationDuration","animation-duration");("animationFillMode","animation-fill-mode");("animationIterationCount","animation-iteration-count");("animationName","animation-name");("animationPlayState","animation-play-state");("animationTimingFunction","animation-timing-function");("transition","transition");("transitionDelay","transition-delay");("transitionDuration","transition-duration");("transitionProperty","transition-property");("transitionTimingFunction","transition-timing-function");("backfaceVisibility","backface-visibility");("perspective","perspective");("perspectiveOrigin","perspective-origin");("transform","transform");("transformOrigin","transform-origin");("transformStyle","transform-style");("justifyItems","justify-items");("justifySelf","justify-self");("placeContent","place-content");("placeItems","place-items");("placeSelf","place-self");("appearance","appearance");("caret","caret");("caretAnimation","caret-animation");("caretShape","caret-shape");("userSelect","user-select");("maxLines","max-lines");("marqueeDirection","marquee-direction");("marqueeLoop","marquee-loop");("marqueeSpeed","marquee-speed");("marqueeStyle","marquee-style");("overflowStyle","overflow-style");("rotation","rotation");("rotationPoint","rotation-point");("alignmentBaseline","alignment-baseline");("baselineShift","baseline-shift");("clip","clip");("clipPath","clip-path");("clipRule","clip-rule");("colorInterpolation","color-interpolation");("colorInterpolationFilters","color-interpolation-filters");("colorProfile","color-profile");("colorRendering","color-rendering");("dominantBaseline","dominant-baseline");("fill","fill");("fillOpacity","fill-opacity");("fillRule","fill-rule");("filter","filter");("floodColor","flood-color");("floodOpacity","flood-opacity");("glyphOrientationHorizontal","glyph-orientation-horizontal");("glyphOrientationVertical","glyph-orientation-vertical");("imageRendering","image-rendering");("kerning","kerning");("lightingColor","lighting-color");("markerEnd","marker-end");("markerMid","marker-mid");("markerStart","marker-start");("pointerEvents","pointer-events");("shapeRendering","shape-rendering");("stopColor","stop-color");("stopOpacity","stop-opacity");("stroke","stroke");("strokeDasharray","stroke-dasharray");("strokeDashoffset","stroke-dashoffset");("strokeLinecap","stroke-linecap");("strokeLinejoin","stroke-linejoin");("strokeMiterlimit","stroke-miterlimit");("strokeOpacity","stroke-opacity");("strokeWidth","stroke-width");("textAnchor","text-anchor");("textRendering","text-rendering");("rubyAlign","ruby-align");("rubyMerge","ruby-merge");("rubyPosition","ruby-position");](* Assoc of camel -> (kebab, signature_index). *)letindexed_mapping=List.mapi(funi(camel,kebab)->(camel,(kebab,i)))mappingletfind_camelcamel=List.assoc_optcamelindexed_mapping(* Returns true if [longident] is [ReactDOM.Style.make] (possibly prefixed). *)letis_style_make_ident=function|Ldot(Ldot(Lident"ReactDOM","Style"),"make")->true|Ldot(Lident"Style","make")->true(* used inside ReactDOM module *)|_->false(* Attempt to rewrite a call [ReactDOM.Style.make ~foo:a ~bar:b ()] to a direct
list expression. Returns [Some new_expr] on success, [None] otherwise.
The final arg must be [()] (Nolabel, unit), and there must be no optional
args and no unknown label names.
The output list must match the runtime order produced by [Style.make]:
signature order (the body prepends in signature order and reverses at the
end). Signature order is also the JS object key order melange produces for
reason-react's [ReactDOM.Style.make], which react-dom preserves in both
inline style output and the Flight payload. *)lettry_rewrite_call~loc:_args=letreccollectacc=function|[]->None(* missing the final unit *)|[(Nolabel,{pexp_desc=Pexp_construct({txt=Lident"()";_},None);_})]->Someacc|(Labelledname,expr)::rest->(matchfind_camelnamewith|Some(kebab,idx)->collect((idx,name,kebab,expr)::acc)rest|None->(* unknown style name — fall back *)None)|(Optional_,_)::_->None(* optional arg — fall back *)|(Nolabel,_)::_->None(* unexpected positional — fall back *)inmatchcollect[]argswith|None->None|Someentries->(* Sort by signature index ascending, matching the runtime [make]'s
output order. *)letsorted=List.sort(fun(i,_,_,_)(j,_,_,_)->Int.compareij)entriesinletloc=Location.noneinletlist_expr=List.fold_right(fun(_,camel,kebab,expr)acc->letloc=expr.pexp_locin[%expr([%eestring~lockebab],[%eestring~loccamel],[%eexpr])::[%eacc]])sorted[%expr([]:(string*string*string)list)]inSome[%expr([%elist_expr]:ReactDOM.Style.t)](* Top-level rewriter: looks at any [Pexp_apply] and rewrites eligible ones. *)letrewrite_expressionexpr=matchexpr.pexp_descwith|Pexp_apply({pexp_desc=Pexp_ident{txt;_};_},args)whenis_style_make_identtxt->(matchtry_rewrite_call~loc:expr.pexp_locargswithSomenew_expr->new_expr|None->expr)|_->expr