Skip to content

Commit ca4ec29

Browse files
Use TextWriter for rendering to use as template engine
1 parent 3b10e28 commit ca4ec29

File tree

1 file changed

+50
-54
lines changed

1 file changed

+50
-54
lines changed

src/Fable.React/Fable.Helpers.ReactServer.fs

Lines changed: 50 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Fable.Helpers.ReactServer
22

33
open System
4-
open System.Text
4+
open System.IO
55
open System.Text.RegularExpressions
66

77
open Fable.Import.React
@@ -14,39 +14,36 @@ let private unitlessCssProps = System.Collections.Generic.HashSet<_>([ "animatio
1414
let 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("&quot")
22-
| '&' -> sb.Append("&amp;")
23-
| ''' -> sb.Append("&#x27;") // modified from escape-html; used to be '&#39'
24-
| '<' -> sb.Append("&lt;")
25-
| '>' -> sb.Append("&gt;")
26-
| c -> sb.Append(c)
27-
|> ignore
21+
| '"' -> sb.Write("&quot")
22+
| '&' -> sb.Write("&amp;")
23+
| ''' -> sb.Write("&#x27;") // modified from escape-html; used to be '&#39'
24+
| '<' -> sb.Write("&lt;")
25+
| '>' -> sb.Write("&gt;")
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

4542
let private slugRegex = Regex("([A-Z])", RegexOptions.Compiled)
4643
let 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

Comments
 (0)