diff --git a/.version b/.version
index 87e643b96..6c6ec4881 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-0.2.470
\ No newline at end of file
+0.2.470
diff --git a/cmd/templ/migratecmd/main.go b/cmd/templ/migratecmd/main.go
index 1d8a8b17b..fd3bd686c 100644
--- a/cmd/templ/migratecmd/main.go
+++ b/cmd/templ/migratecmd/main.go
@@ -72,7 +72,7 @@ func migrate(fileName string) (err error) {
var v2Template v2.TemplateFile
// Copy the package and any imports.
- var sb strings.Builder
+ sb := new(strings.Builder)
sb.WriteString("package " + v1Template.Package.Expression.Value)
sb.WriteString("\n")
if len(v1Template.Imports) > 0 {
diff --git a/parser/v1/elementparser_test.go b/parser/v1/elementparser_test.go
index 0640813ef..22c416002 100644
--- a/parser/v1/elementparser_test.go
+++ b/parser/v1/elementparser_test.go
@@ -601,7 +601,7 @@ func TestElementParserErrors(t *testing.T) {
}
func TestBigElement(t *testing.T) {
- var sb strings.Builder
+ sb := new(strings.Builder)
sb.WriteString("
")
for i := 0; i < 4096*4; i++ {
sb.WriteString("a")
diff --git a/parser/v2/calltemplateparser.go b/parser/v2/calltemplateparser.go
index 5c68954ea..66cc610e8 100644
--- a/parser/v2/calltemplateparser.go
+++ b/parser/v2/calltemplateparser.go
@@ -10,13 +10,14 @@ var callTemplateExpressionStart = parse.Or(parse.String("{! "), parse.String("{!
type callTemplateExpressionParser struct{}
-func (p callTemplateExpressionParser) Parse(pi *parse.Input) (r CallTemplateExpression, ok bool, err error) {
+func (p callTemplateExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) {
// Check the prefix first.
if _, ok, err = callTemplateExpressionStart.Parse(pi); err != nil || !ok {
return
}
// Once we have a prefix, we must have an expression that returns a template.
+ var r CallTemplateExpression
if r.Expression, ok, err = exp.Parse(pi); err != nil || !ok {
return
}
diff --git a/parser/v2/childrenparser.go b/parser/v2/childrenparser.go
index 30a6a57a8..127537be1 100644
--- a/parser/v2/childrenparser.go
+++ b/parser/v2/childrenparser.go
@@ -4,13 +4,18 @@ import (
"github.com/a-h/parse"
)
-var childrenExpression = parse.Func(func(in *parse.Input) (out ChildrenExpression, ok bool, err error) {
- _, ok, err = parse.StringFrom(
- openBraceWithOptionalPadding,
- parse.OptionalWhitespace,
- parse.String("children..."),
- parse.OptionalWhitespace,
- closeBraceWithOptionalPadding,
- ).Parse(in)
- return out, ok, err
+var childrenExpressionParser = parse.StringFrom(
+ openBraceWithOptionalPadding,
+ parse.OptionalWhitespace,
+ parse.String("children..."),
+ parse.OptionalWhitespace,
+ closeBraceWithOptionalPadding,
+)
+
+var childrenExpression = parse.Func(func(in *parse.Input) (n Node, ok bool, err error) {
+ _, ok, err = childrenExpressionParser.Parse(in)
+ if err != nil || !ok {
+ return
+ }
+ return ChildrenExpression{}, true, nil
})
diff --git a/parser/v2/cssparser.go b/parser/v2/cssparser.go
index 88b762fed..7a22151e7 100644
--- a/parser/v2/cssparser.go
+++ b/parser/v2/cssparser.go
@@ -166,10 +166,12 @@ var expressionCSSPropertyParser = parse.Func(func(pi *parse.Input) (r Expression
}
// { string }
- if r.Value, ok, err = stringExpression.Parse(pi); err != nil || !ok {
+ var se Node
+ if se, ok, err = stringExpression.Parse(pi); err != nil || !ok {
pi.Seek(start)
return
}
+ r.Value = se.(StringExpression)
// ;
if _, ok, err = Must(parse.String(";"), "missing expected semicolon (;)").Parse(pi); err != nil || !ok {
diff --git a/parser/v2/doctypeparser.go b/parser/v2/doctypeparser.go
index ed79a8ccb..0039e0820 100644
--- a/parser/v2/doctypeparser.go
+++ b/parser/v2/doctypeparser.go
@@ -6,7 +6,8 @@ import (
var doctypeStartParser = parse.StringInsensitive("")
for i := 0; i < 4096*4; i++ {
sb.WriteString("a")
diff --git a/parser/v2/expressionparser.go b/parser/v2/expressionparser.go
index abd194f20..ac26f8f62 100644
--- a/parser/v2/expressionparser.go
+++ b/parser/v2/expressionparser.go
@@ -7,8 +7,8 @@ import (
)
// StripType takes the parser and throws away the return value.
-func StripType[T any](p parse.Parser[T]) parse.Parser[interface{}] {
- return parse.Func(func(in *parse.Input) (out interface{}, ok bool, err error) {
+func StripType[T any](p parse.Parser[T]) parse.Parser[any] {
+ return parse.Func(func(in *parse.Input) (out any, ok bool, err error) {
return p.Parse(in)
})
}
@@ -57,6 +57,10 @@ var openBracket = parse.String("(")
var closeBracket = parse.String(")")
var closeBracketWithOptionalPadding = parse.StringFrom(optionalSpaces, closeBracket)
+var stringUntilNewLine = parse.StringUntil[string](parse.NewLine)
+var newLineOrEOF = parse.Or(parse.NewLine, parse.EOF[string]())
+var stringUntilNewLineOrEOF = parse.StringUntil(newLineOrEOF)
+
var exp = expressionParser{
startBraceCount: 1,
}
@@ -70,7 +74,7 @@ func (p expressionParser) Parse(pi *parse.Input) (s Expression, ok bool, err err
braceCount := p.startBraceCount
- var sb strings.Builder
+ sb := new(strings.Builder)
loop:
for {
var result string
@@ -147,7 +151,7 @@ func (p functionArgsParser) Parse(pi *parse.Input) (s Expression, ok bool, err e
bracketCount := p.startBracketCount
- var sb strings.Builder
+ sb := new(strings.Builder)
loop:
for {
var result string
diff --git a/parser/v2/forexpressionparser.go b/parser/v2/forexpressionparser.go
index 3ab04926f..ac0a8527c 100644
--- a/parser/v2/forexpressionparser.go
+++ b/parser/v2/forexpressionparser.go
@@ -4,7 +4,7 @@ import (
"github.com/a-h/parse"
)
-var forExpression = parse.Func(func(pi *parse.Input) (r ForExpression, ok bool, err error) {
+var forExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) {
// Check the prefix first.
if _, ok, err = parse.String("for ").Parse(pi); err != nil || !ok {
return
@@ -13,6 +13,7 @@ var forExpression = parse.Func(func(pi *parse.Input) (r ForExpression, ok bool,
// Once we've got a prefix, read until {\n.
// If there's no match, there's no {\n, which is an error.
from := pi.Position()
+ var r ForExpression
until := parse.All(openBraceWithOptionalPadding, parse.NewLine)
var fexp string
if fexp, ok, err = Must(parse.StringUntil(until), "for: "+unterminatedMissingCurly).Parse(pi); err != nil || !ok {
diff --git a/parser/v2/gocommentparser.go b/parser/v2/gocommentparser.go
index 106148689..e27eb29a7 100644
--- a/parser/v2/gocommentparser.go
+++ b/parser/v2/gocommentparser.go
@@ -12,8 +12,9 @@ type goSingleLineCommentParser struct {
var goSingleLineComment = goSingleLineCommentParser{}
-func (p goSingleLineCommentParser) Parse(pi *parse.Input) (c GoComment, ok bool, err error) {
+func (p goSingleLineCommentParser) Parse(pi *parse.Input) (n Node, ok bool, err error) {
// Comment start.
+ var c GoComment
if _, ok, err = goSingleLineCommentStart.Parse(pi); err != nil || !ok {
return
}
@@ -37,8 +38,9 @@ type goMultiLineCommentParser struct {
var goMultiLineComment = goMultiLineCommentParser{}
-func (p goMultiLineCommentParser) Parse(pi *parse.Input) (c GoComment, ok bool, err error) {
+func (p goMultiLineCommentParser) Parse(pi *parse.Input) (n Node, ok bool, err error) {
// Comment start.
+ var c GoComment
if _, ok, err = goMultiLineCommentStart.Parse(pi); err != nil || !ok {
return
}
@@ -55,4 +57,4 @@ func (p goMultiLineCommentParser) Parse(pi *parse.Input) (c GoComment, ok bool,
return c, true, nil
}
-var goComment = parse.Any[GoComment](goSingleLineComment, goMultiLineComment)
+var goComment = parse.Any[Node](goSingleLineComment, goMultiLineComment)
diff --git a/parser/v2/htmlcommentparser.go b/parser/v2/htmlcommentparser.go
index 4a570075f..e42db0003 100644
--- a/parser/v2/htmlcommentparser.go
+++ b/parser/v2/htmlcommentparser.go
@@ -12,8 +12,9 @@ type htmlCommentParser struct {
var htmlComment = htmlCommentParser{}
-func (p htmlCommentParser) Parse(pi *parse.Input) (c HTMLComment, ok bool, err error) {
+func (p htmlCommentParser) Parse(pi *parse.Input) (n Node, ok bool, err error) {
// Comment start.
+ var c HTMLComment
if _, ok, err = htmlCommentStart.Parse(pi); err != nil || !ok {
return
}
diff --git a/parser/v2/ifexpressionparser.go b/parser/v2/ifexpressionparser.go
index d1d4b41a3..9e756d6e8 100644
--- a/parser/v2/ifexpressionparser.go
+++ b/parser/v2/ifexpressionparser.go
@@ -8,7 +8,7 @@ var ifExpression ifExpressionParser
type ifExpressionParser struct{}
-func (ifExpressionParser) Parse(pi *parse.Input) (r IfExpression, ok bool, err error) {
+func (ifExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) {
// Check the prefix first.
if _, ok, err = parse.String("if ").Parse(pi); err != nil || !ok {
return
@@ -16,6 +16,7 @@ func (ifExpressionParser) Parse(pi *parse.Input) (r IfExpression, ok bool, err e
// Once we've got a prefix, read until {\n.
// If there's no match, there's no {\n, which is an error.
+ var r IfExpression
if r.Expression, ok, err = Must(ExpressionOf(parse.StringUntil(parse.All(openBraceWithOptionalPadding, parse.NewLine))), "if: "+unterminatedMissingCurly).Parse(pi); err != nil || !ok {
return
}
diff --git a/parser/v2/packageparser.go b/parser/v2/packageparser.go
index 8bdd63e90..2e973dbbd 100644
--- a/parser/v2/packageparser.go
+++ b/parser/v2/packageparser.go
@@ -15,7 +15,7 @@ var pkg = parse.Func(func(pi *parse.Input) (pkg Package, ok bool, err error) {
// Once we have the prefix, it's an expression until the end of the line.
var exp string
- if exp, ok, err = Must(parse.StringUntil(parse.NewLine), "package literal not terminated").Parse(pi); err != nil || !ok {
+ if exp, ok, err = Must(stringUntilNewLine, "package literal not terminated").Parse(pi); err != nil || !ok {
return
}
if len(exp) == 0 {
diff --git a/parser/v2/raw.go b/parser/v2/raw.go
index 706eb2d3c..004e346a9 100644
--- a/parser/v2/raw.go
+++ b/parser/v2/raw.go
@@ -18,7 +18,7 @@ type rawElementParser struct {
name string
}
-func (p rawElementParser) Parse(pi *parse.Input) (e RawElement, ok bool, err error) {
+func (p rawElementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) {
start := pi.Index()
// <
@@ -27,6 +27,7 @@ func (p rawElementParser) Parse(pi *parse.Input) (e RawElement, ok bool, err err
}
// Element name.
+ var e RawElement
if e.Name, ok, err = parse.String(p.name).Parse(pi); err != nil || !ok {
pi.Seek(start)
return
diff --git a/parser/v2/stringexpressionparser.go b/parser/v2/stringexpressionparser.go
index cd8f8318f..80ad2b311 100644
--- a/parser/v2/stringexpressionparser.go
+++ b/parser/v2/stringexpressionparser.go
@@ -4,13 +4,14 @@ import (
"github.com/a-h/parse"
)
-var stringExpression = parse.Func(func(pi *parse.Input) (r StringExpression, ok bool, err error) {
+var stringExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) {
// Check the prefix first.
if _, ok, err = parse.Or(parse.String("{ "), parse.String("{")).Parse(pi); err != nil || !ok {
return
}
// Once we have a prefix, we must have an expression that returns a string.
+ var r StringExpression
if r.Expression, ok, err = exp.Parse(pi); err != nil || !ok {
return
}
diff --git a/parser/v2/stringexpressionparser_test.go b/parser/v2/stringexpressionparser_test.go
index c4bc83eb1..e7a777e22 100644
--- a/parser/v2/stringexpressionparser_test.go
+++ b/parser/v2/stringexpressionparser_test.go
@@ -62,13 +62,14 @@ func TestStringExpressionParser(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
input := parse.NewInput(tt.input)
- actual, ok, err := stringExpression.Parse(input)
+ an, ok, err := stringExpression.Parse(input)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !ok {
t.Fatalf("unexpected failure for input %q", tt.input)
}
+ actual := an.(StringExpression)
if diff := cmp.Diff(tt.expected, actual); diff != "" {
t.Error(diff)
}
diff --git a/parser/v2/switchexpressionparser.go b/parser/v2/switchexpressionparser.go
index 780b34f8b..9f7694e38 100644
--- a/parser/v2/switchexpressionparser.go
+++ b/parser/v2/switchexpressionparser.go
@@ -6,13 +6,14 @@ import (
"github.com/a-h/parse"
)
-var switchExpression = parse.Func(func(pi *parse.Input) (r SwitchExpression, ok bool, err error) {
+var switchExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) {
// Check the prefix first.
if _, ok, err = parse.String("switch ").Parse(pi); err != nil || !ok {
return
}
// Once we've got a prefix, read until {\n.
+ var r SwitchExpression
endOfStatementExpression := ExpressionOf(parse.StringUntil(parse.All(openBraceWithOptionalPadding, parse.NewLine)))
if r.Expression, ok, err = Must(endOfStatementExpression, "switch: "+unterminatedMissingCurly).Parse(pi); err != nil || !ok {
return
diff --git a/parser/v2/templatefile.go b/parser/v2/templatefile.go
index 782d5d3f7..d6afd2000 100644
--- a/parser/v2/templatefile.go
+++ b/parser/v2/templatefile.go
@@ -60,16 +60,18 @@ func NewTemplateFileParser(pkg string) TemplateFileParser {
}
}
-var ErrLegacyFileFormat = errors.New("Legacy file format - run templ migrate")
-var ErrTemplateNotFound = errors.New("Template not found")
+var ErrLegacyFileFormat = errors.New("legacy file format - run templ migrate")
+var ErrTemplateNotFound = errors.New("template not found")
type TemplateFileParser struct {
DefaultPackage string
}
+var legacyPackageParser = parse.String("{% package")
+
func (p TemplateFileParser) Parse(pi *parse.Input) (tf TemplateFile, ok bool, err error) {
// If we're parsing a legacy file, complain that migration needs to happen.
- _, ok, err = parse.String("{% package").Parse(pi)
+ _, ok, err = legacyPackageParser.Parse(pi)
if err != nil {
return
}
@@ -91,7 +93,7 @@ func (p TemplateFileParser) Parse(pi *parse.Input) (tf TemplateFile, ok bool, er
}
var line string
- line, ok, err = parse.StringUntil(parse.NewLine).Parse(pi)
+ line, ok, err = stringUntilNewLine.Parse(pi)
if err != nil {
return
}
@@ -146,14 +148,14 @@ outer:
}
// Anything that isn't template content is Go code.
- var code strings.Builder
+ code := new(strings.Builder)
from := pi.Position()
inner:
for {
// Check to see if this line isn't Go code.
last := pi.Index()
var l string
- if l, ok, err = parse.StringUntil(parse.Or(parse.NewLine, parse.EOF[string]())).Parse(pi); err != nil {
+ if l, ok, err = stringUntilNewLineOrEOF.Parse(pi); err != nil {
return
}
hasTemplatePrefix := strings.HasPrefix(l, "templ ") || strings.HasPrefix(l, "css ") || strings.HasPrefix(l, "script ")
diff --git a/parser/v2/templateparser.go b/parser/v2/templateparser.go
index c619c6ad9..673f139ba 100644
--- a/parser/v2/templateparser.go
+++ b/parser/v2/templateparser.go
@@ -63,7 +63,24 @@ type templateNodeParser[TUntil any] struct {
untilName string
}
-var rawElements = parse.Any[RawElement](styleElement, scriptElement)
+var rawElements = parse.Any[Node](styleElement, scriptElement)
+
+var templateNodeParsers = []parse.Parser[Node]{
+ docType, //
+ htmlComment, //