learnlytics-go/templ/generator/generator.go

1639 lines
46 KiB
Go
Raw Normal View History

2025-03-20 12:35:13 +01:00
package generator
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"html"
"io"
"path/filepath"
"reflect"
"strconv"
"strings"
"time"
"unicode"
_ "embed"
"github.com/a-h/templ/parser/v2"
)
type GenerateOpt func(g *generator) error
// WithVersion enables the version to be included in the generated code.
func WithVersion(v string) GenerateOpt {
return func(g *generator) error {
g.options.Version = v
return nil
}
}
// WithTimestamp enables the generated date to be included in the generated code.
func WithTimestamp(d time.Time) GenerateOpt {
return func(g *generator) error {
g.options.GeneratedDate = d.Format(time.RFC3339)
return nil
}
}
// WithFileName sets the filename of the templ file in template rendering error messages.
func WithFileName(name string) GenerateOpt {
return func(g *generator) error {
if filepath.IsAbs(name) {
_, g.options.FileName = filepath.Split(name)
return nil
}
g.options.FileName = name
return nil
}
}
// WithSkipCodeGeneratedComment skips the code generated comment at the top of the file.
// gopls disables edit related functionality for generated files, so the templ LSP may
// wish to skip generation of this comment so that gopls provides expected results.
func WithSkipCodeGeneratedComment() GenerateOpt {
return func(g *generator) error {
g.options.SkipCodeGeneratedComment = true
return nil
}
}
type GeneratorOutput struct {
Options GeneratorOptions `json:"meta"`
SourceMap *parser.SourceMap `json:"sourceMap"`
Literals []string `json:"literals"`
}
type GeneratorOptions struct {
// Version of templ.
Version string
// FileName to include in error messages if string expressions return an error.
FileName string
// SkipCodeGeneratedComment skips the code generated comment at the top of the file.
SkipCodeGeneratedComment bool
// GeneratedDate to include as a comment.
GeneratedDate string
}
// HasChanged returns true if the generated file should be written to disk, and therefore, also
// requires a recompilation.
func HasChanged(previous, updated GeneratorOutput) bool {
// If generator options have changed, we need to recompile.
if previous.Options.Version != updated.Options.Version {
return true
}
if previous.Options.FileName != updated.Options.FileName {
return true
}
if previous.Options.SkipCodeGeneratedComment != updated.Options.SkipCodeGeneratedComment {
return true
}
// We don't check the generated date as it's not used for determining if the file has changed.
// If the number of literals has changed, we need to recompile.
if len(previous.Literals) != len(updated.Literals) {
return true
}
// If the Go code has changed, we need to recompile.
if len(previous.SourceMap.Expressions) != len(updated.SourceMap.Expressions) {
return true
}
for i, prev := range previous.SourceMap.Expressions {
if prev != updated.SourceMap.Expressions[i] {
return true
}
}
return false
}
// Generate generates Go code from the input template file to w, and returns a map of the location of Go expressions in the template
// to the location of the generated Go code in the output.
func Generate(template parser.TemplateFile, w io.Writer, opts ...GenerateOpt) (op GeneratorOutput, err error) {
g := &generator{
tf: template,
w: NewRangeWriter(w),
sourceMap: parser.NewSourceMap(),
}
for _, opt := range opts {
if err = opt(g); err != nil {
return
}
}
err = g.generate()
if err != nil {
return op, err
}
op.Options = g.options
op.SourceMap = g.sourceMap
op.Literals = g.w.Literals
return op, nil
}
type generator struct {
tf parser.TemplateFile
w *RangeWriter
sourceMap *parser.SourceMap
variableID int
childrenVar string
options GeneratorOptions
}
func (g *generator) generate() (err error) {
if err = g.writeCodeGeneratedComment(); err != nil {
return
}
if err = g.writeVersionComment(); err != nil {
return
}
if err = g.writeGeneratedDateComment(); err != nil {
return
}
if err = g.writeHeader(); err != nil {
return
}
if err = g.writePackage(); err != nil {
return
}
if err = g.writeImports(); err != nil {
return
}
if err = g.writeTemplateNodes(); err != nil {
return
}
if err = g.writeBlankAssignmentForRuntimeImport(); err != nil {
return
}
return err
}
// See https://pkg.go.dev/cmd/go#hdr-Generate_Go_files_by_processing_source
// Automatically generated files have a comment in the header that instructs the LSP
// to stop operating.
func (g *generator) writeCodeGeneratedComment() (err error) {
if g.options.SkipCodeGeneratedComment {
// Write an empty comment so that the file is the same shape.
_, err = g.w.Write("//\n\n")
return err
}
_, err = g.w.Write("// Code generated by templ - DO NOT EDIT.\n\n")
return err
}
func (g *generator) writeVersionComment() (err error) {
if g.options.Version != "" {
_, err = g.w.Write("// templ: version: " + g.options.Version + "\n")
}
return err
}
func (g *generator) writeGeneratedDateComment() (err error) {
if g.options.GeneratedDate != "" {
_, err = g.w.Write("// templ: generated: " + g.options.GeneratedDate + "\n")
}
return err
}
func (g *generator) writeHeader() (err error) {
if len(g.tf.Header) == 0 {
return nil
}
for _, n := range g.tf.Header {
if err := g.writeGoExpression(n); err != nil {
return err
}
}
return err
}
func (g *generator) writePackage() error {
var r parser.Range
var err error
// package ...
if r, err = g.w.Write(g.tf.Package.Expression.Value + "\n\n"); err != nil {
return err
}
g.sourceMap.Add(g.tf.Package.Expression, r)
if _, err = g.w.Write("//lint:file-ignore SA4006 This context is only used if a nested component is present.\n\n"); err != nil {
return err
}
return nil
}
func (g *generator) writeImports() error {
var err error
// Always import templ because it's the interface type of all templates.
if _, err = g.w.Write("import \"github.com/a-h/templ\"\n"); err != nil {
return err
}
if _, err = g.w.Write("import templruntime \"github.com/a-h/templ/runtime\"\n"); err != nil {
return err
}
if _, err = g.w.Write("\n"); err != nil {
return err
}
return nil
}
func (g *generator) writeTemplateNodes() error {
for i := 0; i < len(g.tf.Nodes); i++ {
switch n := g.tf.Nodes[i].(type) {
case parser.TemplateFileGoExpression:
if err := g.writeGoExpression(n); err != nil {
return err
}
case parser.HTMLTemplate:
if err := g.writeTemplate(i, n); err != nil {
return err
}
case parser.CSSTemplate:
if err := g.writeCSS(n); err != nil {
return err
}
case parser.ScriptTemplate:
if err := g.writeScript(n); err != nil {
return err
}
default:
return fmt.Errorf("unknown node type: %v", reflect.TypeOf(n))
}
}
return nil
}
func (g *generator) writeCSS(n parser.CSSTemplate) error {
var r parser.Range
var tgtSymbolRange parser.Range
var err error
var indentLevel int
// func
if r, err = g.w.Write("func "); err != nil {
return err
}
tgtSymbolRange.From = r.From
if r, err = g.w.Write(n.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(n.Expression, r)
// templ.CSSClass {
if _, err = g.w.Write(" templ.CSSClass {\n"); err != nil {
return err
}
{
indentLevel++
// templ_7745c5c3_CSSBuilder := templruntim.GetBuilder()
if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_CSSBuilder := templruntime.GetBuilder()\n"); err != nil {
return err
}
for i := 0; i < len(n.Properties); i++ {
switch p := n.Properties[i].(type) {
case parser.ConstantCSSProperty:
// Constant CSS property values are not sanitized.
if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_CSSBuilder.WriteString("+createGoString(p.String(true))+")\n"); err != nil {
return err
}
case parser.ExpressionCSSProperty:
// templ_7745c5c3_CSSBuilder.WriteString(templ.SanitizeCSS('name', p.Expression()))
if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf("templ_7745c5c3_CSSBuilder.WriteString(string(templ.SanitizeCSS(`%s`, ", p.Name)); err != nil {
return err
}
if r, err = g.w.Write(p.Value.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(p.Value.Expression, r)
if _, err = g.w.Write(")))\n"); err != nil {
return err
}
default:
return fmt.Errorf("unknown CSS property type: %v", reflect.TypeOf(p))
}
}
if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf("templ_7745c5c3_CSSID := templ.CSSID(`%s`, templ_7745c5c3_CSSBuilder.String())\n", n.Name)); err != nil {
return err
}
// return templ.CSS {
if _, err = g.w.WriteIndent(indentLevel, "return templ.ComponentCSSClass{\n"); err != nil {
return err
}
{
indentLevel++
// ID: templ_7745c5c3_CSSID,
if _, err = g.w.WriteIndent(indentLevel, "ID: templ_7745c5c3_CSSID,\n"); err != nil {
return err
}
// Class: templ.SafeCSS(".cssID{" + templ.CSSBuilder.String() + "}"),
if _, err = g.w.WriteIndent(indentLevel, "Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`),\n"); err != nil {
return err
}
indentLevel--
}
if _, err = g.w.WriteIndent(indentLevel, "}\n"); err != nil {
return err
}
indentLevel--
}
// }
if r, err = g.w.WriteIndent(indentLevel, "}\n\n"); err != nil {
return err
}
// Keep a track of symbol ranges for the LSP.
tgtSymbolRange.To = r.To
g.sourceMap.AddSymbolRange(n.Range, tgtSymbolRange)
return nil
}
func (g *generator) writeGoExpression(n parser.TemplateFileGoExpression) (err error) {
var tgtSymbolRange parser.Range
r, err := g.w.Write(n.Expression.Value)
if err != nil {
return err
}
tgtSymbolRange.From = r.From
g.sourceMap.Add(n.Expression, r)
v := n.Expression.Value
lineSlice := strings.Split(v, "\n")
lastLine := lineSlice[len(lineSlice)-1]
if strings.HasPrefix(lastLine, "//") {
if _, err = g.w.WriteIndent(0, "\n"); err != nil {
return err
}
return err
}
if r, err = g.w.WriteIndent(0, "\n\n"); err != nil {
return err
}
// Keep a track of symbol ranges for the LSP.
tgtSymbolRange.To = r.To
g.sourceMap.AddSymbolRange(n.Expression.Range, tgtSymbolRange)
return err
}
func (g *generator) writeTemplBuffer(indentLevel int) (err error) {
// templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)\n"); err != nil {
return err
}
// if !templ_7745c5c3_IsBuffer {
// defer func() {
// templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
// if templ_7745c5c3_Err == nil {
// templ_7745c5c3_Err = templ_7745c5c3_BufErr
// }
// }()
// }
if _, err = g.w.WriteIndent(indentLevel, "if !templ_7745c5c3_IsBuffer {\n"); err != nil {
return err
}
{
indentLevel++
if _, err = g.w.WriteIndent(indentLevel, "defer func() {\n"); err != nil {
return err
}
{
indentLevel++
if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)\n"); err != nil {
return err
}
if _, err = g.w.WriteIndent(indentLevel, "if templ_7745c5c3_Err == nil {\n"); err != nil {
return err
}
{
indentLevel++
if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_Err = templ_7745c5c3_BufErr\n"); err != nil {
return err
}
indentLevel--
}
if _, err = g.w.WriteIndent(indentLevel, "}\n"); err != nil {
return err
}
indentLevel--
}
if _, err = g.w.WriteIndent(indentLevel, "}()\n"); err != nil {
return err
}
indentLevel--
}
if _, err = g.w.WriteIndent(indentLevel, "}\n"); err != nil {
return err
}
return
}
func (g *generator) writeTemplate(nodeIdx int, t parser.HTMLTemplate) error {
var r parser.Range
var tgtSymbolRange parser.Range
var err error
var indentLevel int
// func
if r, err = g.w.Write("func "); err != nil {
return err
}
tgtSymbolRange.From = r.From
// (r *Receiver) Name(params []string)
if r, err = g.w.Write(t.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(t.Expression, r)
// templ.Component {
if _, err = g.w.Write(" templ.Component {\n"); err != nil {
return err
}
indentLevel++
// return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
if _, err = g.w.WriteIndent(indentLevel, "return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {\n"); err != nil {
return err
}
{
indentLevel++
if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context\n"); err != nil {
return err
}
if _, err = g.w.WriteIndent(indentLevel, "if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {\n"); err != nil {
return err
}
{
indentLevel++
if _, err = g.w.WriteIndent(indentLevel, "return templ_7745c5c3_CtxErr"); err != nil {
return err
}
indentLevel--
}
if _, err = g.w.WriteIndent(indentLevel, "}\n"); err != nil {
return err
}
if err := g.writeTemplBuffer(indentLevel); err != nil {
return err
}
// ctx = templ.InitializeContext(ctx)
if _, err = g.w.WriteIndent(indentLevel, "ctx = templ.InitializeContext(ctx)\n"); err != nil {
return err
}
g.childrenVar = g.createVariableName()
// templ_7745c5c3_Var1 := templ.GetChildren(ctx)
// if templ_7745c5c3_Var1 == nil {
// templ_7745c5c3_Var1 = templ.NopComponent
// }
if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf("%s := templ.GetChildren(ctx)\n", g.childrenVar)); err != nil {
return err
}
if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf("if %s == nil {\n", g.childrenVar)); err != nil {
return err
}
{
indentLevel++
if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf("%s = templ.NopComponent\n", g.childrenVar)); err != nil {
return err
}
indentLevel--
}
if _, err = g.w.WriteIndent(indentLevel, "}\n"); err != nil {
return err
}
// ctx = templ.ClearChildren(children)
if _, err = g.w.WriteIndent(indentLevel, "ctx = templ.ClearChildren(ctx)\n"); err != nil {
return err
}
// Nodes.
if err = g.writeNodes(indentLevel, stripWhitespace(t.Children), nil); err != nil {
return err
}
// return nil
if _, err = g.w.WriteIndent(indentLevel, "return nil\n"); err != nil {
return err
}
indentLevel--
}
// })
if _, err = g.w.WriteIndent(indentLevel, "})\n"); err != nil {
return err
}
indentLevel--
// }
// Note: gofmt wants to remove a single empty line at the end of a file
// so we have to make sure we don't output one if this is the last node.
closingBrace := "}\n\n"
if nodeIdx+1 >= len(g.tf.Nodes) {
closingBrace = "}\n"
}
if r, err = g.w.WriteIndent(indentLevel, closingBrace); err != nil {
return err
}
// Keep a track of symbol ranges for the LSP.
tgtSymbolRange.To = r.To
g.sourceMap.AddSymbolRange(t.Range, tgtSymbolRange)
return nil
}
func stripWhitespace(input []parser.Node) (output []parser.Node) {
for i, n := range input {
if _, isWhiteSpace := n.(parser.Whitespace); !isWhiteSpace {
output = append(output, input[i])
}
}
return output
}
func stripLeadingWhitespace(nodes []parser.Node) []parser.Node {
for i := 0; i < len(nodes); i++ {
n := nodes[i]
if _, isWhiteSpace := n.(parser.Whitespace); !isWhiteSpace {
return nodes[i:]
}
}
return []parser.Node{}
}
func stripTrailingWhitespace(nodes []parser.Node) []parser.Node {
for i := len(nodes) - 1; i >= 0; i-- {
n := nodes[i]
if _, isWhiteSpace := n.(parser.Whitespace); !isWhiteSpace {
return nodes[0 : i+1]
}
}
return []parser.Node{}
}
func stripLeadingAndTrailingWhitespace(nodes []parser.Node) []parser.Node {
return stripTrailingWhitespace(stripLeadingWhitespace(nodes))
}
func (g *generator) writeNodes(indentLevel int, nodes []parser.Node, next parser.Node) error {
for i, curr := range nodes {
var nextNode parser.Node
if i+1 < len(nodes) {
nextNode = nodes[i+1]
}
if nextNode == nil {
nextNode = next
}
if err := g.writeNode(indentLevel, curr, nextNode); err != nil {
return err
}
}
return nil
}
func (g *generator) writeNode(indentLevel int, current parser.Node, next parser.Node) (err error) {
switch n := current.(type) {
case parser.DocType:
err = g.writeDocType(indentLevel, n)
case parser.Element:
err = g.writeElement(indentLevel, n)
case parser.HTMLComment:
err = g.writeComment(indentLevel, n)
case parser.ChildrenExpression:
err = g.writeChildrenExpression(indentLevel)
case parser.RawElement:
err = g.writeRawElement(indentLevel, n)
case parser.ForExpression:
err = g.writeForExpression(indentLevel, n, next)
case parser.CallTemplateExpression:
err = g.writeCallTemplateExpression(indentLevel, n)
case parser.TemplElementExpression:
err = g.writeTemplElementExpression(indentLevel, n)
case parser.IfExpression:
err = g.writeIfExpression(indentLevel, n, next)
case parser.SwitchExpression:
err = g.writeSwitchExpression(indentLevel, n, next)
case parser.StringExpression:
err = g.writeStringExpression(indentLevel, n.Expression)
case parser.GoCode:
err = g.writeGoCode(indentLevel, n.Expression)
case parser.Whitespace:
err = g.writeWhitespace(indentLevel, n)
case parser.Text:
err = g.writeText(indentLevel, n)
case parser.GoComment:
// Do not render Go comments in the output HTML.
return
default:
return fmt.Errorf("unhandled type: %v", reflect.TypeOf(n))
}
// Write trailing whitespace, if there is a next node that might need the space.
// If the next node is inline or text, we might need it.
// If the current node is a block element, we don't need it.
needed := (isInlineOrText(current) && isInlineOrText(next))
if ws, ok := current.(parser.WhitespaceTrailer); ok && needed {
if err := g.writeWhitespaceTrailer(indentLevel, ws.Trailing()); err != nil {
return err
}
}
return
}
func isInlineOrText(next parser.Node) bool {
// While these are formatted as blocks when they're written in the HTML template.
// They're inline - i.e. there's no whitespace rendered around them at runtime for minification.
if next == nil {
return false
}
switch n := next.(type) {
case parser.IfExpression:
return true
case parser.SwitchExpression:
return true
case parser.ForExpression:
return true
case parser.Element:
return !n.IsBlockElement()
case parser.Text:
return true
case parser.StringExpression:
return true
}
return false
}
func (g *generator) writeWhitespaceTrailer(indentLevel int, n parser.TrailingSpace) (err error) {
if n == parser.SpaceNone {
return nil
}
// Normalize whitespace for minified output. In HTML, a single space is equivalent to
// any number of spaces, tabs, or newlines.
if n == parser.SpaceVertical {
n = parser.SpaceHorizontal
}
if _, err = g.w.WriteStringLiteral(indentLevel, string(n)); err != nil {
return err
}
return nil
}
func (g *generator) writeDocType(indentLevel int, n parser.DocType) (err error) {
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf("<!doctype %s>", n.Value)); err != nil {
return err
}
return nil
}
func (g *generator) writeIfExpression(indentLevel int, n parser.IfExpression, nextNode parser.Node) (err error) {
var r parser.Range
// if
if _, err = g.w.WriteIndent(indentLevel, `if `); err != nil {
return err
}
// x == y {
if r, err = g.w.Write(n.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(n.Expression, r)
// {
if _, err = g.w.Write(` {` + "\n"); err != nil {
return err
}
{
indentLevel++
if err = g.writeNodes(indentLevel, stripLeadingAndTrailingWhitespace(n.Then), nextNode); err != nil {
return err
}
indentLevel--
}
for _, elseIf := range n.ElseIfs {
// } else if {
if _, err = g.w.WriteIndent(indentLevel, `} else if `); err != nil {
return err
}
// x == y {
if r, err = g.w.Write(elseIf.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(elseIf.Expression, r)
// {
if _, err = g.w.Write(` {` + "\n"); err != nil {
return err
}
{
indentLevel++
if err = g.writeNodes(indentLevel, stripLeadingAndTrailingWhitespace(elseIf.Then), nextNode); err != nil {
return err
}
indentLevel--
}
}
if len(n.Else) > 0 {
// } else {
if _, err = g.w.WriteIndent(indentLevel, `} else {`+"\n"); err != nil {
return err
}
{
indentLevel++
if err = g.writeNodes(indentLevel, stripLeadingAndTrailingWhitespace(n.Else), nextNode); err != nil {
return err
}
indentLevel--
}
}
// }
if _, err = g.w.WriteIndent(indentLevel, `}`+"\n"); err != nil {
return err
}
return nil
}
func (g *generator) writeSwitchExpression(indentLevel int, n parser.SwitchExpression, next parser.Node) (err error) {
var r parser.Range
// switch
if _, err = g.w.WriteIndent(indentLevel, `switch `); err != nil {
return err
}
// val
if r, err = g.w.Write(n.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(n.Expression, r)
// {
if _, err = g.w.Write(` {` + "\n"); err != nil {
return err
}
if len(n.Cases) > 0 {
for _, c := range n.Cases {
// case x:
// default:
if r, err = g.w.WriteIndent(indentLevel, c.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(c.Expression, r)
indentLevel++
if err = g.writeNodes(indentLevel, stripLeadingAndTrailingWhitespace(c.Children), next); err != nil {
return err
}
indentLevel--
}
}
// }
if _, err = g.w.WriteIndent(indentLevel, `}`+"\n"); err != nil {
return err
}
return nil
}
func (g *generator) writeChildrenExpression(indentLevel int) (err error) {
if _, err = g.w.WriteIndent(indentLevel, fmt.Sprintf("templ_7745c5c3_Err = %s.Render(ctx, templ_7745c5c3_Buffer)\n", g.childrenVar)); err != nil {
return err
}
if err = g.writeErrorHandler(indentLevel); err != nil {
return err
}
return nil
}
func (g *generator) writeTemplElementExpression(indentLevel int, n parser.TemplElementExpression) (err error) {
if len(n.Children) == 0 {
return g.writeSelfClosingTemplElementExpression(indentLevel, n)
}
return g.writeBlockTemplElementExpression(indentLevel, n)
}
func (g *generator) writeBlockTemplElementExpression(indentLevel int, n parser.TemplElementExpression) (err error) {
var r parser.Range
childrenName := g.createVariableName()
if _, err = g.w.WriteIndent(indentLevel, childrenName+" := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {\n"); err != nil {
return err
}
indentLevel++
if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context\n"); err != nil {
return err
}
if err := g.writeTemplBuffer(indentLevel); err != nil {
return err
}
// ctx = templ.InitializeContext(ctx)
if _, err = g.w.WriteIndent(indentLevel, "ctx = templ.InitializeContext(ctx)\n"); err != nil {
return err
}
if err = g.writeNodes(indentLevel, stripLeadingAndTrailingWhitespace(n.Children), nil); err != nil {
return err
}
// return nil
if _, err = g.w.WriteIndent(indentLevel, "return nil\n"); err != nil {
return err
}
indentLevel--
if _, err = g.w.WriteIndent(indentLevel, "})\n"); err != nil {
return err
}
if _, err = g.w.WriteIndent(indentLevel, `templ_7745c5c3_Err = `); err != nil {
return err
}
if r, err = g.w.Write(n.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(n.Expression, r)
// .Render(templ.WithChildren(ctx, children), templ_7745c5c3_Buffer)
if _, err = g.w.Write(".Render(templ.WithChildren(ctx, " + childrenName + "), templ_7745c5c3_Buffer)\n"); err != nil {
return err
}
if err = g.writeErrorHandler(indentLevel); err != nil {
return err
}
return nil
}
func (g *generator) writeSelfClosingTemplElementExpression(indentLevel int, n parser.TemplElementExpression) (err error) {
if _, err = g.w.WriteIndent(indentLevel, `templ_7745c5c3_Err = `); err != nil {
return err
}
// Template expression.
var r parser.Range
if r, err = g.w.Write(n.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(n.Expression, r)
// .Render(ctx, templ_7745c5c3_Buffer)
if _, err = g.w.Write(".Render(ctx, templ_7745c5c3_Buffer)\n"); err != nil {
return err
}
if err = g.writeErrorHandler(indentLevel); err != nil {
return err
}
return nil
}
func (g *generator) writeCallTemplateExpression(indentLevel int, n parser.CallTemplateExpression) (err error) {
if _, err = g.w.WriteIndent(indentLevel, `templ_7745c5c3_Err = `); err != nil {
return err
}
// Template expression.
var r parser.Range
if r, err = g.w.Write(n.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(n.Expression, r)
// .Render(ctx, templ_7745c5c3_Buffer)
if _, err = g.w.Write(".Render(ctx, templ_7745c5c3_Buffer)\n"); err != nil {
return err
}
if err = g.writeErrorHandler(indentLevel); err != nil {
return err
}
return nil
}
func (g *generator) writeForExpression(indentLevel int, n parser.ForExpression, next parser.Node) (err error) {
var r parser.Range
// for
if _, err = g.w.WriteIndent(indentLevel, `for `); err != nil {
return err
}
// i, v := range p.Stuff
if r, err = g.w.Write(n.Expression.Value); err != nil {
return err
}
g.sourceMap.Add(n.Expression, r)
// {
if _, err = g.w.Write(` {` + "\n"); err != nil {
return err
}
// Children.
indentLevel++
if err = g.writeNodes(indentLevel, stripLeadingAndTrailingWhitespace(n.Children), next); err != nil {
return err
}
indentLevel--
// }
if _, err = g.w.WriteIndent(indentLevel, `}`+"\n"); err != nil {
return err
}
return nil
}
func (g *generator) writeErrorHandler(indentLevel int) (err error) {
_, err = g.w.WriteIndent(indentLevel, "if templ_7745c5c3_Err != nil {\n")
if err != nil {
return err
}
indentLevel++
_, err = g.w.WriteIndent(indentLevel, "return templ_7745c5c3_Err\n")
if err != nil {
return err
}
indentLevel--
_, err = g.w.WriteIndent(indentLevel, "}\n")
if err != nil {
return err
}
return err
}
func (g *generator) writeExpressionErrorHandler(indentLevel int, expression parser.Expression) (err error) {
_, err = g.w.WriteIndent(indentLevel, "if templ_7745c5c3_Err != nil {\n")
if err != nil {
return err
}
indentLevel++
line := int(expression.Range.To.Line + 1)
col := int(expression.Range.To.Col)
_, err = g.w.WriteIndent(indentLevel, "return templ.Error{Err: templ_7745c5c3_Err, FileName: "+createGoString(g.options.FileName)+", Line: "+strconv.Itoa(line)+", Col: "+strconv.Itoa(col)+"}\n")
if err != nil {
return err
}
indentLevel--
_, err = g.w.WriteIndent(indentLevel, "}\n")
if err != nil {
return err
}
return err
}
func copyAttributes(attr []parser.Attribute) []parser.Attribute {
o := make([]parser.Attribute, len(attr))
for i, a := range attr {
if c, ok := a.(parser.ConditionalAttribute); ok {
c.Then = copyAttributes(c.Then)
c.Else = copyAttributes(c.Else)
o[i] = c
continue
}
o[i] = a
}
return o
}
func (g *generator) writeElement(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 {
return err
}
} else {
attrs := copyAttributes(n.Attributes)
// <style type="text/css"></style>
if err = g.writeElementCSS(indentLevel, attrs); err != nil {
return err
}
// <script></script>
if err = g.writeElementScript(indentLevel, attrs); err != nil {
return err
}
// <div
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(`<%s`, html.EscapeString(n.Name))); err != nil {
return err
}
if err = g.writeElementAttributes(indentLevel, n.Name, attrs); err != nil {
return err
}
// >
if _, err = g.w.WriteStringLiteral(indentLevel, `>`); err != nil {
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
}
// </div>
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(`</%s>`, html.EscapeString(n.Name))); err != nil {
return err
}
return err
}
func (g *generator) writeAttributeCSS(indentLevel int, attr parser.ExpressionAttribute) (result parser.ExpressionAttribute, ok bool, err error) {
var r parser.Range
name := html.EscapeString(attr.Name)
if name != "class" {
ok = false
return
}
// Create a class name for the style.
// The expression can either be expecting a templ.Classes call, or an expression that returns
// var templ_7745c5c3_CSSClasses = []any{
classesName := g.createVariableName()
if _, err = g.w.WriteIndent(indentLevel, "var "+classesName+" = []any{"); err != nil {
return
}
// p.Name()
if r, err = g.w.Write(attr.Expression.Value); err != nil {
return
}
g.sourceMap.Add(attr.Expression, r)
// }\n
if _, err = g.w.Write("}\n"); err != nil {
return
}
// Render the CSS before the element if required.
// templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_CSSClasses...)
if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, "+classesName+"...)\n"); err != nil {
return
}
if err = g.writeErrorHandler(indentLevel); err != nil {
return
}
// Rewrite the ExpressionAttribute to point at the new variable.
attr.Expression = parser.Expression{
Value: "templ.CSSClasses(" + classesName + ").String()",
}
return attr, true, nil
}
func (g *generator) writeAttributesCSS(indentLevel int, attrs []parser.Attribute) (err error) {
for i := 0; i < len(attrs); i++ {
if attr, ok := attrs[i].(parser.ExpressionAttribute); ok {
attr, ok, err = g.writeAttributeCSS(indentLevel, attr)
if err != nil {
return err
}
if ok {
attrs[i] = attr
}
}
if cattr, ok := attrs[i].(parser.ConditionalAttribute); ok {
err = g.writeAttributesCSS(indentLevel, cattr.Then)
if err != nil {
return err
}
err = g.writeAttributesCSS(indentLevel, cattr.Else)
if err != nil {
return err
}
attrs[i] = cattr
}
}
return nil
}
func (g *generator) writeElementCSS(indentLevel int, attrs []parser.Attribute) (err error) {
return g.writeAttributesCSS(indentLevel, attrs)
}
func isScriptAttribute(name string) bool {
for _, prefix := range []string{"on", "hx-on:"} {
if strings.HasPrefix(name, prefix) {
return true
}
}
return false
}
func (g *generator) writeElementScript(indentLevel int, attrs []parser.Attribute) (err error) {
var scriptExpressions []string
for _, attr := range attrs {
scriptExpressions = append(scriptExpressions, getAttributeScripts(attr)...)
}
if len(scriptExpressions) == 0 {
return
}
// Render the scripts before the element if required.
// templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, a, b, c)
if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_Err = templ.RenderScriptItems(ctx, templ_7745c5c3_Buffer, "+strings.Join(scriptExpressions, ", ")+")\n"); err != nil {
return err
}
if err = g.writeErrorHandler(indentLevel); err != nil {
return err
}
return err
}
func getAttributeScripts(attr parser.Attribute) (scripts []string) {
if attr, ok := attr.(parser.ConditionalAttribute); ok {
for _, attr := range attr.Then {
scripts = append(scripts, getAttributeScripts(attr)...)
}
for _, attr := range attr.Else {
scripts = append(scripts, getAttributeScripts(attr)...)
}
}
if attr, ok := attr.(parser.ExpressionAttribute); ok {
name := html.EscapeString(attr.Name)
if isScriptAttribute(name) {
scripts = append(scripts, attr.Expression.Value)
}
}
return scripts
}
func (g *generator) writeBoolConstantAttribute(indentLevel int, attr parser.BoolConstantAttribute) (err error) {
name := html.EscapeString(attr.Name)
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(` %s`, name)); err != nil {
return err
}
return nil
}
func (g *generator) writeConstantAttribute(indentLevel int, attr parser.ConstantAttribute) (err error) {
name := html.EscapeString(attr.Name)
value := html.EscapeString(attr.Value)
value = strconv.Quote(value)
value = value[1 : len(value)-1]
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(` %s=\"%s\"`, name, value)); err != nil {
return err
}
return nil
}
func (g *generator) writeBoolExpressionAttribute(indentLevel int, attr parser.BoolExpressionAttribute) (err error) {
name := html.EscapeString(attr.Name)
// if
if _, err = g.w.WriteIndent(indentLevel, `if `); err != nil {
return err
}
// x == y
var r parser.Range
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
}
{
indentLevel++
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(` %s`, name)); err != nil {
return err
}
indentLevel--
}
// }
if _, err = g.w.WriteIndent(indentLevel, `}`+"\n"); err != nil {
return err
}
return nil
}
func (g *generator) writeExpressionAttributeValueURL(indentLevel int, attr parser.ExpressionAttribute) (err error) {
vn := g.createVariableName()
// var vn templ.SafeURL =
if _, err = g.w.WriteIndent(indentLevel, "var "+vn+" templ.SafeURL = "); err != nil {
return err
}
// p.Name()
var r parser.Range
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.w.WriteIndent(indentLevel, "_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string("+vn+")))\n"); err != nil {
return err
}
return g.writeErrorHandler(indentLevel)
}
func (g *generator) writeExpressionAttributeValueScript(indentLevel int, attr parser.ExpressionAttribute) (err error) {
// It's a JavaScript handler, and requires special handling, because we expect a JavaScript expression.
vn := g.createVariableName()
// var vn templ.ComponentScript =
if _, err = g.w.WriteIndent(indentLevel, "var "+vn+" templ.ComponentScript = "); err != nil {
return err
}
// p.Name()
var r parser.Range
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.w.WriteIndent(indentLevel, "_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("+vn+".Call)\n"); err != nil {
return err
}
return g.writeErrorHandler(indentLevel)
}
func (g *generator) writeExpressionAttributeValueDefault(indentLevel int, attr parser.ExpressionAttribute) (err error) {
var r parser.Range
vn := g.createVariableName()
// var vn string
if _, err = g.w.WriteIndent(indentLevel, "var "+vn+" string\n"); err != nil {
return err
}
// vn, templ_7745c5c3_Err = templ.JoinStringErrs(
if _, err = g.w.WriteIndent(indentLevel, vn+", templ_7745c5c3_Err = templ.JoinStringErrs("); 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
}
// Attribute expression error handler.
err = g.writeExpressionErrorHandler(indentLevel, attr.Expression)
if err != nil {
return err
}
// _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(vn)
if _, err = g.w.WriteIndent(indentLevel, "_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("+vn+"))\n"); err != nil {
return err
}
return g.writeErrorHandler(indentLevel)
}
func (g *generator) writeExpressionAttributeValueStyle(indentLevel int, attr parser.ExpressionAttribute) (err error) {
var r parser.Range
vn := g.createVariableName()
// var vn string
if _, err = g.w.WriteIndent(indentLevel, "var "+vn+" string\n"); err != nil {
return err
}
// vn, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(
if _, err = g.w.WriteIndent(indentLevel, vn+", templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("); err != nil {
return err
}
// value
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
}
// Attribute expression error handler.
err = g.writeExpressionErrorHandler(indentLevel, attr.Expression)
if err != nil {
return err
}
// _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(vn))
if _, err = g.w.WriteIndent(indentLevel, "_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("+vn+"))\n"); err != nil {
return err
}
return g.writeErrorHandler(indentLevel)
}
func (g *generator) writeExpressionAttribute(indentLevel int, elementName string, attr parser.ExpressionAttribute) (err error) {
attrName := html.EscapeString(attr.Name)
// Name
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(` %s=`, attrName)); err != nil {
return err
}
// Open quote.
if _, err = g.w.WriteStringLiteral(indentLevel, `\"`); err != nil {
return err
}
// Value.
if (elementName == "a" && attr.Name == "href") || (elementName == "form" && attr.Name == "action") {
if err := g.writeExpressionAttributeValueURL(indentLevel, attr); err != nil {
return err
}
} else if isScriptAttribute(attr.Name) {
if err := g.writeExpressionAttributeValueScript(indentLevel, attr); err != nil {
return err
}
} else if attr.Name == "style" {
if err := g.writeExpressionAttributeValueStyle(indentLevel, attr); err != nil {
return err
}
} else {
if err := g.writeExpressionAttributeValueDefault(indentLevel, attr); err != nil {
return err
}
}
// Close quote.
if _, err = g.w.WriteStringLiteral(indentLevel, `\"`); err != nil {
return err
}
return nil
}
func (g *generator) writeSpreadAttributes(indentLevel int, attr parser.SpreadAttributes) (err error) {
// templ.RenderAttributes(ctx, w, spreadAttrs)
if _, err = g.w.WriteIndent(indentLevel, `templ_7745c5c3_Err = templ.RenderAttributes(ctx, templ_7745c5c3_Buffer, `); err != nil {
return err
}
// spreadAttrs
var r parser.Range
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
}
return nil
}
func (g *generator) writeConditionalAttribute(indentLevel int, elementName string, attr parser.ConditionalAttribute) (err error) {
// if
if _, err = g.w.WriteIndent(indentLevel, `if `); err != nil {
return err
}
// x == y
var r parser.Range
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
}
{
indentLevel++
if err = g.writeElementAttributes(indentLevel, elementName, attr.Then); err != nil {
return err
}
indentLevel--
}
if len(attr.Else) > 0 {
// } else {
if _, err = g.w.WriteIndent(indentLevel, `} else {`+"\n"); err != nil {
return err
}
{
indentLevel++
if err = g.writeElementAttributes(indentLevel, elementName, attr.Else); err != nil {
return err
}
indentLevel--
}
}
// }
if _, err = g.w.WriteIndent(indentLevel, `}`+"\n"); err != nil {
return err
}
return nil
}
func (g *generator) writeElementAttributes(indentLevel int, name string, attrs []parser.Attribute) (err error) {
for i := 0; i < len(attrs); i++ {
switch attr := attrs[i].(type) {
case parser.BoolConstantAttribute:
err = g.writeBoolConstantAttribute(indentLevel, attr)
case parser.ConstantAttribute:
err = g.writeConstantAttribute(indentLevel, attr)
case parser.BoolExpressionAttribute:
err = g.writeBoolExpressionAttribute(indentLevel, attr)
case parser.ExpressionAttribute:
err = g.writeExpressionAttribute(indentLevel, name, attr)
case parser.SpreadAttributes:
err = g.writeSpreadAttributes(indentLevel, attr)
case parser.ConditionalAttribute:
err = g.writeConditionalAttribute(indentLevel, name, attr)
default:
err = fmt.Errorf("unknown attribute type %s", reflect.TypeOf(attrs[i]))
}
}
return
}
func (g *generator) writeRawElement(indentLevel int, n parser.RawElement) (err error) {
if len(n.Attributes) == 0 {
// <div>
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(`<%s>`, html.EscapeString(n.Name))); err != nil {
return err
}
} else {
// <script></script>
if err = g.writeElementScript(indentLevel, n.Attributes); err != nil {
return err
}
// <div
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
}
}
// Contents.
if err = g.writeText(indentLevel, parser.Text{Value: n.Contents}); err != nil {
return err
}
// </div>
if _, err = g.w.WriteStringLiteral(indentLevel, fmt.Sprintf(`</%s>`, html.EscapeString(n.Name))); err != nil {
return err
}
return err
}
func (g *generator) writeComment(indentLevel int, c parser.HTMLComment) (err error) {
// <!--
if _, err = g.w.WriteStringLiteral(indentLevel, "<!--"); err != nil {
return err
}
// Contents.
if err = g.writeText(indentLevel, parser.Text{Value: c.Contents}); err != nil {
return err
}
// -->
if _, err = g.w.WriteStringLiteral(indentLevel, "-->"); err != nil {
return err
}
return err
}
func (g *generator) createVariableName() string {
g.variableID++
return "templ_7745c5c3_Var" + strconv.Itoa(g.variableID)
}
func (g *generator) writeGoCode(indentLevel int, e parser.Expression) (err error) {
if strings.TrimSpace(e.Value) == "" {
return
}
var r parser.Range
if r, err = g.w.WriteIndent(indentLevel, e.Value+"\n"); err != nil {
return err
}
g.sourceMap.Add(e, r)
return nil
}
func (g *generator) writeStringExpression(indentLevel int, e parser.Expression) (err error) {
if strings.TrimSpace(e.Value) == "" {
return
}
var r parser.Range
vn := g.createVariableName()
// var vn string
if _, err = g.w.WriteIndent(indentLevel, "var "+vn+" string\n"); err != nil {
return err
}
// vn, templ_7745c5c3_Err = templ.JoinStringErrs(
if _, err = g.w.WriteIndent(indentLevel, vn+", templ_7745c5c3_Err = templ.JoinStringErrs("); err != nil {
return err
}
// p.Name()
if r, err = g.w.Write(e.Value); err != nil {
return err
}
g.sourceMap.Add(e, r)
// )
if _, err = g.w.Write(")\n"); err != nil {
return err
}
// String expression error handler.
err = g.writeExpressionErrorHandler(indentLevel, e)
if err != nil {
return err
}
// _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(vn)
if _, err = g.w.WriteIndent(indentLevel, "_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString("+vn+"))\n"); err != nil {
return err
}
if err = g.writeErrorHandler(indentLevel); err != nil {
return err
}
return nil
}
func (g *generator) writeWhitespace(indentLevel int, n parser.Whitespace) (err error) {
if len(n.Value) == 0 {
return
}
// _, err = templ_7745c5c3_Buffer.WriteString(` `)
if _, err = g.w.WriteStringLiteral(indentLevel, " "); err != nil {
return err
}
return nil
}
func (g *generator) writeText(indentLevel int, n parser.Text) (err error) {
quoted := strconv.Quote(n.Value)
_, err = g.w.WriteStringLiteral(indentLevel, quoted[1:len(quoted)-1])
return err
}
func createGoString(s string) string {
var sb strings.Builder
sb.WriteRune('`')
sects := strings.Split(s, "`")
for i := 0; i < len(sects); i++ {
sb.WriteString(sects[i])
if len(sects) > i+1 {
sb.WriteString("` + \"`\" + `")
}
}
sb.WriteRune('`')
return sb.String()
}
func (g *generator) writeScript(t parser.ScriptTemplate) error {
var r parser.Range
var tgtSymbolRange parser.Range
var err error
var indentLevel int
// func
if r, err = g.w.Write("func "); err != nil {
return err
}
tgtSymbolRange.From = r.From
if r, err = g.w.Write(t.Name.Value); err != nil {
return err
}
g.sourceMap.Add(t.Name, r)
// (
if _, err = g.w.Write("("); err != nil {
return err
}
// Write parameters.
if r, err = g.w.Write(t.Parameters.Value); err != nil {
return err
}
g.sourceMap.Add(t.Parameters, r)
// ) templ.ComponentScript {
if _, err = g.w.Write(") templ.ComponentScript {\n"); err != nil {
return err
}
indentLevel++
// return templ.ComponentScript{
if _, err = g.w.WriteIndent(indentLevel, "return templ.ComponentScript{\n"); err != nil {
return err
}
{
indentLevel++
fn := functionName(t.Name.Value, t.Value)
goFn := createGoString(fn)
// Name: "scriptName",
if _, err = g.w.WriteIndent(indentLevel, "Name: "+goFn+",\n"); err != nil {
return err
}
// Function: `function scriptName(a, b, c){` + `constantScriptValue` + `}`,
prefix := "function " + fn + "(" + stripTypes(t.Parameters.Value) + "){"
body := strings.TrimLeftFunc(t.Value, unicode.IsSpace)
suffix := "}"
if _, err = g.w.WriteIndent(indentLevel, "Function: "+createGoString(prefix+body+suffix)+",\n"); err != nil {
return err
}
// Call: templ.SafeScript(scriptName, a, b, c)
if _, err = g.w.WriteIndent(indentLevel, "Call: templ.SafeScript("+goFn+", "+stripTypes(t.Parameters.Value)+"),\n"); err != nil {
return err
}
// CallInline: templ.SafeScriptInline(scriptName, a, b, c)
if _, err = g.w.WriteIndent(indentLevel, "CallInline: templ.SafeScriptInline("+goFn+", "+stripTypes(t.Parameters.Value)+"),\n"); err != nil {
return err
}
indentLevel--
}
// }
if _, err = g.w.WriteIndent(indentLevel, "}\n"); err != nil {
return err
}
indentLevel--
// }
if r, err = g.w.WriteIndent(indentLevel, "}\n\n"); err != nil {
return err
}
// Keep track of the symbol range for the LSP.
tgtSymbolRange.To = r.To
g.sourceMap.AddSymbolRange(t.Range, tgtSymbolRange)
return nil
}
// writeBlankAssignmentForRuntimeImport writes out a blank identifier assignment.
// This ensures that even if the github.com/a-h/templ/runtime package is not used in the generated code,
// the Go compiler will not complain about the unused import.
func (g *generator) writeBlankAssignmentForRuntimeImport() error {
var err error
if _, err = g.w.Write("var _ = templruntime.GeneratedTemplate"); err != nil {
return err
}
return nil
}
func functionName(name string, body string) string {
h := sha256.New()
h.Write([]byte(body))
hp := hex.EncodeToString(h.Sum(nil))[0:4]
return "__templ_" + name + "_" + hp
}
func stripTypes(parameters string) string {
variableNames := []string{}
params := strings.Split(parameters, ",")
for i := 0; i < len(params); i++ {
p := strings.Split(strings.TrimSpace(params[i]), " ")
variableNames = append(variableNames, strings.TrimSpace(p[0]))
}
return strings.Join(variableNames, ", ")
}