Skip to content
Snippets Groups Projects
cut.go 3.36 KiB
Newer Older
package html

import (
	"github.com/andybalholm/cascadia"
	"gitlab.schukai.com/oss/bob.git/types"
	"gitlab.schukai.com/oss/libraries/go/markup/html/engine"
	"golang.org/x/net/html"
	"gopkg.in/yaml.v3"
	"os"
	"path/filepath"
	"strings"
)

func CutHtml(p string) error {

	content, err := os.ReadFile(p)
	if err != nil {
		return err
	}

	currentDir, _ := os.Getwd()
	os.Chdir(filepath.Dir(p))
	defer os.Chdir(currentDir)

	specification := types.SnippetsSpecification{}

	if err := yaml.Unmarshal(content, &specification); err != nil {
		return err
	}

	sourceFiles := make(map[string]*html.Node)

	for _, r := range specification.Snippets {

		source := r.Source

		absSource, err := filepath.Abs(source)
		if err != nil {
			return err
		}

		if sourceFiles[absSource], err = readHTML(absSource); err != nil {
			return err
		}
	}

	for _, r := range specification.Snippets {

		source := r.Source
		destination := r.Destination

		absSource, err := filepath.Abs(source)
		if err != nil {
			return err
		}

		sourceNode, ok := sourceFiles[absSource]
		if !ok {
			continue
		}

		replacedNodes, err := replaceNodes(sourceNode, r.Replacement)
		if err != nil {
			return err
		}

		err = setAttributes(replacedNodes, r.Attributes)
		if err != nil {
			return err
		}

		selectors := r.Selector
		query, err := cascadia.Compile(selectors)
		if err != nil {
			return err
		}

		snippetNode := query.MatchFirst(replacedNodes)

		absDestination, err := filepath.Abs(destination)
		if err != nil {
			return err
		}

		fp, err := os.Create(absDestination)
		if err != nil {
			return err
		}

		err = html.Render(fp, snippetNode)
		err2 := fp.Close()
		if err2 != nil {
			return err2
		}

		if err != nil {
			return err
		}

	}

	return nil

}

func setAttributes(n *html.Node, attributes []types.Attributes) error {

	for _, a := range attributes {

		sel, err := cascadia.Compile(a.Selector)
		if err != nil {
			return err
		}

		for _, h := range sel.MatchAll(n) {
			removeAttribute(h.Attr, a.Name)
			h.Attr = append(h.Attr, html.Attribute{Key: a.Name, Val: a.Value})
		}

	}

	return nil

}

func removeAttribute(attrs []html.Attribute, key string) []html.Attribute {

	var result []html.Attribute

	for _, attr := range attrs {
		if attr.Key == key {
			continue
		}

		result = append(result, attr)
	}

	return result
}

func replaceNodes(n *html.Node, replacements []types.ContentReplacement) (*html.Node, error) {

	ret := engine.CloneNode(n)

	for _, r := range replacements {

		doc, err := html.Parse(strings.NewReader(r.Content))
		if err != nil {
			return nil, err
		}

		nodeList := []html.Node{}

		sel, err := cascadia.Compile("head")
		if err != nil {
			return nil, err
		}

		head := sel.MatchAll(doc)
		for _, h := range head {
			if h.FirstChild == nil {
				continue
			}

			for c := h.FirstChild; c != nil; c = c.NextSibling {
				nodeList = append(nodeList, *c)
			}
		}

		sel, err = cascadia.Compile("body")
		if err != nil {
			return nil, err
		}
		for _, h := range sel.MatchAll(doc) {
			if h.FirstChild == nil {
				continue
			}

			for c := h.FirstChild; c != nil; c = c.NextSibling {
				nodeList = append(nodeList, *c)
			}
		}

		sel, err = cascadia.Compile(r.Selector)
		if err != nil {
			return nil, err
		}

		for _, h := range sel.MatchAll(ret) {
			for _, node := range nodeList {
				h.Parent.InsertBefore(engine.CloneNode(&node), h)
			}
			h.Parent.RemoveChild(h)
		}

	}

	return ret, nil

}