all repos

mugit @ 6e228d9192b98e484881b8e702734ae920511538

🐮 git server that your cow will love

mugit/internal/mdx/relative_link.go(view raw)

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
95
96
97
98
99
100
101
package mdx

import (
	"fmt"
	"net/url"
	"path"
	"path/filepath"
	"strings"

	"github.com/yuin/goldmark"
	"github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
)

var (
	repoNameKey = parser.NewContextKey()
	baseDirKey  = parser.NewContextKey()
)

func NewRelativeLinkCtx(repoName, readmePath string) parser.ParseOption {
	ctx := parser.NewContext()
	ctx.Set(repoNameKey, repoName)
	ctx.Set(baseDirKey, filepath.Dir(readmePath))
	return parser.WithContext(ctx)
}

var RelativeLink = &relativeLink{}

type relativeLink struct{}

func (e *relativeLink) Extend(m goldmark.Markdown) {
	m.Parser().AddOptions(
		parser.WithASTTransformers(
			util.Prioritized(&relinkTransformer{}, 100),
		),
	)
}

type relinkTransformer struct{}

func (t *relinkTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
	repoName, _ := pc.Get(repoNameKey).(string)
	baseDir, _ := pc.Get(baseDirKey).(string)

	if repoName == "" {
		return
	}

	_ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
		if !entering {
			return ast.WalkContinue, nil
		}

		var dest *[]byte
		switch v := n.(type) {
		case *ast.Image:
			dest = &v.Destination
		case *ast.Link:
			dest = &v.Destination
		default:
			return ast.WalkContinue, nil
		}

		urlStr := string(*dest)

		// skip absolute URLs
		if strings.HasPrefix(urlStr, "http://") ||
			strings.HasPrefix(urlStr, "https://") ||
			strings.HasPrefix(urlStr, "//") ||
			strings.HasPrefix(urlStr, "#") ||
			strings.HasPrefix(urlStr, "mailto:") ||
			strings.HasPrefix(urlStr, "data:") {
			return ast.WalkContinue, nil
		}

		urlStr = strings.TrimPrefix(urlStr, "./")

		var absPath string
		if after, ok := strings.CutPrefix(urlStr, "/"); ok {
			absPath = after // abs from repo root
		} else {
			// relative to repo location
			if baseDir == "" || baseDir == "." {
				absPath = urlStr
			} else {
				absPath = path.Join(baseDir, urlStr)
			}
		}

		absPath = path.Clean(absPath)
		absPath = strings.TrimPrefix(absPath, "/")

		// FIXME:hardcoded link
		*dest = fmt.Appendf(nil, "/%s/blob/HEAD/%s?raw=true",
			url.PathEscape(repoName), absPath)

		return ast.WalkContinue, nil
	})
}