]> git.earlybird.gay Git - today/commitdiff
make conditional params work
authorearly <me@earlybird.gay>
Sun, 25 Aug 2024 06:49:57 +0000 (00:49 -0600)
committerearly <me@earlybird.gay>
Sun, 25 Aug 2024 06:49:57 +0000 (00:49 -0600)
internal/compile/compile.go
internal/compile/template.go
render/data.go

index 7b12752ea0f971d5b652ca32a66a0a178c4742c5..16554a0919fc48347b59aca28ccd79bf9b7062fa 100644 (file)
@@ -5,6 +5,7 @@ import (
        "errors"
        "html/template"
        "maps"
+       "regexp"
        "strings"
 
        "git.earlybird.gay/today-engine/include"
@@ -116,15 +117,35 @@ func Compile(root TemplateSource, transform ...func(root *html.Node)) (Result, e
                tf(document)
        }
 
-       buf := new(strings.Builder)
-       err = html.Render(buf, document)
+       raw, err := renderDocument(document)
        if err != nil {
                return result, err
        }
        result = Result{
-               TemplateRaw:        html.UnescapeString(buf.String()),
+               TemplateRaw:        raw,
                TemplateFuncs:      templateFuncs,
                TemplateDataLoader: computeRoot,
        }
        return result, err
 }
+
+func renderDocument(document *html.Node) (string, error) {
+       // Basic render
+       buf := new(strings.Builder)
+       err := html.Render(buf, document)
+       if err != nil {
+               return "", err
+       }
+       raw := html.UnescapeString(buf.String())
+
+       // Clean boolean attributes
+       raw = removeEmptyAttrValues(raw)
+
+       return raw, nil
+}
+
+var emptyAttrRegex = regexp.MustCompile(`%%(?P<templateattr>[^<>]+?)%%=""`)
+
+func removeEmptyAttrValues(raw string) string {
+       return emptyAttrRegex.ReplaceAllString(raw, "$templateattr")
+}
index a53bdc69ff9141d56266e9d64808e9ee41a6e87a..bbac33702873cc96dc9e70f7d786e1650c2e17c7 100644 (file)
@@ -19,6 +19,7 @@ const (
 
 var funcRegexp = regexp.MustCompile(`^[a-zA-Z]\w*$`)
 var fieldRegexp = regexp.MustCompile(`^(?:.[a-zA-Z]\w*)+$`)
+var pipelineTokens = []string{"with", "if"}
 
 func isFunc(token string) bool {
        return funcRegexp.MatchString(token)
@@ -28,8 +29,22 @@ func isField(token string) bool {
        return fieldRegexp.MatchString(token)
 }
 
+func isPipeline(token string) bool {
+       for _, ptok := range pipelineTokens {
+               if strings.Contains(token, ptok) {
+                       return true
+               }
+       }
+       return false
+}
+
+func isEndPipeline(token string) bool {
+       return strings.Contains(token, "end")
+}
+
+// replaceTemplateFuncs replaces template functions in a node with their
+// namespaced versions.
 func replaceTemplateFuncs(source TemplateSource, n *html.Node) {
-       var done, inTemplate bool
        var datas []string
        var sets []func(string)
 
@@ -40,14 +55,69 @@ func replaceTemplateFuncs(source TemplateSource, n *html.Node) {
                        n.Data = s
                })
        case html.ElementNode:
+               depth := 0
+               inTemplate := false
+               var key, space string
                for _, attr := range n.Attr {
+                       key += space + attr.Key
+                       // Track if in a template in the attributes list
+                       if !inTemplate && strings.HasPrefix(attr.Key, tokenOpenTemplate) {
+                               inTemplate = true
+                               if depth == 0 {
+                                       key = "%%" + key
+                               }
+                       }
+                       // If in a template, and the current key indicates entering a
+                       // template pipeline, increase depth
+                       if inTemplate && isPipeline(attr.Key) {
+                               depth++
+                       }
+                       if inTemplate && isEndPipeline(attr.Key) {
+                               depth--
+                       }
+                       // Track exiting a template
+                       if inTemplate && strings.HasSuffix(attr.Key, tokenCloseTemplate) {
+                               inTemplate = false
+                               if depth == 0 {
+                                       key = key + "%%"
+                               }
+                       }
+                       // Accumulate keys if they are in a template, or outside of a template
+                       // but inside a pipeline
+                       if inTemplate || depth > 0 {
+                               space = " "
+                               // Accumulate attribute values outside of templates into the key
+                               // IF the attribute is rendered as a result of a pipeline
+                               if depth > 0 && attr.Val != "" {
+                                       key += `="` + attr.Val + `"`
+                                       space = ""
+                                       continue
+                               }
+                               continue
+                       }
+                       setKey := key
+                       // set 1; process key and transform setKey
+                       datas = append(datas, setKey)
+                       sets = append(sets, func(s string) {
+                               setKey = s
+                       })
+
+                       // set 2; set attr setKey="s"
                        datas = append(datas, attr.Val)
                        sets = append(sets, func(s string) {
-                               htmltree.SetAttr(n, attr.Key, s)
+                               htmltree.SetAttr(n, setKey, s)
                        })
+
+                       // Reset key and spacing
+                       key = ""
+                       space = ""
                }
+
+               // Reset attributes
+               n.Attr = make([]html.Attribute, 0, len(n.Attr))
        }
        for i, data := range datas {
+               var done, inTemplate bool
                var output []string
                for {
                        endToken := strings.Index(data, " ")
@@ -56,7 +126,6 @@ func replaceTemplateFuncs(source TemplateSource, n *html.Node) {
                                done = true
                        }
                        token := data[:endToken]
-
                        // Track when we're in a template using open/close brackets
                        if token == tokenOpenTemplate {
                                inTemplate = true
@@ -178,6 +247,7 @@ func insertTemplateSource(subSource TemplateSource, context *html.Node) (*comput
        // Remove :data attributes from the root of context, and prepare them
        // to be used as data for the computeNode.
        asData := removeDataAttrs(context)
+       asData["id"] = insertedId
        // ===== SUBSOURCE PROCESSING =====
        // Parse the subSource in the context of context.
        // Require that subSource be contained in a template.
index 5029a849a7796208cd11e4708de15a559a3c663d..d76dc2ff62d3578c91093f6efca18c8467144252 100644 (file)
@@ -20,15 +20,19 @@ func (d Data) Get(key string) any {
        return d[key]
 }
 
+func (d Data) Request() *http.Request {
+       return d.Get("request").(*http.Request)
+}
+
+func (d Data) ID() string {
+       return d.Get("id").(string)
+}
+
 // Set sets key to value.
-// Any key may only be set *once*. Trying to set it again will panic.
 func (d Data) Set(key string, value any) {
        if !keyRegexp.MatchString(key) {
                panic("key " + key + " is not valid")
        }
-       if _, ok := d[key]; ok {
-               panic("key " + key + " already set")
-       }
        d[key] = value
 }