package gen import ( "fmt" "math/rand" "os" "path/filepath" "strings" "git.clearsky.net.au/cody/gex.git/gen/domquery" "git.clearsky.net.au/cody/gex.git/gen/img" "git.clearsky.net.au/cody/gex.git/gen/layout" "git.clearsky.net.au/cody/gex.git/gen/partial" ) var appRuleList cssRuleList //var appJsStr string func GeneratePages(dir string) error { err := processTemplate(dir) if err != nil { fmt.Println(err) return err } // Process Partials err = processPartials(dir) if err != nil { fmt.Println(err) return err } // Build the layout & blocks err = processLayout(dir) if err != nil { fmt.Println(err) return err } // Process IMG on the generated files err = ProcessImg(dir) if err != nil { fmt.Println(err) return err } // Clean block tags err = cleanBlocks(dir) if err != nil { fmt.Println(err) return err } appRuleList = mergeMediaRules(appRuleList) appRuleList = orderRules(appRuleList) cssStr := rulesToCSSstr(appRuleList) os.WriteFile("app/pub/css/app.gen.css", []byte(cssStr), 0655) return nil } func processTemplate(dir string) error { processFile := func(fp string) error { if strings.Contains(fp, ".gen.tpl") { return nil } if !strings.Contains(fp, ".tpl") { return nil } // get the html string data, err := os.ReadFile(fp) if err != nil { return err } htmlStr := string(data) // add the scope class to attributes className := genRandName(8) htmlStr = AddClassAttr(htmlStr, className) // get & strip the css rules cssStr := ExtractCSS(&htmlStr) ruleList := toRuleList(cssStr) // scope the rules with css class ruleList = scopeSelectors(ruleList, className) appRuleList = append(appRuleList, ruleList...) // Process JS on the generated file htmlStr = ProcessJs(htmlStr, className) // write the .gen.html file as newPath := strings.Replace(fp, ".tpl", ".gen.tpl", 1) return os.WriteFile(newPath, []byte(htmlStr), 0655) } walk := func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } return processFile(path) } return filepath.Walk(dir, walk) } func processPartials(dir string) error { processFile := func(fp string) error { if !strings.Contains(fp, ".gen.tpl") { return nil } data, err := os.ReadFile(fp) if err != nil { return err } htmlStr := string(data) htmlStr, err = partial.ProcessHTML(htmlStr, fp) if err != nil { fmt.Println("\n" + fp) return err } return os.WriteFile(fp, []byte(htmlStr), 0655) } walk := func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } return processFile(path) } err := filepath.Walk(dir, walk) if err != nil { return err } return nil } func processLayout(dir string) error { processFile := func(fp string) error { if !strings.Contains(fp, ".gen.tpl") { return nil } data, err := os.ReadFile(fp) if err != nil { return err } htmlStr := string(data) htmlStr, err = layout.ProcessHTML(htmlStr, fp) if err != nil { fmt.Println("\n" + fp) return err } os.WriteFile(fp, []byte(htmlStr), 0655) return nil } walk := func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } return processFile(path) } filepath.Walk(dir, walk) return nil } func ProcessJs(htmlStr string, className string) string { dom := domquery.LoadHTML(htmlStr) scripts := dom.QuerySelectorAll("script") i := 0 jsTag := "" for _, script := range scripts { scriptName := className + fmt.Sprintf("%d", i) if script.GetAttribute("inline") != "" { continue } if script.GetAttribute("src") != "" { continue } jsStr := script.InnerHTML() funcStr := `if (typeof ` + scriptName + ` === 'undefined') {` funcStr += `window.` + scriptName + ` = () => {` funcStr += jsStr funcStr += ` if(typeof unload == "undefined") return const watchInt = setInterval(() => { let watch = document.querySelector(".` + className + `"); if (!watch) { unload(); clearInterval(watchInt); } },100); } } ` + scriptName + `() ` srcPath := "app/pub/gen/" + scriptName + ".gen.js" os.WriteFile(srcPath, []byte(funcStr), 0655) jsTag += `` if script.GetAttribute("type") == "module" { jsTag = `` } script.Remove() i++ } insDiv := dom.QuerySelector("." + className) insDiv.SetInnerHTML(jsTag + insDiv.InnerHTML()) htmlStr = dom.InnerHTML() return htmlStr } func ProcessImg(dir string) error { processFile := func(fp string) error { htmlDir := filepath.Dir(fp) if !strings.Contains(fp, ".gen.tpl") { return nil } // get the html string data, err := os.ReadFile(fp) if err != nil { return err } htmlStr := string(data) document := domquery.LoadHTML(htmlStr) imgTags := document.QuerySelectorAll("img") for _, tag := range imgTags { if !(tag.HasAttribute("webp") && tag.HasAttribute("src")) { continue } img.Process(htmlDir, tag) } htmlStr = document.InnerHTML() os.WriteFile(fp, []byte(htmlStr), 0655) return nil } walk := func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } return processFile(path) } return filepath.Walk(dir, walk) } func cleanBlocks(dir string) error { processFile := func(fp string) error { if !strings.Contains(fp, ".gen.tpl") { return nil } // get the html string data, err := os.ReadFile(fp) if err != nil { return err } htmlStr := string(data) dom := domquery.LoadHTML(htmlStr) var clean func() clean = func() { blocks := dom.QuerySelectorAll("block") // grab the top block only as blocks is invalid after a SetOuterHTML event for _, block := range blocks { block.SetOuterHTML(block.InnerHTML()) clean() return } } clean() htmlStr = dom.InnerHTML() os.WriteFile(fp, []byte(htmlStr), 0655) return nil } walk := func(dir string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } return processFile(dir) } return filepath.Walk(dir, walk) } // generate a random string to use as a class name func genRandName(length int) string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" b := make([]byte, length) for i := range b { b[i] = charset[rand.Intn(len(charset))] } return string(b) }