generator API

generator

package

API reference for the generator package.

S
struct

PageData

PageData describes the data structure passed to the package entities template.

pkg/generator/generator.go:16-30
type PageData struct

Fields

Name Type Description
Title string
Entities []parser.EntityInfo
Imports []parser.ImportInfo
Readme template.HTML
FileGroups map[string][]parser.EntityInfo
Initials string
PackageName string
AllPackages []PackageLink
TypeIndex map[string]TypeInfo
ProjectPackages []string
ModulePath string
DocPages []DocPage
DocSections []DocSection
S
struct

DocPage

DocPage represents a documentation page (e.g., from docs/ folder).

pkg/generator/generator.go:33-39
type DocPage struct

Fields

Name Type Description
Title string
Filename string
Content template.HTML
ID string
Section string
S
struct

DocSection

DocSection represents a collapsible section of documentation pages.

pkg/generator/generator.go:42-46
type DocSection struct

Fields

Name Type Description
Name string
Pages []DocPage
HasIndex bool
S
struct

TypeInfo

TypeInfo stores cross-reference data for a Go type.

pkg/generator/generator.go:49-53
type TypeInfo struct

Fields

Name Type Description
Name string
Package string
Link string
F
function

GenerateHTML

GenerateHTML orchestrates the generation of HTML documentation for all packages in the project.

Parameters

title
string
outputDir
string
entities
imports
readme
string
initials
string
modulePath
string
docPages
docSections

Returns

error
pkg/generator/generator.go:63-124
func GenerateHTML(title string, outputDir string, entities []parser.EntityInfo, imports []parser.ImportInfo, readme string, initials string, modulePath string, docPages []DocPage, docSections []DocSection) error

{
	packageGroups := make(map[string][]parser.EntityInfo)
	for _, e := range entities {
		pkg := e.Package
		if pkg == "" {
			pkg = "main"
		}
		packageGroups[pkg] = append(packageGroups[pkg], e)
	}

	var allPackages []PackageLink
	for pkg := range packageGroups {
		allPackages = append(allPackages, PackageLink{
			Name: pkg,
			Link: packageFilename(pkg),
		})
	}

	typeIndex := make(map[string]TypeInfo)
	for _, e := range entities {
		pkg := e.Package
		if pkg == "" {
			pkg = "main"
		}
		if e.Type == "struct" || e.Type == "interface" || e.Type == "type" {
			info := TypeInfo{
				Name:    e.Name,
				Package: pkg,
				Link:    packageFilename(pkg) + "#" + e.Name,
			}
			typeIndex[pkg+"."+e.Name] = info
			if _, exists := typeIndex[e.Name]; !exists {
				typeIndex[e.Name] = info
			}
		}
	}

	var projectPackages []string
	for pkg := range packageGroups {
		projectPackages = append(projectPackages, pkg)
	}

	// docPages here contains ALL pages for generation, but we filter root-only for sidebar
	var rootDocPages []DocPage
	for _, p := range docPages {
		if p.Section == "" {
			rootDocPages = append(rootDocPages, p)
		}
	}

	for pkg, pkgEntities := range packageGroups {
		if err := generatePackagePage(title, outputDir, pkg, pkgEntities, imports, initials, allPackages, modulePath, typeIndex, projectPackages, rootDocPages, docSections); err != nil {
			return err
		}
	}

	if err := GenerateDocPages(title, outputDir, docPages, rootDocPages, initials, allPackages, modulePath, typeIndex, projectPackages, docSections); err != nil {
		return err
	}

	return GenerateSearchIndex(outputDir, entities, docPages)
}
F
function

packageFilename

packageFilename generates a safe filename for a package’s documentation page.

Parameters

pkg
string

Returns

string
pkg/generator/generator.go:127-130
func packageFilename(pkg string) string

{
	safe := regexp.MustCompile(`[^a-zA-Z0-9_-]`).ReplaceAllString(pkg, "_")
	return "pkg_" + safe + ".html"
}
F
function

generatePackagePage

generatePackagePage generates a single HTML page for a specific package.

Parameters

