blog/cmd_addtag.go

149 lines
4.3 KiB
Go

package main
import (
"fmt"
"os"
"strings"
"unicode"
)
// AddTag generates code for a new custom tag
func AddTag(delimiter, tagName, targetElement string) {
fmt.Printf("Adding tag: %s -> %s (delimiter: %s)\n", tagName, targetElement, delimiter)
if err := updateTagsFile(delimiter, tagName); err != nil {
fmt.Printf("Error updating templates/tags.go: %v\n", err)
os.Exit(1)
}
if err := updateParseFile(tagName, targetElement); err != nil {
fmt.Printf("Error updating parse.go: %v\n", err)
os.Exit(1)
}
fmt.Println("Successfully added tag!")
}
func updateTagsFile(delimiter, tagName string) error {
path := "templates/tags.go"
content, err := os.ReadFile(path)
if err != nil {
return err
}
pascalTagName := toPascalCase(tagName)
newLine := fmt.Sprintf("var %s = customtag.New(\"%s\", \"%s\")\n", pascalTagName, delimiter, tagName)
// check if already exists
if strings.Contains(string(content), newLine) {
fmt.Println("Tag definition already exists in templates/tags.go")
return nil
}
// Append to file
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
if _, err := f.WriteString(newLine); err != nil {
return err
}
return nil
}
func updateParseFile(tagName, targetElement string) error {
path := "parse.go"
contentBytes, err := os.ReadFile(path)
if err != nil {
return err
}
content := string(contentBytes)
pascalTagName := toPascalCase(tagName)
// 1. Add to Goldmark Extensions
// Find `templates.SidebarTag,` or `templates.TopBanner,` and add after it
// We'll look for `goldmark.WithExtensions(` and find the closing `),`
extLine := fmt.Sprintf("\t\t\t\ttemplates.%s,", pascalTagName)
if !strings.Contains(content, extLine) {
// Simple insertion strategy: find `templates.TopBanner,`
anchor := "templates.TopBanner,"
if idx := strings.Index(content, anchor); idx != -1 {
insertAt := idx + len(anchor)
content = content[:insertAt] + "\n" + extLine + content[insertAt:]
} else {
return fmt.Errorf("could not find anchor 'templates.TopBanner,' in parse.go to insert extension registration")
}
} else {
fmt.Println("Extension registration already exists in parse.go")
}
// 2. Add Post-Processing Logic
// We'll look for `// Build Full Page` and insert before it, or after the TopBanner block.
// A good anchor is `// _-_- TopBanner` block's end.
// Let's construct the code block to insert
targetTag, targetClass := parseTargetElement(targetElement)
// Prepare the replacement logic
var replacementLogic string
if targetClass != "" {
replacementLogic = fmt.Sprintf(`return fmt.Sprintf("<%s class=\"%s\">%%s</%s>", innerContent)`, targetTag, targetClass, targetTag)
} else {
replacementLogic = fmt.Sprintf(`return fmt.Sprintf("<%s>%%s</%s>", innerContent)`, targetTag, targetTag)
}
codeBlock := fmt.Sprintf(`
// %s %s
%sRegex := regexp.MustCompile("(?s)<%s>.*?</%s>")
if %sRegex.MatchString(htmlContent) {
htmlContent = %sRegex.ReplaceAllStringFunc(htmlContent, func(match string) string {
innerContent := strings.TrimPrefix(match, "<%s>")
innerContent = strings.TrimSuffix(innerContent, "</%s>")
%s
})
}
`, pascalTagName, tagName, tagName, tagName, tagName, tagName, tagName, tagName, tagName, replacementLogic)
if !strings.Contains(content, fmt.Sprintf("%sRegex :=", tagName)) {
// Anchor: find end of TopBanner block
// We know TopBanner block ends with a closing brace for the `if topbannerRegex.MatchString` block
// But strictly speaking, we are inside `if file.RoutePath == "/" { ... }`.
// Let's look for `// Build Full Page` and insert BEFORE it.
anchor := "// Build Full Page"
if idx := strings.Index(content, anchor); idx != -1 {
// Insert BEFORE "// Build Full Page", which matches the global scope (after the if block)
content = content[:idx] + codeBlock + content[idx:]
} else {
return fmt.Errorf("could not find anchor '// Build Full Page' in parse.go")
}
} else {
fmt.Println("Regex logic already exists in parse.go")
}
return os.WriteFile(path, []byte(content), 0644)
}
func toPascalCase(s string) string {
if s == "" {
return ""
}
runes := []rune(s)
runes[0] = unicode.ToUpper(runes[0])
return string(runes)
}
func parseTargetElement(t string) (string, string) {
parts := strings.Split(t, ".")
if len(parts) == 2 {
return parts[0], parts[1]
}
return t, ""
}