~repos /gromer
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.
58731858
—
Peter John 3 years ago
implement for
- gsx/gsx.go +54 -47
- gsx/gsx_test.go +180 -64
- gsx/parser.go +87 -8
- gsx/parser_test.go +27 -2
gsx/gsx.go
CHANGED
|
@@ -26,6 +26,7 @@ type (
|
|
|
26
26
|
MS map[string]string
|
|
27
27
|
Arr []interface{}
|
|
28
28
|
ComponentFunc struct {
|
|
29
|
+
Name string
|
|
29
30
|
Func interface{}
|
|
30
31
|
Args []string
|
|
31
32
|
Classes M
|
|
@@ -41,6 +42,7 @@ type (
|
|
|
41
42
|
func RegisterComponent(f interface{}, classes M, args ...string) {
|
|
42
43
|
name := getFunctionName(f)
|
|
43
44
|
compMap[name] = ComponentFunc{
|
|
45
|
+
Name: name,
|
|
44
46
|
Func: f,
|
|
45
47
|
Args: args,
|
|
46
48
|
Classes: classes,
|
|
@@ -64,19 +66,36 @@ func (comp ComponentFunc) Render(c *Context, tag *Tag) []*Tag {
|
|
|
64
66
|
if v, ok := c.data[arg]; ok {
|
|
65
67
|
args = append(args, reflect.ValueOf(v))
|
|
66
68
|
} else {
|
|
69
|
+
t := funcType.In(i + 1)
|
|
67
70
|
v, _ := lo.Find(tag.Attributes, func(a *Attribute) bool {
|
|
68
71
|
return a.Key == arg
|
|
69
72
|
})
|
|
73
|
+
var data interface{}
|
|
74
|
+
if v.Value.Ref != nil {
|
|
75
|
+
data = c.data[*v.Value.Ref]
|
|
70
|
-
|
|
76
|
+
} else if v.Value.Str != nil {
|
|
77
|
+
data = *v.Value.Str
|
|
78
|
+
}
|
|
71
79
|
switch t.Kind() {
|
|
72
80
|
case reflect.Int:
|
|
81
|
+
s, ok := data.(string)
|
|
82
|
+
if !ok {
|
|
83
|
+
panic(fmt.Errorf("expected component %s: prop %s to be of type string but got %+v ", comp.Name, arg, data))
|
|
84
|
+
}
|
|
73
|
-
value, _ := strconv.Atoi(
|
|
85
|
+
value, _ := strconv.Atoi(s)
|
|
86
|
+
c.Set(arg, value)
|
|
74
87
|
args = append(args, reflect.ValueOf(value))
|
|
75
88
|
case reflect.Bool:
|
|
89
|
+
s, ok := data.(string)
|
|
90
|
+
if !ok {
|
|
91
|
+
panic(fmt.Errorf("expected component %s: prop %s to be of type string but got %+v ", comp.Name, arg, data))
|
|
92
|
+
}
|
|
76
|
-
value, _ := strconv.ParseBool(
|
|
93
|
+
value, _ := strconv.ParseBool(s)
|
|
94
|
+
c.Set(arg, value)
|
|
77
95
|
args = append(args, reflect.ValueOf(value))
|
|
78
96
|
default:
|
|
97
|
+
c.Set(arg, data)
|
|
79
|
-
args = append(args, reflect.ValueOf(
|
|
98
|
+
args = append(args, reflect.ValueOf(data))
|
|
80
99
|
}
|
|
81
100
|
}
|
|
82
101
|
}
|
|
@@ -219,39 +238,40 @@ func populateTag(c *Context, tag *Tag) {
|
|
|
219
238
|
sValue := fmt.Sprintf("%+v", value)
|
|
220
239
|
tag.Text.Str = &sValue
|
|
221
240
|
}
|
|
241
|
+
} else if loop := tag.Text.For; loop != nil {
|
|
242
|
+
tag.Name = "fragment"
|
|
243
|
+
data := c.data[loop.Reference]
|
|
244
|
+
statement := loop.Statements[0].ReturnStatement
|
|
245
|
+
switch reflect.TypeOf(data).Kind() {
|
|
246
|
+
case reflect.Slice:
|
|
247
|
+
v := reflect.ValueOf(data)
|
|
248
|
+
for i := 0; i < v.Len(); i++ {
|
|
249
|
+
compContext := c.Clone(tag.Name)
|
|
250
|
+
compContext.data[loop.Index] = i
|
|
251
|
+
compContext.data[loop.Key] = v.Index(i).Interface()
|
|
252
|
+
newTags := populate(compContext, cloneTags(statement.Tags))
|
|
253
|
+
for _, t := range newTags {
|
|
254
|
+
tag.Children = append(tag.Children, t)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
222
258
|
}
|
|
223
259
|
} else {
|
|
260
|
+
if comp, ok := compMap[tag.Name]; ok {
|
|
261
|
+
if tag.SelfClosing {
|
|
262
|
+
tag.SelfClosing = false
|
|
263
|
+
}
|
|
264
|
+
compContext := c.Clone(tag.Name)
|
|
265
|
+
nodes := comp.Render(compContext, tag)
|
|
266
|
+
populate(compContext, tag.Children)
|
|
267
|
+
compContext.Set("children", tag.Children)
|
|
268
|
+
tag.Children = nodes
|
|
269
|
+
populate(compContext, tag.Children)
|
|
270
|
+
} else {
|
|
271
|
+
populate(c, tag.Children)
|
|
272
|
+
}
|
|
224
273
|
for _, a := range tag.Attributes {
|
|
225
|
-
if a.Key == "x-for" {
|
|
226
|
-
arr := strings.Split(*a.Value.Str, " in ")
|
|
227
|
-
// ctxItemKey := arr[0]
|
|
228
|
-
ctxKey := arr[1]
|
|
229
|
-
data := c.data[ctxKey]
|
|
230
|
-
switch reflect.TypeOf(data).Kind() {
|
|
231
|
-
case reflect.Slice:
|
|
232
|
-
v := reflect.ValueOf(data)
|
|
233
|
-
for i := 0; i < v.Len(); i++ {
|
|
234
|
-
// ctx["_space"] = space + " "
|
|
235
|
-
// ctx[ctxName] = v.Index(i).Interface()
|
|
236
|
-
// s += render(x.Children[0], ctx) + "\n"
|
|
237
|
-
|
|
238
|
-
// compCtx := &Context{
|
|
239
|
-
// Context: c.Context,
|
|
240
|
-
// data: map[string]interface{}{
|
|
241
|
-
// ctxItemKey: v.Index(i).Interface(),
|
|
242
|
-
// },
|
|
243
|
-
// }
|
|
244
|
-
// tag.Children
|
|
245
|
-
// if comp, ok := compMap[itemChild.Data]; ok {
|
|
246
|
-
// newNode := populateComponent(compCtx, comp, itemChild, false)
|
|
247
|
-
// n.AppendChild(newNode)
|
|
248
|
-
// } else {
|
|
249
|
-
// n.AppendChild(itemChild)
|
|
250
|
-
// populate(compCtx, itemChild)
|
|
251
|
-
// }
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
274
|
+
if a.Value.Str != nil {
|
|
255
275
|
if strings.Contains(*a.Value.Str, "{") {
|
|
256
276
|
subs := substituteString(c, removeQuotes(*a.Value.Str))
|
|
257
277
|
a.Value = &Literal{Str: &subs}
|
|
@@ -273,18 +293,5 @@ func populateTag(c *Context, tag *Tag) {
|
|
|
273
293
|
a.Value.Str = &result
|
|
274
294
|
}
|
|
275
295
|
}
|
|
276
|
-
if comp, ok := compMap[tag.Name]; ok {
|
|
277
|
-
if tag.SelfClosing {
|
|
278
|
-
tag.SelfClosing = false
|
|
279
|
-
}
|
|
280
|
-
compContext := c.Clone(tag.Name)
|
|
281
|
-
nodes := comp.Render(compContext, tag)
|
|
282
|
-
populate(compContext, tag.Children)
|
|
283
|
-
compContext.Set("children", tag.Children)
|
|
284
|
-
tag.Children = nodes
|
|
285
|
-
populate(compContext, tag.Children)
|
|
286
|
-
} else {
|
|
287
|
-
populate(c, tag.Children)
|
|
288
|
-
}
|
|
289
296
|
}
|
|
290
297
|
}
|
gsx/gsx_test.go
CHANGED
|
@@ -49,6 +49,10 @@ func WebsiteName() string {
|
|
|
49
49
|
return "My Website"
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
func trimLeft(s string) string {
|
|
53
|
+
return strings.TrimLeft(s, "\n")
|
|
54
|
+
}
|
|
55
|
+
|
|
52
56
|
func TestComponent(t *testing.T) {
|
|
53
57
|
r := require.New(t)
|
|
54
58
|
RegisterComponent(Todo, nil, "todo")
|
|
@@ -66,7 +70,7 @@ func TestComponent(t *testing.T) {
|
|
|
66
70
|
<Todo />
|
|
67
71
|
`)
|
|
68
72
|
actual := renderString(nodes)
|
|
69
|
-
expected :=
|
|
73
|
+
expected := trimLeft(`
|
|
70
74
|
<Todo>
|
|
71
75
|
<li id="todo-4" class="completed">
|
|
72
76
|
<div class="upper">
|
|
@@ -115,7 +119,7 @@ func TestComponent(t *testing.T) {
|
|
|
115
119
|
</div>
|
|
116
120
|
</li>
|
|
117
121
|
</Todo>
|
|
118
|
-
`
|
|
122
|
+
`)
|
|
119
123
|
r.Equal(expected, actual)
|
|
120
124
|
}
|
|
121
125
|
|
|
@@ -134,7 +138,7 @@ func TestMultipleComponent(t *testing.T) {
|
|
|
134
138
|
<TodoCount />
|
|
135
139
|
`)
|
|
136
140
|
actual := renderString(nodes)
|
|
137
|
-
expected :=
|
|
141
|
+
expected := trimLeft(`
|
|
138
142
|
<Todo>
|
|
139
143
|
<li id="todo-4" class="completed">
|
|
140
144
|
<div class="upper">
|
|
@@ -164,70 +168,182 @@ func TestMultipleComponent(t *testing.T) {
|
|
|
164
168
|
item left
|
|
165
169
|
</span>
|
|
166
170
|
</TodoCount>
|
|
167
|
-
`
|
|
171
|
+
`)
|
|
168
172
|
r.Equal(expected, actual)
|
|
169
173
|
}
|
|
170
174
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
/
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
/
|
|
175
|
+
func TestFor(t *testing.T) {
|
|
176
|
+
r := require.New(t)
|
|
177
|
+
RegisterComponent(Todo, nil, "todo")
|
|
178
|
+
RegisterFunc(WebsiteName)
|
|
179
|
+
h := Context{
|
|
180
|
+
data: map[string]interface{}{
|
|
181
|
+
"todos": []*TodoData{
|
|
182
|
+
{ID: "1", Text: "My first todo", Completed: true},
|
|
183
|
+
{ID: "2", Text: "My second todo", Completed: false},
|
|
184
|
+
{ID: "3", Text: "My third todo", Completed: false},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
nodes := h.Render(`
|
|
189
|
+
<ul class="relative">
|
|
190
|
+
for i, v := range todos {
|
|
191
|
+
return (
|
|
192
|
+
<li>
|
|
193
|
+
<span>{v.Text}</span>
|
|
194
|
+
<span>{v.Completed}</span>
|
|
195
|
+
<a>"link to" {v.ID}</a>
|
|
196
|
+
</li>
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
</ul>
|
|
200
|
+
<ol>
|
|
201
|
+
for i, v := range todos {
|
|
202
|
+
return (
|
|
203
|
+
<Todo todo={v}>
|
|
204
|
+
<div class="todo-panel">
|
|
205
|
+
<span>{v.Text}</span>
|
|
206
|
+
<span>{v.Completed}</span>
|
|
207
|
+
</div>
|
|
208
|
+
</Todo>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
</ol>
|
|
212
|
+
`)
|
|
213
|
+
actual := renderString(nodes)
|
|
214
|
+
expected := trimLeft(`
|
|
215
|
+
<ul class="relative">
|
|
216
|
+
<li>
|
|
217
|
+
<span>
|
|
218
|
+
My first todo
|
|
219
|
+
</span>
|
|
220
|
+
<span>
|
|
221
|
+
true
|
|
222
|
+
</span>
|
|
223
|
+
<a>
|
|
224
|
+
link to
|
|
225
|
+
1
|
|
226
|
+
</a>
|
|
227
|
+
</li>
|
|
228
|
+
<li>
|
|
229
|
+
<span>
|
|
230
|
+
My second todo
|
|
231
|
+
</span>
|
|
232
|
+
<span>
|
|
233
|
+
false
|
|
234
|
+
</span>
|
|
235
|
+
<a>
|
|
236
|
+
link to
|
|
237
|
+
2
|
|
238
|
+
</a>
|
|
239
|
+
</li>
|
|
240
|
+
<li>
|
|
241
|
+
<span>
|
|
242
|
+
My third todo
|
|
243
|
+
</span>
|
|
244
|
+
<span>
|
|
245
|
+
false
|
|
246
|
+
</span>
|
|
247
|
+
<a>
|
|
248
|
+
link to
|
|
249
|
+
3
|
|
250
|
+
</a>
|
|
251
|
+
</li>
|
|
252
|
+
|
|
253
|
+
</ul>
|
|
254
|
+
<ol>
|
|
255
|
+
<Todo todo="v">
|
|
256
|
+
<li id="todo-1" class="completed">
|
|
257
|
+
<div class="upper">
|
|
258
|
+
<span>
|
|
259
|
+
My first todo
|
|
260
|
+
</span>
|
|
261
|
+
<span>
|
|
262
|
+
My first todo
|
|
263
|
+
</span>
|
|
264
|
+
</div>
|
|
265
|
+
<div class="todo-panel">
|
|
266
|
+
<span>
|
|
267
|
+
My first todo
|
|
268
|
+
</span>
|
|
269
|
+
<span>
|
|
270
|
+
true
|
|
271
|
+
</span>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<div class="bottom">
|
|
275
|
+
<span>
|
|
276
|
+
true
|
|
277
|
+
</span>
|
|
278
|
+
<span>
|
|
279
|
+
true
|
|
280
|
+
</span>
|
|
281
|
+
</div>
|
|
282
|
+
</li>
|
|
283
|
+
</Todo>
|
|
284
|
+
<Todo todo="v">
|
|
285
|
+
<li id="todo-2">
|
|
286
|
+
<div class="upper">
|
|
287
|
+
<span>
|
|
288
|
+
My second todo
|
|
289
|
+
</span>
|
|
290
|
+
<span>
|
|
291
|
+
My second todo
|
|
292
|
+
</span>
|
|
293
|
+
</div>
|
|
294
|
+
<div class="todo-panel">
|
|
295
|
+
<span>
|
|
296
|
+
My second todo
|
|
297
|
+
</span>
|
|
298
|
+
<span>
|
|
299
|
+
false
|
|
300
|
+
</span>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
<div class="bottom">
|
|
304
|
+
<span>
|
|
305
|
+
false
|
|
306
|
+
</span>
|
|
307
|
+
<span>
|
|
308
|
+
false
|
|
309
|
+
</span>
|
|
310
|
+
</div>
|
|
311
|
+
</li>
|
|
312
|
+
</Todo>
|
|
313
|
+
<Todo todo="v">
|
|
314
|
+
<li id="todo-3">
|
|
315
|
+
<div class="upper">
|
|
316
|
+
<span>
|
|
317
|
+
My third todo
|
|
318
|
+
</span>
|
|
319
|
+
<span>
|
|
320
|
+
My third todo
|
|
321
|
+
</span>
|
|
322
|
+
</div>
|
|
323
|
+
<div class="todo-panel">
|
|
324
|
+
<span>
|
|
325
|
+
My third todo
|
|
326
|
+
</span>
|
|
327
|
+
<span>
|
|
328
|
+
false
|
|
329
|
+
</span>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<div class="bottom">
|
|
333
|
+
<span>
|
|
334
|
+
false
|
|
335
|
+
</span>
|
|
336
|
+
<span>
|
|
337
|
+
false
|
|
338
|
+
</span>
|
|
339
|
+
</div>
|
|
340
|
+
</li>
|
|
341
|
+
</Todo>
|
|
342
|
+
|
|
343
|
+
</ol>
|
|
344
|
+
`)
|
|
345
|
+
r.Equal(expected, actual)
|
|
346
|
+
}
|
|
231
347
|
|
|
232
348
|
// func TestForComponent(t *testing.T) {
|
|
233
349
|
// r := require.New(t)
|
gsx/parser.go
CHANGED
|
@@ -10,8 +10,8 @@ import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
type Module struct {
|
|
13
|
-
Pos
|
|
13
|
+
Pos lexer.Position
|
|
14
|
-
|
|
14
|
+
Nodes []*AstNode `@@*`
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
type AstNode struct {
|
|
@@ -33,6 +33,23 @@ type Close struct {
|
|
|
33
33
|
Name string `"<""/"@Ident">"`
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
type ForStatement struct {
|
|
37
|
+
Pos lexer.Position `"for"`
|
|
38
|
+
Index string `@Ident ","`
|
|
39
|
+
Key string `@Ident`
|
|
40
|
+
Reference string `":""=""range" @Ident`
|
|
41
|
+
Statements []*Statement `"{" @@* "}"`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type Statement struct {
|
|
45
|
+
ReturnStatement *ReturnStatement `@@`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type ReturnStatement struct {
|
|
49
|
+
Nodes []*AstNode `"return" "(" @@* ")"`
|
|
50
|
+
Tags []*Tag
|
|
51
|
+
}
|
|
52
|
+
|
|
36
53
|
type Attribute struct {
|
|
37
54
|
Pos lexer.Position
|
|
38
55
|
Key string `@":"? @Ident ( @"-" @Ident )*`
|
|
@@ -47,9 +64,36 @@ type KV struct {
|
|
|
47
64
|
|
|
48
65
|
type Literal struct {
|
|
49
66
|
Pos lexer.Position
|
|
50
|
-
Str *string
|
|
67
|
+
Str *string `@String`
|
|
51
|
-
Ref *string
|
|
68
|
+
Ref *string `| "{" @Ident ( @"." @Ident )* "}"`
|
|
52
|
-
KV []*KV
|
|
69
|
+
KV []*KV `| "{" @@* "}"`
|
|
70
|
+
For *ForStatement `| @@`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func (l *Literal) Clone() *Literal {
|
|
74
|
+
if l == nil {
|
|
75
|
+
return nil
|
|
76
|
+
}
|
|
77
|
+
newLiteral := &Literal{}
|
|
78
|
+
if l.Str != nil {
|
|
79
|
+
v := "" + *l.Str
|
|
80
|
+
newLiteral.Str = &v
|
|
81
|
+
}
|
|
82
|
+
if l.Ref != nil {
|
|
83
|
+
v := "" + *l.Ref
|
|
84
|
+
newLiteral.Ref = &v
|
|
85
|
+
}
|
|
86
|
+
if l.KV != nil {
|
|
87
|
+
newLiteral.KV = []*KV{}
|
|
88
|
+
for _, kv := range l.KV {
|
|
89
|
+
newLiteral.KV = append(newLiteral.KV, &KV{
|
|
90
|
+
Key: "" + kv.Key,
|
|
91
|
+
Value: "" + kv.Value,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// TODO copy for
|
|
96
|
+
return newLiteral
|
|
53
97
|
}
|
|
54
98
|
|
|
55
99
|
var htmlParser = participle.MustBuild[Module]()
|
|
@@ -62,6 +106,34 @@ type Tag struct {
|
|
|
62
106
|
SelfClosing bool
|
|
63
107
|
}
|
|
64
108
|
|
|
109
|
+
func (t *Tag) Clone() *Tag {
|
|
110
|
+
newTag := &Tag{
|
|
111
|
+
Name: t.Name,
|
|
112
|
+
Text: t.Text.Clone(),
|
|
113
|
+
Attributes: []*Attribute{},
|
|
114
|
+
SelfClosing: t.SelfClosing,
|
|
115
|
+
Children: []*Tag{},
|
|
116
|
+
}
|
|
117
|
+
for _, v := range t.Attributes {
|
|
118
|
+
newTag.Attributes = append(newTag.Attributes, &Attribute{
|
|
119
|
+
Key: v.Key,
|
|
120
|
+
Value: v.Value.Clone(),
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
for _, child := range t.Children {
|
|
124
|
+
newTag.Children = append(newTag.Children, child.Clone())
|
|
125
|
+
}
|
|
126
|
+
return newTag
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
func cloneTags(tags []*Tag) []*Tag {
|
|
130
|
+
newTags := []*Tag{}
|
|
131
|
+
for _, v := range tags {
|
|
132
|
+
newTags = append(newTags, v.Clone())
|
|
133
|
+
}
|
|
134
|
+
return newTags
|
|
135
|
+
}
|
|
136
|
+
|
|
65
137
|
func renderString(tags []*Tag) string {
|
|
66
138
|
s := ""
|
|
67
139
|
for _, t := range tags {
|
|
@@ -106,11 +178,11 @@ func renderTagString(x *Tag, space string) string {
|
|
|
106
178
|
return s
|
|
107
179
|
}
|
|
108
180
|
|
|
109
|
-
func processTree(
|
|
181
|
+
func processTree(nodes []*AstNode) []*Tag {
|
|
110
182
|
tags := []*Tag{}
|
|
111
183
|
var prevTag *Tag
|
|
112
184
|
stack := stack.New[*Tag]()
|
|
113
|
-
for _, n := range
|
|
185
|
+
for _, n := range nodes {
|
|
114
186
|
if n.Open != nil {
|
|
115
187
|
newTag := &Tag{
|
|
116
188
|
Name: n.Open.Name,
|
|
@@ -140,6 +212,13 @@ func processTree(module *Module) []*Tag {
|
|
|
140
212
|
Name: "",
|
|
141
213
|
Text: n.Content,
|
|
142
214
|
}
|
|
215
|
+
if n.Content.For != nil {
|
|
216
|
+
for _, s := range n.Content.For.Statements {
|
|
217
|
+
if s.ReturnStatement != nil {
|
|
218
|
+
s.ReturnStatement.Tags = processTree(s.ReturnStatement.Nodes)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
143
222
|
if prevTag != nil {
|
|
144
223
|
prevTag.Children = append(prevTag.Children, newTag)
|
|
145
224
|
} else {
|
|
@@ -155,5 +234,5 @@ func parse(name, s string) []*Tag {
|
|
|
155
234
|
if err != nil {
|
|
156
235
|
panic(err)
|
|
157
236
|
}
|
|
158
|
-
return processTree(ast)
|
|
237
|
+
return processTree(ast.Nodes)
|
|
159
238
|
}
|
gsx/parser_test.go
CHANGED
|
@@ -23,7 +23,6 @@ func TestParse(t *testing.T) {
|
|
|
23
23
|
</p>
|
|
24
24
|
</div>
|
|
25
25
|
`))
|
|
26
|
-
println(actual)
|
|
27
26
|
expected := strings.TrimLeft(`
|
|
28
27
|
<ul id=""todo-list"" class=""relative"">
|
|
29
28
|
<Todo>
|
|
@@ -51,10 +50,36 @@ func TestSelfClose(t *testing.T) {
|
|
|
51
50
|
<Todo />
|
|
52
51
|
<TodoCount />
|
|
53
52
|
`))
|
|
54
|
-
println(actual)
|
|
55
53
|
expected := strings.TrimLeft(`
|
|
56
54
|
<Todo />
|
|
57
55
|
<TodoCount />
|
|
58
56
|
`, "\n")
|
|
59
57
|
r.Equal(expected, actual)
|
|
60
58
|
}
|
|
59
|
+
|
|
60
|
+
func TestForLoop(t *testing.T) {
|
|
61
|
+
r := require.New(t)
|
|
62
|
+
actual := renderString(parse("test", `
|
|
63
|
+
<ul>
|
|
64
|
+
for k, v := range todos {
|
|
65
|
+
return (
|
|
66
|
+
<li>
|
|
67
|
+
"data"
|
|
68
|
+
</li>
|
|
69
|
+
<div>
|
|
70
|
+
<span>
|
|
71
|
+
{name}
|
|
72
|
+
</span>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
</ul>
|
|
77
|
+
`))
|
|
78
|
+
expected := strings.TrimLeft(`
|
|
79
|
+
<ul>
|
|
80
|
+
<>
|
|
81
|
+
</>
|
|
82
|
+
</ul>
|
|
83
|
+
`, "\n")
|
|
84
|
+
r.Equal(expected, actual)
|
|
85
|
+
}
|