package html import ( "fmt" "github.com/andybalholm/cascadia" "gitlab.schukai.com/oss/bob/types" "gitlab.schukai.com/oss/libraries/go/markup/html/engine" "golang.org/x/net/html" "gopkg.in/yaml.v3" "os" "path/filepath" ) type Specification struct { Selector string } func readHTML(p string) (*html.Node, error) { htmlFile, err := os.Open(p) if err != nil { return nil, err } defer htmlFile.Close() return html.Parse(htmlFile) } func SyncHtml(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.SyncSpecification{} if err := yaml.Unmarshal(content, &specification); err != nil { return err } sourceFiles := make(map[string]*html.Node) destinationFiles := make(map[string]*html.Node) destinationFile := make(map[string][]string) for _, r := range specification.Sync { source := r.Source destination := r.Destination exclude := r.Destination.Exclude absSource, err := filepath.Abs(source.Path) if err != nil { return err } if err != nil { return err } if sourceFiles[absSource], err = readHTML(absSource); err != nil { return err } for _, d := range destination.Path { d, err := filepath.Abs(d) if err != nil { return err } fileinfo, err := os.Stat(d) if err != nil { return err } if fileinfo.IsDir() { filepath.Walk(d, func(pp string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } if exclude != nil { for _, e := range exclude { e, err := filepath.Abs(e) if err != nil { return err } if e == pp { return nil } } } ext := filepath.Ext(pp) if ext == ".html" { var dd *html.Node if dd, err = readHTML(pp); err != nil { return err } destinationFiles[pp] = dd destinationFile[absSource] = append(destinationFile[absSource], pp) } return nil }) } else if filepath.Ext(d) == ".html" { if exclude != nil { for _, e := range exclude { e, err := filepath.Abs(e) if err != nil { return err } if e == d { return nil } } } var dd *html.Node if dd, err = readHTML(d); err != nil { return err } destinationFiles[d] = dd destinationFile[absSource] = append(destinationFile[absSource], d) } } for _, r := range specification.Sync { source := r.Source absSource, err := filepath.Abs(source.Path) if err != nil { return err } sourceSelector := source.Selector query, err := cascadia.Compile(sourceSelector) if err != nil { return err } sourceNode := query.MatchFirst(sourceFiles[absSource]) dp := destinationFile[absSource] for _, d := range dp { destinationSelector := r.Destination.Selector if destinationSelector == "" { destinationSelector = sourceSelector } keepMap := make(map[string][]*html.Node) for _, n := range r.Destination.Keep { q, err := cascadia.Compile(n) if err != nil { return err } kNode := q.MatchAll(destinationFiles[d]) if kNode == nil { return fmt.Errorf("keep node not found: %s", n) } for _, k := range kNode { keepMap[n] = append(keepMap[n], k) } } query, err := cascadia.Compile(destinationSelector) if err != nil { return err } destinationData := query.MatchFirst(destinationFiles[d]) if destinationData == nil { return fmt.Errorf("could not find selector %s in %s", destinationSelector, d) } n := engine.CloneNode(sourceNode) destinationData.Parent.InsertBefore(n, destinationData) destinationData.Parent.RemoveChild(destinationData) for sel, k := range keepMap { cas, err := cascadia.Compile(sel) if err != nil { return err } x := cas.MatchAll(destinationFiles[d]) if x == nil { return fmt.Errorf("could not find selector %s in %s", sel, d) } for _, n1 := range x { for _, kk := range k { // node already removed, for example, by a previous keep if n1.Parent == nil { continue } n1.Parent.InsertBefore(engine.CloneNode(kk), n1) n1.Parent.RemoveChild(n1) } } } } } for p, d := range destinationFiles { fp, err := os.Create(p) if err != nil { return err } err = html.Render(fp, d) err2 := fp.Close() if err2 != nil { return err2 } if err != nil { return err } } } return nil }