diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..64681d0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +templates/* linguist-vendored diff --git a/README.org b/README.org new file mode 100644 index 0000000..a452a26 --- /dev/null +++ b/README.org @@ -0,0 +1,22 @@ +#+title: Template Nest +#+subtitle: manipulate a generic template structure + +~Template Nest~ is a template engine module for Go, designed to process nested +templates quickly and efficiently. + +For more details on the idea behind ~Template::Nest~ read: +https://metacpan.org/pod/Template::Nest#DESCRIPTION and +https://pypi.org/project/template-nest/. + +* News + +** v0.1.0 - Upcoming + ++ Initial Release. + +* Other Implementations + +- [[https://metacpan.org/pod/Template::Nest][Template::Nest (Perl 5)]] +- [[https://pypi.org/project/template-nest/][template-nest (Python)]] +- [[https://raku.land/zef:jaffa4/Template::Nest::XS][Template::Nest::XS (Raku)]] +- [[https://raku.land/zef:andinus/Template::Nest::Fast][Template::Nest::Fast (Raku)]] diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7664081 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module git.virtual.blue/tomgracey/template-nest-go + +go 1.22 + +require github.com/stretchr/testify v1.9.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..60ce688 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/template_nest.go b/template_nest.go new file mode 100644 index 0000000..cfdde42 --- /dev/null +++ b/template_nest.go @@ -0,0 +1,262 @@ +package templatenest + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + "time" +) + +type Hash map[string]interface{} + +// Option holds configuration for TemplateNest +type Option struct { + Delimiters [2]string + NameLabel string + TemplateDir string + TemplateExtension string + DieOnBadParams bool +} + +type TemplateNest struct { + option Option + cache map[string]TemplateFileIndex +} + +// TemplateFileIndex represents an indexed template file. +type TemplateFileIndex struct { + Contents string + LastModified time.Time // Last modified timestamp of the template file + Variables []TemplateFileVariable // List of variables in the template file + VariableNames map[string]struct{} // Set of variable names +} + +// TemplateFileVariable represents a variable in a template file. +type TemplateFileVariable struct { + Name string + StartPosition uint // Start position of the complete variable string (including delimiters) + EndPosition uint // End position of the complete variable string (including delimiters) + IndentLevel uint // Indentation level of the variable + EscapedToken bool // Indicates if the variable was escaped with token escape character +} + +func New(opts Option) (*TemplateNest, error) { + // Check if the TemplateDir exists and is a directory. + templateDirInfo, err := os.Stat(opts.TemplateDir) + if err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("template dir `%s` does not exist", opts.TemplateDir) + } + return nil, fmt.Errorf("error checking template dir: %w", err) + } + if !templateDirInfo.IsDir() { + return nil, fmt.Errorf("template dir `%s` is not a directory", opts.TemplateDir) + } + + // Set defaults for options that the user hasn't provided. + if opts.Delimiters == [2]string{} { + opts.Delimiters = [2]string{""} + } + if opts.NameLabel == "" { + opts.NameLabel = "TEMPLATE" + } + if opts.TemplateExtension == "" { + opts.TemplateExtension = "html" + } + + // Initialize TemplateNest with the final options. + nest := &TemplateNest{ + option: opts, + cache: make(map[string]TemplateFileIndex), + } + + // Walk through the template directory and index the templates. + err = filepath.Walk(opts.TemplateDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + // Check if the file has the correct extension. + if !strings.HasSuffix(info.Name(), "."+opts.TemplateExtension) { + return nil + } + + // Index the template and store it in the cache. + templateIndex, err := nest.index(path) + if err != nil { + return err + } + + // Get the relative path of the file. + relPath, err := filepath.Rel(opts.TemplateDir, path) + if err != nil { + return err + } + + // Remove the extension from the relative path. + templateName := strings.TrimSuffix(relPath, "."+opts.TemplateExtension) + nest.cache[templateName] = templateIndex + return nil + }) + if err != nil { + return nil, err + } + + return nest, nil +} + +func (nest *TemplateNest) index(filePath string) (TemplateFileIndex, error) { + contents, err := ioutil.ReadFile(filePath) + if err != nil { + return TemplateFileIndex{}, fmt.Errorf("Error reading file (`%s`): %w", filePath, err) + } + // Capture last modified time + fileInfo, err := os.Stat(filePath) + if err != nil { + return TemplateFileIndex{}, fmt.Errorf("Error getting file (`%s`) info: %w", filePath, err) + } + + variableNames := make(map[string]struct{}) + variables := []TemplateFileVariable{} + + delimiterStart := nest.option.Delimiters[0] + delimiterEnd := nest.option.Delimiters[1] + + re := regexp.MustCompile(fmt.Sprintf( + "%s\\s*(.+?)\\s*%s", regexp.QuoteMeta(delimiterStart), regexp.QuoteMeta(delimiterEnd), + )) + + matches := re.FindAllStringSubmatchIndex(string(contents), -1) + for _, match := range matches { + if len(match) < 4 { + continue + } + + startIdx := match[0] + endIdx := match[1] + nameStartIdx := match[2] + nameEndIdx := match[3] + + varName := string(contents[nameStartIdx:nameEndIdx]) + variableNames[varName] = struct{}{} + + variables = append(variables, TemplateFileVariable{ + Name: varName, + StartPosition: uint(startIdx), + EndPosition: uint(endIdx), + }) + } + + fileIndex := TemplateFileIndex{ + Contents: string(contents), + LastModified: fileInfo.ModTime(), + VariableNames: variableNames, + Variables: variables, + } + + return fileIndex, nil +} + +// getTemplateFilePath takes a template name and returns the file path for the +// template. +func (nest *TemplateNest) getTemplateFilePath(templateName string) string { + return filepath.Join( + nest.option.TemplateDir, + fmt.Sprintf("%s.%s", templateName, nest.option.TemplateExtension), + ) +} + +func (nest *TemplateNest) MustRender(toRender interface{}) string { + render, err := nest.Render(toRender) + if err != nil { + panic(err) + } + return render +} + +func (nest *TemplateNest) Render(toRender interface{}) (string, error) { + switch v := toRender.(type) { + case nil: + return "", nil + + case bool: + return fmt.Sprintf("%t", v), nil + + case string: + return v, nil + + case float64, int, int64: + return fmt.Sprintf("%v", v), nil + + case []Hash: + var rendered strings.Builder + for _, item := range v { + renderedItem, err := nest.Render(item) + if err != nil { + return "", err + } + rendered.WriteString(renderedItem) + } + return rendered.String(), nil + + case Hash: + tLabel, ok := v[nest.option.NameLabel] + if !ok { + return "", fmt.Errorf("missing template label: %s", nest.option.NameLabel) + } + + tName, ok := tLabel.(string) + if !ok { + return "", fmt.Errorf("invalid template label: %+v", tLabel) + } + + tFile := nest.getTemplateFilePath(tName) + fileInfo, err := os.Stat(tFile) + if err != nil { + return "", fmt.Errorf("error getting file info: %w", err) + } + + tIndex, exists := nest.cache[tName] + + // If cache doesn't exist or has expired, re-index the file. + if !exists || fileInfo.ModTime().After(tIndex.LastModified) { + newIndex, err := nest.index(tFile) + if err != nil { + return "", fmt.Errorf("File index failed: %w", err) + } + tIndex = newIndex + } + + rendered := tIndex.Contents + for i := len(tIndex.Variables) - 1; i >= 0; i-- { + variable := tIndex.Variables[i] + + // If the variable doesn't exist in template hash then replace it + // with an empty string. + replacement := "" + + if value, exists := v[variable.Name]; exists { + if text, ok := value.(string); ok { + replacement = text + } else { + subRender, err := nest.Render(value) + if err != nil { + return "", err + } + replacement = subRender + } + } + + // Replace in rendered template + rendered = rendered[:variable.StartPosition] + replacement + rendered[variable.EndPosition:] + } + + return strings.TrimSpace(rendered), nil + + default: + return "", fmt.Errorf("Unsupported template type: %+v", v) + } +} diff --git a/template_nest/nest.go b/template_nest/nest.go deleted file mode 100644 index a9f5f4e..0000000 --- a/template_nest/nest.go +++ /dev/null @@ -1,131 +0,0 @@ -package template_nest - - -import ( - "encoding/json" - "log" - "regexp" -) - - -type Nest struct { - - templateLabel string - templateDir string - templates map[string]string - tokenDelims [2]string - -} - - -func New( templates map[string]string ) Nest { - - nest := Nest{ - templateLabel: "TEMPLATE", - tokenDelims: [2]string{""}, - templates: templates, - } - - return nest -} - - - - -func (obj *Nest) Render ( sjson []byte ) string { - - var nesti interface{} - err := json.Unmarshal(sjson, &nesti) - if err != nil { - log.Fatal("Failed to parse JSON") - } - - nested := nesti.(map[string]interface{}) - - html := obj._Render( nested ) - - return html - -} - - -func (obj *Nest) _Render( nested interface{} ) string{ - - var html string - - switch vtype := nested.(type) { - - case string: - html = vtype - - case []interface{}: - - html = obj._RenderArray( vtype ) - - case map[string]interface{}: - html = obj._RenderMap( vtype ) - - } - - return html - -} - - -func (obj *Nest) _RenderArray( nested []interface{} ) string { - - html := "" - for v := range nested { - html += obj._Render( v ) - } - - return html -} - - - - -func (obj *Nest) _RenderMap( nested map[string]interface{} ) string { - - templateName := nested[ obj.templateLabel ].(string) - if templateName == "" { - panic("Encountered map with no TEMPLATE label") - } - - template := obj.templates[ templateName ] - - params := make(map[string]string) - - for k, v := range nested { - - if k == obj.templateLabel { - continue - } - - params[ k ] = obj._Render( v ) - - } - - html := obj._FillIn( templateName, template, params ) - - return html - -} - - -func (obj *Nest) _FillIn( templateName string, template string, params map[string]string ) string { - - html := template - for param, val := range params { - - regex, err := regexp.Compile( obj.tokenDelims[0] + `\s*` + param + `\s*` + obj.tokenDelims[1] ) - if err != nil { - panic( err ) - } - - html = regex.ReplaceAllString( html, val ) - - } - - return html -} diff --git a/test_nest.go b/test_nest.go deleted file mode 100644 index a0c18c7..0000000 --- a/test_nest.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "fmt" - "tntester/template_nest" -) - -func main(){ - - templates := map[string]string{ - "00-simple-page": ` - - - - - Simple Page - - -

A fairly simple page to test the performance of Template::Nest.

-

- - - `, "00-simple-component": `

`} - - json := []byte(`{ - "TEMPLATE": "00-simple-page", - "variable": "Simple Variable", - "simple_component": { - "TEMPLATE":"01-simple-component", - "variable": "Simple Variable in Simple Component" - } - }`) - - - nest := template_nest.New( templates ) - - html := nest.Render( json ) - - fmt.Println( html ) - - -} diff --git a/tests/00_basic_test.go b/tests/00_basic_test.go new file mode 100644 index 0000000..dc8baaa --- /dev/null +++ b/tests/00_basic_test.go @@ -0,0 +1,15 @@ +package tests + +import ( + "git.virtual.blue/tomgracey/template-nest-go" + "testing" +) + +func TestInitialize(t *testing.T) { + _, err := templatenest.New(templatenest.Option{ + TemplateDir: "templates", + }) + if err != nil { + t.Fatalf("Failed to initialize TemplateNest: %+v", err) + } +} diff --git a/tests/01_render_test.go b/tests/01_render_test.go new file mode 100644 index 0000000..cfec9c2 --- /dev/null +++ b/tests/01_render_test.go @@ -0,0 +1,145 @@ +package tests + +import ( + "git.virtual.blue/tomgracey/template-nest-go" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestRenderSimplePage(t *testing.T) { + nest, err := templatenest.New(templatenest.Option{ + TemplateDir: "templates", + }) + 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{ + templatenest.Hash{ + "TEMPLATE": "01-simple-component", + "variable": "Simple Variable in Simple Component", + }, + }, + } + + outputPage := templatenest.Hash{"TEMPLATE": "output/01-simple-page"} + + render, err := nest.Render(page) + if err != nil { + t.Fatalf("Render failed for page: %+v", err) + } + + outputRender := nest.MustRender(outputPage) + + assert.Equal(t, outputRender, render, "Rendered output does not match expected output") +} + +func TestRenderIncompletePage(t *testing.T) { + nest, err := templatenest.New(templatenest.Option{TemplateDir: "templates"}) + 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{ + templatenest.Hash{ + "TEMPLATE": "01-simple-component", + }, + }, + } + + outputPage := templatenest.Hash{"TEMPLATE": "output/03-incomplete-page"} + + render := nest.MustRender(page) + outputRender := nest.MustRender(outputPage) + + assert.Equal(t, outputRender, render, "Rendered output does not match expected output") +} + +func TestRenderComplexPage(t *testing.T) { + nest, err := templatenest.New(templatenest.Option{TemplateDir: "templates"}) + 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/02-complex-page"} + + render := nest.MustRender(page) + outputRender := nest.MustRender(outputPage) + + assert.Equal(t, outputRender, render, "Rendered output does not match expected output") +} + +func TestRenderArrayOfTemplateHash(t *testing.T) { + nest, err := templatenest.New(templatenest.Option{TemplateDir: "templates"}) + if err != nil { + t.Fatalf("Failed to initialize TemplateNest: %+v", err) + } + + page := []templatenest.Hash{ + templatenest.Hash{ + "TEMPLATE": "01-simple-component", + "variable": "This is a variable", + }, + templatenest.Hash{ + "TEMPLATE": "01-simple-component", + "variable": "This is another variable", + }, + } + + outputPage := templatenest.Hash{"TEMPLATE": "output/13-render-with-array-of-template-hash"} + + render := nest.MustRender(page) + outputRender := nest.MustRender(outputPage) + + assert.Equal(t, outputRender, render, "Rendered output does not match expected output") +} diff --git a/tests/templates/00-simple-page-alt-delim.html b/tests/templates/00-simple-page-alt-delim.html new file mode 100644 index 0000000..7e8d232 --- /dev/null +++ b/tests/templates/00-simple-page-alt-delim.html @@ -0,0 +1,13 @@ + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

<% variable %>

+ <% simple_component %> + + diff --git a/tests/templates/00-simple-page.html b/tests/templates/00-simple-page.html new file mode 100644 index 0000000..c9844b1 --- /dev/null +++ b/tests/templates/00-simple-page.html @@ -0,0 +1,13 @@ + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

+ + + diff --git a/tests/templates/01-simple-component-alt-delim.html b/tests/templates/01-simple-component-alt-delim.html new file mode 100644 index 0000000..a927699 --- /dev/null +++ b/tests/templates/01-simple-component-alt-delim.html @@ -0,0 +1 @@ +

<% variable %>

diff --git a/tests/templates/01-simple-component-token-escape.html b/tests/templates/01-simple-component-token-escape.html new file mode 100644 index 0000000..6390ad0 --- /dev/null +++ b/tests/templates/01-simple-component-token-escape.html @@ -0,0 +1 @@ +

\

diff --git a/tests/templates/01-simple-component.html b/tests/templates/01-simple-component.html new file mode 100644 index 0000000..c003f8d --- /dev/null +++ b/tests/templates/01-simple-component.html @@ -0,0 +1 @@ +

diff --git a/tests/templates/02-simple-component-multi-line-alt-delim.html b/tests/templates/02-simple-component-multi-line-alt-delim.html new file mode 100644 index 0000000..3a614cb --- /dev/null +++ b/tests/templates/02-simple-component-multi-line-alt-delim.html @@ -0,0 +1,7 @@ +

+ This is a simple component on multiple lines. +

+ +

+ This is used for fixed-indent testing. +

diff --git a/tests/templates/02-simple-component-multi-line.html b/tests/templates/02-simple-component-multi-line.html new file mode 100644 index 0000000..3a614cb --- /dev/null +++ b/tests/templates/02-simple-component-multi-line.html @@ -0,0 +1,7 @@ +

+ This is a simple component on multiple lines. +

+ +

+ This is used for fixed-indent testing. +

diff --git a/tests/templates/03-namespace-page.html b/tests/templates/03-namespace-page.html new file mode 100644 index 0000000..9e518c7 --- /dev/null +++ b/tests/templates/03-namespace-page.html @@ -0,0 +1,12 @@ + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

+ + diff --git a/tests/templates/03-var-at-begin.html b/tests/templates/03-var-at-begin.html new file mode 100644 index 0000000..c31bc6c --- /dev/null +++ b/tests/templates/03-var-at-begin.html @@ -0,0 +1 @@ + diff --git a/tests/templates/10-complex-page.html b/tests/templates/10-complex-page.html new file mode 100644 index 0000000..aca4e5d --- /dev/null +++ b/tests/templates/10-complex-page.html @@ -0,0 +1,29 @@ + + + + + + <!--% title %--> + + + + + + +
+ +
+ +
+ +
+ + + + + + diff --git a/tests/templates/11-navigation.html b/tests/templates/11-navigation.html new file mode 100644 index 0000000..def69d2 --- /dev/null +++ b/tests/templates/11-navigation.html @@ -0,0 +1,20 @@ +
+ + +
diff --git a/tests/templates/12-navigation-banner.html b/tests/templates/12-navigation-banner.html new file mode 100644 index 0000000..cc2ffd2 --- /dev/null +++ b/tests/templates/12-navigation-banner.html @@ -0,0 +1,11 @@ +
+ + Menu + Close menu +
diff --git a/tests/templates/13-navigation-item-00-services.html b/tests/templates/13-navigation-item-00-services.html new file mode 100644 index 0000000..c05f3b3 --- /dev/null +++ b/tests/templates/13-navigation-item-00-services.html @@ -0,0 +1,17 @@ + diff --git a/tests/templates/13-navigation-item-01-resources.html b/tests/templates/13-navigation-item-01-resources.html new file mode 100644 index 0000000..2de1cf6 --- /dev/null +++ b/tests/templates/13-navigation-item-01-resources.html @@ -0,0 +1,14 @@ + diff --git a/tests/templates/14-hero-section.html b/tests/templates/14-hero-section.html new file mode 100644 index 0000000..5201e65 --- /dev/null +++ b/tests/templates/14-hero-section.html @@ -0,0 +1,4 @@ +
+

Software Systems

+

Quality web and app specialists at low cost. New app development. Web-scrapers and crawlers. Full systems. Legacy repairs.

+
diff --git a/tests/templates/15-isdc-card.html b/tests/templates/15-isdc-card.html new file mode 100644 index 0000000..0a25b3a --- /dev/null +++ b/tests/templates/15-isdc-card.html @@ -0,0 +1,14 @@ +
+
+
+ +
+
+

Raku Prodigy ISDC

+

+ Are you a rising coding talent with a flair for innovative system development? + Make a name for yourself by winning our web development competition. +

+
+
+
diff --git a/tests/templates/16-vb-brand-cards.html b/tests/templates/16-vb-brand-cards.html new file mode 100644 index 0000000..6972956 --- /dev/null +++ b/tests/templates/16-vb-brand-cards.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/tests/templates/17-vb-brand-card-00.html b/tests/templates/17-vb-brand-card-00.html new file mode 100644 index 0000000..8dd702b --- /dev/null +++ b/tests/templates/17-vb-brand-card-00.html @@ -0,0 +1,9 @@ +
+
+ +

+ Cost Minimising +

+

Through our efficient approach to project management we are able to offer some of the lowest service rates in the industry - without compromising on quality.

+
+
diff --git a/tests/templates/17-vb-brand-card-01.html b/tests/templates/17-vb-brand-card-01.html new file mode 100644 index 0000000..c3bc1c7 --- /dev/null +++ b/tests/templates/17-vb-brand-card-01.html @@ -0,0 +1,9 @@ +
+
+ +

+ Code Warranty +

+

As standard we continue to provide support for 6 months after you have accepted the code. This includes explaining usage, performing minor adjustments and ironing out any bugs. Subject to contract terms.

+
+
diff --git a/tests/templates/17-vb-brand-card-02.html b/tests/templates/17-vb-brand-card-02.html new file mode 100644 index 0000000..bbf6d67 --- /dev/null +++ b/tests/templates/17-vb-brand-card-02.html @@ -0,0 +1,9 @@ +
+
+ +

+ Tailor Made Solutions +

+

When evaluating your case we'll consider your individual requirements, your specific business needs and the particular problem you are facing - so we'll always propose solutions which are uniquely tailored to your situation.

+
+
diff --git a/tests/templates/18-styles.html b/tests/templates/18-styles.html new file mode 100644 index 0000000..edfdece --- /dev/null +++ b/tests/templates/18-styles.html @@ -0,0 +1,20 @@ + diff --git a/tests/templates/19-scripts.html b/tests/templates/19-scripts.html new file mode 100644 index 0000000..f930204 --- /dev/null +++ b/tests/templates/19-scripts.html @@ -0,0 +1,49 @@ + diff --git a/tests/templates/30-main.js b/tests/templates/30-main.js new file mode 100644 index 0000000..96053a4 --- /dev/null +++ b/tests/templates/30-main.js @@ -0,0 +1,3 @@ +const tmp = () => { + /* */ +}; diff --git a/tests/templates/output/01-simple-page.html b/tests/templates/output/01-simple-page.html new file mode 100644 index 0000000..9169885 --- /dev/null +++ b/tests/templates/output/01-simple-page.html @@ -0,0 +1,13 @@ + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

