tools/docs2wiki/main.go (126 lines of code) (raw):

// Copyright 2022 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !windows // +build !windows // Tool docs2wiki rewrites links in ./docs/* to wiki link format. // This program may call the 'diff' tool which may be missing on Windows. package main import ( "bytes" "flag" "fmt" "io/fs" "io/ioutil" "net/url" "os" "os/exec" "path/filepath" "regexp" "strings" "text/template" ) var writeFlag = flag.Bool("w", false, "Overwrite new file contents to disk.") func main() { flag.Parse() if len(flag.Args()) != 1 { errorf("Usage: %v <dir>", os.Args[0]) os.Exit(1) } genFooter := footerGenerator("https://github.com/golang/vscode-go/edit/master/docs/") if err := rewriteLinks(flag.Arg(0), genFooter, *writeFlag); err != nil { errorf("failed to rewrite links: %v", err) os.Exit(1) } } func rewriteLinks(dir string, genFooter func(srcPath string) []byte, overwrite bool) error { return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if d.IsDir() { return nil } name := d.Name() if filepath.Ext(name) != ".md" { return nil } errorf("processing %v... %v", name, path) data, err := ioutil.ReadFile(path) if err != nil { return fmt.Errorf("failed to read file %v: %w", name, err) } converted := stripTitleInPage(data) converted = markdownLink2WikiLink(converted) if name != "_Footer.md" && name != "_Sidebar.md" { relPath, _ := filepath.Rel(dir, path) converted = append(converted, genFooter(filepath.ToSlash(relPath))...) } if overwrite { return ioutil.WriteFile(path, converted, 0644) } tmp, err := writeToTempFile(converted) if err != nil { return fmt.Errorf("failed to write to temp file for diff: %w", err) } defer os.Remove(tmp) diff(path, tmp) return nil }) } func diff(f1, f2 string) { cmd := exec.Command("diff", f1, f2) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { errorf("failed diff %v %v: %v", f1, f2, err) } } func writeToTempFile(content []byte) (filename string, err error) { dst, err := ioutil.TempFile("", "tmp") if err != nil { return "", fmt.Errorf("failed to write to a temporary file for diff: %v", err) } defer func() { if err == nil { err = dst.Close() } }() dst.Write(content) return dst.Name(), nil } func stripTitleInPage(src []byte) []byte { // remove the first line if it starts with "#" if len(src) == 0 || src[0] != '#' { return src } index := bytes.Index(src, []byte("\n")) if index < 0 { return src } return src[index+1:] } // find pattern like '](link.md)' var markdownLinkRE = regexp.MustCompile(`\]\(\S+(:?\.md|\.md#[^)]*)\)`) func markdownLink2WikiLink(src []byte) []byte { return markdownLinkRE.ReplaceAllFunc(src, func(s []byte) []byte { part := string(s[2 : len(s)-1]) // remove leading `](` and ending `)` u, err := url.Parse(part) if err != nil { return s } if u.Scheme != "" { return s } u.Path = strings.TrimSuffix(u.Path, ".md") b := &bytes.Buffer{} fmt.Fprintf(b, "](%s)", u.String()) return b.Bytes() }) } func errorf(format string, a ...interface{}) { fmt.Fprintf(os.Stderr, format+"\n", a...) } func footerGenerator(sourceEditURL string) func(string) []byte { return func(srcPath string) []byte { editURL := sourceEditURL + srcPath buf := new(bytes.Buffer) footerTmpl.Execute(buf, editURL) return buf.Bytes() } } var footerTmpl = template.Must(template.New("footer").Parse(` --- [*✏️ Want to contribute to this wiki?*]({{.}}) Update [the source]({{.}}) and send a PR.`))