github.com/jgarto/itcv@v0.0.0-20180826224514-4eea09c1aa0d/jsx/jsx.go (about) 1 /* 2 3 Package jsx allows you to render blocks of HTML as myitcv.io/react elements. 4 It is a temporary runtime solution for what will become a compile-time 5 transpilation, much like JSX's relationship with Javascript. 6 7 For more information see https://github.com/myitcv/react/wiki 8 9 */ 10 package jsx 11 12 import ( 13 "fmt" 14 "strings" 15 16 "myitcv.io/react" 17 18 "github.com/russross/blackfriday" 19 20 "golang.org/x/net/html" 21 "golang.org/x/net/html/atom" 22 ) 23 24 var htmlCache = make(map[string][]react.Element) 25 26 // HTML is a runtime JSX-like parsereact. It parses the supplied HTML string into 27 // myitcv.io/react element values. It exists as a stop-gap runtime solution to 28 // full JSX-like support within the GopherJS compilereact. It should only be used 29 // where the argument is a compile-time constant string (TODO enforce this 30 // within reactVet). HTML will panic in case s cannot be parsed as a valid HTML 31 // fragment 32 // 33 func HTML(s string) []react.Element { 34 s = strings.TrimSpace(s) 35 36 if v, ok := htmlCache[s]; ok { 37 return v 38 } 39 40 // a dummy div for parsing the fragment 41 div := &html.Node{ 42 Type: html.ElementNode, 43 Data: "div", 44 DataAtom: atom.Div, 45 } 46 47 elems, err := html.ParseFragment(strings.NewReader(s), div) 48 if err != nil { 49 panic(fmt.Errorf("failed to parse HTML %q: %v", s, err)) 50 } 51 52 var toParse []*html.Node 53 var toWalk []*html.Node 54 55 for _, v := range elems { 56 if v.Type == html.TextNode && strings.TrimSpace(v.Data) == "" { 57 continue 58 } 59 toParse = append(toParse, v) 60 toWalk = append(toWalk, v) 61 } 62 63 var v *html.Node 64 65 for len(toWalk) > 0 { 66 v, toWalk = toWalk[0], toWalk[1:] 67 68 c := v.FirstChild 69 70 for c != nil { 71 if c.Type == html.TextNode && strings.TrimSpace(c.Data) == "" { 72 v.RemoveChild(c) 73 } 74 75 toWalk = append(toWalk, c) 76 c = c.NextSibling 77 } 78 } 79 80 var res []react.Element 81 82 for _, v := range toParse { 83 res = append(res, parse(v)) 84 } 85 86 htmlCache[s] = res 87 88 return res 89 } 90 91 // HTMLElem is a convenience wrapper around HTML where only a single root 92 // element is expected. HTMLElem will panic if more than one HTML element 93 // results 94 // 95 func HTMLElem(s string) react.Element { 96 res := HTML(s) 97 98 if v := len(res); v != 1 { 99 panic(fmt.Errorf("expected single element result from %q; got %v", s, v)) 100 } 101 102 return res[0] 103 } 104 105 // Markdown is a runtime JSX-like parser for markdown. It parses the supplied 106 // markdown string into an HTML string and then hands off to the HTML function. 107 // Like the HTML function, it exists as a stop-gap runtime solution to full 108 // JSX-like support within the GopherJS compilereact. It should only be used where 109 // the argument is a compile-time constant string (TODO enforce this within 110 // reactVet). Markdown will panic in case the markdown string s results in an 111 // invalid HTML string 112 // 113 func Markdown(s string) []react.Element { 114 115 h := blackfriday.MarkdownCommon([]byte(s)) 116 117 return HTML(string(h)) 118 }