mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
a03c631c42
This is a big commit, but it deletes lots of code and simplifies a lot. * Resolving the template funcs at execution time means we don't have to create template clones per site * Having a custom map resolver means that we can remove the AST lower case transformation for the special lower case Params map Not only is the above easier to reason about, it's also faster, especially if you have more than one language, as in the benchmark below: ``` name old time/op new time/op delta SiteNew/Deep_content_tree-16 53.7ms ± 0% 48.1ms ± 2% -10.38% (p=0.029 n=4+4) name old alloc/op new alloc/op delta SiteNew/Deep_content_tree-16 41.0MB ± 0% 36.8MB ± 0% -10.26% (p=0.029 n=4+4) name old allocs/op new allocs/op delta SiteNew/Deep_content_tree-16 481k ± 0% 410k ± 0% -14.66% (p=0.029 n=4+4) ``` This should be even better if you also have lots of templates. Closes #6594
209 lines
5.3 KiB
Go
209 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/gohugoio/hugo/common/hugio"
|
|
|
|
"github.com/spf13/afero"
|
|
)
|
|
|
|
func main() {
|
|
// TODO(bep) git checkout tag
|
|
// The current is built with Go version 9341fe073e6f7742c9d61982084874560dac2014 / go1.13.5
|
|
fmt.Println("Forking ...")
|
|
defer fmt.Println("Done ...")
|
|
|
|
cleanFork()
|
|
|
|
htmlRoot := filepath.Join(forkRoot, "htmltemplate")
|
|
|
|
for _, pkg := range goPackages {
|
|
copyGoPackage(pkg.dstPkg, pkg.srcPkg)
|
|
}
|
|
|
|
for _, pkg := range goPackages {
|
|
doWithGoFiles(pkg.dstPkg, pkg.rewriter, pkg.replacer)
|
|
}
|
|
|
|
goimports(htmlRoot)
|
|
gofmt(forkRoot)
|
|
|
|
}
|
|
|
|
const (
|
|
// TODO(bep)
|
|
goSource = "/Users/bep/dev/go/dump/go/src"
|
|
forkRoot = "../../tpl/internal/go_templates"
|
|
)
|
|
|
|
type goPackage struct {
|
|
srcPkg string
|
|
dstPkg string
|
|
replacer func(name, content string) string
|
|
rewriter func(name string)
|
|
}
|
|
|
|
var (
|
|
textTemplateReplacers = strings.NewReplacer(
|
|
`"text/template/`, `"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/`,
|
|
`"internal/fmtsort"`, `"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"`,
|
|
// Rename types and function that we want to overload.
|
|
"type state struct", "type stateOld struct",
|
|
"func (s *state) evalFunction", "func (s *state) evalFunctionOld",
|
|
"func (s *state) evalField(", "func (s *state) evalFieldOld(",
|
|
)
|
|
|
|
htmlTemplateReplacers = strings.NewReplacer(
|
|
`. "html/template"`, `. "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"`,
|
|
`"html/template"`, `template "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"`,
|
|
"\"text/template\"\n", "template \"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate\"\n",
|
|
`"html/template"`, `htmltemplate "html/template"`,
|
|
`"fmt"`, `htmltemplate "html/template"`,
|
|
)
|
|
)
|
|
|
|
func commonReplace(name, content string) string {
|
|
if strings.HasSuffix(name, "_test.go") {
|
|
content = strings.Replace(content, "package template\n", `// +build go1.13,!windows
|
|
|
|
package template
|
|
`, 1)
|
|
content = strings.Replace(content, "package template_test\n", `// +build go1.13
|
|
|
|
package template_test
|
|
`, 1)
|
|
|
|
content = strings.Replace(content, "package parse\n", `// +build go1.13
|
|
|
|
package parse
|
|
`, 1)
|
|
|
|
}
|
|
|
|
return content
|
|
|
|
}
|
|
|
|
var goPackages = []goPackage{
|
|
goPackage{srcPkg: "text/template", dstPkg: "texttemplate",
|
|
replacer: func(name, content string) string { return textTemplateReplacers.Replace(commonReplace(name, content)) }},
|
|
goPackage{srcPkg: "html/template", dstPkg: "htmltemplate", replacer: func(name, content string) string {
|
|
if strings.HasSuffix(name, "content.go") {
|
|
// Remove template.HTML types. We need to use the Go types.
|
|
content = removeAll(`(?s)// Strings of content.*?\)\n`, content)
|
|
}
|
|
|
|
content = commonReplace(name, content)
|
|
|
|
return htmlTemplateReplacers.Replace(content)
|
|
},
|
|
rewriter: func(name string) {
|
|
for _, s := range []string{"CSS", "HTML", "HTMLAttr", "JS", "JSStr", "URL", "Srcset"} {
|
|
rewrite(name, fmt.Sprintf("%s -> htmltemplate.%s", s, s))
|
|
}
|
|
rewrite(name, `"text/template/parse" -> "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"`)
|
|
}},
|
|
goPackage{srcPkg: "internal/fmtsort", dstPkg: "fmtsort", rewriter: func(name string) {
|
|
rewrite(name, `"internal/fmtsort" -> "github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"`)
|
|
}},
|
|
}
|
|
|
|
var fs = afero.NewOsFs()
|
|
|
|
// Removes all non-Hugo files in the go_templates folder.
|
|
func cleanFork() {
|
|
must(filepath.Walk(filepath.Join(forkRoot), func(path string, info os.FileInfo, err error) error {
|
|
if !info.IsDir() && len(path) > 10 && !strings.Contains(path, "hugo") {
|
|
must(fs.Remove(path))
|
|
}
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
func must(err error, what ...string) {
|
|
if err != nil {
|
|
log.Fatal(what, " ERROR: ", err)
|
|
}
|
|
}
|
|
|
|
func copyGoPackage(dst, src string) {
|
|
from := filepath.Join(goSource, src)
|
|
to := filepath.Join(forkRoot, dst)
|
|
fmt.Println("Copy", from, "to", to)
|
|
must(hugio.CopyDir(fs, from, to, func(s string) bool { return true }))
|
|
}
|
|
|
|
func doWithGoFiles(dir string,
|
|
rewrite func(name string),
|
|
transform func(name, in string) string) {
|
|
if rewrite == nil && transform == nil {
|
|
return
|
|
}
|
|
must(filepath.Walk(filepath.Join(forkRoot, dir), func(path string, info os.FileInfo, err error) error {
|
|
if info.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
if !strings.HasSuffix(path, ".go") || strings.Contains(path, "hugo_") {
|
|
return nil
|
|
}
|
|
|
|
fmt.Println("Handle", path)
|
|
|
|
if rewrite != nil {
|
|
rewrite(path)
|
|
}
|
|
|
|
if transform == nil {
|
|
return nil
|
|
}
|
|
|
|
data, err := ioutil.ReadFile(path)
|
|
must(err)
|
|
f, err := os.Create(path)
|
|
must(err)
|
|
defer f.Close()
|
|
_, err = f.WriteString(transform(path, string(data)))
|
|
must(err)
|
|
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
func removeAll(expression, content string) string {
|
|
re := regexp.MustCompile(expression)
|
|
return re.ReplaceAllString(content, "")
|
|
|
|
}
|
|
|
|
func rewrite(filename, rule string) {
|
|
cmf := exec.Command("gofmt", "-w", "-r", rule, filename)
|
|
out, err := cmf.CombinedOutput()
|
|
if err != nil {
|
|
log.Fatal("gofmt failed:", string(out))
|
|
}
|
|
}
|
|
|
|
func goimports(dir string) {
|
|
cmf := exec.Command("goimports", "-w", dir)
|
|
out, err := cmf.CombinedOutput()
|
|
if err != nil {
|
|
log.Fatal("goimports failed:", string(out))
|
|
}
|
|
}
|
|
|
|
func gofmt(dir string) {
|
|
cmf := exec.Command("gofmt", "-w", dir)
|
|
out, err := cmf.CombinedOutput()
|
|
if err != nil {
|
|
log.Fatal("gofmt failed:", string(out))
|
|
}
|
|
}
|