~repos /gromer

#golang#htmx#ssr

git clone https://pyrossh.dev/repos/gromer.git

gromer is a framework and cli to build multipage web apps in golang using htmx and alpinejs.


e0e17961 Peter John

3 years ago
add gsx
{template → gsx}/parser.go RENAMED
@@ -1,4 +1,4 @@
1
- package template
1
+ package gsx
2
2
 
3
3
  import (
4
4
  "github.com/alecthomas/participle/v2"
@@ -12,8 +12,8 @@ type Module struct {
12
12
 
13
13
  type Attribute struct {
14
14
  Pos lexer.Position
15
- Key string `parser:"@\":\"? @Ident ( @\"-\" @Ident )*"`
15
+ Key string `@":"? @Ident ( @"-" @Ident )*`
16
- Value *Literal `parser:"\"=\" @@"`
16
+ Value *Literal `"=" @@`
17
17
  }
18
18
 
19
19
  type KV struct {
@@ -31,11 +31,11 @@ type Literal struct {
31
31
 
32
32
  type Xml struct {
33
33
  Pos lexer.Position
34
- Name string `parser:"\"<\"@Ident"`
34
+ Name string `"<" @Ident`
35
- Attributes []*Attribute `parser:"[ @@ { @@ } ] \">\""`
35
+ Attributes []*Attribute `[ @@ { @@ } ]">"`
36
- Children []*Xml `parser:"{ @@ }"`
36
+ Children []*Xml `{ @@ }`
37
- Value *Literal `parser:"{ @@ }"` // Todo make this match with @String or Literal
37
+ Value *Literal `{ @@ }` // Todo make this match with @String or Literal
38
- Close string `parser:"\"<\"\"/\"@Ident\">\""`
38
+ Close string `("<""/"@Ident">")?`
39
39
  }
40
40
 
41
41
  var xmlParser = participle.MustBuild(&Module{})
{template → gsx}/template.go RENAMED
@@ -1,12 +1,10 @@
1
- package template
1
+ package gsx
2
2
 
3
3
  import (
4
4
  "fmt"
5
5
  "reflect"
6
6
  "runtime"
7
7
  "strings"
8
-
9
- "github.com/alecthomas/repr"
10
8
  )
11
9
 
12
10
  type ComponentFunc struct {
@@ -14,9 +12,23 @@ type ComponentFunc struct {
14
12
  Args []string
15
13
  }
16
14
 
17
- type Html func(string) string
15
+ type Html map[string]interface{}
16
+
17
+ func (h Html) Render(tpl string) string {
18
+ tree := &Module{}
19
+ err := xmlParser.ParseBytes(tpl, []byte(tpl), tree)
20
+ if err != nil {
21
+ panic(err)
22
+ }
23
+ o := ""
24
+ for _, n := range tree.Nodes {
25
+ v := render(n, h)
26
+ o += v
27
+ }
28
+ return o
29
+ }
18
30
 
19
- var htmlTags = []string{"ul", "li", "span", "div"}
31
+ var htmlTags = []string{"a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "b", "base", "basefont", "bb", "bdo", "big", "blockquote", "body", "br /", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "command", "datagrid", "datalist", "dd", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "em", "embed", "eventsource", "fieldset", "figcaption", "figure", "font", "footer", "form", "frame", "frameset", "h1 to <h6>", "head", "header", "hgroup", "hr /", "html", "i", "iframe", "img", "input", "ins", "isindex", "kbd", "keygen", "label", "legend", "li", "link", "map", "mark", "menu", "meta", "meter", "nav", "noframes", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "small", "source", "span", "strike", "strong", "style", "sub", "sup", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"}
20
32
  var compMap = map[string]ComponentFunc{}
21
33
  var funcMap = map[string]interface{}{}
22
34
 
@@ -71,7 +83,7 @@ func subsRef(ctx map[string]interface{}, ref string) interface{} {
71
83
  return nil
72
84
  }
73
85
 
74
- func Render(x *Xml, ctx map[string]interface{}) string {
86
+ func render(x *Xml, ctx map[string]interface{}) string {
75
87
  space, _ := ctx["_space"].(string)
76
88
  s := space + "<" + x.Name
77
89
  if len(x.Attributes) > 0 {
@@ -86,7 +98,6 @@ func Render(x *Xml, ctx map[string]interface{}) string {
86
98
  }
87
99
  }
88
100
  s += param.Key + `="` + strings.Join(values, "") + `"`
89
- repr.Println(s)
90
101
  } else if param.Value.Ref != "" {
91
102
  s += param.Key + `="` + subsRef(ctx, param.Value.Ref).(string) + `"`
92
103
  } else {
@@ -112,13 +123,13 @@ func Render(x *Xml, ctx map[string]interface{}) string {
112
123
  for i := 0; i < v.Len(); i++ {
113
124
  ctx["_space"] = space + " "
114
125
  ctx[ctxName] = v.Index(i).Interface()
115
- s += Render(x.Children[0], ctx) + "\n"
126
+ s += render(x.Children[0], ctx) + "\n"
116
127
  }
117
128
  }
118
129
  } else {
119
130
  if comp, ok := compMap[x.Name]; ok {
120
131
  ctx["_space"] = space + " "
121
- h := HtmlFunc(ctx)
132
+ h := Html(ctx)
122
133
  args := []reflect.Value{reflect.ValueOf(h)}
123
134
  for _, k := range comp.Args {
124
135
  if v, ok := ctx[k]; ok {
@@ -143,29 +154,13 @@ func Render(x *Xml, ctx map[string]interface{}) string {
143
154
  }
144
155
  for _, c := range x.Children {
145
156
  ctx["_space"] = space + " "
146
- s += Render(c, ctx) + "\n"
157
+ s += render(c, ctx) + "\n"
147
158
  }
148
159
  }
149
160
  s += space + "</" + x.Name + ">"
150
161
  return s
151
162
  }
152
163
 
153
- func HtmlFunc(ctx map[string]interface{}) Html {
154
- return func(tpl string) string {
155
- tree := &Module{}
156
- err := xmlParser.ParseBytes("filename", []byte(tpl), tree)
157
- if err != nil {
158
- panic(err)
159
- }
160
- o := ""
161
- for _, n := range tree.Nodes {
162
- v := Render(n, ctx)
163
- o += v
164
- }
165
- return o
166
- }
167
- }
168
-
169
164
  // <script>
170
165
  // document.addEventListener('alpine:init', () => {
171
166
  // Alpine.store('todos', {
{template → gsx}/template_test.go RENAMED
@@ -1,4 +1,4 @@
1
- package template
1
+ package gsx
2
2
 
3
3
  import (
4
4
  "testing"
@@ -12,8 +12,8 @@ type TodoData struct {
12
12
  Completed bool
13
13
  }
14
14
 
15
- func Todo(html Html, todo *TodoData) string {
15
+ func Todo(h Html, todo *TodoData) string {
16
- return html(`
16
+ return h.Render(`
17
17
  <li id={todo.ID} class={{ completed: todo.Completed }}>
18
18
  <div class="view">
19
19
  <span>{todo.Text}</span>
@@ -36,8 +36,8 @@ func TestHtml(t *testing.T) {
36
36
  {ID: "b1a7359c-ebb4-11ec-8ea0-0242ac120002", Text: "My first todo", Completed: true},
37
37
  },
38
38
  }
39
- actual := HtmlFunc(ctx)(`
39
+ actual := Html(ctx).Render(`
40
- <ul id="todo-list" class="relative">
40
+ <ul id="todo-list" class="relative">
41
41
  <For key="todos" itemKey="todo">
42
42
  <Todo key="todo"></Todo>
43
43
  </For>
@@ -45,6 +45,20 @@ func TestHtml(t *testing.T) {
45
45
  </ul>
46
46
  `)
47
47
  expected := `<ul id="todo-list" class="relative">
48
+ <For key="todos" itemKey="todo">
49
+ <Todo key="todo">
50
+ <li id="b1a7359c-ebb4-11ec-8ea0-0242ac120002" class="completed">
51
+ <div class="view">
52
+ <span>
53
+ My first todo
54
+ </span>
55
+ </div>
56
+ </li>
57
+ </Todo>
58
+ </For>
59
+ <span>
60
+ My Website
61
+ </span>
48
62
  </ul>`
49
63
  r.Equal(expected, actual)
50
64
  }