title
string
outputDir
string
pkg
string
entities
imports
initials
string
allPackages
modulePath
string
typeIndex
map[string]TypeInfo
projectPackages
[]string
docPages
docSections

Returns

error
pkg/generator/generator.go:133-285
func generatePackagePage(title string, outputDir string, pkg string, entities []parser.EntityInfo, imports []parser.ImportInfo, initials string, allPackages []PackageLink, modulePath string, typeIndex map[string]TypeInfo, projectPackages []string, docPages []DocPage, docSections []DocSection) error

{
	tmplPath := "templates/entities.html"
	tmplName := filepath.Base(tmplPath)

	tmpl, err := template.New(tmplName).Funcs(template.FuncMap{
		"contains": strings.Contains,
		"replace":  strings.ReplaceAll,
		"lower":    strings.ToLower,
		"lineCount": func(s string) int {
			return strings.Count(s, "\n") + 1
		},
		"html": func(s string) template.HTML {
			return template.HTML(s)
		},
		"json": func(v interface{}) string {
			b, _ := json.Marshal(v)
			return string(b)
		},
		"pkgGoDevURL": func(importPath string) string {
			return "https://pkg.go.dev/" + importPath
		},
		"isStdLib": func(importPath string) bool {
			parts := strings.Split(importPath, "/")
			return len(parts) > 0 && !strings.Contains(parts[0], ".")
		},
		"isInternalImport": func(importPath string) bool {
			return modulePath != "" && strings.HasPrefix(importPath, modulePath)
		},
		"internalImportLink": func(importPath string) string {
			parts := strings.Split(importPath, "/")
			if len(parts) > 0 {
				pkgName := parts[len(parts)-1]
				return packageFilename(pkgName)
			}
			return "#"
		},
		"packageLink": func(pkgName string) string {
			return packageFilename(pkgName)
		},
		"splitParam": func(param string) struct{ Name, Type string } {
			parts := strings.Fields(param)
			if len(parts) == 1 {
				return struct{ Name, Type string }{Name: "", Type: parts[0]}
			}
			if len(parts) >= 2 {
				return struct{ Name, Type string }{Name: parts[0], Type: strings.Join(parts[1:], " ")}
			}
			return struct{ Name, Type string }{Name: "", Type: ""}
		},
		"linkType": func(typeName string) template.HTML {
			orig := typeName
			prefix := ""
			base := typeName

			for {
				if strings.HasPrefix(base, "*") {
					prefix += "*"
					base = base[1:]
				} else if strings.HasPrefix(base, "[]") {
					prefix += "[]"
					base = base[2:]
				} else {
					break
				}
			}

			if info, ok := typeIndex[base]; ok {
				return template.HTML(fmt.Sprintf(`%s<a href="%s" class="text-brand-600 dark:text-brand-400 hover:underline">%s</a>`, prefix, info.Link, base))
			}

			if strings.Contains(base, ".") {
				parts := strings.Split(base, ".")
				pkgName := parts[0]
				typeNameOnly := parts[1]

				for _, imp := range imports {
					isMatch := (imp.Alias != "" && imp.Alias == pkgName) ||
						(imp.Alias == "" && (strings.HasSuffix(imp.Path, "/"+pkgName) || imp.Path == pkgName))

					if isMatch {
						url := "https://pkg.go.dev/" + imp.Path + "#" + typeNameOnly
						return template.HTML(fmt.Sprintf(`%s<a href="%s" target="_blank" class="text-zinc-600 dark:text-zinc-400 hover:text-brand-600 dark:hover:text-brand-400 underline decoration-dotted underline-offset-2">%s</a>`, prefix, url, base))
					}
				}
			}

			return template.HTML(template.HTMLEscapeString(orig))
		},
		"linkTypes": func(code string) template.HTML {
			result := template.HTMLEscapeString(code)
			for typeName, info := range typeIndex {
				pattern := regexp.MustCompile(`\b` + regexp.QuoteMeta(typeName) + `\b`)
				link := fmt.Sprintf(`<a href="%s" class="text-brand-600 dark:text-brand-400 hover:underline">%s</a>`, info.Link, typeName)
				result = pattern.ReplaceAllString(result, link)
			}
			return template.HTML(result)
		},
	}).ParseFS(templatesFS, tmplPath)
	if err != nil {
		return err
	}

	fileGroups := make(map[string][]parser.EntityInfo)
	for _, e := range entities {
		f := filepath.Base(e.File)
		if f == "" || e.File == "" {
			f = "Globals"
		}
		fileGroups[f] = append(fileGroups[f], e)
	}

	seenImports := make(map[string]bool)
	var pkgImports []parser.ImportInfo
	for _, imp := range imports {
		if imp.Package == pkg && !seenImports[imp.Path] {
			seenImports[imp.Path] = true
			pkgImports = append(pkgImports, imp)
		}
	}

	navPackages := make([]PackageLink, len(allPackages))
	for i, p := range allPackages {
		navPackages[i] = PackageLink{
			Name:      p.Name,
			Link:      p.Link,
			IsCurrent: p.Name == pkg,
		}
	}

	data := PageData{
		Title:           title,
		Entities:        entities,
		Imports:         pkgImports,
		FileGroups:      fileGroups,
		Initials:        initials,
		PackageName:     pkg,
		AllPackages:     navPackages,
		TypeIndex:       typeIndex,
		ProjectPackages: projectPackages,
		ModulePath:      modulePath,
		DocPages:        docPages,
		DocSections:     docSections,
	}

	outputPath := filepath.Join(outputDir, packageFilename(pkg))
	f, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer f.Close()

	return tmpl.Execute(f, data)
}
F
function

