Page generation to use title instead of key
This commit is contained in:
@@ -330,6 +330,13 @@ func collectLocalPages(sourceDir string, namespaces []string) (map[string]wikiDe
|
||||
return err
|
||||
}
|
||||
content := string(raw)
|
||||
if markerPageID := extractWikiPageID(content); markerPageID != "" {
|
||||
pageID = markerPageID
|
||||
namespace = strings.SplitN(pageID, ":", 2)[0]
|
||||
if _, ok := namespaceSet[namespace]; !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
pages[pageID] = wikiDeployPage{
|
||||
PageID: pageID,
|
||||
Title: extractPageTitle(content, pageID),
|
||||
@@ -924,6 +931,22 @@ func renderArchivedWikiPage(pageID, title string) string {
|
||||
}, "\n"))
|
||||
}
|
||||
|
||||
func extractWikiPageID(content string) string {
|
||||
const pageMarker = "sow-topdata-wiki:page="
|
||||
start := strings.Index(content, pageMarker)
|
||||
if start < 0 {
|
||||
return ""
|
||||
}
|
||||
value := content[start+len(pageMarker):]
|
||||
if end := strings.Index(value, "-->"); end >= 0 {
|
||||
value = value[:end]
|
||||
}
|
||||
if fields := strings.Fields(value); len(fields) > 0 {
|
||||
return strings.Trim(strings.TrimSpace(fields[0]), `"`)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func extractWikiPagePublicPath(content string) string {
|
||||
const pageMarker = "sow-topdata-wiki:page="
|
||||
const pathField = "public_path="
|
||||
|
||||
@@ -76,6 +76,38 @@ func TestDeployWikiDryRunDoesNotWriteRemoteOrManifest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectLocalPagesReadsGeneratedPageIDFromMarkerWhenPathIsTitleBased(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
sourceDir := filepath.Join(root, "pages")
|
||||
if err := os.MkdirAll(filepath.Join(sourceDir, "feat"), 0755); err != nil {
|
||||
t.Fatalf("create source dir: %v", err)
|
||||
}
|
||||
generated := `<!-- sow-topdata-wiki:page=feat:bardic_music_inspire_competence -->
|
||||
<!-- sow-topdata-wiki:managed:start hash="sha256:local" -->
|
||||
<h1>Inspire Competence</h1>
|
||||
<p>Generated page.</p>
|
||||
<!-- sow-topdata-wiki:managed:end -->
|
||||
`
|
||||
if err := os.WriteFile(filepath.Join(sourceDir, "feat", "Inspire_Competence.html"), []byte(generated), 0644); err != nil {
|
||||
t.Fatalf("write source page: %v", err)
|
||||
}
|
||||
|
||||
pages, err := collectLocalPages(sourceDir, []string{"feat"})
|
||||
if err != nil {
|
||||
t.Fatalf("collectLocalPages failed: %v", err)
|
||||
}
|
||||
page, ok := pages["feat:bardic_music_inspire_competence"]
|
||||
if !ok {
|
||||
t.Fatalf("expected marker page ID to be used as deploy identity, got keys %#v", sortedKeysFromMap(pages))
|
||||
}
|
||||
if page.PageID != "feat:bardic_music_inspire_competence" || page.Namespace != "feat" || page.Title != "Inspire Competence" {
|
||||
t.Fatalf("unexpected collected page metadata: %#v", page)
|
||||
}
|
||||
if _, ok := pages["feat:Inspire_Competence"]; ok {
|
||||
t.Fatalf("expected title-based path not to become generated page identity")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeployWikiDryRunReadoptsMissingMappedPost(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
sourceDir := filepath.Join(root, "pages")
|
||||
|
||||
@@ -189,9 +189,15 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres
|
||||
pageTitles := map[string]string{}
|
||||
pageStatuses := map[string]string{}
|
||||
pageIndex := wikiPageIndex{Version: wikiGeneratorVersion}
|
||||
outputPaths := map[string]string{}
|
||||
|
||||
writePage := func(pageID, content, title, status string) error {
|
||||
path := filepath.Join(outputDir, wikiPageIDToRelPath(pageID))
|
||||
relPath := wikiPageOutputRelPath(pageID, title)
|
||||
if previous := outputPaths[relPath]; previous != "" {
|
||||
return fmt.Errorf("duplicate wiki page output path %q for %s and %s", filepath.ToSlash(relPath), previous, pageID)
|
||||
}
|
||||
outputPaths[relPath] = pageID
|
||||
path := filepath.Join(outputDir, relPath)
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -366,15 +372,18 @@ func indexUnregisteredWikiPages(rootDir, outputDir, stalePolicy string, index *w
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pageID := strings.TrimSuffix(filepath.ToSlash(relToPages), filepath.Ext(relToPages))
|
||||
pageID = strings.ReplaceAll(pageID, "/", ":")
|
||||
if _, ok := seen[pageID]; ok {
|
||||
return nil
|
||||
}
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pageID := extractWikiPageID(string(raw))
|
||||
if pageID == "" {
|
||||
pageID = strings.TrimSuffix(filepath.ToSlash(relToPages), filepath.Ext(relToPages))
|
||||
pageID = strings.ReplaceAll(pageID, "/", ":")
|
||||
}
|
||||
if _, ok := seen[pageID]; ok {
|
||||
return nil
|
||||
}
|
||||
namespace := strings.SplitN(pageID, ":", 2)[0]
|
||||
relToRoot, err := filepath.Rel(rootDir, path)
|
||||
if err != nil {
|
||||
@@ -1669,7 +1678,7 @@ func wikiPageIDForKey(key string) string {
|
||||
if namespace == "" {
|
||||
return ""
|
||||
}
|
||||
value = strings.NewReplacer("/", ":", "\\", ":").Replace(value)
|
||||
value = strings.NewReplacer("/", "_", "\\", "_").Replace(value)
|
||||
return namespace + ":" + value
|
||||
}
|
||||
|
||||
@@ -1711,6 +1720,23 @@ func wikiSlugifyTitle(value string) string {
|
||||
return slugifyWikiText(value, "topic")
|
||||
}
|
||||
|
||||
func wikiPageOutputRelPath(pageID, title string) string {
|
||||
namespace := strings.TrimSpace(strings.SplitN(pageID, ":", 2)[0])
|
||||
if namespace == "" {
|
||||
namespace = "page"
|
||||
}
|
||||
parts := []string{namespace}
|
||||
for _, part := range strings.Split(title, " :: ") {
|
||||
parts = append(parts, part)
|
||||
}
|
||||
path := canonicalWikiPath(parts...)
|
||||
if path == "" {
|
||||
path = wikiPageIDToRelPath(pageID)
|
||||
return path
|
||||
}
|
||||
return filepath.FromSlash(path + ".html")
|
||||
}
|
||||
|
||||
func wikiPageIDToRelPath(pageID string) string {
|
||||
return filepath.FromSlash(strings.ReplaceAll(pageID, ":", "/") + ".html")
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestBuildNativeGeneratesAndSkipsWikiPages(t *testing.T) {
|
||||
if result.WikiPages != generatedHTMLCount {
|
||||
t.Fatalf("expected wiki page count to match generated HTML files, got result=%d files=%d", result.WikiPages, generatedHTMLCount)
|
||||
}
|
||||
pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "athletics.html")
|
||||
pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "Athletics.html")
|
||||
got, err := os.ReadFile(pagePath)
|
||||
if err != nil {
|
||||
t.Fatalf("read generated page: %v", err)
|
||||
@@ -85,7 +85,7 @@ func TestBuildNativeGeneratesAndSkipsWikiPages(t *testing.T) {
|
||||
t.Fatalf("expected page-index entries to match generated HTML files, got index=%d files=%d", len(pageIndex.Pages), generatedHTMLCount)
|
||||
}
|
||||
indexText := string(indexRaw)
|
||||
if !strings.Contains(indexText, `"page_id": "skills:athletics"`) || !strings.Contains(indexText, `"public_path": "Skills/Athletics"`) || strings.Contains(indexText, `"public_slug"`) || !strings.Contains(indexText, `"output_path": "pages/skills/athletics.html"`) || !strings.Contains(indexText, `"edit_policy": "preserve_manual_sections"`) {
|
||||
if !strings.Contains(indexText, `"page_id": "skills:athletics"`) || !strings.Contains(indexText, `"public_path": "Skills/Athletics"`) || strings.Contains(indexText, `"public_slug"`) || !strings.Contains(indexText, `"output_path": "pages/skills/Athletics.html"`) || !strings.Contains(indexText, `"edit_policy": "preserve_manual_sections"`) {
|
||||
t.Fatalf("expected deterministic page-index metadata, got:\n%s", indexText)
|
||||
}
|
||||
if !strings.Contains(indexText, `"page_id": "meta:wikistatus"`) || !strings.Contains(indexText, `"output_path": "pages/meta/wikistatus.html"`) || !strings.Contains(indexText, `"edit_policy": "generated_only"`) {
|
||||
@@ -160,7 +160,7 @@ func TestBuildWikiRegeneratesMissingCachedPages(t *testing.T) {
|
||||
if result.Status != wikiGeneratedStatus {
|
||||
t.Fatalf("expected missing cached pages to regenerate, got wiki status %q", result.Status)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(pagesDir, "skills", "athletics.html")); err != nil {
|
||||
if _, err := os.Stat(filepath.Join(pagesDir, "skills", "Athletics.html")); err != nil {
|
||||
t.Fatalf("expected regenerated skill page: %v", err)
|
||||
}
|
||||
|
||||
@@ -934,14 +934,14 @@ datasets:
|
||||
}
|
||||
}
|
||||
for _, path := range []string{
|
||||
filepath.Join(root, ".cache", "wiki", "pages", "feat", "visible.html"),
|
||||
filepath.Join(root, ".cache", "wiki", "pages", "feat", "class_only.html"),
|
||||
filepath.Join(root, ".cache", "wiki", "pages", "feat", "Visible_Feat.html"),
|
||||
filepath.Join(root, ".cache", "wiki", "pages", "feat", "Class_Feat.html"),
|
||||
} {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
t.Fatalf("expected visible wiki page %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
classPage, err := os.ReadFile(filepath.Join(root, ".cache", "wiki", "pages", "classes", "fighter.html"))
|
||||
classPage, err := os.ReadFile(filepath.Join(root, ".cache", "wiki", "pages", "classes", "Fighter.html"))
|
||||
if err != nil {
|
||||
t.Fatalf("read class page: %v", err)
|
||||
}
|
||||
@@ -1321,20 +1321,23 @@ func TestWikiSourceDigestIncludesTemplates(t *testing.T) {
|
||||
|
||||
func TestWikiPageIDForKeyNormalizesSlashSeparatorsOnly(t *testing.T) {
|
||||
got := wikiPageIDForKey("feat:special/attacks_bull_rush")
|
||||
if want := "feat:special:attacks_bull_rush"; got != want {
|
||||
if want := "feat:special_attacks_bull_rush"; got != want {
|
||||
t.Fatalf("expected normalized page ID %q, got %q", want, got)
|
||||
}
|
||||
if got := wikiPageIDForKey("feat:bardic_music_inspire_competence"); got != "feat:bardic_music_inspire_competence" {
|
||||
t.Fatalf("expected underscore to remain in page ID leaf, got %q", got)
|
||||
}
|
||||
if got := wikiPageIDForKey("feat:bardic_music/inspire_competence"); got != "feat:bardic_music_inspire_competence" {
|
||||
t.Fatalf("expected slash-authored key to stay in one page ID leaf, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWikiPageIDToRelPathKeepsUnderscoreInFileLeaf(t *testing.T) {
|
||||
if got, want := wikiPageIDToRelPath("feat:bardic_music_inspire_competence"), filepath.FromSlash("feat/bardic_music_inspire_competence.html"); got != want {
|
||||
func TestWikiPageOutputRelPathUsesCanonicalTitleLeaf(t *testing.T) {
|
||||
if got, want := wikiPageOutputRelPath("feat:bardic_music_inspire_competence", "Inspire Competence"), filepath.FromSlash("feat/Inspire_Competence.html"); got != want {
|
||||
t.Fatalf("expected underscore page ID to stay in one file leaf, got %q want %q", got, want)
|
||||
}
|
||||
if got := wikiPageIDToRelPath("feat:bardic:music:inspire:competence"); got == filepath.FromSlash("feat/bardic_music_inspire_competence.html") {
|
||||
t.Fatalf("expected explicit hierarchy path to remain distinct from underscore leaf, got %q", got)
|
||||
if got, want := wikiPageOutputRelPath(wikiPageIDForKey("feat:bardic_music/inspire_competence"), "Inspire Competence"), filepath.FromSlash("feat/Inspire_Competence.html"); got != want {
|
||||
t.Fatalf("expected slash-authored key to stay in one file leaf, got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1531,7 +1534,7 @@ func TestBuildNativeRegeneratesWikiWhenTLKTextChanges(t *testing.T) {
|
||||
if _, err := BuildNative(proj, nil); err != nil {
|
||||
t.Fatalf("BuildNative after text change failed: %v", err)
|
||||
}
|
||||
pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "athletics.html")
|
||||
pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "Athletics.html")
|
||||
got, err := os.ReadFile(pagePath)
|
||||
if err != nil {
|
||||
t.Fatalf("read regenerated page: %v", err)
|
||||
@@ -1573,7 +1576,7 @@ func TestBuildPackageIgnoresWikiSourcesForFreshness(t *testing.T) {
|
||||
t.Fatalf("BuildNative failed: %v", err)
|
||||
}
|
||||
|
||||
pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "athletics.html")
|
||||
pagePath := filepath.Join(root, ".cache", "wiki", "pages", "skills", "Athletics.html")
|
||||
newTime := time.Now().Add(2 * time.Second)
|
||||
if err := os.Chtimes(pagePath, newTime, newTime); err != nil {
|
||||
t.Fatalf("touch wiki page: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user