diff options
| author | Michael Matloob <matloob@golang.org> | 2023-12-14 13:10:46 -0500 |
|---|---|---|
| committer | Michael Matloob <matloob@golang.org> | 2023-12-18 22:12:03 +0000 |
| commit | 8d0563ae243c7730ff0ea324d6b44b460f58d5fb (patch) | |
| tree | 18a6952443543865df72ef435409d3cddaa4c622 | |
| parent | 9ad9de49cb949ecfee34b8f80af60b2005d39275 (diff) | |
| download | go-x-pkgsite-8d0563ae243c7730ff0ea324d6b44b460f58d5fb.tar.xz | |
internal/frontend: remove use of goldmark for readme rendering
delete the code that uses goldmark and clean up the variants of the
code that use the markdown parser
For golang/go#61399
Change-Id: I03e8c303086110278dd0f3994ba97729e0cbf7c1
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/550039
Reviewed-by: Jonathan Amsterdam <jba@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
TryBot-Bypass: Michael Matloob <matloob@golang.org>
| -rw-r--r-- | go.mod | 2 | ||||
| -rw-r--r-- | go.sum | 3 | ||||
| -rw-r--r-- | internal/frontend/goldmark.go | 346 | ||||
| -rw-r--r-- | internal/frontend/main.go | 2 | ||||
| -rw-r--r-- | internal/frontend/markdown.go | 68 | ||||
| -rw-r--r-- | internal/frontend/readme.go | 119 | ||||
| -rw-r--r-- | internal/frontend/readme_test.go | 78 |
7 files changed, 83 insertions, 535 deletions
@@ -27,8 +27,6 @@ require ( github.com/jba/templatecheck v0.6.0 github.com/lib/pq v1.10.9 github.com/russross/blackfriday/v2 v2.1.0 - github.com/yuin/goldmark v1.6.0 - github.com/yuin/goldmark-emoji v1.0.1 go.opencensus.io v0.24.0 golang.org/x/mod v0.14.0 golang.org/x/net v0.19.0 @@ -1039,9 +1039,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= -github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= -github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da h1:NimzV1aGyq29m5ukMK0AMWEhFaL/lrEOaephfuoiARg= github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= diff --git a/internal/frontend/goldmark.go b/internal/frontend/goldmark.go deleted file mode 100644 index 22f6e2cd..00000000 --- a/internal/frontend/goldmark.go +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright 2020 The Go Authors. All rights reserved. - * Use of this source code is governed by a BSD-style - * license that can be found in the LICENSE file. - */ - -package frontend - -import ( - "bytes" - "context" - "fmt" - "strings" - - "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/parser" - "github.com/yuin/goldmark/renderer" - "github.com/yuin/goldmark/renderer/html" - "github.com/yuin/goldmark/text" - "github.com/yuin/goldmark/util" - "golang.org/x/pkgsite/internal" - "golang.org/x/pkgsite/internal/log" - "golang.org/x/pkgsite/internal/source" - "rsc.io/markdown" -) - -// astTransformer is a default transformer of the goldmark tree. We pass in -// readme information to use for the link transformations. -type astTransformer struct { - info *source.Info - readme *internal.Readme -} - -// Transform transforms the given AST tree. -func (g *astTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { - _ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { - if !entering { - return ast.WalkContinue, nil - } - switch v := n.(type) { - case *ast.Image: - if d := translateLink(string(v.Destination), g.info, true, g.readme); d != "" { - v.Destination = []byte(d) - } - case *ast.Link: - if d := translateLink(string(v.Destination), g.info, false, g.readme); d != "" { - v.Destination = []byte(d) - } - } - return ast.WalkContinue, nil - }) -} - -// htmlRenderer is a renderer.NodeRenderer implementation that renders -// pkg.go.dev readme features. -type htmlRenderer struct { - html.Config - info *source.Info - readme *internal.Readme - // firstHeading and offset are used to calculate the first heading tag's level in a readme. - firstHeading bool - offset int -} - -// newHTMLRenderer creates a new HTMLRenderer for a readme. -func newHTMLRenderer(info *source.Info, readme *internal.Readme, opts ...html.Option) renderer.NodeRenderer { - r := &htmlRenderer{ - info: info, - readme: readme, - Config: html.NewConfig(), - firstHeading: true, - offset: 0, - } - for _, opt := range opts { - opt.SetHTMLOption(&r.Config) - } - return r -} - -// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs. -func (r *htmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { - reg.Register(ast.KindHeading, r.renderHeading) - reg.Register(ast.KindHTMLBlock, r.renderHTMLBlock) - reg.Register(ast.KindRawHTML, r.renderRawHTML) -} - -func (r *htmlRenderer) renderHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { - n := node.(*ast.Heading) - if r.firstHeading { - // The offset ensures the first heading is always an <h3>. - r.offset = 3 - n.Level - r.firstHeading = false - } - newLevel := n.Level + r.offset - if entering { - // TODO(matloob): Do we want the div and h elements to have analogous classes? - // Currently we're using newLevel for the div's class but n.Level for the h element's - // class. - if newLevel > 6 { - _, _ = w.WriteString(fmt.Sprintf(`<div class="h%d" role="heading" aria-level="%d"`, newLevel, n.Level)) - } else { - _, _ = w.WriteString(fmt.Sprintf(`<h%d class="h%d"`, newLevel, n.Level)) - } - if n.Attributes() != nil { - html.RenderAttributes(w, node, html.HeadingAttributeFilter) - } - _ = w.WriteByte('>') - } else { - if newLevel > 6 { - _, _ = w.WriteString("</div>\n") - } else { - _, _ = w.WriteString(fmt.Sprintf("</h%d>\n", newLevel)) - } - } - return ast.WalkContinue, nil -} - -// renderHTMLBlock is copied directly from the goldmark source code and -// modified to call translateHTML in every block -func (r *htmlRenderer) renderHTMLBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { - n := node.(*ast.HTMLBlock) - if entering { - if r.Unsafe { - l := n.Lines().Len() - for i := 0; i < l; i++ { - line := n.Lines().At(i) - d, err := translateHTML(line.Value(source), r.info, r.readme) - if err != nil { - return ast.WalkStop, err - } - _, _ = w.Write(d) - } - } else { - _, _ = w.WriteString("<!-- raw HTML omitted -->\n") - } - } else { - if n.HasClosure() { - if r.Unsafe { - closure := n.ClosureLine - _, _ = w.Write(closure.Value(source)) - } else { - _, _ = w.WriteString("<!-- raw HTML omitted -->\n") - } - } - } - return ast.WalkContinue, nil -} - -func (r *htmlRenderer) renderRawHTML(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { - if !entering { - return ast.WalkSkipChildren, nil - } - if r.Unsafe { - n := node.(*ast.RawHTML) - for i := 0; i < n.Segments.Len(); i++ { - segment := n.Segments.At(i) - d, err := translateHTML(segment.Value(source), r.info, r.readme) - if err != nil { - return ast.WalkStop, err - } - _, _ = w.Write(d) - } - return ast.WalkSkipChildren, nil - } - _, _ = w.WriteString("<!-- raw HTML omitted -->") - return ast.WalkSkipChildren, nil -} - -// ids is a collection of element ids in document. -type ids struct { - values map[string]bool -} - -// newIDs creates a collection of element ids in a document. -func newIDs() parser.IDs { - return &ids{ - values: map[string]bool{}, - } -} - -// Generate turns heading content from a markdown document into a heading id. -// First HTML markup and markdown images are stripped then ASCII letters -// and numbers are used to generate the final result. Finally, all heading ids -// are prefixed with "readme-" to avoid name collisions with other ids on the -// unit page. Duplicated heading ids are given an incremental suffix. See -// readme_test.go for examples. -func (s *ids) Generate(value []byte, kind ast.NodeKind) []byte { - var defaultID string - if kind == ast.KindHeading { - defaultID = "heading" - } else { - defaultID = "id" - } - - parser := &markdown.Parser{} - doc := parser.Parse("# " + string(value)) - return []byte(s.generateID(doc, defaultID)) -} - -func (s *ids) generateID(block markdown.Block, defaultID string) string { - var buf bytes.Buffer - walkBlocks([]markdown.Block{block}, func(b markdown.Block) error { - if t, ok := b.(*markdown.Text); ok { - for _, inl := range t.Inline { - inl.PrintText(&buf) - } - } - return nil - }) - f := func(c rune) bool { - return !('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z') && !('0' <= c && c <= '9') - } - str := strings.Join(strings.FieldsFunc(buf.String(), f), "-") - str = strings.ToLower(str) - if len(str) == 0 { - str = defaultID - } - key := str - for i := 1; ; i++ { - if _, ok := s.values[key]; !ok { - s.values[key] = true - break - } - key = fmt.Sprintf("%s-%d", str, i) - } - return "readme-" + key -} - -// Put implements Put from the goldmark parser IDs interface. -func (s *ids) Put(value []byte) { - s.values[string(value)] = true -} - -type extractLinks struct { - ctx context.Context - inLinksHeading bool - links []link -} - -// The name of the heading from which we extract links. -const linkHeadingText = "Links" - -var linkHeadingBytes = []byte(linkHeadingText) // for faster comparison to node contents - -// Transform extracts links from the "Links" section of a README. -func (e *extractLinks) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { - err := ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { - if !entering { - return ast.WalkContinue, nil - } - switch n := n.(type) { - - case *ast.Heading: - // We are in the links heading from the point we see a heading with - // linkHeadingText until the point we see the next heading. - if e.inLinksHeading { - return ast.WalkStop, nil - } - if bytes.Equal(n.Text(reader.Source()), linkHeadingBytes) { - e.inLinksHeading = true - } - - case *ast.ListItem: - // When in the links heading, extract links from list items. - if !e.inLinksHeading { - return ast.WalkSkipChildren, nil - } - // We expect the pattern: ListItem -> TextBlock -> Link, with no - // other children. - if tb, ok := n.FirstChild().(*ast.TextBlock); ok { - if l, ok := tb.FirstChild().(*ast.Link); ok && l.NextSibling() == nil { - // Record the link. - e.links = append(e.links, link{ - Href: string(l.Destination), - Body: string(l.Text(reader.Source())), - }) - } - } - return ast.WalkSkipChildren, nil - } - - return ast.WalkContinue, nil - }) - if err != nil { - log.Errorf(e.ctx, "extractLinks.Transform: %v", err) - } -} - -type extractTOC struct { - ctx context.Context - Headings []*Heading - removeTitle bool // omit title from TOC -} - -// Transform collects the headings from a readme into an outline -// of the document. It nests the headings based on the h-level hierarchy. -// See tests for heading levels in TestReadme for behavior. -func (e *extractTOC) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { - var headings []*Heading - err := ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { - if n.Kind() == ast.KindHeading && entering { - heading := n.(*ast.Heading) - section := &Heading{ - Level: heading.Level, - Text: string(n.Text(reader.Source())), - } - if id, ok := heading.AttributeString("id"); ok { - section.ID = string(id.([]byte)) - } - headings = append(headings, section) - return ast.WalkSkipChildren, nil - } - return ast.WalkContinue, nil - }) - if err != nil { - log.Errorf(e.ctx, "extractTOC.Transform: %v", err) - } - - // We nest the headings by walking through the list we extracted and - // establishing parent child relationships based on heading levels. - var nested []*Heading - for i, h := range headings { - if i == 0 { - nested = append(nested, h) - continue - } - parent := headings[i-1] - for parent != nil && parent.Level >= h.Level { - parent = parent.parent - } - if parent == nil { - nested = append(nested, h) - } else { - h.parent = parent - parent.Children = append(parent.Children, h) - } - } - if e.removeTitle { - // If there is only one top tevel heading with 1 or more children we - // assume it is the title of the document and remove it from the TOC. - if len(nested) == 1 && len(nested[0].Children) > 0 { - nested = nested[0].Children - } - } - e.Headings = nested -} diff --git a/internal/frontend/main.go b/internal/frontend/main.go index ae8f03d3..32a4dc82 100644 --- a/internal/frontend/main.go +++ b/internal/frontend/main.go @@ -259,7 +259,7 @@ func readmeContent(ctx context.Context, u *internal.Unit) (_ *Readme, err error) if !u.IsRedistributable { return &Readme{}, nil } - return ProcessReadmeMarkdown(ctx, u) + return ProcessReadme(ctx, u) } const missingDocReplacement = `<p>Documentation is missing.</p>` diff --git a/internal/frontend/markdown.go b/internal/frontend/markdown.go index fcb5d987..94274476 100644 --- a/internal/frontend/markdown.go +++ b/internal/frontend/markdown.go @@ -19,7 +19,7 @@ import ( "rsc.io/markdown" ) -// ProcessReadmeMarkdown processes the README of unit u, if it has one. +// ProcessReadme processes the README of unit u, if it has one. // Processing includes rendering and sanitizing the HTML or Markdown, // and extracting headings and links. // @@ -31,12 +31,12 @@ import ( // The extracted links are for display outside of the readme contents. // // This function is exported for use by external tools. -func ProcessReadmeMarkdown(ctx context.Context, u *internal.Unit) (_ *Readme, err error) { - defer derrors.WrapAndReport(&err, "ProcessReadmeMarkdown(%q, %q, %q)", u.Path, u.ModulePath, u.Version) - return processReadmeMarkdown(ctx, u.Readme, u.SourceInfo) +func ProcessReadme(ctx context.Context, u *internal.Unit) (_ *Readme, err error) { + defer derrors.WrapAndReport(&err, "ProcessReadme(%q, %q, %q)", u.Path, u.ModulePath, u.Version) + return processReadme(ctx, u.Readme, u.SourceInfo) } -func processReadmeMarkdown(ctx context.Context, readme *internal.Readme, info *source.Info) (frontendReadme *Readme, err error) { +func processReadme(ctx context.Context, readme *internal.Readme, info *source.Info) (frontendReadme *Readme, err error) { if readme == nil || readme.Contents == "" { return &Readme{}, nil } @@ -160,6 +160,15 @@ func walkBlocks(blocks []markdown.Block, walkFunc func(b markdown.Block) error) return nil } +type extractTOC struct { + ctx context.Context + Headings []*Heading + removeTitle bool // omit title from TOC +} + +// extract collects the headings from a readme into an outline +// of the document. It nests the headings based on the h-level hierarchy. +// See tests for heading levels in TestReadme for behavior. func (e *extractTOC) extract(doc *markdown.Document) { var headings []*Heading err := walkBlocks(doc.Blocks, func(b markdown.Block) error { @@ -211,6 +220,18 @@ func (e *extractTOC) extract(doc *markdown.Document) { e.Headings = nested } +type extractLinks struct { + ctx context.Context + inLinksHeading bool + links []link +} + +// The name of the heading from which we extract links. +const linkHeadingText = "Links" + +var linkHeadingBytes = []byte(linkHeadingText) // for faster comparison to node contents + +// extract extracts links from the "Links" section of a README. func (e *extractLinks) extract(doc *markdown.Document) { var seenLinksHeading bool err := walkBlocks(doc.Blocks, func(b markdown.Block) error { @@ -371,16 +392,41 @@ var htmlQuoteEscaper = strings.NewReplacer( ) // rewriteHeadingIDs generates ids based on the body of the heading. -// The original code uses the raw markdown as the input to the ids.Generate -// function, but we don't have the raw markdown anymore, so we use the -// text instead. +// The ASCII letters and numbers from the text are used to generate +// each of the ids. Finally, all heading ids +// are prefixed with "readme-" to avoid name collisions with other ids on the +// unit page. Duplicated heading ids are given an incremental suffix. See +// readme_test.go for examples. func rewriteHeadingIDs(doc *markdown.Document) { - ids := &ids{ - values: map[string]bool{}, + ids := map[string]bool{} + + generateID := func(heading *markdown.Heading) string { + var buf bytes.Buffer + for _, inl := range heading.Text.Inline { + inl.PrintText(&buf) + } + f := func(c rune) bool { + return !('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z') && !('0' <= c && c <= '9') + } + str := strings.Join(strings.FieldsFunc(buf.String(), f), "-") + str = strings.ToLower(str) + if len(str) == 0 { + str = "heading" + } + key := str + for i := 1; ; i++ { + if _, ok := ids[key]; !ok { + ids[key] = true + break + } + key = fmt.Sprintf("%s-%d", str, i) + } + return "readme-" + key } + walkBlocks(doc.Blocks, func(b markdown.Block) error { if heading, ok := b.(*markdown.Heading); ok { - id := ids.generateID(heading, "heading") + id := generateID(heading) heading.ID = string(id) } return nil diff --git a/internal/frontend/readme.go b/internal/frontend/readme.go index 67098558..7cd159f5 100644 --- a/internal/frontend/readme.go +++ b/internal/frontend/readme.go @@ -6,24 +6,10 @@ package frontend import ( "bytes" - "context" "github.com/google/safehtml" - "github.com/google/safehtml/template" "github.com/google/safehtml/uncheckedconversions" - "github.com/yuin/goldmark" - emoji "github.com/yuin/goldmark-emoji" - "github.com/yuin/goldmark/extension" - "github.com/yuin/goldmark/parser" - "github.com/yuin/goldmark/renderer" - goldmarkHtml "github.com/yuin/goldmark/renderer/html" - gmtext "github.com/yuin/goldmark/text" - "github.com/yuin/goldmark/util" - "golang.org/x/pkgsite/internal" - "golang.org/x/pkgsite/internal/derrors" - "golang.org/x/pkgsite/internal/log" "golang.org/x/pkgsite/internal/sanitizer" - "golang.org/x/pkgsite/internal/source" ) // Heading holds data about a heading and nested headings within a readme. @@ -52,111 +38,6 @@ type Readme struct { Links []link // links from the "Links" section } -// ProcessReadme processes the README of unit u, if it has one. -// Processing includes rendering and sanitizing the HTML or Markdown, -// and extracting headings and links. -// -// Headings are prefixed with "readme-" and heading levels are adjusted to start -// at h3 in order to nest them properly within the rest of the page. The -// readme's original styling is preserved in the html by giving headings a css -// class styled identical to their original heading level. -// -// The extracted links are for display outside of the readme contents. -// -// This function is exported for use by external tools. -func ProcessReadme(ctx context.Context, u *internal.Unit) (_ *Readme, err error) { - defer derrors.WrapAndReport(&err, "ProcessReadme(%q, %q, %q)", u.Path, u.ModulePath, u.Version) - return processReadme(ctx, u.Readme, u.SourceInfo) -} - -func processReadme(ctx context.Context, readme *internal.Readme, sourceInfo *source.Info) (frontendReadme *Readme, err error) { - if readme == nil || readme.Contents == "" { - return &Readme{}, nil - } - if !isMarkdown(readme.Filepath) { - t := template.Must(template.New("").Parse(`<pre class="readme">{{.}}</pre>`)) - h, err := t.ExecuteToHTML(readme.Contents) - if err != nil { - return nil, err - } - return &Readme{HTML: h}, nil - } - - // Sets priority value so that we always use our custom transformer - // instead of the default ones. The default values are in: - // https://github.com/yuin/goldmark/blob/7b90f04af43131db79ec320be0bd4744079b346f/parser/parser.go#L567 - const astTransformerPriority = 10000 - el := &extractLinks{ctx: ctx} - et := &extractTOC{ctx: ctx, removeTitle: true} - gdMarkdown := goldmark.New( - goldmark.WithParserOptions( - // WithHeadingAttribute allows us to include other attributes in - // heading tags. This is useful for our aria-level implementation of - // increasing heading rankings. - parser.WithHeadingAttribute(), - // Generates an id in every heading tag. This is used in github in - // order to generate a link with a hash that a user would scroll to - // <h1 id="goldmark">goldmark</h1> => github.com/yuin/goldmark#goldmark - parser.WithAutoHeadingID(), - // Include custom ASTTransformer using the readme and module info to - // use translateRelativeLink and translateHTML to modify the AST - // before it is rendered. - parser.WithASTTransformers( - util.Prioritized(&astTransformer{ - info: sourceInfo, - readme: readme, - }, astTransformerPriority), - // Extract links after we have transformed the URLs. - util.Prioritized(el, astTransformerPriority+1), - util.Prioritized(et, astTransformerPriority+1), - ), - ), - // These extensions lets users write HTML code in the README. This is - // fine since we process the contents using the sanitizer after. - goldmark.WithRendererOptions(goldmarkHtml.WithUnsafe(), goldmarkHtml.WithXHTML()), - goldmark.WithExtensions( - extension.GFM, // Support Github Flavored Markdown. - emoji.Emoji, // Support Github markdown emoji markup. - ), - ) - gdMarkdown.Renderer().AddOptions( - renderer.WithNodeRenderers( - util.Prioritized(newHTMLRenderer(sourceInfo, readme), 100), - ), - ) - contents := []byte(readme.Contents) - gdParser := gdMarkdown.Parser() - reader := gmtext.NewReader(contents) - pctx := parser.NewContext(parser.WithIDs(newIDs())) - doc := gdParser.Parse(reader, parser.WithContext(pctx)) - gdRenderer := gdMarkdown.Renderer() - - var b bytes.Buffer - defer func() { - // It's possible for gdRenderer.Render to panic. For example, - // https://pkg.go.dev/github.com/jinghzhu/k8scrd/pkg/crd/jinghzhu/v1 - // results in a panic because gdRenderer.Render tries to index a slice - // out of bounds. - // - // In case of a panic from gdRenderer.Render, treat this as a normal - // error from that function. - if p := recover(); p != nil { - log.Debugf(ctx, "gdRenderer.Render: %v", p) - frontendReadme = &Readme{} - err = nil - } - }() - if err := gdRenderer.Render(&b, contents, doc); err != nil { - log.Debugf(ctx, "gdRenderer.Render: %v", err) - return &Readme{}, nil - } - return &Readme{ - HTML: sanitizeHTML(&b), - Outline: et.Headings, - Links: el.links, - }, nil -} - // sanitizeHTML sanitizes HTML from a bytes.Buffer so that it is safe. func sanitizeHTML(b *bytes.Buffer) safehtml.HTML { s := string(sanitizer.SanitizeBytes(b.Bytes())) diff --git a/internal/frontend/readme_test.go b/internal/frontend/readme_test.go index 91b4b3eb..60591ed9 100644 --- a/internal/frontend/readme_test.go +++ b/internal/frontend/readme_test.go @@ -161,9 +161,9 @@ func TestReadme(t *testing.T) { Filepath: sample.ReadmeFilePath, Contents: "# :zap: Zap \n\n :joy:", }, - wantHTML: "<h3 class=\"h1\" id=\"readme-zap-zap\">⚡ Zap</h3>\n<p>😂</p>", + wantHTML: "<h3 class=\"h1\" id=\"readme-zap\">⚡ Zap</h3>\n<p>😂</p>", wantOutline: []*Heading{ - {Level: 1, Text: " Zap", ID: "readme-zap-zap"}, + {Level: 1, Text: "⚡ Zap", ID: "readme-zap"}, }, }, { @@ -379,7 +379,7 @@ func TestReadme(t *testing.T) { Contents: `<p><img src="./foo.png"></p><p><img src="../bar.png"</p>` + "\n", }, wantHTML: `<p><img src="https://github.com/valid/module_name/raw/v1.0.0/dir/sub/foo.png"/></p>` + - `<p><img src="https://github.com/valid/module_name/raw/v1.0.0/dir/bar.png"/>` + "\n</p>", + `<p><img src="https://github.com/valid/module_name/raw/v1.0.0/dir/bar.png"/></p>`, wantOutline: nil, }, { @@ -481,41 +481,20 @@ func TestReadme(t *testing.T) { }, } { t.Run(test.name, func(t *testing.T) { - processReadmes := map[string]func(ctx context.Context, u *internal.Unit) (frontendReadme *Readme, err error){ - "goldmark": ProcessReadme, - "markdown": ProcessReadmeMarkdown, + test.unit.Readme = test.readme + readme, err := ProcessReadme(ctx, test.unit) + if err != nil { + t.Fatal(err) } - for processFuncName, processFunc := range processReadmes { - t.Run(processFuncName, func(t *testing.T) { - wantHTML := test.wantHTML - if processFuncName == "markdown" { - if test.name == "Github markdown emoji markup is properly rendered" { - t.Skip("github markdown emoji is not yet supported with the markdown package") - } - if test.name == "body has more than one child" { - // The markdown package treats the newline differently when there's an incomplete tag. - wantHTML = `<p><img src="https://github.com/valid/module_name/raw/v1.0.0/dir/sub/foo.png"/></p>` + - `<p><img src="https://github.com/valid/module_name/raw/v1.0.0/dir/bar.png"/></p>` - } - } - - test.unit.Readme = test.readme - readme, err := processFunc(ctx, test.unit) - if err != nil { - t.Fatal(err) - } - gotHTML := strings.TrimSpace(readme.HTML.String()) - if diff := cmp.Diff(wantHTML, gotHTML); diff != "" { - t.Errorf("Readme(%v) html mismatch (-want +got):\n%s", test.unit.UnitMeta, diff) - } - if diff := cmp.Diff(test.wantOutline, readme.Outline, cmp.Options{ - cmpopts.IgnoreUnexported(Heading{}), - }); diff != "" { - t.Errorf("Readme(%v) outline mismatch (-want +got):\n%s", test.unit.UnitMeta, diff) - } - }) + gotHTML := strings.TrimSpace(readme.HTML.String()) + if diff := cmp.Diff(test.wantHTML, gotHTML); diff != "" { + t.Errorf("Readme(%v) html mismatch (-want +got):\n%s", test.unit.UnitMeta, diff) + } + if diff := cmp.Diff(test.wantOutline, readme.Outline, cmp.Options{ + cmpopts.IgnoreUnexported(Heading{}), + }); diff != "" { + t.Errorf("Readme(%v) outline mismatch (-want +got):\n%s", test.unit.UnitMeta, diff) } - }) } } @@ -617,24 +596,17 @@ func TestReadmeLinks(t *testing.T) { }, } { t.Run(test.name, func(t *testing.T) { - processReadmes := map[string]func(ctx context.Context, u *internal.Unit) (frontendReadme *Readme, err error){ - "goldmark": ProcessReadme, - "markdown": ProcessReadmeMarkdown, + + unit.Readme = &internal.Readme{ + Filepath: "README.md", + Contents: unindent(test.contents), + } + got, err := ProcessReadme(ctx, unit) + if err != nil { + t.Fatal(err) } - for name, processFunc := range processReadmes { - t.Run(name, func(t *testing.T) { - unit.Readme = &internal.Readme{ - Filepath: "README.md", - Contents: unindent(test.contents), - } - got, err := processFunc(ctx, unit) - if err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(test.want, got.Links); diff != "" { - t.Errorf("mismatch (-want +got):\n%s", diff) - } - }) + if diff := cmp.Diff(test.want, got.Links); diff != "" { + t.Errorf("mismatch (-want +got):\n%s", diff) } }) } |