Simple Variable

+

Simple Variable in Simple Component

+ + diff --git a/tests/templates/output/02-complex-page.html b/tests/templates/output/02-complex-page.html new file mode 100644 index 0000000..e8e0f6c --- /dev/null +++ b/tests/templates/output/02-complex-page.html @@ -0,0 +1,196 @@ + + + + + + Complex Page + + + + + + +
+
+

Software Systems

+

Quality web and app specialists at low cost. New app development. Web-scrapers and crawlers. Full systems. Legacy repairs.

+
+
+ +
+
+
+
+ +
+
+

Raku Prodigy ISDC

+

+ Are you a rising coding talent with a flair for innovative system development? + Make a name for yourself by winning our web development competition. +

+
+
+
+
+
+ +

+ Cost Minimising +

+

Through our efficient approach to project management we are able to offer some of the lowest service rates in the industry - without compromising on quality.

+
+
+
+ +

+ Code Warranty +

+

As standard we continue to provide support for 6 months after you have accepted the code. This includes explaining usage, performing minor adjustments and ironing out any bugs. Subject to contract terms.

+
+
+
+ +

+ Tailor Made Solutions +

+

When evaluating your case we'll consider your individual requirements, your specific business needs and the particular problem you are facing - so we'll always propose solutions which are uniquely tailored to your situation.

