深入理解Hugo: Page State

package main
import (
    "fmt"
    "html/template"
)
func main() {
    outputFormats := createOutputFormats()
    renderFormats := initRenderFormats(outputFormats)
    s := &site{
        outputFormats: outputFormats,
        renderFormats: renderFormats,
    }
    ps := &pageState{
        pageOutputs: nil,
        pageOutput:  nil,
        pageCommon:  &pageCommon{m: &pageMeta{kind: KindPage}},
    }
    ps.init(s)

prepare

    ps.pageOutput = ps.pageOutputs[0]

render

    fmt.Println(ps.targetPaths().TargetFilename)
    fmt.Println(ps.Content())
    fmt.Println(ps.m.kind)
}
type site struct {
    outputFormats map[string]Formats
    renderFormats Formats
}

This slice will be of same length as the number of global slice of output formats (for all sites).

type pageState struct {
    pageOutputs []*pageOutput

This will be shifted out when we start to render a new output format.

    *pageOutput

Common for all output formats.

    *pageCommon
}
func (p *pageState) init(s *site) {
    pp := newPagePaths(s)
    p.pageOutputs = make([]*pageOutput, len(s.renderFormats))
    for i, f := range s.renderFormats {
        ft, found := pp.targetPaths[f.Name]
        if !found {
            panic("target path not found")
        }
        providers := struct{ targetPather }{ft}
        po := &pageOutput{
            f:                      f,
            pagePerOutputProviders: providers,
            ContentProvider:        nil,
        }
        contentProvider := newPageContentOutput(po)
        po.ContentProvider = contentProvider
        p.pageOutputs[i] = po
    }
}
func newPageContentOutput(po *pageOutput) *pageContentOutput {
    cp := &pageContentOutput{
        f: po.f,
    }
    initContent := func() {
        cp.content = template.HTML("<p>hello content</p>")
    }
    cp.initMain = func() {
        initContent()
    }
    return cp
}
func newPagePaths(s *site) pagePaths {
    outputFormats := s.renderFormats
    targets := make(map[string]targetPathsHolder)
    for _, f := range outputFormats {
        target := "/" + "blog" + "/" + f.BaseName +
            "." + f.MediaType.SubType
        paths := TargetPaths{
            TargetFilename: target,
        }
        targets[f.Name] = targetPathsHolder{
            paths: paths,
        }
    }
    return pagePaths{
        targetPaths: targets,
    }
}
type pagePaths struct {
    targetPaths map[string]targetPathsHolder
}
type targetPathsHolder struct {
    paths TargetPaths
}
func (t targetPathsHolder) targetPaths() TargetPaths {
    return t.paths
}
type pageOutput struct {
    f Format

These interface provides the functionality that is specific for this output format.

    pagePerOutputProviders
    ContentProvider

May be nil.

    cp *pageContentOutput
}

pageContentOutput represents the Page content for a given output format.

type pageContentOutput struct {
    f        Format
    initMain func()
    content  template.HTML
}
func (p *pageContentOutput) Content() any {
    p.initMain()
    return p.content
}

these will be shifted out when rendering a given output format.

type pagePerOutputProviders interface {
    targetPather
}
type targetPather interface {
    targetPaths() TargetPaths
}

Where to store the file on disk relative to the publish dir. OS slashes.

type TargetPaths struct {
    TargetFilename string
}
type ContentProvider interface {
    Content() any
}
type pageCommon struct {
    m *pageMeta
}

kind is the discriminator that identifies the different page types in the different page collections. This can, as an example, be used to to filter regular pages, find sections etc. Kind will, for the pages available to the templates, be one of: page, home, section, taxonomy and term. It is of string type to make it easy to reason about in the templates.

type pageMeta struct {
    kind string
}
func initRenderFormats(
    outputFormats map[string]Formats) Formats {
    return outputFormats[KindPage]
}
func createOutputFormats() map[string]Formats {
    m := map[string]Formats{
        KindPage: {HTMLFormat},
    }
    return m
}
const (
    KindPage = "page"
)
var HTMLType = newMediaType("text", "html")

HTMLFormat An ordered list of built-in output formats.

var HTMLFormat = Format{
    Name:      "HTML",
    MediaType: HTMLType,
    BaseName:  "index",
}
func newMediaType(main, sub string) Type {
    t := Type{
        MainType:  main,
        SubType:   sub,
        Delimiter: "."}
    return t
}
type Type struct {
    MainType  string `json:"mainType"`  // i.e. text
    SubType   string `json:"subType"`   // i.e. html
    Delimiter string `json:"delimiter"` // e.g. "."
}

The Name is used as an identifier. Internal output formats (i.e. HTML and RSS) can be overridden by providing a new definition for those types.

type Format struct {
    Name string `json:"name"`
    MediaType Type `json:"-"`

The base output file name used when not using “ugly URLs”, defaults to “index”.

    BaseName string `json:"baseName"`
}
type Formats []Format
/blog/index.html
<p>hello content</p>
page
Program exited.

Next example: Publisher.