css.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. package gen
  2. import (
  3. "regexp"
  4. "strings"
  5. )
  6. type cssRule struct {
  7. typeOf string
  8. selector string
  9. block string
  10. ruleList cssRuleList
  11. }
  12. type cssRuleList []cssRule
  13. // Prepare the css for parsing
  14. func clean(css *string) {
  15. // remove spaces
  16. re := regexp.MustCompile(`\s+`)
  17. *css = re.ReplaceAllString(*css, " ")
  18. // remove newlines
  19. *css = strings.ReplaceAll(*css, "\n", "")
  20. // remove comments
  21. r := regexp.MustCompile(`/\*[\s\S]*?\*/`)
  22. *css = r.ReplaceAllString(*css, "")
  23. }
  24. // Parse the css into a ruleList
  25. func toRuleList(css string) cssRuleList {
  26. clean(&css)
  27. dep := 0
  28. sel := ""
  29. blk := ""
  30. var ruleList cssRuleList
  31. for i := 0; i < len(css); i++ {
  32. chr := string(css[i])
  33. if chr == "{" && dep == 0 {
  34. dep++
  35. continue
  36. }
  37. if chr == "{" {
  38. dep++
  39. }
  40. if chr == "}" {
  41. dep--
  42. }
  43. if chr == "}" && dep == 0 {
  44. sel = strings.TrimSpace(sel)
  45. if strings.Contains(sel, "@media") {
  46. typeOf := "media"
  47. mediaRuleList := toRuleList(blk)
  48. ruleList = append(ruleList, cssRule{typeOf, sel, "", mediaRuleList})
  49. } else {
  50. typeOf := "rule"
  51. ruleList = append(ruleList, cssRule{typeOf, sel, blk, cssRuleList{}})
  52. }
  53. sel = ""
  54. blk = ""
  55. continue
  56. }
  57. if dep == 0 {
  58. sel = sel + chr
  59. } else {
  60. blk = blk + chr
  61. }
  62. }
  63. return ruleList
  64. }
  65. // scope the rules by adding a css class to each selector
  66. func scopeSelectors(ruleList cssRuleList, className string) cssRuleList {
  67. newRuleList := cssRuleList{}
  68. for i := 0; i < len(ruleList); i++ {
  69. rule := &ruleList[i]
  70. if strings.Contains(rule.selector, ":root") {
  71. continue
  72. }
  73. if strings.Contains(rule.selector, ":global") {
  74. continue
  75. }
  76. if strings.Contains(rule.selector, "@font-face") {
  77. continue
  78. }
  79. if strings.Contains(rule.selector, "@import") {
  80. continue
  81. }
  82. if rule.typeOf == "media" {
  83. rule.ruleList = scopeSelectors(rule.ruleList, className)
  84. continue
  85. }
  86. //
  87. if strings.Contains(rule.selector, ":cascade") {
  88. newRule := cssRule{}
  89. newRule.selector = "." + className + " " + rule.selector
  90. //newRule.selector = strings.ReplaceAll(newRule.selector, ":cascade", "")
  91. newRule.block = rule.block
  92. newRuleList = append(newRuleList, newRule)
  93. rule.selector = "." + className + " " + rule.selector
  94. continue
  95. }
  96. rule.selector = strings.ReplaceAll(rule.selector, " ", "."+className+" ")
  97. rule.selector = rule.selector + "." + className
  98. }
  99. ruleList = append(ruleList, newRuleList...)
  100. return ruleList
  101. }
  102. func mergeMediaRules(ruleList cssRuleList) cssRuleList {
  103. mediaMap := make(map[string]cssRuleList)
  104. var newRuleList cssRuleList
  105. for i := 0; i < len(ruleList); i++ {
  106. rule := ruleList[i]
  107. if rule.typeOf != "media" {
  108. newRuleList = append(newRuleList, rule)
  109. continue
  110. }
  111. mediaMap[rule.selector] = append(mediaMap[rule.selector], rule.ruleList...)
  112. }
  113. for sel, rList := range mediaMap {
  114. newRuleList = append(newRuleList, cssRule{"media", sel, "", rList})
  115. }
  116. return newRuleList
  117. }
  118. // order the appRules correctly, stip the :cascade tag too
  119. func orderRules(ruleList cssRuleList) cssRuleList {
  120. var newRuleList cssRuleList
  121. for i := 0; i < len(ruleList); i++ {
  122. rule := ruleList[i]
  123. if rule.typeOf == "media" {
  124. rule.ruleList = orderRules(rule.ruleList)
  125. }
  126. if strings.Contains(rule.selector, ":cascade") {
  127. rule.selector = strings.ReplaceAll(rule.selector, ":cascade", "")
  128. newRuleList = append(cssRuleList{rule}, newRuleList...)
  129. continue
  130. }
  131. if strings.Contains(rule.selector, ":global") {
  132. rule.selector = strings.ReplaceAll(rule.selector, ":global", "")
  133. newRuleList = append(cssRuleList{rule}, newRuleList...)
  134. continue
  135. }
  136. newRuleList = append(newRuleList, rule)
  137. }
  138. return newRuleList
  139. }
  140. func rulesToCSSstr(ruleList cssRuleList) string {
  141. cssStr := ""
  142. for i := 0; i < len(ruleList); i++ {
  143. rule := ruleList[i]
  144. if rule.typeOf == "media" {
  145. rule.block = rulesToCSSstr(rule.ruleList)
  146. }
  147. cssStr += rule.selector + "{" + rule.block + "}"
  148. }
  149. return cssStr
  150. }