+
+
+
+
+ + + + + + diff --git a/tests/templates/output/03-incomplete-page.html b/tests/templates/output/03-incomplete-page.html new file mode 100644 index 0000000..740a805 --- /dev/null +++ b/tests/templates/output/03-incomplete-page.html @@ -0,0 +1,13 @@ + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

Simple Variable

+

+ + diff --git a/tests/templates/output/04-simple-page-with-labels.html b/tests/templates/output/04-simple-page-with-labels.html new file mode 100644 index 0000000..c53405d --- /dev/null +++ b/tests/templates/output/04-simple-page-with-labels.html @@ -0,0 +1,17 @@ + + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

Simple Variable

+ +

Simple Variable in Simple Component

+ + + + diff --git a/tests/templates/output/05-simple-page-with-labels-alt-delims.html b/tests/templates/output/05-simple-page-with-labels-alt-delims.html new file mode 100644 index 0000000..0445f68 --- /dev/null +++ b/tests/templates/output/05-simple-page-with-labels-alt-delims.html @@ -0,0 +1,17 @@ + + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

Simple Variable

+ +

Simple Variable in Simple Component

+ + + + diff --git a/tests/templates/output/06-main-template-extension.js b/tests/templates/output/06-main-template-extension.js new file mode 100644 index 0000000..c65cab1 --- /dev/null +++ b/tests/templates/output/06-main-template-extension.js @@ -0,0 +1,3 @@ +const tmp = () => { + /* Simple Variable */ +}; diff --git a/tests/templates/output/07-simple-page-fixed-indent.html b/tests/templates/output/07-simple-page-fixed-indent.html new file mode 100644 index 0000000..531d070 --- /dev/null +++ b/tests/templates/output/07-simple-page-fixed-indent.html @@ -0,0 +1,19 @@ + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

