blob: 35caffac8bf9bd7ecf4f8372dcac8ee2faf1a97d [file] [log] [blame]
Nigel Tao0152bd92019-12-11 16:07:23 +11001// Copyright 2019 The Wuffs Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
Nigel Taod3c7d102021-08-22 10:49:18 +100015//go:build ignore
Nigel Tao0152bd92019-12-11 16:07:23 +110016// +build ignore
17
18package main
19
20// print-markdown-links.go prints a sorted list of the link targets in the
21// given .md files (or directories containing .md files).
22//
23// Usage: go run print-markdown-links.go dirname0 filename1 etc
24//
25// For link targets that look like "/file/names" (as opposed to starting with
26// web URLs starting with "http:" or "https:"), they are preceded by a '+' or a
27// '-' depending on whether or not that file or directory exists, assuming that
28// this script is run in the root directory to resolve filename links that
29// start with a slash.
30//
31// Printing web URLs can be skipped entirely by passing -skiphttp
32//
33// For example, running:
34// go run script/print-markdown-links.go -skiphttp doc | grep "^-"
35// can find broken Markdown links in the documentation.
36
37import (
38 "flag"
39 "fmt"
40 "io"
41 "io/ioutil"
42 "os"
43 "path/filepath"
44 "sort"
45 "strings"
46
Nigel Taod3c7d102021-08-22 10:49:18 +100047 "github.com/russross/blackfriday/v2"
Nigel Tao0152bd92019-12-11 16:07:23 +110048)
49
50var skiphttp = flag.Bool("skiphttp", false, `skip "http:" or "https:" links`)
51
52func main() {
53 if err := main1(); err != nil {
54 os.Stderr.WriteString(err.Error() + "\n")
55 os.Exit(1)
56 }
57}
58
59func main1() error {
60 flag.Parse()
61 r := &renderer{
62 links: map[string]struct{}{},
63 }
64
65 for _, arg := range flag.Args() {
66 err := filepath.Walk(arg, func(path string, info os.FileInfo, walkErr error) error {
67 if walkErr != nil {
68 return walkErr
69 }
70 if info.IsDir() || !strings.HasSuffix(path, ".md") {
71 return nil
72 }
73 return visit(r, path)
74 })
75 if err != nil {
76 return err
77 }
78 }
79
80 sorted := make([]string, 0, len(r.links))
81 for k := range r.links {
82 sorted = append(sorted, k)
83 }
84 sort.Strings(sorted)
85
86 for _, s := range sorted {
87 mark := '-'
88 switch {
89 case strings.HasPrefix(s, "http:"), strings.HasPrefix(s, "https:"):
90 if *skiphttp {
91 continue
92 }
93 mark = ':'
94 case (len(s) > 0) && (s[0] == '/'):
95 filename := s[1:]
96 if i := strings.IndexByte(filename, '#'); i >= 0 {
97 filename = filename[:i]
98 }
99 if _, err := os.Stat(filename); err == nil {
100 mark = '+'
101 }
102 }
103 fmt.Printf("%c %s\n", mark, s)
104 }
105 return nil
106}
107
108func visit(r *renderer, filename string) error {
109 src, err := ioutil.ReadFile(filename)
110 if err != nil {
111 return err
112 }
113 blackfriday.Run(src, blackfriday.WithRenderer(r))
114 return nil
115}
116
117type renderer struct {
118 links map[string]struct{}
119}
120
121func (r *renderer) RenderHeader(w io.Writer, n *blackfriday.Node) {}
122func (r *renderer) RenderFooter(w io.Writer, n *blackfriday.Node) {}
123
124func (r *renderer) RenderNode(w io.Writer, n *blackfriday.Node, entering bool) blackfriday.WalkStatus {
125 if entering {
126 if dst := n.LinkData.Destination; len(dst) != 0 {
127 r.links[string(dst)] = struct{}{}
128 }
129 }
130 return blackfriday.GoToNext
131}