~samwhited/checkmd

ref: 9dd91e3bdaead5f50cbce825a598a08fb644b308 checkmd/main.go -rw-r--r-- 1.9 KiB
9dd91e3bSam Whited Initial commit with simple link parsing 1 year, 2 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
// 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)
	}
	for _, root := range os.Args[1:] {
		if isMD(root) {
			err := checkFile(root)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				os.Exit(1)
			}
			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
			}

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

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

func checkFile(fpath string) error {
	/* #nosec */
	md, err := ioutil.ReadFile(fpath)
	if err != nil {
		return err
	}
	node := blackfriday.New().Parse(md)
	var hasErrs int
	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++
			fmt.Fprintf(os.Stderr, "link %q is not a valid URL: %v\n", node.LinkData.Destination, err)
			return blackfriday.GoToNext
		}
		if u.Scheme == "" {
			hasErrs++
			fmt.Fprintf(os.Stderr, "%s: link is missing the URL scheme: %s\n", fpath, u)
		}
		return blackfriday.GoToNext
	})
	if hasErrs > 0 {
		return fmt.Errorf("found %d errors", hasErrs)
	}
	return nil
}