Simple Variable

+

+ This is a simple component on multiple lines. +

+ +

+ This is used for fixed-indent testing. +

+ + diff --git a/tests/templates/output/08-complex-page-fixed-indent.html b/tests/templates/output/08-complex-page-fixed-indent.html new file mode 100644 index 0000000..fd9bde3 --- /dev/null +++ b/tests/templates/output/08-complex-page-fixed-indent.html @@ -0,0 +1,196 @@ + + + + + + Complex Page + + + + + + +
+
+

Software Systems

+

Quality web and app specialists at low cost. New app development. Web-scrapers and crawlers. Full systems. Legacy repairs.

+
+
+ +
+
+
+
+ +
+
+

Raku Prodigy ISDC

+

+ Are you a rising coding talent with a flair for innovative system development? + Make a name for yourself by winning our web development competition. +

+
+
+
+
+
+ +

+ Cost Minimising +

+

Through our efficient approach to project management we are able to offer some of the lowest service rates in the industry - without compromising on quality.

+
+
+
+ +

+ Code Warranty +

+

As standard we continue to provide support for 6 months after you have accepted the code. This includes explaining usage, performing minor adjustments and ironing out any bugs. Subject to contract terms.

+
+
+
+ +

+ Tailor Made Solutions +

+

When evaluating your case we'll consider your individual requirements, your specific business needs and the particular problem you are facing - so we'll always propose solutions which are uniquely tailored to your situation.

