- relative links and anchors resolve against the renderer's heading IDs - summer and ./bin/<app> command names come from the real command constructors through docsite.Options.Commands; a nil set is a problem - consuming-application names fail in page sources and built outputs - go fences in docs/ pages need src=, callouts are NOTE, TIP or WARNING, docs/ headings are plain ASCII - gate gains --claude and self-test plants for each new rule
67 lines
2.0 KiB
Go
67 lines
2.0 KiB
Go
package docsite
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"regexp"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
// forbiddenName matches the consuming-application spellings framework docs
|
|
// must never contain (D-11), the accented variant included.
|
|
var forbiddenName = regexp.MustCompile(`(?i)fonoteka|p[lł][yý]tarium`)
|
|
|
|
const forbiddenMessage = "consuming-application name in output"
|
|
|
|
// forbiddenLines returns the 1-based line numbers of text that name a
|
|
// consuming application. The match itself is never reported.
|
|
func forbiddenLines(text []byte) []int {
|
|
var lines []int
|
|
for i, line := range strings.Split(string(text), "\n") {
|
|
if forbiddenName.MatchString(line) {
|
|
lines = append(lines, i+1)
|
|
}
|
|
}
|
|
return lines
|
|
}
|
|
|
|
// checkForbiddenSources scans the full source file of every page (the
|
|
// frontmatter and a README's summary line included).
|
|
func (s *site) checkForbiddenSources() ([]Problem, error) {
|
|
var problems []Problem
|
|
for _, p := range s.pages {
|
|
raw, err := os.ReadFile(p.abs)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("docsite: read %s: %w", p.Source, err)
|
|
}
|
|
for _, line := range forbiddenLines(raw) {
|
|
problems = append(problems, Problem{File: p.Source, Line: line, Rule: "forbidden", Message: forbiddenMessage})
|
|
}
|
|
}
|
|
return problems, nil
|
|
}
|
|
|
|
// forbiddenOutputExts are the text outputs scanned after rendering.
|
|
var forbiddenOutputExts = []string{".html", ".md", ".txt", ".json", ".js", ".css"}
|
|
|
|
// checkForbiddenOutputs scans every rendered text output, which covers
|
|
// text pulled in through src= snippet copies and templates.
|
|
func (s *site) checkForbiddenOutputs() []Problem {
|
|
names := make([]string, 0, len(s.outputs))
|
|
for name := range s.outputs {
|
|
if slices.Contains(forbiddenOutputExts, path.Ext(name)) {
|
|
names = append(names, name)
|
|
}
|
|
}
|
|
slices.Sort(names)
|
|
var problems []Problem
|
|
for _, name := range names {
|
|
for _, line := range forbiddenLines(s.outputs[name]) {
|
|
problems = append(problems, Problem{File: name, Line: line, Rule: "forbidden", Message: forbiddenMessage})
|
|
}
|
|
}
|
|
return problems
|
|
}
|