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 }