{
slug := strings.ToLower(text)
slug = slugRe.ReplaceAllString(slug, "")
slug = strings.TrimSpace(slug)
slug = strings.ReplaceAll(slug, " ", "-")
if n, ok := s.seen[slug]; ok {
s.seen[slug] = n + 1
return fmt.Sprintf("%s-%d", slug, n)
}
s.seen[slug] = 1
return slug
}
{ return &slugger{seen: make(map[string]int)} }
{ return "docs" }
{
if p.disableSEO {
return nil
}
return []core.Plugin{seo.New()}
}
{
doc := js.Document()
js.Fetch(p.Sidebar).Call("then", js.FuncOf(func(this js.Value, args []js.Value) any {
res := args[0]
res.Call("text").Call("then", js.FuncOf(func(this js.Value, args []js.Value) any {
js.Set("__rfwDocsSidebar", args[0].String())
doc.Call("dispatchEvent", js.CustomEvent().New("rfwSidebar"))
return nil
}))
return nil
}))
p.loader = js.FuncOf(func(this js.Value, args []js.Value) any {
if len(args) < 1 {
return nil
}
path := args[0].String()
js.Fetch(path).Call("then", js.FuncOf(func(this js.Value, args []js.Value) any {
res := args[0]
res.Call("text").Call("then", js.FuncOf(func(this js.Value, args []js.Value) any {
content := args[0].String()
mhs := markdown.Headings(content)
headings := make([]any, len(mhs))
for i, h := range mhs {
headings[i] = map[string]any{"text": h.Text, "depth": h.Depth, "id": h.ID}
}
doc.Call("dispatchEvent", js.CustomEvent().New("rfwDoc", map[string]any{"detail": map[string]any{"path": path, "content": content, "headings": headings}}))
return nil
}))
return nil
}))
return nil
})
js.Set("rfwLoadDoc", p.loader)
}
{ return nil }
{
sidebar = fmt.Sprintf("%s?%s", sidebar, time.Now().Unix())
p := &Plugin{Sidebar: sidebar}
if len(disableSEO) > 0 {
p.disableSEO = disableSEO[0]
}
return p
}
TestSlugger ensures slug generation is deterministic and handles duplicates.
{
s := newSlugger()
first := s.slug("Hello World!")
if first != "hello-world" {
t.Fatalf("expected 'hello-world', got %q", first)
}
second := s.slug("Hello World!")
if second != "hello-world-1" {
t.Fatalf("expected 'hello-world-1', got %q", second)
}
}
LoadArticle fetches and renders the markdown document at the given path.
It relies on the rfwLoadDoc loader injected by the docs plugin and should
be used instead of direct js.Call invocations.
{
js.Call("rfwLoadDoc", path)
}
LoadArticle is a no-op when not running in a js/wasm environment.
{}
import "fmt"
import "regexp"
import "strings"
import "encoding/json"
import "fmt"
import "time"
import "github.com/rfwlab/rfw/v1/core"
import "github.com/rfwlab/rfw/v1/js"
js
import "github.com/rfwlab/rfw/v1/markdown"
import "github.com/rfwlab/rfw/v1/plugins/seo"
import "testing"
import "github.com/rfwlab/rfw/v1/js"
js