diff --git a/.gitignore b/.gitignore index fbba37a10..805cc1002 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ cmd/example/*.go dist cmd/templ/templ -cmd/lspcmd/*log.txt +cmd/templ/lspcmd/*log.txt .DS_Store diff --git a/generator/generator.go b/generator/generator.go index ccd3b4d77..76c0a83e3 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -393,9 +393,47 @@ func (g *generator) writeErrorHandler(indentLevel int) (err error) { } func (g *generator) writeElement(indentLevel int, n templ.Element) error { - var r templ.Range - var err error - // Attributes. + if n.IsVoidElement() { + return g.writeVoidElement(indentLevel, n) + } + return g.writeStandardElement(indentLevel, n) +} + +func (g *generator) writeVoidElement(indentLevel int, n templ.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 { + //
+ if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf(`_, err = io.WriteString(w, "<%s/>")`+"\n", html.EscapeString(n.Name))); err != nil { + return err + } + if err = g.writeErrorHandler(indentLevel); err != nil { + return err + } + } else { + //
+ if _, err = g.w.WriteIndent(indentLevel, `_, err = io.WriteString(w, "/>")`+"\n"); err != nil { + return err + } + if err = g.writeErrorHandler(indentLevel); err != nil { + return err + } + } + return err +} + +func (g *generator) writeStandardElement(indentLevel int, n templ.Element) (err error) { if len(n.Attributes) == 0 { //
if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf(`_, err = io.WriteString(w, "<%s>")`+"\n", html.EscapeString(n.Name))); err != nil { @@ -412,60 +450,8 @@ func (g *generator) writeElement(indentLevel int, n templ.Element) error { if err = g.writeErrorHandler(indentLevel); err != nil { return err } - for i := 0; i < len(n.Attributes); i++ { - switch attr := n.Attributes[i].(type) { - case templ.ConstantAttribute: - name := html.EscapeString(attr.Name) - value := html.EscapeString(attr.Value) - if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf(`_, err = io.WriteString(w, " %s=\"%s\"")`+"\n", name, value)); err != nil { - return err - } - if err = g.writeErrorHandler(indentLevel); err != nil { - return err - } - case templ.ExpressionAttribute: - name := html.EscapeString(attr.Name) - // Name - if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf(`_, err = io.WriteString(w, " %s=")`+"\n", name)); err != nil { - return err - } - if err = g.writeErrorHandler(indentLevel); err != nil { - return err - } - // Value. - // Open quote. - if _, err = g.w.WriteIndent(indentLevel, `_, err = io.WriteString(w, "\"")`+"\n"); err != nil { - return err - } - if err = g.writeErrorHandler(indentLevel); err != nil { - return err - } - // io.WriteString(w, templ.EscapeString( - if _, err = g.w.WriteIndent(indentLevel, "_, err = io.WriteString(w, templ.EscapeString("); err != nil { - return err - } - // p.Name() - if r, err = g.w.Write(attr.Expression.Value); err != nil { - return err - } - g.sourceMap.Add(attr.Expression, r) - // )) - if _, err = g.w.Write("))\n"); err != nil { - return err - } - if err = g.writeErrorHandler(indentLevel); err != nil { - return err - } - // Close quote. - if _, err = g.w.WriteIndent(indentLevel, `_, err = io.WriteString(w, "\"")`+"\n"); err != nil { - return err - } - if err = g.writeErrorHandler(indentLevel); err != nil { - return err - } - default: - return fmt.Errorf("unknown attribute type %s", reflect.TypeOf(n.Attributes[i])) - } + if err = g.writeElementAttributes(indentLevel, n); err != nil { + return err } // > if _, err = g.w.WriteIndent(indentLevel, `_, err = io.WriteString(w, ">")`+"\n"); err != nil { @@ -484,7 +470,67 @@ func (g *generator) writeElement(indentLevel int, n templ.Element) error { if err = g.writeErrorHandler(indentLevel); err != nil { return err } - return nil + return err +} + +func (g *generator) writeElementAttributes(indentLevel int, n templ.Element) (err error) { + var r templ.Range + for i := 0; i < len(n.Attributes); i++ { + switch attr := n.Attributes[i].(type) { + case templ.ConstantAttribute: + name := html.EscapeString(attr.Name) + value := html.EscapeString(attr.Value) + if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf(`_, err = io.WriteString(w, " %s=\"%s\"")`+"\n", name, value)); err != nil { + return err + } + if err = g.writeErrorHandler(indentLevel); err != nil { + return err + } + case templ.ExpressionAttribute: + name := html.EscapeString(attr.Name) + // Name + if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf(`_, err = io.WriteString(w, " %s=")`+"\n", name)); err != nil { + return err + } + if err = g.writeErrorHandler(indentLevel); err != nil { + return err + } + // Value. + // Open quote. + if _, err = g.w.WriteIndent(indentLevel, `_, err = io.WriteString(w, "\"")`+"\n"); err != nil { + return err + } + if err = g.writeErrorHandler(indentLevel); err != nil { + return err + } + // io.WriteString(w, templ.EscapeString( + if _, err = g.w.WriteIndent(indentLevel, "_, err = io.WriteString(w, templ.EscapeString("); err != nil { + return err + } + // p.Name() + if r, err = g.w.Write(attr.Expression.Value); err != nil { + return err + } + g.sourceMap.Add(attr.Expression, r) + // )) + if _, err = g.w.Write("))\n"); err != nil { + return err + } + if err = g.writeErrorHandler(indentLevel); err != nil { + return err + } + // Close quote. + if _, err = g.w.WriteIndent(indentLevel, `_, err = io.WriteString(w, "\"")`+"\n"); err != nil { + return err + } + if err = g.writeErrorHandler(indentLevel); err != nil { + return err + } + default: + return fmt.Errorf("unknown attribute type %s", reflect.TypeOf(n.Attributes[i])) + } + } + return err } func (g *generator) writeStringExpression(indentLevel int, e templ.Expression) error { diff --git a/generator/test-void/render_test.go b/generator/test-void/render_test.go new file mode 100644 index 000000000..5b0350ff7 --- /dev/null +++ b/generator/test-void/render_test.go @@ -0,0 +1,22 @@ +package testvoid + +import ( + "context" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +const expected = `


` + +func TestRender(t *testing.T) { + w := new(strings.Builder) + err := render().Render(context.Background(), w) + if err != nil { + t.Errorf("failed to render: %v", err) + } + if diff := cmp.Diff(expected, w.String()); diff != "" { + t.Error(diff) + } +} diff --git a/generator/test-void/template.templ b/generator/test-void/template.templ new file mode 100644 index 000000000..35a255a1b --- /dev/null +++ b/generator/test-void/template.templ @@ -0,0 +1,8 @@ +{% package testvoid %} + +{% templ render() %} +
+ +

+{% endtempl %} + diff --git a/generator/test-void/template_templ.go b/generator/test-void/template_templ.go new file mode 100644 index 000000000..cf90e3712 --- /dev/null +++ b/generator/test-void/template_templ.go @@ -0,0 +1,38 @@ +// Code generated by templ DO NOT EDIT. + +package testvoid + +import "github.com/a-h/templ" +import "context" +import "io" + +func render() templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + _, err = io.WriteString(w, "
") + if err != nil { + return err + } + _, err = io.WriteString(w, "") + if err != nil { + return err + } + _, err = io.WriteString(w, "
") + if err != nil { + return err + } + _, err = io.WriteString(w, "
") + if err != nil { + return err + } + return err + }) +} + diff --git a/types.go b/types.go index 52e845706..1491e22fb 100644 --- a/types.go +++ b/types.go @@ -248,7 +248,7 @@ var voidElements = map[string]struct{}{ "area": {}, "base": {}, "br": {}, "col": {}, "command": {}, "embed": {}, "hr": {}, "img": {}, "input": {}, "keygen": {}, "link": {}, "meta": {}, "param": {}, "source": {}, "track": {}, "wbr": {}} // https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#void-element -func (e Element) isVoidElement() bool { +func (e Element) IsVoidElement() bool { _, ok := voidElements[e.Name] return ok } @@ -328,7 +328,7 @@ func (e Element) Write(w io.Writer, indent int) error { } return nil } - if e.isVoidElement() { + if e.IsVoidElement() { if _, err := w.Write([]byte("/>")); err != nil { return err }