package gen import ( "regexp" "strings" ) type cssRule struct { typeOf string selector string block string ruleList cssRuleList } type cssRuleList []cssRule // Prepare the css for parsing func clean(css *string) { // remove spaces re := regexp.MustCompile(`\s+`) *css = re.ReplaceAllString(*css, " ") // remove newlines *css = strings.ReplaceAll(*css, "\n", "") // remove comments r := regexp.MustCompile(`/\*[\s\S]*?\*/`) *css = r.ReplaceAllString(*css, "") } // Parse the css into a ruleList func toRuleList(css string) cssRuleList { clean(&css) dep := 0 sel := "" blk := "" var ruleList cssRuleList for i := 0; i < len(css); i++ { chr := string(css[i]) if chr == "{" && dep == 0 { dep++ continue } if chr == "{" { dep++ } if chr == "}" { dep-- } if chr == "}" && dep == 0 { sel = strings.TrimSpace(sel) if strings.Contains(sel, "@media") { typeOf := "media" mediaRuleList := toRuleList(blk) ruleList = append(ruleList, cssRule{typeOf, sel, "", mediaRuleList}) } else { typeOf := "rule" ruleList = append(ruleList, cssRule{typeOf, sel, blk, cssRuleList{}}) } sel = "" blk = "" continue } if dep == 0 { sel = sel + chr } else { blk = blk + chr } } return ruleList } // scope the rules by adding a css class to each selector func scopeSelectors(ruleList cssRuleList, className string) cssRuleList { newRuleList := cssRuleList{} for i := 0; i < len(ruleList); i++ { rule := &ruleList[i] if strings.Contains(rule.selector, ":root") { continue } if strings.Contains(rule.selector, ":global") { continue } if strings.Contains(rule.selector, "@font-face") { continue } if strings.Contains(rule.selector, "@import") { continue } if rule.typeOf == "media" { rule.ruleList = scopeSelectors(rule.ruleList, className) continue } // if strings.Contains(rule.selector, ":cascade") { newRule := cssRule{} newRule.selector = "." + className + " " + rule.selector //newRule.selector = strings.ReplaceAll(newRule.selector, ":cascade", "") newRule.block = rule.block newRuleList = append(newRuleList, newRule) rule.selector = "." + className + " " + rule.selector continue } rule.selector = strings.ReplaceAll(rule.selector, " ", "."+className+" ") rule.selector = rule.selector + "." + className } ruleList = append(ruleList, newRuleList...) return ruleList } func mergeMediaRules(ruleList cssRuleList) cssRuleList { mediaMap := make(map[string]cssRuleList) var newRuleList cssRuleList for i := 0; i < len(ruleList); i++ { rule := ruleList[i] if rule.typeOf != "media" { newRuleList = append(newRuleList, rule) continue } mediaMap[rule.selector] = append(mediaMap[rule.selector], rule.ruleList...) } for sel, rList := range mediaMap { newRuleList = append(newRuleList, cssRule{"media", sel, "", rList}) } return newRuleList } // order the appRules correctly, stip the :cascade tag too func orderRules(ruleList cssRuleList) cssRuleList { var newRuleList cssRuleList for i := 0; i < len(ruleList); i++ { rule := ruleList[i] if rule.typeOf == "media" { rule.ruleList = orderRules(rule.ruleList) } if strings.Contains(rule.selector, ":cascade") { rule.selector = strings.ReplaceAll(rule.selector, ":cascade", "") newRuleList = append(cssRuleList{rule}, newRuleList...) continue } if strings.Contains(rule.selector, ":global") { rule.selector = strings.ReplaceAll(rule.selector, ":global", "") newRuleList = append(cssRuleList{rule}, newRuleList...) continue } newRuleList = append(newRuleList, rule) } return newRuleList } func rulesToCSSstr(ruleList cssRuleList) string { cssStr := "" for i := 0; i < len(ruleList); i++ { rule := ruleList[i] if rule.typeOf == "media" { rule.block = rulesToCSSstr(rule.ruleList) } cssStr += rule.selector + "{" + rule.block + "}" } return cssStr }