github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/scripts/fork_go_templates/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "os" 8 "path/filepath" 9 "regexp" 10 "strings" 11 12 "github.com/gohugoio/hugo/common/hexec" 13 14 "github.com/gohugoio/hugo/common/hugio" 15 16 "github.com/spf13/afero" 17 ) 18 19 func main() { 20 // TODO(bep) git checkout tag 21 // The current is built with Go version 2f0da6d9e29d9b9d5a4d10427ca9f71d12bbacc8 / go1.16 22 fmt.Println("Forking ...") 23 defer fmt.Println("Done ...") 24 25 cleanFork() 26 27 htmlRoot := filepath.Join(forkRoot, "htmltemplate") 28 29 for _, pkg := range goPackages { 30 copyGoPackage(pkg.dstPkg, pkg.srcPkg) 31 } 32 33 for _, pkg := range goPackages { 34 doWithGoFiles(pkg.dstPkg, pkg.rewriter, pkg.replacer) 35 } 36 37 goimports(htmlRoot) 38 gofmt(forkRoot) 39 } 40 41 const ( 42 // TODO(bep) 43 goSource = "/Users/bep/dev/go/dump/go/src" 44 forkRoot = "../../tpl/internal/go_templates" 45 ) 46 47 type goPackage struct { 48 srcPkg string 49 dstPkg string 50 replacer func(name, content string) string 51 rewriter func(name string) 52 } 53 54 var ( 55 textTemplateReplacers = strings.NewReplacer( 56 `"text/template/`, `"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/`, 57 `"internal/fmtsort"`, `"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"`, 58 `"internal/testenv"`, `"github.com/gohugoio/hugo/tpl/internal/go_templates/testenv"`, 59 "TestLinkerGC", "_TestLinkerGC", 60 // Rename types and function that we want to overload. 61 "type state struct", "type stateOld struct", 62 "func (s *state) evalFunction", "func (s *state) evalFunctionOld", 63 "func (s *state) evalField(", "func (s *state) evalFieldOld(", 64 "func (s *state) evalCall(", "func (s *state) evalCallOld(", 65 "func isTrue(val reflect.Value) (truth, ok bool) {", "func isTrueOld(val reflect.Value) (truth, ok bool) {", 66 ) 67 68 testEnvReplacers = strings.NewReplacer( 69 `"internal/cfg"`, `"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"`, 70 ) 71 72 htmlTemplateReplacers = strings.NewReplacer( 73 `. "html/template"`, `. "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"`, 74 `"html/template"`, `template "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"`, 75 "\"text/template\"\n", "template \"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate\"\n", 76 `"html/template"`, `htmltemplate "html/template"`, 77 `"fmt"`, `htmltemplate "html/template"`, 78 `t.Skip("this test currently fails with -race; see issue #39807")`, `// t.Skip("this test currently fails with -race; see issue #39807")`, 79 ) 80 ) 81 82 func commonReplace(name, content string) string { 83 if strings.HasSuffix(name, "_test.go") { 84 content = strings.Replace(content, "package template\n", `// +build go1.13,!windows 85 86 package template 87 `, 1) 88 content = strings.Replace(content, "package template_test\n", `// +build go1.13 89 90 package template_test 91 `, 1) 92 93 content = strings.Replace(content, "package parse\n", `// +build go1.13 94 95 package parse 96 `, 1) 97 98 } 99 100 return content 101 } 102 103 var goPackages = []goPackage{ 104 { 105 srcPkg: "text/template", dstPkg: "texttemplate", 106 replacer: func(name, content string) string { return textTemplateReplacers.Replace(commonReplace(name, content)) }, 107 }, 108 { 109 srcPkg: "html/template", dstPkg: "htmltemplate", replacer: func(name, content string) string { 110 if strings.HasSuffix(name, "content.go") { 111 // Remove template.HTML types. We need to use the Go types. 112 content = removeAll(`(?s)// Strings of content.*?\)\n`, content) 113 } 114 115 content = commonReplace(name, content) 116 117 return htmlTemplateReplacers.Replace(content) 118 }, 119 rewriter: func(name string) { 120 for _, s := range []string{"CSS", "HTML", "HTMLAttr", "JS", "JSStr", "URL", "Srcset"} { 121 rewrite(name, fmt.Sprintf("%s -> htmltemplate.%s", s, s)) 122 } 123 rewrite(name, `"text/template/parse" -> "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"`) 124 }, 125 }, 126 {srcPkg: "internal/fmtsort", dstPkg: "fmtsort", rewriter: func(name string) { 127 rewrite(name, `"internal/fmtsort" -> "github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"`) 128 }}, 129 { 130 srcPkg: "internal/testenv", dstPkg: "testenv", 131 replacer: func(name, content string) string { return testEnvReplacers.Replace(content) }, rewriter: func(name string) { 132 rewrite(name, `"internal/testenv" -> "github.com/gohugoio/hugo/tpl/internal/go_templates/testenv"`) 133 }, 134 }, 135 {srcPkg: "internal/cfg", dstPkg: "cfg", rewriter: func(name string) { 136 rewrite(name, `"internal/cfg" -> "github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"`) 137 }}, 138 } 139 140 var fs = afero.NewOsFs() 141 142 // Removes all non-Hugo files in the go_templates folder. 143 func cleanFork() { 144 must(filepath.Walk(filepath.Join(forkRoot), func(path string, info os.FileInfo, err error) error { 145 if !info.IsDir() && len(path) > 10 && !strings.Contains(path, "hugo") { 146 must(fs.Remove(path)) 147 } 148 return nil 149 })) 150 } 151 152 func must(err error, what ...string) { 153 if err != nil { 154 log.Fatal(what, " ERROR: ", err) 155 } 156 } 157 158 func copyGoPackage(dst, src string) { 159 from := filepath.Join(goSource, src) 160 to := filepath.Join(forkRoot, dst) 161 fmt.Println("Copy", from, "to", to) 162 must(hugio.CopyDir(fs, from, to, func(s string) bool { return true })) 163 } 164 165 func doWithGoFiles(dir string, 166 rewrite func(name string), 167 transform func(name, in string) string) { 168 if rewrite == nil && transform == nil { 169 return 170 } 171 must(filepath.Walk(filepath.Join(forkRoot, dir), func(path string, info os.FileInfo, err error) error { 172 if info.IsDir() { 173 return nil 174 } 175 176 if !strings.HasSuffix(path, ".go") || strings.Contains(path, "hugo_") { 177 return nil 178 } 179 180 fmt.Println("Handle", path) 181 182 if rewrite != nil { 183 rewrite(path) 184 } 185 186 if transform == nil { 187 return nil 188 } 189 190 data, err := ioutil.ReadFile(path) 191 must(err) 192 f, err := os.Create(path) 193 must(err) 194 defer f.Close() 195 _, err = f.WriteString(transform(path, string(data))) 196 must(err) 197 198 return nil 199 })) 200 } 201 202 func removeAll(expression, content string) string { 203 re := regexp.MustCompile(expression) 204 return re.ReplaceAllString(content, "") 205 } 206 207 func rewrite(filename, rule string) { 208 cmf, _ := hexec.SafeCommand("gofmt", "-w", "-r", rule, filename) 209 out, err := cmf.CombinedOutput() 210 if err != nil { 211 log.Fatal("gofmt failed:", string(out)) 212 } 213 } 214 215 func goimports(dir string) { 216 cmf, _ := hexec.SafeCommand("goimports", "-w", dir) 217 out, err := cmf.CombinedOutput() 218 if err != nil { 219 log.Fatal("goimports failed:", string(out)) 220 } 221 } 222 223 func gofmt(dir string) { 224 cmf, _ := hexec.SafeCommand("gofmt", "-w", dir) 225 out, err := cmf.CombinedOutput() 226 if err != nil { 227 log.Fatal("gofmt failed:", string(out)) 228 } 229 }