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
})
}
|