GenerateDocPages

GenerateDocPages generates HTML pages for the documentation files found in the docs/ directory.

Parameters

title
string
outputDir
string
allDocPages
rootDocPages
initials
string
allPackages
modulePath
string
typeIndex
map[string]TypeInfo
projectPackages
[]string
docSections

Returns

error
pkg/generator/generator.go:288-364
func GenerateDocPages(title string, outputDir string, allDocPages []DocPage, rootDocPages []DocPage, initials string, allPackages []PackageLink, modulePath string, typeIndex map[string]TypeInfo, projectPackages []string, docSections []DocSection) error

{
	tmplPath := "templates/page.html"
	tmplName := filepath.Base(tmplPath)

	tmpl, err := template.New(tmplName).Funcs(template.FuncMap{
		"contains": strings.Contains,
		"replace":  strings.ReplaceAll,
		"lower":    strings.ToLower,
		"json": func(v interface{}) string {
			b, _ := json.Marshal(v)
			return string(b)
		},
		"html": func(s string) template.HTML {
			return template.HTML(s)
		},
		"linkTypes": func(input interface{}) template.HTML {
			var code string
			switch v := input.(type) {
			case string:
				code = v
			case template.HTML:
				code = string(v)
			default:
				return ""
			}

			// If it matches [Package.Type], replace it
			re := regexp.MustCompile(`\[([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)\]`)
			code = re.ReplaceAllStringFunc(code, func(match string) string {
				parts := re.FindStringSubmatch(match)
				if len(parts) == 3 {
					pkg := parts[1]
					typ := parts[2]
					key := pkg + "." + typ
					if info, ok := typeIndex[key]; ok {
						return fmt.Sprintf(`<a href="%s" class="text-brand-600 dark:text-brand-400 hover:underline">%s</a>`, info.Link, typ)
					}
				}
				return match
			})

			return template.HTML(code)
		},
	}).ParseFS(templatesFS, tmplPath)
	if err != nil {
		return err
	}

	for _, page := range allDocPages {
		data := PageData{
			Title:           title,
			Readme:          page.Content,
			Initials:        initials,
			PackageName:     page.Title,
			AllPackages:     allPackages,
			TypeIndex:       typeIndex,
			ProjectPackages: projectPackages,
			ModulePath:      modulePath,
			DocPages:        rootDocPages,
			DocSections:     docSections,
		}

		outputPath := filepath.Join(outputDir, page.Filename)
		f, err := os.Create(outputPath)
		if err != nil {
			return fmt.Errorf("error creating doc page %s: %v", page.Filename, err)
		}

		if err := tmpl.Execute(f, data); err != nil {
			f.Close()
			return fmt.Errorf("error executing template for %s: %v", page.Filename, err)
		}
		f.Close()
	}

	return nil
}
F
function

