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