github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/tpl/internal/go_templates/htmltemplate/examplefiles_test.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build go1.13
     6  // +build go1.13
     7  
     8  package template_test
     9  
    10  import (
    11  	"io"
    12  	"log"
    13  	"os"
    14  	"path/filepath"
    15  
    16  	template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
    17  )
    18  
    19  // templateFile defines the contents of a template to be stored in a file, for testing.
    20  type templateFile struct {
    21  	name     string
    22  	contents string
    23  }
    24  
    25  func createTestDir(files []templateFile) string {
    26  	dir, err := os.MkdirTemp("", "template")
    27  	if err != nil {
    28  		log.Fatal(err)
    29  	}
    30  	for _, file := range files {
    31  		f, err := os.Create(filepath.Join(dir, file.name))
    32  		if err != nil {
    33  			log.Fatal(err)
    34  		}
    35  		defer f.Close()
    36  		_, err = io.WriteString(f, file.contents)
    37  		if err != nil {
    38  			log.Fatal(err)
    39  		}
    40  	}
    41  	return dir
    42  }
    43  
    44  // The following example is duplicated in text/template; keep them in sync.
    45  
    46  // Here we demonstrate loading a set of templates from a directory.
    47  func ExampleTemplate_glob() {
    48  	// Here we create a temporary directory and populate it with our sample
    49  	// template definition files; usually the template files would already
    50  	// exist in some location known to the program.
    51  	dir := createTestDir([]templateFile{
    52  		// T0.tmpl is a plain template file that just invokes T1.
    53  		{"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
    54  		// T1.tmpl defines a template, T1 that invokes T2.
    55  		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
    56  		// T2.tmpl defines a template T2.
    57  		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
    58  	})
    59  	// Clean up after the test; another quirk of running as an example.
    60  	defer os.RemoveAll(dir)
    61  
    62  	// pattern is the glob pattern used to find all the template files.
    63  	pattern := filepath.Join(dir, "*.tmpl")
    64  
    65  	// Here starts the example proper.
    66  	// T0.tmpl is the first name matched, so it becomes the starting template,
    67  	// the value returned by ParseGlob.
    68  	tmpl := template.Must(template.ParseGlob(pattern))
    69  
    70  	err := tmpl.Execute(os.Stdout, nil)
    71  	if err != nil {
    72  		log.Fatalf("template execution: %s", err)
    73  	}
    74  	// Output:
    75  	// T0 invokes T1: (T1 invokes T2: (This is T2))
    76  }
    77  
    78  // Here we demonstrate loading a set of templates from files in different directories
    79  func ExampleTemplate_parsefiles() {
    80  	// Here we create different temporary directories and populate them with our sample
    81  	// template definition files; usually the template files would already
    82  	// exist in some location known to the program.
    83  	dir1 := createTestDir([]templateFile{
    84  		// T1.tmpl is a plain template file that just invokes T2.
    85  		{"T1.tmpl", `T1 invokes T2: ({{template "T2"}})`},
    86  	})
    87  
    88  	dir2 := createTestDir([]templateFile{
    89  		// T2.tmpl defines a template T2.
    90  		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
    91  	})
    92  
    93  	// Clean up after the test; another quirk of running as an example.
    94  	defer func(dirs ...string) {
    95  		for _, dir := range dirs {
    96  			os.RemoveAll(dir)
    97  		}
    98  	}(dir1, dir2)
    99  
   100  	// Here starts the example proper.
   101  	// Let's just parse only dir1/T0 and dir2/T2
   102  	paths := []string{
   103  		filepath.Join(dir1, "T1.tmpl"),
   104  		filepath.Join(dir2, "T2.tmpl"),
   105  	}
   106  	tmpl := template.Must(template.ParseFiles(paths...))
   107  
   108  	err := tmpl.Execute(os.Stdout, nil)
   109  	if err != nil {
   110  		log.Fatalf("template execution: %s", err)
   111  	}
   112  	// Output:
   113  	// T1 invokes T2: (This is T2)
   114  }
   115  
   116  // The following example is duplicated in text/template; keep them in sync.
   117  
   118  // This example demonstrates one way to share some templates
   119  // and use them in different contexts. In this variant we add multiple driver
   120  // templates by hand to an existing bundle of templates.
   121  func ExampleTemplate_helpers() {
   122  	// Here we create a temporary directory and populate it with our sample
   123  	// template definition files; usually the template files would already
   124  	// exist in some location known to the program.
   125  	dir := createTestDir([]templateFile{
   126  		// T1.tmpl defines a template, T1 that invokes T2.
   127  		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
   128  		// T2.tmpl defines a template T2.
   129  		{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
   130  	})
   131  	// Clean up after the test; another quirk of running as an example.
   132  	defer os.RemoveAll(dir)
   133  
   134  	// pattern is the glob pattern used to find all the template files.
   135  	pattern := filepath.Join(dir, "*.tmpl")
   136  
   137  	// Here starts the example proper.
   138  	// Load the helpers.
   139  	templates := template.Must(template.ParseGlob(pattern))
   140  	// Add one driver template to the bunch; we do this with an explicit template definition.
   141  	_, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
   142  	if err != nil {
   143  		log.Fatal("parsing driver1: ", err)
   144  	}
   145  	// Add another driver template.
   146  	_, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
   147  	if err != nil {
   148  		log.Fatal("parsing driver2: ", err)
   149  	}
   150  	// We load all the templates before execution. This package does not require
   151  	// that behavior but html/template's escaping does, so it's a good habit.
   152  	err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
   153  	if err != nil {
   154  		log.Fatalf("driver1 execution: %s", err)
   155  	}
   156  	err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
   157  	if err != nil {
   158  		log.Fatalf("driver2 execution: %s", err)
   159  	}
   160  	// Output:
   161  	// Driver 1 calls T1: (T1 invokes T2: (This is T2))
   162  	// Driver 2 calls T2: (This is T2)
   163  }
   164  
   165  // The following example is duplicated in text/template; keep them in sync.
   166  
   167  // This example demonstrates how to use one group of driver
   168  // templates with distinct sets of helper templates.
   169  func ExampleTemplate_share() {
   170  	// Here we create a temporary directory and populate it with our sample
   171  	// template definition files; usually the template files would already
   172  	// exist in some location known to the program.
   173  	dir := createTestDir([]templateFile{
   174  		// T0.tmpl is a plain template file that just invokes T1.
   175  		{"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
   176  		// T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
   177  		{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
   178  	})
   179  	// Clean up after the test; another quirk of running as an example.
   180  	defer os.RemoveAll(dir)
   181  
   182  	// pattern is the glob pattern used to find all the template files.
   183  	pattern := filepath.Join(dir, "*.tmpl")
   184  
   185  	// Here starts the example proper.
   186  	// Load the drivers.
   187  	drivers := template.Must(template.ParseGlob(pattern))
   188  
   189  	// We must define an implementation of the T2 template. First we clone
   190  	// the drivers, then add a definition of T2 to the template name space.
   191  
   192  	// 1. Clone the helper set to create a new name space from which to run them.
   193  	first, err := drivers.Clone()
   194  	if err != nil {
   195  		log.Fatal("cloning helpers: ", err)
   196  	}
   197  	// 2. Define T2, version A, and parse it.
   198  	_, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
   199  	if err != nil {
   200  		log.Fatal("parsing T2: ", err)
   201  	}
   202  
   203  	// Now repeat the whole thing, using a different version of T2.
   204  	// 1. Clone the drivers.
   205  	second, err := drivers.Clone()
   206  	if err != nil {
   207  		log.Fatal("cloning drivers: ", err)
   208  	}
   209  	// 2. Define T2, version B, and parse it.
   210  	_, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
   211  	if err != nil {
   212  		log.Fatal("parsing T2: ", err)
   213  	}
   214  
   215  	// Execute the templates in the reverse order to verify the
   216  	// first is unaffected by the second.
   217  	err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
   218  	if err != nil {
   219  		log.Fatalf("second execution: %s", err)
   220  	}
   221  	err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
   222  	if err != nil {
   223  		log.Fatalf("first: execution: %s", err)
   224  	}
   225  
   226  	// Output:
   227  	// T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
   228  	// T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
   229  }