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