Skip to content

Commit

Permalink
feat: support parsing (and converting) void elements, fixes #637 (#732)
Browse files Browse the repository at this point in the history
  • Loading branch information
a-h authored May 10, 2024
1 parent 7d8287e commit ad707cb
Show file tree
Hide file tree
Showing 45 changed files with 953 additions and 1,042 deletions.
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.682
0.2.683
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ go run ./cmd/templ generate -include-version=false
go test ./...
```

### test-short

Run Go tests.

```sh
go run ./get-version > .version
go run ./cmd/templ generate -include-version=false
go test ./... -short
```

### test-cover

Run Go tests.
Expand Down
44 changes: 4 additions & 40 deletions generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,46 +880,6 @@ func (g *generator) writeExpressionErrorHandler(indentLevel int, expression pars
}

func (g *generator) writeElement(indentLevel int, n parser.Element) (err error) {
if n.IsVoidElement() {
return g.writeVoidElement(indentLevel, n)
}
return g.writeStandardElement(indentLevel, n)
}

func (g *generator) writeVoidElement(indentLevel int, n parser.Element) (err error) {
if len(n.Children) > 0 {
return fmt.Errorf("writeVoidElement: void element %q must not have child elements", n.Name)
}
if len(n.Attributes) == 0 {
// <br>
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(`<%s>`, html.EscapeString(n.Name))); err != nil {
return err
}
} else {
// <style type="text/css"></style>
if err = g.writeElementCSS(indentLevel, n); err != nil {
return err
}
// <script type="text/javascript"></script>
if err = g.writeElementScript(indentLevel, n); err != nil {
return err
}
// <hr
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(`<%s`, html.EscapeString(n.Name))); err != nil {
return err
}
if err = g.writeElementAttributes(indentLevel, n.Name, n.Attributes); err != nil {
return err
}
// >
if _, err = g.w.WriteStringLiteral(indentLevel, `>`); err != nil {
return err
}
}
return err
}

func (g *generator) writeStandardElement(indentLevel int, n parser.Element) (err error) {
if len(n.Attributes) == 0 {
// <div>
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(`<%s>`, html.EscapeString(n.Name))); err != nil {
Expand All @@ -946,6 +906,10 @@ func (g *generator) writeStandardElement(indentLevel int, n parser.Element) (err
return err
}
}
// Skip children and close tag for void elements.
if n.IsVoidElement() && len(n.Children) == 0 {
return nil
}
// Children.
if err = g.writeNodes(indentLevel, stripWhitespace(n.Children), nil); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion generator/test-html/expected.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ <h1>Luiz Bonfa</h1>
<hr noshade>
<hr optionA optionB optionC="other">
<hr noshade>

<input name="test">Text</input>
1 change: 1 addition & 0 deletions generator/test-html/template.templ
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ templ render(p person) {
<hr noshade?={ true }/>
<hr optionA optionB?={ true } optionC="other" optionD?={ false }/>
<hr noshade/>
<input name="test">Text</input>
}
2 changes: 1 addition & 1 deletion generator/test-html/template_templ.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 27 additions & 5 deletions parser/v2/diagnostics.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package parser

import "errors"
import (
"errors"
"fmt"
)

type diagnoser func(Node) ([]Diagnostic, error)

Expand Down Expand Up @@ -30,10 +33,12 @@ func walkNodes(t []Node, f func(Node) bool) {
}
}

var diagnosers = []diagnoser{
useOfLegacyCallSyntaxDiagnoser,
voidElementWithChildrenDiagnoser,
}

func Diagnose(t TemplateFile) ([]Diagnostic, error) {
diagnosers := []diagnoser{
legacyCallSyntaxDiagnoser,
}
var diags []Diagnostic
var errs error
walkTemplate(t, func(n Node) bool {
Expand All @@ -50,7 +55,7 @@ func Diagnose(t TemplateFile) ([]Diagnostic, error) {
return diags, errs
}

func legacyCallSyntaxDiagnoser(n Node) ([]Diagnostic, error) {
func useOfLegacyCallSyntaxDiagnoser(n Node) ([]Diagnostic, error) {
if c, ok := n.(CallTemplateExpression); ok {
return []Diagnostic{{
Message: "`{! foo }` syntax is deprecated. Use `@foo` syntax instead. Run `templ fmt .` to fix all instances.",
Expand All @@ -59,3 +64,20 @@ func legacyCallSyntaxDiagnoser(n Node) ([]Diagnostic, error) {
}
return nil, nil
}

func voidElementWithChildrenDiagnoser(n Node) (d []Diagnostic, err error) {
e, ok := n.(Element)
if !ok {
return
}
if !e.IsVoidElement() {
return
}
if len(e.Children) == 0 {
return
}
return []Diagnostic{{
Message: fmt.Sprintf("void element <%s> should not have child content", e.Name),
Range: e.NameRange,
}}, nil
}
43 changes: 35 additions & 8 deletions parser/v2/diagnostics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ templ template () {
want: nil,
},

// legacyCallSyntaxDiagnoser
// useOfLegacyCallSyntaxDiagnoser

{
name: "legacyCallSyntaxDiagnoser: template root",
name: "useOfLegacyCallSyntaxDiagnoser: template root",
template: `
package main
Expand All @@ -39,7 +39,7 @@ templ template () {
}},
},
{
name: "legacyCallSyntaxDiagnoser: in div",
name: "useOfLegacyCallSyntaxDiagnoser: in div",
template: `
package main
Expand All @@ -54,7 +54,7 @@ templ template () {
}},
},
{
name: "legacyCallSyntaxDiagnoser: in if",
name: "useOfLegacyCallSyntaxDiagnoser: in if",
template: `
package main
Expand All @@ -69,7 +69,7 @@ templ template () {
}},
},
{
name: "legacyCallSyntaxDiagnoser: in for",
name: "useOfLegacyCallSyntaxDiagnoser: in for",
template: `
package main
Expand All @@ -84,7 +84,7 @@ templ template () {
}},
},
{
name: "legacyCallSyntaxDiagnoser: in switch",
name: "useOfLegacyCallSyntaxDiagnoser: in switch",
template: `
package main
Expand All @@ -108,7 +108,7 @@ templ template () {
},
},
{
name: "legacyCallSyntaxDiagnoser: in block",
name: "useOfLegacyCallSyntaxDiagnoser: in block",
template: `
package main
Expand All @@ -122,6 +122,33 @@ templ template () {
Range: Range{Position{59, 5, 5}, Position{75, 5, 21}},
}},
},
{
name: "voidElementWithChildrenDiagnoser: no diagnostics",
template: `
package main
templ template () {
<div>
<input/>
</div>
}`,
want: nil,
},
{
name: "voidElementWithChildrenDiagnoser: with diagnostics",
template: `
package main
templ template () {
<div>
<input>Child content</input>
</div>
}`,
want: []Diagnostic{{
Message: "void element <input> should not have child content",
Range: Range{Position{46, 5, 4}, Position{51, 5, 9}},
}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -133,7 +160,7 @@ templ template () {
if err != nil {
t.Fatalf("Diagnose() error = %v", err)
}
if diff := cmp.Diff(got, tt.want); diff != "" {
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Errorf("Diagnose() mismatch (-got +want):\n%s", diff)
}
})
Expand Down
Loading

0 comments on commit ad707cb

Please sign in to comment.