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  }