gitInfo *gitInfo
codeownerInfo *codeownerInfo
+ markdownTitleInfo *markdownTitleInfo
+
// As loaded from the /data dirs
data map[string]any
// Loads the Git info and CODEOWNERS for all the pages if enabled.
gitInfo hsync.FuncResetter
+
+ markdownTitleInfo hsync.FuncResetter
}
func (h *HugoSites) Data() map[string]any {
return h.codeownerInfo.forPage(p), nil
}
+func (h *HugoSites) markdownTitleInfoForPage(p page.Page) (*MarkdownTitleInfo, error) {
+ if err := h.init.markdownTitleInfo.Do(context.Background()); err != nil {
+ return nil, err
+ }
+
+ if h.markdownTitleInfo == nil {
+ return nil, nil
+ }
+
+ return h.markdownTitleInfo.forPage(p), nil
+}
+
func (h *HugoSites) reportProgress(f func() (state terminal.ProgressState, progress float64)) {
h.progressReporter.mu.Lock()
defer h.progressReporter.mu.Unlock()
return nil
}
+func (h *HugoSites) loadMarkdownTitleInfo() error {
+ // TODO: Make it configurable
+ // if h.Configs.Base.EnableMarkdownTitle {
+ // cfg := gitInfoConfig{
+ // Deps: h.Deps,
+ // Modules: h.Configs.Modules,
+ // GitInfoCache: h.Configs.FileCaches.ModuleGitInfoCache(),
+ // Logger: h.Log,
+ // }
+ mt, err := newMarkdownTitleInfo(h.Deps.Conf.DirsBase().ContentDir)
+ if err != nil {
+ return err
+ } else {
+ h.markdownTitleInfo = mt
+ }
+ // }
+ return nil
+}
+
// Reset resets the sites and template caches etc., making it ready for a full rebuild.
func (h *HugoSites) reset() {
h.fatalErrorHandler = &fatalErrorHandler{
--- /dev/null
+package hugolib
+
+import (
+ "path/filepath"
+ "strings"
+ "os"
+
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/text"
+ "github.com/yuin/goldmark/ast"
+ "github.com/leonhfr/goldmark-frontmatter"
+
+ "github.com/gohugoio/hugo/resources/page"
+)
+
+type MarkdownTitleInfo struct {
+ Title string `json:"title"` // Title
+}
+
+type MarkdownTitleMap map[string]*MarkdownTitleInfo
+
+type markdownTitleInfo struct {
+ contentDir string
+ Map MarkdownTitleMap
+}
+
+func findFirstH1(mdContent []byte) string {
+ md := goldmark.New(
+ goldmark.WithExtensions(
+ &frontmatter.Extender{},
+ ),
+ )
+
+ doc := md.Parser().Parse(text.NewReader(mdContent))
+
+ var headingText string
+ ast.Walk(doc, func(node ast.Node, entering bool) (ast.WalkStatus, error) {
+ if entering && node.Kind() == ast.KindHeading {
+ if heading, ok := node.(*ast.Heading); ok && heading.Level == 1 {
+ headingText = string(heading.Text(mdContent))
+ return ast.WalkStop, nil
+ }
+ }
+
+ return ast.WalkContinue, nil
+ })
+
+ return strings.TrimSpace(headingText)
+}
+
+func (m *markdownTitleInfo) forPage(p page.Page) *MarkdownTitleInfo {
+ name := strings.TrimPrefix(filepath.ToSlash(p.File().Filename()), m.contentDir)
+
+ source, err := os.ReadFile(name)
+ if err != nil {
+ return nil
+ }
+
+ title := findFirstH1([]byte(source))
+
+ return &MarkdownTitleInfo{Title: title}
+}
+
+func newMarkdownTitleInfo(_contentDir string) (*markdownTitleInfo, error) {
+ contentDir := _contentDir
+
+ return &markdownTitleInfo{contentDir: contentDir}, nil
+}
\ No newline at end of file
return ps.codeowners
}
+func (p *pageState) MarkdownTitleInfo() *MarkdownTitleInfo {
+ return p.markdownTitleInfo
+}
+
// GetTerms gets the terms defined on this page in the given taxonomy.
// The pages returned will be ordered according to the front matter.
func (ps *pageState) GetTerms(taxonomy string) page.Pages {
gitInfo *source.GitInfo
codeowners []string
+ markdownTitleInfo *MarkdownTitleInfo
+
// Positional navigation
posNextPrev *nextPrev
posNextPrevSection *nextPrev
return nil, fmt.Errorf("failed to load CODEOWNERS: %w", err)
}
ps.codeowners = owners
+
+ mt, err := s.h.markdownTitleInfoForPage(ps)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load MarkdownTitle data: %w", err)
+ }
+ ps.markdownTitleInfo = mt
}
ps.pageMenus = &pageMenus{p: ps}
return h.loadGitInfo()
})
+ h.init.markdownTitleInfo = hsync.OnceMoreFunc(func(ctx context.Context) error {
+ return h.loadMarkdownTitleInfo()
+ })
+
return h, nil
}