GenerateSearchIndex

GenerateSearchIndex creates a JSON file used for client-side documentation search.

Parameters

outputDir
string
entities
docPages

Returns

error
pkg/generator/generator.go:367-420
func GenerateSearchIndex(outputDir string, entities []parser.EntityInfo, docPages []DocPage) error

{
	type SearchItem struct {
		Name        string `json:"name"`
		Type        string `json:"type"`
		Description string `json:"description"`
		Link        string `json:"link"`
		Package     string `json:"package"`
	}

	var items []SearchItem
	for _, e := range entities {
		pkg := e.Package
		if pkg == "" {
			pkg = "main"
		}
		link := packageFilename(pkg) + "#" + e.Name

		items = append(items, SearchItem{
			Name:        e.Name,
			Type:        e.Type,
			Description: e.DescriptionRaw,
			Link:        link,
			Package:     pkg,
		})

		for _, m := range e.Methods {
			items = append(items, SearchItem{
				Name:        e.Name + "." + m.Name,
				Type:        "method",
				Description: m.DescriptionRaw,
				Link:        link,
				Package:     pkg,
			})
		}
	}

	for _, p := range docPages {
		items = append(items, SearchItem{
			Name:        p.Title,
			Type:        "page",
			Description: "Documentation page: " + p.Title,
			Link:        p.Filename,
			Package:     "docs",
		})
	}

	f, err := os.Create(filepath.Join(outputDir, "search.json"))
	if err != nil {
		return err
	}
	defer f.Close()

	return json.NewEncoder(f).Encode(items)
}
S
struct

TOCEntry

TOCEntry represents an entry in the documentation’s Table of Contents.

pkg/generator/index.go:13-17
type TOCEntry struct

Fields

Name Type Description
ID string
Title string
Level int
S
struct

IndexPageData

IndexPageData contains the data necessary to render the documentation home page.

pkg/generator/index.go:20-29
type IndexPageData struct

Fields

Name Type Description
Title string
Entities []parser.EntityInfo
Readme template.HTML
Initials string
TOC []TOCEntry
Packages []PackageLink
DocPages []DocPage
DocSections []DocSection
F
function

GenerateIndex

GenerateIndex creates the main entry point (index.html) of the documentation.

Parameters

title
string
outputDir
string
entities
readme
string
initials
string
toc
packages
docPages
docSections

Returns

error
pkg/generator/index.go:32-67
func GenerateIndex(title string, outputDir string, entities []parser.EntityInfo, readme string, initials string, toc []TOCEntry, packages []PackageLink, docPages []DocPage, docSections []DocSection) error

{
	tmplPath := "templates/index.html"
	tmplName := filepath.Base(tmplPath)

	tmpl, err := template.New(tmplName).Funcs(template.FuncMap{
		"contains": strings.Contains,
		"replace":  strings.ReplaceAll,
		"lower":    strings.ToLower,
		"html": func(s string) template.HTML {
			return template.HTML(s)
		},
	}).ParseFS(templatesFS, tmplPath)
	if err != nil {
		return err
	}

	data := IndexPageData{
		Title:       title,
		Entities:    entities,
		Readme:      template.HTML(readme),
		Initials:    initials,
		TOC:         toc,
		Packages:    packages,
		DocPages:    docPages,
		DocSections: docSections,
	}

	outputPath := filepath.Join(outputDir, "index.html")
	f, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer f.Close()

	return tmpl.Execute(f, data)
}
F
function

CopyStaticAssets

CopyStaticAssets is a legacy function. Pallas now uses CDNs for static assets.

Parameters

outputDir
string

Returns

error
pkg/generator/utils.go:6-8
func CopyStaticAssets(outputDir string) error

{
	return nil
}