Wiki Deploy Code
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -24,7 +25,7 @@ const (
|
||||
legacyWikiRootDirName = ".wiki"
|
||||
wikiPagesDirName = "pages"
|
||||
wikiStateFileName = "state.json"
|
||||
wikiGeneratorVersion = "native-v1"
|
||||
wikiGeneratorVersion = "nodebb-html-v1"
|
||||
wikiGeneratedStatus = "generated"
|
||||
wikiSkippedStatus = "skipped"
|
||||
)
|
||||
@@ -80,7 +81,7 @@ type wikiContext struct {
|
||||
implementedFeats map[string]struct{}
|
||||
}
|
||||
|
||||
func buildWiki(p *project.Project, nativeResult BuildResult, progress func(string)) (wikiResult, error) {
|
||||
func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progress func(string)) (wikiResult, error) {
|
||||
if progress == nil {
|
||||
progress = func(string) {}
|
||||
}
|
||||
@@ -94,7 +95,7 @@ func buildWiki(p *project.Project, nativeResult BuildResult, progress func(strin
|
||||
}
|
||||
|
||||
state, _ := loadWikiState(statePath)
|
||||
if digest != "" && state.Digest == digest {
|
||||
if !force && digest != "" && state.Digest == digest {
|
||||
return wikiResult{
|
||||
OutputDir: outputDir,
|
||||
PageCount: state.Pages,
|
||||
@@ -125,6 +126,7 @@ func buildWiki(p *project.Project, nativeResult BuildResult, progress func(strin
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
content = renderNodeBBManagedHTML(pageID, content)
|
||||
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1234,24 +1236,24 @@ func wikiStatusNoun(category string) string {
|
||||
|
||||
func generateStatusPages(outputDir string, pageStatuses map[string]string, pageTitles map[string]string) error {
|
||||
summaries := buildWikiNamespaceSummaries(pageStatuses)
|
||||
if err := writeWikiFile(filepath.Join(outputDir, "meta", "wikistatus.txt"), renderWikiStatusReportPage(summaries)); err != nil {
|
||||
if err := writeWikiFile(filepath.Join(outputDir, "meta", "wikistatus.html"), renderWikiStatusReportPage(summaries)); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, namespace := range wikiManagedNamespaces {
|
||||
summary := summaries[namespace]
|
||||
if err := writeWikiFile(filepath.Join(outputDir, "meta", "wikistatus", namespace+".txt"), renderWikiNamespaceFragment(namespace, summary)); err != nil {
|
||||
if err := writeWikiFile(filepath.Join(outputDir, "meta", "wikistatus", namespace+".html"), renderWikiNamespaceFragment(namespace, summary)); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, status := range []string{wikiStatusNew, wikiStatusModified, wikiStatusVanilla} {
|
||||
pageIDs := filterWikiPageIDs(pageStatuses, namespace, status)
|
||||
if err := writeWikiFile(filepath.Join(outputDir, "meta", "wikistatus", namespace, status+".txt"), renderWikiStatusListingPage(namespaceTitle(namespace)+" "+wikiStatusLabels[status]+" Pages", pageIDs, pageTitles)); err != nil {
|
||||
if err := writeWikiFile(filepath.Join(outputDir, "meta", "wikistatus", namespace, status+".html"), renderWikiStatusListingPage(namespaceTitle(namespace)+" "+wikiStatusLabels[status]+" Pages", pageIDs, pageTitles)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, status := range []string{wikiStatusNew, wikiStatusModified, wikiStatusVanilla} {
|
||||
pageIDs := filterWikiPageIDs(pageStatuses, "", status)
|
||||
if err := writeWikiFile(filepath.Join(outputDir, "meta", "wikistatus", status+".txt"), renderWikiStatusListingPage(wikiStatusLabels[status]+" Pages", pageIDs, pageTitles)); err != nil {
|
||||
if err := writeWikiFile(filepath.Join(outputDir, "meta", "wikistatus", status+".html"), renderWikiStatusListingPage(wikiStatusLabels[status]+" Pages", pageIDs, pageTitles)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1262,6 +1264,7 @@ func writeWikiFile(path, content string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
content = renderNodeBBManagedHTML(wikiRelPathToPageID(path), content)
|
||||
return os.WriteFile(path, []byte(ensureTrailingNewline(content)), 0o644)
|
||||
}
|
||||
|
||||
@@ -1390,7 +1393,16 @@ func wikiPageIDForKey(key string) string {
|
||||
}
|
||||
|
||||
func wikiPageIDToRelPath(pageID string) string {
|
||||
return filepath.FromSlash(strings.ReplaceAll(pageID, ":", "/") + ".txt")
|
||||
return filepath.FromSlash(strings.ReplaceAll(pageID, ":", "/") + ".html")
|
||||
}
|
||||
|
||||
func wikiRelPathToPageID(path string) string {
|
||||
path = filepath.ToSlash(path)
|
||||
path = strings.TrimSuffix(path, ".html")
|
||||
if index := strings.LastIndex(path, "/pages/"); index >= 0 {
|
||||
path = path[index+len("/pages/"):]
|
||||
}
|
||||
return strings.ReplaceAll(path, "/", ":")
|
||||
}
|
||||
|
||||
func namespaceTitle(namespace string) string {
|
||||
@@ -1567,6 +1579,184 @@ func toDokuWiki(text string) string {
|
||||
return strings.Join(out, "\n")
|
||||
}
|
||||
|
||||
func renderNodeBBManagedHTML(pageID, dokuText string) string {
|
||||
generated, notes := splitDokuWikiNotes(dokuText)
|
||||
body := dokuWikiToNodeBBHTML(generated)
|
||||
parts := []string{
|
||||
"<!-- sow-topdata-wiki:page=" + html.EscapeString(pageID) + " -->",
|
||||
"<!-- sow-topdata-wiki:managed:start -->",
|
||||
body,
|
||||
"<!-- sow-topdata-wiki:managed:end -->",
|
||||
}
|
||||
notesHTML := dokuWikiToNodeBBHTML("===== Notes =====\n" + notes)
|
||||
if strings.TrimSpace(notesHTML) != "" {
|
||||
parts = append(parts, notesHTML)
|
||||
}
|
||||
return ensureTrailingNewline(strings.Join(parts, "\n"))
|
||||
}
|
||||
|
||||
func splitDokuWikiNotes(text string) (string, string) {
|
||||
const delimiter = "===== Notes ====="
|
||||
before, after, ok := strings.Cut(text, delimiter)
|
||||
if !ok {
|
||||
return text, ""
|
||||
}
|
||||
return before, after
|
||||
}
|
||||
|
||||
func dokuWikiToNodeBBHTML(text string) string {
|
||||
text = strings.ReplaceAll(strings.TrimSpace(text), "\r\n", "\n")
|
||||
if text == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var out []string
|
||||
var paragraph []string
|
||||
inList := false
|
||||
inNotice := false
|
||||
|
||||
flushParagraph := func() {
|
||||
if len(paragraph) == 0 {
|
||||
return
|
||||
}
|
||||
out = append(out, "<p>"+strings.Join(paragraph, "<br>")+"</p>")
|
||||
paragraph = nil
|
||||
}
|
||||
closeList := func() {
|
||||
if inList {
|
||||
out = append(out, "</ul>")
|
||||
inList = false
|
||||
}
|
||||
}
|
||||
closeNotice := func() {
|
||||
if inNotice {
|
||||
out = append(out, "</aside>")
|
||||
inNotice = false
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if trimmed == "" {
|
||||
flushParagraph()
|
||||
closeList()
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(trimmed, "<WRAP") {
|
||||
flushParagraph()
|
||||
closeList()
|
||||
out = append(out, `<aside class="sow-generated-note">`)
|
||||
inNotice = true
|
||||
continue
|
||||
}
|
||||
if trimmed == "</WRAP>" {
|
||||
flushParagraph()
|
||||
closeList()
|
||||
closeNotice()
|
||||
continue
|
||||
}
|
||||
if heading, level, ok := parseDokuHeading(trimmed); ok {
|
||||
flushParagraph()
|
||||
closeList()
|
||||
closeNotice()
|
||||
out = append(out, fmt.Sprintf("<h%d>%s</h%d>", level, renderInlineNodeBBHTML(heading), level))
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(trimmed, " * ") || strings.HasPrefix(trimmed, "* ") || strings.HasPrefix(trimmed, "- ") {
|
||||
flushParagraph()
|
||||
if !inList {
|
||||
out = append(out, "<ul>")
|
||||
inList = true
|
||||
}
|
||||
item := strings.TrimSpace(strings.TrimPrefix(strings.TrimPrefix(strings.TrimPrefix(trimmed, " * "), "* "), "- "))
|
||||
out = append(out, "<li>"+renderInlineNodeBBHTML(item)+"</li>")
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(trimmed, `\\`)
|
||||
for _, part := range parts {
|
||||
part = strings.TrimSpace(part)
|
||||
if part != "" {
|
||||
paragraph = append(paragraph, renderInlineNodeBBHTML(part))
|
||||
}
|
||||
}
|
||||
}
|
||||
flushParagraph()
|
||||
closeList()
|
||||
closeNotice()
|
||||
return strings.Join(out, "\n")
|
||||
}
|
||||
|
||||
func parseDokuHeading(line string) (string, int, bool) {
|
||||
if !strings.HasPrefix(line, "=") || !strings.HasSuffix(line, "=") {
|
||||
return "", 0, false
|
||||
}
|
||||
left := len(line) - len(strings.TrimLeft(line, "="))
|
||||
right := len(line) - len(strings.TrimRight(line, "="))
|
||||
if left == 0 || left != right {
|
||||
return "", 0, false
|
||||
}
|
||||
title := strings.TrimSpace(line[left : len(line)-right])
|
||||
if title == "" {
|
||||
return "", 0, false
|
||||
}
|
||||
level := 7 - left
|
||||
if level < 1 {
|
||||
level = 1
|
||||
}
|
||||
if level > 6 {
|
||||
level = 6
|
||||
}
|
||||
return title, level, true
|
||||
}
|
||||
|
||||
func renderInlineNodeBBHTML(text string) string {
|
||||
escaped := html.EscapeString(text)
|
||||
var out strings.Builder
|
||||
boldOpen := false
|
||||
for {
|
||||
index := strings.Index(escaped, "**")
|
||||
if index < 0 {
|
||||
out.WriteString(escaped)
|
||||
break
|
||||
}
|
||||
out.WriteString(escaped[:index])
|
||||
if boldOpen {
|
||||
out.WriteString("</strong>")
|
||||
} else {
|
||||
out.WriteString("<strong>")
|
||||
}
|
||||
boldOpen = !boldOpen
|
||||
escaped = escaped[index+2:]
|
||||
}
|
||||
if boldOpen {
|
||||
out.WriteString("</strong>")
|
||||
}
|
||||
return renderWikiLinks(out.String())
|
||||
}
|
||||
|
||||
func renderWikiLinks(text string) string {
|
||||
var out strings.Builder
|
||||
for {
|
||||
start := strings.Index(text, "[[")
|
||||
if start < 0 {
|
||||
out.WriteString(text)
|
||||
break
|
||||
}
|
||||
end := strings.Index(text[start+2:], "]]")
|
||||
if end < 0 {
|
||||
out.WriteString(text)
|
||||
break
|
||||
}
|
||||
end += start + 2
|
||||
out.WriteString(text[:start])
|
||||
marker := text[start : end+2]
|
||||
out.WriteString(marker)
|
||||
text = text[end+2:]
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func ensureTrailingNewline(text string) string {
|
||||
if strings.HasSuffix(text, "\n") {
|
||||
return text
|
||||
|
||||
Reference in New Issue
Block a user