/* * Copyright (c) 2013 Dario Castañé. * This file is part of Zas. * * Zas is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Zas is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Zas. If not, see . */ package zas import ( "bytes" "errors" "fmt" thtml "html/template" "io" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "sync" "sync/atomic" ttext "text/template" "time" "github.com/PuerkitoBio/goquery" "github.com/darccio/zas/internal/i18n" markdown "github.com/yuin/goldmark" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/renderer" "github.com/yuin/goldmark/util" yaml "go.yaml.in/yaml/v3" html5 "golang.org/x/net/html" "golang.org/x/net/html/atom" ) var helpers = thtml.FuncMap{ "noescape": noescape, } // rawHTMLRenderer overrides only goldmark's raw-HTML node kinds (block and // inline) to pass their source through unchanged, instead of the default // renderer's "" placeholder. Unlike // html.WithUnsafe(), this leaves every other renderer - including the ones // that sanitize link and image destinations against javascript:/data:/etc. // URLs - on their default, safe behavior. It's registered at a lower // priority number than the default HTML renderer (1000), which wins ties. type rawHTMLRenderer struct{} func (r *rawHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { reg.Register(ast.KindHTMLBlock, r.renderHTMLBlock) reg.Register(ast.KindRawHTML, r.renderRawHTML) } func (r *rawHTMLRenderer) renderHTMLBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { n := node.(*ast.HTMLBlock) if entering { for i := 0; i < n.Lines().Len(); i++ { line := n.Lines().At(i) _, _ = w.Write(line.Value(source)) } } else if n.HasClosure() { _, _ = w.Write(n.ClosureLine.Value(source)) } return ast.WalkContinue, nil } func (r *rawHTMLRenderer) renderRawHTML(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { if !entering { return ast.WalkSkipChildren, nil } n := node.(*ast.RawHTML) for i := 0; i < n.Segments.Len(); i++ { segment := n.Segments.At(i) _, _ = w.Write(segment.Value(source)) } return ast.WalkSkipChildren, nil } // markdownConverter passes raw HTML through instead of dropping it - a // leading HTML comment (per-page config) and an tag written // directly in a .md file would otherwise never reach Zas. Built once at // package init and shared across renderAsync goroutines: goldmark builds a // fresh parse context per Convert call, so this is safe for concurrent use. // // extension.GFM (tables, strikethrough, task lists, autolinks) and // extension.Footnote are enabled on top of goldmark's default CommonMark // parsing - previously neither was, so e.g. a GFM table rendered as a // literal paragraph of pipe characters instead of a . Both are // purely additive: they recognize syntax CommonMark alone leaves as plain // text, so existing content that doesn't use any of it renders exactly as // before. var markdownConverter = markdown.New( markdown.WithExtensions(extension.GFM, extension.Footnote), markdown.WithRendererOptions( renderer.WithNodeRenderers(util.Prioritized(&rawHTMLRenderer{}, 100)), ), ) // NewGenerator returns a Generator ready to Run with the given verbosity, // full-generation, and plugin-execution settings. func NewGenerator(verbose, full, noPlugins bool) *Generator { return &Generator{ Verbose: verbose, Full: full, NoPlugins: noPlugins, } } // Generator groups relevant rendering info. Like sync.WaitGroup, which it // embeds, a Generator must not be copied after it has been constructed - // use NewGenerator, or a Generator{} literal, and refer to it thereafter // only by pointer. type Generator struct { // Verbose output. Verbose bool // Full generation (non-incremental mode). Full bool // NoPlugins disables every content-triggered plugin execution: an // resolving to an external MIME-type plugin // (handleMIMETypePlugin) and a