~samwhited/checkmd

ref: 610e955e5400003cf8a6d8b9394bd7fe6de5589c checkmd/main.go -rw-r--r-- 2.0 KiB
610e955eSam Whited checkmd: don't short circuit if file has errors 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// Copyright 2020 The Mellium Contributors.
// Use of this source code is governed by the BSD 2-clause
// license that can be found in the LICENSE file.

// The checkmd command parses Markdown documents and returns an error if any
// relative links or invalid URLs are found.
// Other tests may be added later.
package main

import (
	"fmt"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/russross/blackfriday/v2"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, "usage %s filenames\n", os.Args[0])
		os.Exit(1)
	}

	var hasErrs bool
	for _, root := range os.Args[1:] {
		if isMD(root) {
			ok, err := checkFile(root)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			hasErrs = hasErrs || ok
			continue
		}

		// If the path doesn't appear to be a markdown file assume it's a directory
		// and try to walk it.
		err := filepath.Walk(root, func(fpath string, info os.FileInfo, err error) error {
			if !isMD(fpath) {
				return nil
			}

			ok, err := checkFile(fpath)
			if err != nil {
				return err
			}
			hasErrs = hasErrs || ok
			return nil
		})
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}

	if hasErrs {
		os.Exit(1)
	}
}

func isMD(fpath string) bool {
	return strings.HasSuffix(fpath, ".md") || strings.HasSuffix(fpath, ".markdown")
}

func checkFile(fpath string) (bool, error) {
	/* #nosec */
	md, err := ioutil.ReadFile(fpath)
	if err != nil {
		return false, err
	}
	node := blackfriday.New().Parse(md)
	var hasErrs bool
	node.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
		if node.Type != blackfriday.Link || !entering {
			return blackfriday.GoToNext
		}

		u, err := url.Parse(string(node.LinkData.Destination))
		if err != nil {
			hasErrs = true
			fmt.Fprintf(os.Stderr, "link %q is not a valid URL: %v\n", node.LinkData.Destination, err)
			return blackfriday.GoToNext
		}
		if u.Scheme == "" {
			hasErrs = true
			fmt.Fprintf(os.Stderr, "%s: link is missing the URL scheme: %s\n", fpath, u)
		}
		return blackfriday.GoToNext
	})
	return hasErrs, nil
}