As does 'tpl', so that nested calls to 'tpl' see the templates defined by their enclosing contexts.
(parent *template.Template, includedNames map[string]int, strict bool)
| 151 | // As does 'tpl', so that nested calls to 'tpl' see the templates |
| 152 | // defined by their enclosing contexts. |
| 153 | func tplFun(parent *template.Template, includedNames map[string]int, strict bool) func(string, any) (string, error) { |
| 154 | return func(tpl string, vals any) (string, error) { |
| 155 | t, err := parent.Clone() |
| 156 | if err != nil { |
| 157 | return "", fmt.Errorf("cannot clone template: %w", err) |
| 158 | } |
| 159 | |
| 160 | // Re-inject the missingkey option, see text/template issue https://github.com/golang/go/issues/43022 |
| 161 | // We have to go by strict from our engine configuration, as the option fields are private in Template. |
| 162 | // TODO: Remove workaround (and the strict parameter) once we build only with golang versions with a fix. |
| 163 | if strict { |
| 164 | t.Option("missingkey=error") |
| 165 | } else { |
| 166 | t.Option("missingkey=zero") |
| 167 | } |
| 168 | |
| 169 | // Re-inject 'include' so that it can close over our clone of t; |
| 170 | // this lets any 'define's inside tpl be 'include'd. |
| 171 | t.Funcs(template.FuncMap{ |
| 172 | "include": includeFun(t, includedNames), |
| 173 | "tpl": tplFun(t, includedNames, strict), |
| 174 | }) |
| 175 | |
| 176 | // We need a .New template, as template text which is just blanks |
| 177 | // or comments after parsing out defines just adds new named |
| 178 | // template definitions without changing the main template. |
| 179 | // https://pkg.go.dev/text/template#Template.Parse |
| 180 | // Use the parent's name for lack of a better way to identify the tpl |
| 181 | // text string. (Maybe we could use a hash appended to the name?) |
| 182 | t, err = t.New(parent.Name()).Parse(tpl) |
| 183 | if err != nil { |
| 184 | return "", fmt.Errorf("cannot parse template %q: %w", tpl, err) |
| 185 | } |
| 186 | |
| 187 | var buf strings.Builder |
| 188 | if err := t.Execute(&buf, vals); err != nil { |
| 189 | return "", fmt.Errorf("error during tpl function execution for %q: %w", tpl, err) |
| 190 | } |
| 191 | |
| 192 | // See comment in renderWithReferences explaining the <no value> hack. |
| 193 | return strings.ReplaceAll(buf.String(), "<no value>", ""), nil |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | // initFunMap creates the Engine's FuncMap and adds context-specific functions. |
| 198 | func (e Engine) initFunMap(t *template.Template) { |
no test coverage detected
searching dependent graphs…