Refactor engine into focused files to improve maintainability
Split monolithic engine.go (776 lines) into specialized files: - engine.go: Core orchestration (142 lines, 82% reduction) - collection.go: Collection processing and management (445 lines) - content.go: Content injection and extraction (152 lines) - discovery.go: Element discovery and DOM traversal (85 lines) Benefits: - Single responsibility principle applied to each file - Better code organization and navigation - Improved testability of individual components - Easier team development and code reviews - Maintained full API compatibility with no breaking changes
This commit is contained in:
144
internal/engine/content.go
Normal file
144
internal/engine/content.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// addContentAttributes adds data-content-id attribute only
|
||||
func (e *ContentEngine) addContentAttributes(node *html.Node, contentID string) {
|
||||
// Add data-content-id attribute
|
||||
SetAttribute(node, "data-content-id", contentID)
|
||||
}
|
||||
|
||||
// injectContent injects content from database into elements
|
||||
func (e *ContentEngine) injectContent(elements []ProcessedElement, siteID string) error {
|
||||
for i := range elements {
|
||||
elem := &elements[i]
|
||||
|
||||
// Get content from database by ID
|
||||
contentItem, err := e.client.GetContent(nil, siteID, elem.ID)
|
||||
if err != nil {
|
||||
// Content not found - skip silently (enhancement mode should not fail on missing content)
|
||||
continue
|
||||
}
|
||||
|
||||
if contentItem != nil {
|
||||
// Inject the content into the element
|
||||
elem.Content = contentItem.HTMLContent
|
||||
|
||||
// Update injector siteID for this operation
|
||||
// HACK: I do not like this. Injector refactor?
|
||||
e.injector.siteID = siteID
|
||||
e.injector.injectHTMLContent(elem.Node, contentItem.HTMLContent)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractHTMLContent extracts the inner HTML content from a node
|
||||
func (e *ContentEngine) extractHTMLContent(node *html.Node) string {
|
||||
var content strings.Builder
|
||||
|
||||
// Render all child nodes in order to preserve HTML structure
|
||||
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
||||
if err := html.Render(&content, child); err == nil {
|
||||
// All nodes (text and element) rendered in correct order
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(content.String())
|
||||
}
|
||||
|
||||
// extractOriginalTemplate extracts the outer HTML of the element (including the element itself)
|
||||
func (e *ContentEngine) extractOriginalTemplate(node *html.Node) string {
|
||||
var buf strings.Builder
|
||||
if err := html.Render(&buf, node); err != nil {
|
||||
return ""
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// extractCleanTemplate extracts a clean template without data-content-id attributes and with placeholder content. Used for collection template variants.
|
||||
func (e *ContentEngine) extractCleanTemplate(node *html.Node) string {
|
||||
// Clone the node to avoid modifying the original
|
||||
clonedNode := e.cloneNode(node)
|
||||
|
||||
// Remove all data-content-id attributes and replace content with placeholders
|
||||
e.walkNodes(clonedNode, func(n *html.Node) {
|
||||
if n.Type == html.ElementNode {
|
||||
// Remove data-content-id attribute
|
||||
e.removeAttribute(n, "data-content-id")
|
||||
|
||||
// If this is an .insertr element, replace content with placeholder
|
||||
if e.hasClass(n, "insertr") {
|
||||
placeholderText := e.getPlaceholderForElement(n.Data)
|
||||
// Clear existing children and add placeholder text
|
||||
for child := n.FirstChild; child != nil; {
|
||||
next := child.NextSibling
|
||||
n.RemoveChild(child)
|
||||
child = next
|
||||
}
|
||||
n.AppendChild(&html.Node{
|
||||
Type: html.TextNode,
|
||||
Data: placeholderText,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var buf strings.Builder
|
||||
if err := html.Render(&buf, clonedNode); err != nil {
|
||||
return ""
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// removeAttribute removes an attribute from an HTML node
|
||||
func (e *ContentEngine) removeAttribute(n *html.Node, key string) {
|
||||
for i, attr := range n.Attr {
|
||||
if attr.Key == key {
|
||||
n.Attr = slices.Delete(n.Attr, i, i+1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hasClass checks if a node has a specific class
|
||||
func (e *ContentEngine) hasClass(n *html.Node, className string) bool {
|
||||
for _, attr := range n.Attr {
|
||||
if attr.Key == "class" {
|
||||
classes := strings.Fields(attr.Val)
|
||||
if slices.Contains(classes, className) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getPlaceholderForElement returns appropriate placeholder text for different element types
|
||||
func (e *ContentEngine) getPlaceholderForElement(elementType string) string {
|
||||
placeholders := map[string]string{
|
||||
"h1": "Heading 1",
|
||||
"h2": "Heading 2",
|
||||
"h3": "Heading 3",
|
||||
"h4": "Heading 4",
|
||||
"h5": "Heading 5",
|
||||
"h6": "Heading 6",
|
||||
"p": "Paragraph text",
|
||||
"span": "Text",
|
||||
"div": "Content block",
|
||||
"button": "Button",
|
||||
"a": "Link text",
|
||||
"li": "List item",
|
||||
"blockquote": "Quote text",
|
||||
}
|
||||
|
||||
if placeholder, exists := placeholders[elementType]; exists {
|
||||
return placeholder
|
||||
}
|
||||
return "Enter content..."
|
||||
}
|
||||
Reference in New Issue
Block a user