Copyright 2019 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 (
	
	
	
	
	
	
	
	

	
	
	
	
	
	
	
	ghtml 
	
	
	
	
)
serveStyleGuide serves the styleguide page, the content of which is generated from the markdown files in content/static.
func ( *Server) ( http.ResponseWriter,  *http.Request,  internal.DataSource) error {
	 := .Context()
	if !experiment.IsActive(, internal.ExperimentStyleGuide) {
		return &serverError{status: http.StatusNotFound}
	}
	,  := styleGuide(, .staticPath.String())
	.basePage = .newBasePage(, "")
	if  != nil {
		return 
	}
	.servePage(, , "styleguide", )
	return nil
}

type styleGuidePage struct {
	basePage
	Sections []*StyleSection
	Outline  []*Heading
}
styleGuide collects the paths to the markdown files in content/static, renders them into sections for the styleguide, and merges the document outlines into a single page outline.
func ( context.Context,  string) ( *styleGuidePage,  error) {
	defer derrors.WrapStack(&, "styleGuide(%q)", )
	,  := markdownFiles()
	if  != nil {
		return nil, 
	}
	var  []*StyleSection
	for ,  := range  {
		,  := styleSection(, )
		if  != nil {
			return nil, 
		}
		 = append(, )
	}
	var  []*Heading
	for ,  := range  {
		 = append(, .Outline...)
	}
	return &styleGuidePage{
		Sections: ,
		Outline:  ,
	}, nil
}
StyleSection represents a section on the styleguide page.
ID is the ID for the header element of the section.
Title is the title of the section, taken from the name of the markdown file.
Content is the HTML rendered from the parsed markdown file.
Outline is a collection of headings used in the navigation.
styleSection uses goldmark to parse a markdown file and render a section of the styleguide.
func ( context.Context,  string) ( *StyleSection,  error) {
	defer derrors.WrapStack(&, "styleSection(%q)", )
	var  bytes.Buffer
	,  := ioutil.ReadFile()
	if  != nil {
		return nil, 
	}
We set priority values 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 (
		 = 10000
		  = 100
	)
	 := &extractTOC{ctx: }
	 := goldmark.New(
		goldmark.WithExtensions(extension.GFM),
		goldmark.WithParserOptions(
			parser.WithAutoHeadingID(),
			parser.WithAttribute(),
			parser.WithASTTransformers(
				util.Prioritized(, ),
			),
		),
		goldmark.WithRendererOptions(
			renderer.WithNodeRenderers(
				util.Prioritized(&guideRenderer{}, ),
			),
			ghtml.WithUnsafe(),
			ghtml.WithXHTML(),
		),
	)

	if  := .Convert(, &);  != nil {
		return nil, 
	}

	 := strings.TrimSuffix(filepath.Base(), ".md")
	return &StyleSection{
		ID:      ,
		Title:   camelCase(),
		Content: uncheckedconversions.HTMLFromStringKnownToSatisfyTypeContract(.String()),
		Outline: .Headings,
	}, nil
}
guideRenderer is a renderer.NodeRenderer implementation that renders styleguide sections.
type guideRenderer struct {
	ghtml.Config
}

func ( *guideRenderer) ( util.BufWriter,  []byte,  ast.Node) {
	 := .Lines().Len()
	for  := 0;  < ; ++ {
		 := .Lines().At()
		.Write(.Value())
	}
}

func ( *guideRenderer) ( util.BufWriter,  []byte,  ast.Node) {
	 := .Lines().Len()
	for  := 0;  < ; ++ {
		 := .Lines().At()
		.Write([]byte(html.EscapeString(string(.Value()))))
	}
}
renderFencedCodeBlock writes html code snippets twice, once as actual html for the page and again as a code snippet.
func ( *guideRenderer) ( util.BufWriter,  []byte,  ast.Node,  bool) (ast.WalkStatus, error) {
	if ! {
		return ast.WalkContinue, nil
	}
	 := .(*ast.FencedCodeBlock)
	.WriteString("<span>\n")
	.writeLines(, , )
	.WriteString("</span>\n")
	.WriteString("<pre class=\"StringifyElement-markup js-clipboard\">\n")
	.writeEscapedLines(, , )
	.WriteString("</pre>\n")
	return ast.WalkContinue, nil
}
RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
markdownFiles walks the content/static directory and collects the paths to markdown files.
func ( string) ([]string, error) {
	var  []string
	 := filepath.Walk(, func( string,  os.FileInfo,  error) error {
		if  != nil {
			return 
		}
		if .IsDir() {
			return nil
		}
		if ,  := filepath.Match("*.md", filepath.Base());  != nil {
			return 
		} else if  {
			 = append(, )
		}
		return nil
	})
	if  != nil {
		return nil, 
	}
	return , nil
}
camelCase turns a snake cased strink into a camel case string. For example, hello-world becomes HelloWorld. This function is used to ensure proper casing in the classnames of the style sections.
func ( string) string {
	 := strings.Split(, "-")
	var  []string
	for ,  := range  {
		 = append(, strings.Title())
	}
	return strings.Join(, "")