+
+
+
+
+ + + + + + diff --git a/tests/templates/output/09-simple-page-token-escape.html b/tests/templates/output/09-simple-page-token-escape.html new file mode 100644 index 0000000..8d10c0a --- /dev/null +++ b/tests/templates/output/09-simple-page-token-escape.html @@ -0,0 +1,13 @@ + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

Simple Variable

+

+ + diff --git a/tests/templates/output/10-var-at-begin.html b/tests/templates/output/10-var-at-begin.html new file mode 100644 index 0000000..755cad4 --- /dev/null +++ b/tests/templates/output/10-var-at-begin.html @@ -0,0 +1 @@ +Simple Variable diff --git a/tests/templates/output/11-namespace-page.html b/tests/templates/output/11-namespace-page.html new file mode 100644 index 0000000..ea6d1af --- /dev/null +++ b/tests/templates/output/11-namespace-page.html @@ -0,0 +1,12 @@ + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

A variable inside a space.

+ + diff --git a/tests/templates/output/12-simple-page-arrays.html b/tests/templates/output/12-simple-page-arrays.html new file mode 100644 index 0000000..ab7c58f --- /dev/null +++ b/tests/templates/output/12-simple-page-arrays.html @@ -0,0 +1,13 @@ + + + + + + Simple Page + + +

A fairly simple page to test the performance of Template::Nest.

+

Simple Variable

+

Simple Variable in Simple Component

Another test

Simple Variable in Simple Component

Another nested test 2 + + diff --git a/tests/templates/output/13-render-with-array-of-template-hash.html b/tests/templates/output/13-render-with-array-of-template-hash.html new file mode 100644 index 0000000..3a8bf5f --- /dev/null +++ b/tests/templates/output/13-render-with-array-of-template-hash.html @@ -0,0 +1 @@ +

This is a variable

This is another variable