11module Fable.Helpers.ReactServer
22
33open System
4- open System.Text
4+ open System.IO
55open System.Text .RegularExpressions
66
77open Fable.Import .React
@@ -14,39 +14,36 @@ let private unitlessCssProps = System.Collections.Generic.HashSet<_>([ "animatio
1414let private voidTags = System.Collections.Generic.HashSet<_>([ " area" ; " base" ; " br" ; " col" ; " embed" ; " hr" ; " img" ; " input" ; " keygen" ; " link" ; " menuitem" ; " meta" ; " param" ; " source" ; " track" ; " wbr" ])
1515
1616// Adapted from https://github.com/facebook/react/blob/37e4329bc81def4695211d6e3795a654ef4d84f5/packages/react-dom/src/server/escapeTextForBrowser.js#L49
17- let escapeHtml ( sb : StringBuilder ) ( str : string ) =
17+ let escapeHtml ( sb : TextWriter ) ( str : string ) =
1818 if isNull str then () else
1919 for c in str.ToCharArray() do
2020 match c with
21- | '"' -> sb.Append( " "" )
22- | '&' -> sb.Append( " &" )
23- | ''' -> sb.Append( " '" ) // modified from escape-html; used to be '''
24- | '<' -> sb.Append( " <" )
25- | '>' -> sb.Append( " >" )
26- | c -> sb.Append( c)
27- |> ignore
21+ | '"' -> sb.Write( " "" )
22+ | '&' -> sb.Write( " &" )
23+ | ''' -> sb.Write( " '" ) // modified from escape-html; used to be '''
24+ | '<' -> sb.Write( " <" )
25+ | '>' -> sb.Write( " >" )
26+ | c -> sb.Write( c)
2827
29- let inline private addUnit ( html : StringBuilder ) ( key : string ) ( value : string ) =
30- html.Append value |> ignore
28+ let inline private addUnit ( html : TextWriter ) ( key : string ) ( value : string ) =
29+ html.Write value
3130 if not ( unitlessCssProps.Contains key) then
32- html.Append " px" |> ignore
31+ html.Write " px"
3332
34- let private cssProp ( html : StringBuilder ) ( key : string ) ( value : obj ) =
35- html.Append key |> ignore
36- html.Append ':' |> ignore
33+ let private cssProp ( html : TextWriter ) ( key : string ) ( value : obj ) =
34+ html.Write key
35+ html.Write ':'
3736
3837 match value with
3938 | :? int as v -> addUnit html key ( string v)
4039 | :? float as v -> addUnit html key ( string v)
41- | _ -> escapeHtml html ( value.ToString()) |> ignore
42-
43- html.Append ';' |> ignore
40+ | _ -> escapeHtml html ( value.ToString())
4441
4542let private slugRegex = Regex( " ([A-Z])" , RegexOptions.Compiled)
4643let inline private slugKey key =
4744 slugRegex.Replace( string key, " -$1" ) .ToLower()
4845
49- let private renderCssProp ( html : StringBuilder ) ( prop : CSSProp ) =
46+ let private renderCssProp ( html : TextWriter ) ( prop : CSSProp ) =
5047 match prop with
5148 | AlignContent v -> cssProp html " align-content" v
5249 | AlignItems v -> cssProp html " align-items" v
@@ -457,17 +454,17 @@ let private renderCssProp (html:StringBuilder) (prop: CSSProp) =
457454 | CSSProp.Custom ( key, value) -> cssProp html ( slugKey key) value
458455 #endif
459456
460- let inline boolAttr ( html : StringBuilder ) ( key : string ) ( value : bool ) = if value then html.Append key |> ignore
457+ let inline boolAttr ( html : TextWriter ) ( key : string ) ( value : bool ) = if value then html.Write key
461458
462- let inline strAttr ( html : StringBuilder ) ( key : string ) ( value : string ) =
463- html.Append key |> ignore
464- html.Append " =\" " |> ignore
459+ let inline strAttr ( html : TextWriter ) ( key : string ) ( value : string ) =
460+ html.Write key
461+ html.Write " =\" "
465462 escapeHtml html value
466- html.Append '"' |> ignore
463+ html.Write '"'
467464
468- let inline objAttr ( html : StringBuilder ) ( key : string ) ( value : obj ) = strAttr html key ( string value)
465+ let inline objAttr ( html : TextWriter ) ( key : string ) ( value : obj ) = strAttr html key ( string value)
469466
470- let private renderHtmlAttr ( html : StringBuilder ) ( attr : HTMLAttr ) =
467+ let private renderHtmlAttr ( html : TextWriter ) ( attr : HTMLAttr ) =
471468 match attr with
472469 | DefaultChecked v | Checked v -> boolAttr html " checked" v
473470 | DefaultValue v | Value v -> strAttr html " value" v
@@ -614,20 +611,20 @@ let private renderHtmlAttr (html:StringBuilder) (attr: HTMLAttr) =
614611 | Unselectable v -> boolAttr html " unselectable" v
615612 #if ! FABLE_ COMPILER
616613 | Style cssList ->
617- html.Append " style" |> ignore
618- html.Append " =\" " |> ignore
614+ html.Write " style"
615+ html.Write " =\" "
619616
620- for cssProp in cssList do
621- renderCssProp html cssProp
622- if not ( List.isEmpty cssList ) then
623- html.Remove ( html.Length - 1 , 1 ) |> ignore
624- html.Append '"' |> ignore
617+ cssList
618+ |> List.iteri ( fun i cssProp ->
619+ if i > 0 then html.Write ( ';' )
620+ renderCssProp html cssProp )
621+ html.Write '"'
625622
626623 | HTMLAttr.Custom ( key, value) -> strAttr html ( key.ToLower()) ( string value)
627624 | Data ( key, value) -> strAttr html ( " data-" + key) ( string value)
628625 #endif
629626
630- let private renderSVGAttr ( html : StringBuilder ) ( attr : SVGAttr ) =
627+ let private renderSVGAttr ( html : TextWriter ) ( attr : SVGAttr ) =
631628 match attr with
632629 | SVGAttr.ClipPath v -> objAttr html " clip-path" v
633630 | SVGAttr.Cx v -> objAttr html " cx" v
@@ -690,7 +687,7 @@ let private renderSVGAttr (html:StringBuilder) (attr: SVGAttr) =
690687 | SVGAttr.Custom ( key, value) -> objAttr html ( slugKey key) value
691688 #endif
692689
693- let private renderAttrs ( html : StringBuilder ) ( attrs : IProp seq ) tag =
690+ let private renderAttrs ( html : TextWriter ) ( attrs : IProp seq ) tag =
694691 let mutable childHtml = None
695692 for attr in attrs do
696693 match attr with
@@ -705,10 +702,10 @@ let private renderAttrs (html:StringBuilder) (attrs: IProp seq) tag =
705702 | " textarea" , DefaultValue v ->
706703 childHtml <- Some v
707704 | _, _ ->
708- html.Append ' ' |> ignore
705+ html.Write ' '
709706 renderHtmlAttr html attr
710707 | :? SVGAttr as attr ->
711- html.Append ' ' |> ignore
708+ html.Write ' '
712709 renderSVGAttr html attr
713710 | _ -> ()
714711
@@ -729,38 +726,37 @@ let inline private castHTMLNode (htmlNode: ReactElement): HTMLNode =
729726 else
730727 htmlNode :?> HTMLNode
731728
732- let renderToString ( htmlNode : ReactElement ): string =
733- let htmlNode = addReactMark ( castHTMLNode htmlNode)
734- let html = StringBuilder()
735-
736- let rec render ( htmlNode : HTMLNode ) : unit =
729+ let rec writeTo ( html : TextWriter ) ( htmlNode : HTMLNode ) : unit =
737730 match htmlNode with
738731 | HTMLNode.Text str -> escapeHtml html str
739- | HTMLNode.RawText str -> html.Append str |> ignore
732+ | HTMLNode.RawText str -> html.Write str
740733 | HTMLNode.Node ( tag, attrs, children) ->
741- html.Append '<' |> ignore
742- html.Append tag |> ignore
734+ html.Write '<'
735+ html.Write tag
743736
744737 let child = renderAttrs html attrs tag
745738
746739 if voidTags.Contains tag then
747- html.Append " />" |> ignore
740+ html.Write " />"
748741 else
749- html.Append '>' |> ignore
742+ html.Write '>'
750743
751744 match child with
752- | Some c -> html.Append c |> ignore
745+ | Some c -> html.Write c
753746 | None ->
754747 for child in children do
755- render ( castHTMLNode child)
748+ writeTo html ( castHTMLNode child)
756749
757- html.Append " </" |> ignore
758- html.Append tag |> ignore
759- html.Append '>' |> ignore
750+ html.Write " </"
751+ html.Write tag
752+ html.Write '>'
760753 | HTMLNode.List nodes ->
761754 for node in nodes do
762- render ( castHTMLNode node)
755+ writeTo html ( castHTMLNode node)
763756 | HTMLNode.Empty -> ()
764757
765- render htmlNode
758+ let renderToString ( htmlNode : ReactElement ): string =
759+ let htmlNode = addReactMark ( castHTMLNode htmlNode)
760+ use html = new StringWriter()
761+ htmlNode |> writeTo html
766762 html.ToString()
0 commit comments