Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 0 additions & 79 deletions gen-apidocs/generators/markdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,12 @@ limitations under the License.
package generators

import (
_ "embed"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"text/template"

"github.com/kubernetes-sigs/reference-docs/gen-apidocs/generators/api"
)
Expand Down Expand Up @@ -160,29 +156,6 @@ var utilityStandalone = map[string]bool{

var _ DocWriter = (*MarkdownWriter)(nil)

var anchorRegex = regexp.MustCompile(`[^a-zA-Z0-9]+`)

var (
enumHeaderRegex = regexp.MustCompile(`\s+Possible enum values:`)
enumBulletRegex = regexp.MustCompile(`\s+- ` + "`")
)

//go:embed templates/resource.tmpl
var resourceTemplateSrc string

// q quotes for YAML frontmatter; md escapes `<` for body text; hugoRef
// wraps a relative path in a {{< ref >}} shortcode.
var resourceTemplate = template.Must(template.New("resource").Funcs(template.FuncMap{
"q": strconv.Quote,
"md": escape,
"hugoRef": hugoRef,
}).Parse(resourceTemplateSrc))

// hugoRef wraps a path in a {{< ref >}} shortcode resolved by Hugo at build time.
func hugoRef(path string) string {
return `{{< ref "` + path + `" >}}`
}

func NewMarkdownWriter(config *api.Config, copyright, title string) DocWriter {
outputDir := api.BuildDir
if err := os.MkdirAll(outputDir, 0755); err != nil {
Expand Down Expand Up @@ -849,58 +822,6 @@ func tocSortRank(title string) int {
}
}

func anchor(s string) string {
return strings.Trim(anchorRegex.ReplaceAllString(s, "-"), "-")
}

// escape covers the only markdown-breaking character in OpenAPI descriptions:
// raw `<` that would otherwise be read as HTML.
func escape(s string) string {
s = strings.ReplaceAll(s, "<", `\<`)
s = enumHeaderRegex.ReplaceAllString(s, "<br/><br/>Possible enum values:")
s = enumBulletRegex.ReplaceAllString(s, "<br/> - `")
return s
}

func kebabCase(s string) string {
return strings.Trim(anchorRegex.ReplaceAllString(strings.ToLower(s), "-"), "-")
}

var (
kebabBoundary1 = regexp.MustCompile(`([a-z0-9])([A-Z])`)
kebabBoundary2 = regexp.MustCompile(`([A-Z])([A-Z][a-z])`)
)

func kebabName(s string) string {
s = kebabBoundary2.ReplaceAllString(s, "$1-$2")
s = kebabBoundary1.ReplaceAllString(s, "$1-$2")
return strings.ToLower(s)
}

func groupVersionString(group string, version api.ApiVersion) string {
if group == "" || group == "core" {
return version.String()
}
return fmt.Sprintf("%s/%s", group, version.String())
}

func operationSlug(id string) string {
return strings.Trim(anchorRegex.ReplaceAllString(strings.ToLower(id), "-"), "-")
}

// constValueFor hard-codes the two fields Kubernetes manifests always
// carry with fixed values (apiVersion and kind). Swagger doesn't tag
// them as const so we derive them from the GVK.
func constValueFor(fieldName, apiVersion, kind string) string {
switch fieldName {
case "apiVersion":
return apiVersion
case "kind":
return kind
}
return ""
}

func (m *MarkdownWriter) nextCategoryWeight() int {
m.categoryWeight += 10
return m.categoryWeight
Expand Down
58 changes: 58 additions & 0 deletions gen-apidocs/generators/template_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
Copyright 2026 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package generators

import (
_ "embed"
"regexp"
"strconv"
"strings"
"text/template"
)

//go:embed templates/resource.tmpl
var resourceTemplateSrc string

// Template helpers registered on resourceTemplate:
//
// q quotes for YAML frontmatter
// md escapes `<` for body text
// hugoRef wraps a relative path in a {{< ref >}} shortcode
var resourceTemplate = template.Must(template.New("resource").Funcs(template.FuncMap{
"q": strconv.Quote,
"md": escape,
"hugoRef": hugoRef,
}).Parse(resourceTemplateSrc))

var (
enumHeaderRegex = regexp.MustCompile(`\s+Possible enum values:`)
enumBulletRegex = regexp.MustCompile(`\s+- ` + "`")
)

// escape covers the only markdown-breaking character in OpenAPI descriptions:
// raw `<` that would otherwise be read as HTML.
func escape(s string) string {
s = strings.ReplaceAll(s, "<", `\<`)
s = enumHeaderRegex.ReplaceAllString(s, "<br/><br/>Possible enum values:")
s = enumBulletRegex.ReplaceAllString(s, "<br/> - `")
return s
}

// hugoRef wraps a path in a {{< ref >}} shortcode resolved by Hugo at build time.
func hugoRef(path string) string {
return `{{< ref "` + path + `" >}}`
}
54 changes: 53 additions & 1 deletion gen-apidocs/generators/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,64 @@ package generators

import (
"fmt"

"regexp"
"strings"

"github.com/kubernetes-sigs/reference-docs/gen-apidocs/generators/api"
)

var (
anchorRegex = regexp.MustCompile(`[^a-zA-Z0-9]+`)
kebabBoundary1 = regexp.MustCompile(`([a-z0-9])([A-Z])`)
kebabBoundary2 = regexp.MustCompile(`([A-Z])([A-Z][a-z])`)
)

// anchor produces a stable in-page anchor from a kind or section name.
func anchor(s string) string {
return strings.Trim(anchorRegex.ReplaceAllString(s, "-"), "-")
}

// kebabCase lowercases and slugifies any string into a kebab-case identifier
// used for directory and file names.
func kebabCase(s string) string {
return strings.Trim(anchorRegex.ReplaceAllString(strings.ToLower(s), "-"), "-")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

operationSlug and kebabCase have identical implementations. is keeping them separate intentional to allow independent divergence later, or should operationSlug just assign to kebabCase

}

// kebabName converts a CamelCase API kind (e.g. "PodTemplate") into its
// kebab-cased file form (e.g. "pod-template"), preserving acronym boundaries.
func kebabName(s string) string {
s = kebabBoundary2.ReplaceAllString(s, "$1-$2")
s = kebabBoundary1.ReplaceAllString(s, "$1-$2")
return strings.ToLower(s)
}

// groupVersionString formats the canonical "group/version" string used in
// API references, collapsing the core group to a bare version.
func groupVersionString(group string, version api.ApiVersion) string {
if group == "" || group == "core" {
return version.String()
}
return fmt.Sprintf("%s/%s", group, version.String())
}

// operationSlug derives a filename-safe slug from an operation ID.
func operationSlug(id string) string {
return strings.Trim(anchorRegex.ReplaceAllString(strings.ToLower(id), "-"), "-")
}

// constValueFor hard-codes the two fields Kubernetes manifests always carry
// with fixed values (apiVersion and kind). Swagger does not tag them as const
// so we derive them from the GVK.
func constValueFor(fieldName, apiVersion, kind string) string {
switch fieldName {
case "apiVersion":
return apiVersion
case "kind":
return kind
}
return ""
}

func PrintInfo(config *api.Config) {
definitions := config.Definitions

Expand Down