| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | // Copyright 2019 The Hugo Authors. All rights reserved.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Package goldmark converts Markdown to HTML using Goldmark.
 | 
					
						
							|  |  |  | package goldmark
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"bytes"
 | 
					
						
							|  |  |  | 	"fmt"
 | 
					
						
							|  |  |  | 	"path/filepath"
 | 
					
						
							| 
									
										
										
										
											2019-11-25 18:41:46 +01:00
										 |  |  | 	"runtime/debug"
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 13:04:00 +01:00
										 |  |  | 	"github.com/gohugoio/hugo/markup/goldmark/codeblocks"
 | 
					
						
							| 
									
										
										
										
											2021-02-07 18:08:46 +01:00
										 |  |  | 	"github.com/gohugoio/hugo/markup/goldmark/internal/extensions/attributes"
 | 
					
						
							| 
									
										
										
										
											2022-02-17 13:04:00 +01:00
										 |  |  | 	"github.com/gohugoio/hugo/markup/goldmark/internal/render"
 | 
					
						
							| 
									
										
										
										
											2021-02-07 18:08:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | 	"github.com/gohugoio/hugo/identity"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 	"github.com/pkg/errors"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/spf13/afero"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/gohugoio/hugo/hugofs"
 | 
					
						
							|  |  |  | 	"github.com/gohugoio/hugo/markup/converter"
 | 
					
						
							|  |  |  | 	"github.com/gohugoio/hugo/markup/tableofcontents"
 | 
					
						
							|  |  |  | 	"github.com/yuin/goldmark"
 | 
					
						
							|  |  |  | 	"github.com/yuin/goldmark/extension"
 | 
					
						
							|  |  |  | 	"github.com/yuin/goldmark/parser"
 | 
					
						
							|  |  |  | 	"github.com/yuin/goldmark/renderer"
 | 
					
						
							|  |  |  | 	"github.com/yuin/goldmark/renderer/html"
 | 
					
						
							|  |  |  | 	"github.com/yuin/goldmark/text"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Provider is the package entry point.
 | 
					
						
							|  |  |  | var Provider converter.ProviderProvider = provide{}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 13:44:09 +01:00
										 |  |  | type provide struct{}
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (p provide) New(cfg converter.ProviderConfig) (converter.Provider, error) {
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | 	md := newMarkdown(cfg)
 | 
					
						
							| 
									
										
										
										
											2020-01-04 11:28:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 	return converter.NewProvider("goldmark", func(ctx converter.DocumentContext) (converter.Converter, error) {
 | 
					
						
							|  |  |  | 		return &goldmarkConverter{
 | 
					
						
							|  |  |  | 			ctx: ctx,
 | 
					
						
							|  |  |  | 			cfg: cfg,
 | 
					
						
							|  |  |  | 			md:  md,
 | 
					
						
							| 
									
										
										
										
											2020-01-04 11:28:19 +01:00
										 |  |  | 			sanitizeAnchorName: func(s string) string {
 | 
					
						
							| 
									
										
										
										
											2020-01-05 11:52:00 +01:00
										 |  |  | 				return sanitizeAnchorNameString(s, cfg.MarkupConfig.Goldmark.Parser.AutoHeadingIDType)
 | 
					
						
							| 
									
										
										
										
											2020-01-04 11:28:19 +01:00
										 |  |  | 			},
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 		}, nil
 | 
					
						
							|  |  |  | 	}), nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 13:23:25 +01:00
										 |  |  | var _ converter.AnchorNameSanitizer = (*goldmarkConverter)(nil)
 | 
					
						
							| 
									
										
										
										
											2020-01-04 11:28:19 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | type goldmarkConverter struct {
 | 
					
						
							|  |  |  | 	md  goldmark.Markdown
 | 
					
						
							|  |  |  | 	ctx converter.DocumentContext
 | 
					
						
							|  |  |  | 	cfg converter.ProviderConfig
 | 
					
						
							| 
									
										
										
										
											2020-01-04 11:28:19 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	sanitizeAnchorName func(s string) string
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *goldmarkConverter) SanitizeAnchorName(s string) string {
 | 
					
						
							|  |  |  | 	return c.sanitizeAnchorName(s)
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
 | 
					
						
							|  |  |  | 	mcfg := pcfg.MarkupConfig
 | 
					
						
							|  |  |  | 	cfg := pcfg.MarkupConfig.Goldmark
 | 
					
						
							| 
									
										
										
										
											2020-02-23 02:06:30 +09:00
										 |  |  | 	var rendererOptions []renderer.Option
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Renderer.HardWraps {
 | 
					
						
							|  |  |  | 		rendererOptions = append(rendererOptions, html.WithHardWraps())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Renderer.XHTML {
 | 
					
						
							|  |  |  | 		rendererOptions = append(rendererOptions, html.WithXHTML())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Renderer.Unsafe {
 | 
					
						
							|  |  |  | 		rendererOptions = append(rendererOptions, html.WithUnsafe())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-23 02:06:30 +09:00
										 |  |  | 	var (
 | 
					
						
							|  |  |  | 		extensions = []goldmark.Extender{
 | 
					
						
							| 
									
										
										
										
											2022-03-09 18:26:32 +01:00
										 |  |  | 			newLinks(cfg),
 | 
					
						
							| 
									
										
										
										
											2020-02-23 02:06:30 +09:00
										 |  |  | 			newTocExtension(rendererOptions),
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		parserOptions []parser.Option
 | 
					
						
							|  |  |  | 	)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 	if mcfg.Highlight.CodeFences {
 | 
					
						
							| 
									
										
										
										
											2022-02-17 13:04:00 +01:00
										 |  |  | 		extensions = append(extensions, codeblocks.New())
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Extensions.Table {
 | 
					
						
							|  |  |  | 		extensions = append(extensions, extension.Table)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Extensions.Strikethrough {
 | 
					
						
							|  |  |  | 		extensions = append(extensions, extension.Strikethrough)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Extensions.Linkify {
 | 
					
						
							|  |  |  | 		extensions = append(extensions, extension.Linkify)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Extensions.TaskList {
 | 
					
						
							|  |  |  | 		extensions = append(extensions, extension.TaskList)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Extensions.Typographer {
 | 
					
						
							|  |  |  | 		extensions = append(extensions, extension.Typographer)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Extensions.DefinitionList {
 | 
					
						
							|  |  |  | 		extensions = append(extensions, extension.DefinitionList)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Extensions.Footnote {
 | 
					
						
							|  |  |  | 		extensions = append(extensions, extension.Footnote)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if cfg.Parser.AutoHeadingID {
 | 
					
						
							|  |  |  | 		parserOptions = append(parserOptions, parser.WithAutoHeadingID())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-07 18:08:46 +01:00
										 |  |  | 	if cfg.Parser.Attribute.Title {
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 		parserOptions = append(parserOptions, parser.WithAttribute())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-07 18:08:46 +01:00
										 |  |  | 	if cfg.Parser.Attribute.Block {
 | 
					
						
							|  |  |  | 		extensions = append(extensions, attributes.New())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 	md := goldmark.New(
 | 
					
						
							|  |  |  | 		goldmark.WithExtensions(
 | 
					
						
							|  |  |  | 			extensions...,
 | 
					
						
							|  |  |  | 		),
 | 
					
						
							|  |  |  | 		goldmark.WithParserOptions(
 | 
					
						
							|  |  |  | 			parserOptions...,
 | 
					
						
							|  |  |  | 		),
 | 
					
						
							|  |  |  | 		goldmark.WithRendererOptions(
 | 
					
						
							|  |  |  | 			rendererOptions...,
 | 
					
						
							|  |  |  | 		),
 | 
					
						
							|  |  |  | 	)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return md
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | var _ identity.IdentitiesProvider = (*converterResult)(nil)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | type converterResult struct {
 | 
					
						
							|  |  |  | 	converter.Result
 | 
					
						
							|  |  |  | 	toc tableofcontents.Root
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | 	ids identity.Identities
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c converterResult) TableOfContents() tableofcontents.Root {
 | 
					
						
							|  |  |  | 	return c.toc
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | func (c converterResult) GetIdentities() identity.Identities {
 | 
					
						
							|  |  |  | 	return c.ids
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var converterIdentity = identity.KeyValueIdentity{Key: "goldmark", Value: "converter"}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | func (c *goldmarkConverter) Convert(ctx converter.RenderContext) (result converter.Result, err error) {
 | 
					
						
							|  |  |  | 	defer func() {
 | 
					
						
							|  |  |  | 		if r := recover(); r != nil {
 | 
					
						
							|  |  |  | 			dir := afero.GetTempDir(hugofs.Os, "hugo_bugs")
 | 
					
						
							|  |  |  | 			name := fmt.Sprintf("goldmark_%s.txt", c.ctx.DocumentID)
 | 
					
						
							|  |  |  | 			filename := filepath.Join(dir, name)
 | 
					
						
							|  |  |  | 			afero.WriteFile(hugofs.Os, filename, ctx.Src, 07555)
 | 
					
						
							| 
									
										
										
										
											2019-11-25 18:41:46 +01:00
										 |  |  | 			fmt.Print(string(debug.Stack()))
 | 
					
						
							| 
									
										
										
										
											2019-11-25 12:49:04 +01:00
										 |  |  | 			err = errors.Errorf("[BUG] goldmark: %s: create an issue on GitHub attaching the file in: %s", r, filename)
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 	}()
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 13:04:00 +01:00
										 |  |  | 	buf := &render.BufWriter{Buffer: &bytes.Buffer{}}
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 	result = buf
 | 
					
						
							| 
									
										
										
										
											2020-01-04 11:28:19 +01:00
										 |  |  | 	pctx := c.newParserContext(ctx)
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 	reader := text.NewReader(ctx.Src)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	doc := c.md.Parser().Parse(
 | 
					
						
							|  |  |  | 		reader,
 | 
					
						
							|  |  |  | 		parser.WithContext(pctx),
 | 
					
						
							|  |  |  | 	)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 13:04:00 +01:00
										 |  |  | 	rcx := &render.RenderContextDataHolder{
 | 
					
						
							|  |  |  | 		Rctx: ctx,
 | 
					
						
							|  |  |  | 		Dctx: c.ctx,
 | 
					
						
							|  |  |  | 		IDs:  identity.NewManager(converterIdentity),
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 13:04:00 +01:00
										 |  |  | 	w := &render.Context{
 | 
					
						
							|  |  |  | 		BufWriter:   buf,
 | 
					
						
							|  |  |  | 		ContextData: rcx,
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := c.md.Renderer().Render(w, ctx.Src, doc); err != nil {
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 		return nil, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | 	return converterResult{
 | 
					
						
							|  |  |  | 		Result: buf,
 | 
					
						
							| 
									
										
										
										
											2022-02-17 13:04:00 +01:00
										 |  |  | 		ids:    rcx.IDs.GetIdentities(),
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | 		toc:    pctx.TableOfContents(),
 | 
					
						
							|  |  |  | 	}, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var featureSet = map[identity.Identity]bool{
 | 
					
						
							|  |  |  | 	converter.FeatureRenderHooks: true,
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *goldmarkConverter) Supports(feature identity.Identity) bool {
 | 
					
						
							|  |  |  | 	return featureSet[feature.GetIdentity()]
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-04 11:28:19 +01:00
										 |  |  | func (c *goldmarkConverter) newParserContext(rctx converter.RenderContext) *parserContext {
 | 
					
						
							| 
									
										
										
										
											2020-01-05 11:29:22 +01:00
										 |  |  | 	ctx := parser.NewContext(parser.WithIDs(newIDFactory(c.cfg.MarkupConfig.Goldmark.Parser.AutoHeadingIDType)))
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | 	ctx.Set(tocEnableKey, rctx.RenderTOC)
 | 
					
						
							|  |  |  | 	return &parserContext{
 | 
					
						
							|  |  |  | 		Context: ctx,
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | }
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | type parserContext struct {
 | 
					
						
							|  |  |  | 	parser.Context
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | func (p *parserContext) TableOfContents() tableofcontents.Root {
 | 
					
						
							|  |  |  | 	if v := p.Get(tocResultKey); v != nil {
 | 
					
						
							|  |  |  | 		return v.(tableofcontents.Root)
 | 
					
						
							| 
									
										
										
										
											2019-11-06 20:10:47 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2019-11-27 13:42:36 +01:00
										 |  |  | 	return tableofcontents.Root{}
 | 
					
						
							|  |  |  | }
 |