this repo has no description
1// Package markup is an umbrella package for all markups and their renderers. 2package markup 3 4import ( 5 "bytes" 6 "path" 7 8 "github.com/yuin/goldmark" 9 "github.com/yuin/goldmark/ast" 10 "github.com/yuin/goldmark/extension" 11 "github.com/yuin/goldmark/parser" 12 "github.com/yuin/goldmark/text" 13 "github.com/yuin/goldmark/util" 14) 15 16// RendererType defines the type of renderer to use based on context 17type RendererType int 18 19const ( 20 // RendererTypeRepoMarkdown is for repository documentation markdown files 21 RendererTypeRepoMarkdown RendererType = iota 22 // RendererTypeIssueComment is for issue comments 23 RendererTypeIssueComment 24 // RendererTypePullComment is for pull request comments 25 RendererTypePullComment 26 // RendererTypeDefault is the default renderer with minimal transformations 27 RendererTypeDefault 28) 29 30// RenderContext holds the contextual data for rendering markdown. 31// It can be initialized empty, and that'll skip any transformations 32// and use the default renderer (RendererTypeDefault). 33type RenderContext struct { 34 Ref string 35 FullRepoName string 36 RendererType RendererType 37} 38 39func (rctx *RenderContext) RenderMarkdown(source string) string { 40 md := goldmark.New( 41 goldmark.WithExtensions(extension.GFM), 42 goldmark.WithParserOptions( 43 parser.WithAutoHeadingID(), 44 ), 45 ) 46 47 if rctx != nil { 48 var transformers []util.PrioritizedValue 49 50 transformers = append(transformers, util.Prioritized(&MarkdownTransformer{rctx: rctx}, 10000)) 51 52 md.Parser().AddOptions( 53 parser.WithASTTransformers(transformers...), 54 ) 55 } 56 57 var buf bytes.Buffer 58 if err := md.Convert([]byte(source), &buf); err != nil { 59 return source 60 } 61 return buf.String() 62} 63 64type MarkdownTransformer struct { 65 rctx *RenderContext 66} 67 68func (a *MarkdownTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { 69 _ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { 70 if !entering { 71 return ast.WalkContinue, nil 72 } 73 74 switch a.rctx.RendererType { 75 case RendererTypeRepoMarkdown: 76 a.rctx.relativeLinkTransformer(n.(*ast.Link)) 77 case RendererTypeDefault: 78 a.rctx.relativeLinkTransformer(n.(*ast.Link)) 79 // more types here like RendererTypeIssue/Pull etc. 80 } 81 82 return ast.WalkContinue, nil 83 }) 84} 85 86func (rctx *RenderContext) relativeLinkTransformer(link *ast.Link) { 87 dst := string(link.Destination) 88 89 if len(dst) == 0 || dst[0] == '#' || 90 bytes.Contains(link.Destination, []byte("://")) || 91 bytes.HasPrefix(link.Destination, []byte("mailto:")) { 92 return 93 } 94 95 newPath := path.Join("/", rctx.FullRepoName, "tree", rctx.Ref, dst) 96 link.Destination = []byte(newPath) 97}