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)
}