blob: cc8d13597c823c50c6ebd184a5cbbff695d93a4d [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"
Nigel Tao0152bd92019-12-11 16:07:23 +110041 "os"
42 "path/filepath"
43 "sort"
44 "strings"
45
Nigel Taod3c7d102021-08-22 10:49:18 +100046 "github.com/russross/blackfriday/v2"
Nigel Tao0152bd92019-12-11 16:07:23 +110047)
48
49var skiphttp = flag.Bool("skiphttp", false, `skip "http:" or "https:" links`)
50
51func main() {
52 if err := main1(); err != nil {
53 os.Stderr.WriteString(err.Error() + "\n")
54 os.Exit(1)
55 }
56}
57
58func main1() error {
59 flag.Parse()
60 r := &renderer{
61 links: map[string]struct{}{},
62 }
63
64 for _, arg := range flag.Args() {
65 err := filepath.Walk(arg, func(path string, info os.FileInfo, walkErr error) error {
66 if walkErr != nil {
67 return walkErr
68 }
69 if info.IsDir() || !strings.HasSuffix(path, ".md") {
70 return nil
71 }
72 return visit(r, path)
73 })
74 if err != nil {
75 return err
76 }
77 }
78
79 sorted := make([]string, 0, len(r.links))
80 for k := range r.links {
81 sorted = append(sorted, k)
82 }
83 sort.Strings(sorted)
84
85 for _, s := range sorted {
86 mark := '-'
87 switch {
88 case strings.HasPrefix(s, "http:"), strings.HasPrefix(s, "https:"):
89 if *skiphttp {
90 continue
91 }
92 mark = ':'
93 case (len(s) > 0) && (s[0] == '/'):
94 filename := s[1:]
95 if i := strings.IndexByte(filename, '#'); i >= 0 {
96 filename = filename[:i]
97 }
98 if _, err := os.Stat(filename); err == nil {
99 mark = '+'
100 }
101 }
102 fmt.Printf("%c %s\n", mark, s)
103 }
104 return nil
105}
106
107func visit(r *renderer, filename string) error {
Nigel Tao226c4762021-08-22 11:05:43 +1000108 src, err := os.ReadFile(filename)
Nigel Tao0152bd92019-12-11 16:07:23 +1100109 if err != nil {
110 return err
111 }
112 blackfriday.Run(src, blackfriday.WithRenderer(r))
113 return nil
114}
115
116type renderer struct {
117 links map[string]struct{}
118}
119
120func (r *renderer) RenderHeader(w io.Writer, n *blackfriday.Node) {}
121func (r *renderer) RenderFooter(w io.Writer, n *blackfriday.Node) {}
122
123func (r *renderer) RenderNode(w io.Writer, n *blackfriday.Node, entering bool) blackfriday.WalkStatus {
124 if entering {
125 if dst := n.LinkData.Destination; len(dst) != 0 {
126 r.links[string(dst)] = struct{}{}
127 }
128 }
129 return blackfriday.GoToNext
130}