Support FixedIndent, add tests

This commit is contained in:
Andinus 2024-11-16 11:11:07 +05:30
parent 5b7e923bd8
commit c9c7dd1b22
Signed by: andinus
GPG Key ID: B67D55D482A799FD
2 changed files with 127 additions and 12 deletions

@ -16,11 +16,12 @@ type Hash map[string]interface{}
type Option struct { type Option struct {
Delimiters [2]string Delimiters [2]string
NameLabel string // Identify the template to be used NameLabel string // Identify the template to be used
TemplateDir string // directory where templates are located TemplateDir string // Directory where templates are located
TemplateExtension string // appended on the label to idenfity the template TemplateExtension string // Appended on the label to idenfity the template
DieOnBadParams bool // attempt to populate a variable that doesn't exist should result in an error DieOnBadParams bool // Attempt to populate a variable that doesn't exist should result in an error
ShowLabels bool // Prepend & Append a string to every template, helpful in debugging ShowLabels bool // Prepend & Append a string to every template, helpful in debugging
CommentDelimiters [2]string // Used in conjunction with ShowLabels, if HTML then use '<!--', '-->' CommentDelimiters [2]string // Used in conjunction with ShowLabels, if HTML then use '<!--', '-->'
FixedIndent bool // Intended to improve readability when inspecting nested templates
} }
type TemplateNest struct { type TemplateNest struct {
@ -118,6 +119,7 @@ func (nest *TemplateNest) index(filePath string) (TemplateFileIndex, error) {
if err != nil { if err != nil {
return TemplateFileIndex{}, fmt.Errorf("error reading file (`%s`): %w", filePath, err) return TemplateFileIndex{}, fmt.Errorf("error reading file (`%s`): %w", filePath, err)
} }
// Capture last modified time // Capture last modified time
fileInfo, err := os.Stat(filePath) fileInfo, err := os.Stat(filePath)
if err != nil { if err != nil {
@ -134,6 +136,7 @@ func (nest *TemplateNest) index(filePath string) (TemplateFileIndex, error) {
"%s\\s*(.+?)\\s*%s", regexp.QuoteMeta(delimiterStart), regexp.QuoteMeta(delimiterEnd), "%s\\s*(.+?)\\s*%s", regexp.QuoteMeta(delimiterStart), regexp.QuoteMeta(delimiterEnd),
)) ))
contentsStr := string(contents)
matches := re.FindAllStringSubmatchIndex(string(contents), -1) matches := re.FindAllStringSubmatchIndex(string(contents), -1)
for _, match := range matches { for _, match := range matches {
if len(match) < 4 { if len(match) < 4 {
@ -148,15 +151,33 @@ func (nest *TemplateNest) index(filePath string) (TemplateFileIndex, error) {
varName := string(contents[nameStartIdx:nameEndIdx]) varName := string(contents[nameStartIdx:nameEndIdx])
variableNames[varName] = struct{}{} variableNames[varName] = struct{}{}
// If fixed indent is enabled then record the indent level for this
// variable. To get the indent level we look at each character in
// reverse from the start position of the variable until we find a
// newline character.
indentLevel := 0
if nest.option.FixedIndent {
// If we do not encounter a newline then that means this variable is
// on the first line, we take the start position as the indent
// level.
lineStartIdx := strings.LastIndex(contentsStr[:startIdx], "\n")
if lineStartIdx == -1 {
indentLevel = startIdx
} else {
indentLevel = startIdx - lineStartIdx - 1
}
}
variables = append(variables, TemplateFileVariable{ variables = append(variables, TemplateFileVariable{
Name: varName, Name: varName,
StartPosition: uint(startIdx), StartPosition: uint(startIdx),
EndPosition: uint(endIdx), EndPosition: uint(endIdx),
IndentLevel: uint(indentLevel),
}) })
} }
fileIndex := TemplateFileIndex{ fileIndex := TemplateFileIndex{
Contents: string(contents), Contents: contentsStr,
LastModified: fileInfo.ModTime(), LastModified: fileInfo.ModTime(),
VariableNames: variableNames, VariableNames: variableNames,
Variables: variables, Variables: variables,
@ -261,15 +282,17 @@ func (nest *TemplateNest) Render(toRender interface{}) (string, error) {
replacement := "" replacement := ""
if value, exists := v[variable.Name]; exists { if value, exists := v[variable.Name]; exists {
if text, ok := value.(string); ok { subRender, err := nest.Render(value)
replacement = text if err != nil {
} else { return "", err
subRender, err := nest.Render(value)
if err != nil {
return "", err
}
replacement = subRender
} }
if nest.option.FixedIndent && variable.IndentLevel != 0 {
indentReplacement := "\n" + strings.Repeat(" ", int(variable.IndentLevel))
subRender = strings.ReplaceAll(subRender, "\n", indentReplacement)
}
replacement = subRender
} }
// Replace in rendered template // Replace in rendered template

@ -0,0 +1,92 @@
package tests
import (
"git.virtual.blue/tomgracey/template-nest-go"
"github.com/stretchr/testify/assert"
"testing"
)
func TestRenderSimplePageWithFixedIndent(t *testing.T) {
nest, err := templatenest.New(templatenest.Option{
TemplateDir: "templates",
FixedIndent: true,
})
if err != nil {
t.Fatalf("Failed to initialize TemplateNest: %+v", err)
}
page := templatenest.Hash{
"TEMPLATE": "00-simple-page",
"variable": "Simple Variable",
"simple_component": templatenest.Hash{
"TEMPLATE": "02-simple-component-multi-line",
},
}
outputPage := templatenest.Hash{"TEMPLATE": "output/07-simple-page-fixed-indent"}
render := nest.MustRender(page)
outputRender := nest.MustRender(outputPage)
assert.Equal(t, outputRender, render, "Rendered output does not match expected output")
}
func TestRenderComplexPageWithFixedIndent(t *testing.T) {
nest, err := templatenest.New(templatenest.Option{
TemplateDir: "templates",
FixedIndent: true,
})
if err != nil {
t.Fatalf("Failed to initialize TemplateNest: %+v", err)
}
page := templatenest.Hash{
"TEMPLATE": "10-complex-page",
"title": "Complex Page",
"pre_body": templatenest.Hash{
"TEMPLATE": "18-styles",
},
"navigation": templatenest.Hash{
"TEMPLATE": "11-navigation",
"banner": templatenest.Hash{
"TEMPLATE": "12-navigation-banner",
},
"items": []templatenest.Hash{
templatenest.Hash{"TEMPLATE": "13-navigation-item-00-services"},
templatenest.Hash{"TEMPLATE": "13-navigation-item-01-resources"},
},
},
"hero_section": templatenest.Hash{
"TEMPLATE": "14-hero-section",
},
"main_content": []templatenest.Hash{
templatenest.Hash{"TEMPLATE": "15-isdc-card"},
templatenest.Hash{
"TEMPLATE": "16-vb-brand-cards",
"cards": []templatenest.Hash{
templatenest.Hash{
"TEMPLATE": "17-vb-brand-card-00",
"parent_classes": "p-card brand-card col-4",
},
templatenest.Hash{
"TEMPLATE": "17-vb-brand-card-01",
"parent_classes": "p-card brand-card col-4",
},
templatenest.Hash{
"TEMPLATE": "17-vb-brand-card-02",
"parent_classes": "p-card brand-card col-4",
},
},
},
},
"post_footer": templatenest.Hash{
"TEMPLATE": "19-scripts",
},
}
outputPage := templatenest.Hash{"TEMPLATE": "output/08-complex-page-fixed-indent"}
render := nest.MustRender(page)
outputRender := nest.MustRender(outputPage)
assert.Equal(t, outputRender, render, "Rendered output does not match expected output")
}