parser.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. const blank = {
  2. ' ': true,
  3. '\n': true,
  4. '\t': true,
  5. '\r': true,
  6. '\f': true
  7. }
  8. function Parser () {
  9. this.styles = []
  10. this.selectors = []
  11. }
  12. /**
  13. * @description 解析 css 字符串
  14. * @param {string} content css 内容
  15. */
  16. Parser.prototype.parse = function (content) {
  17. new Lexer(this).parse(content)
  18. return this.styles
  19. }
  20. /**
  21. * @description 解析到一个选择器
  22. * @param {string} name 名称
  23. */
  24. Parser.prototype.onSelector = function (name) {
  25. // 不支持的选择器
  26. if (name.includes('[') || name.includes('*') || name.includes('@')) return
  27. const selector = {}
  28. // 伪类
  29. if (name.includes(':')) {
  30. const info = name.split(':')
  31. const pseudo = info.pop()
  32. if (pseudo === 'before' || pseudo === 'after') {
  33. selector.pseudo = pseudo
  34. name = info[0]
  35. } else return
  36. }
  37. // 分割交集选择器
  38. function splitItem (str) {
  39. const arr = []
  40. let i, start
  41. for (i = 1, start = 0; i < str.length; i++) {
  42. if (str[i] === '.' || str[i] === '#') {
  43. arr.push(str.substring(start, i))
  44. start = i
  45. }
  46. }
  47. if (!arr.length) {
  48. return str
  49. } else {
  50. arr.push(str.substring(start, i))
  51. return arr
  52. }
  53. }
  54. // 后代选择器
  55. if (name.includes(' ')) {
  56. selector.list = []
  57. const list = name.split(' ')
  58. for (let i = 0; i < list.length; i++) {
  59. if (list[i].length) {
  60. // 拆分子选择器
  61. const arr = list[i].split('>')
  62. for (let j = 0; j < arr.length; j++) {
  63. selector.list.push(splitItem(arr[j]))
  64. if (j < arr.length - 1) {
  65. selector.list.push('>')
  66. }
  67. }
  68. }
  69. }
  70. } else {
  71. selector.key = splitItem(name)
  72. }
  73. this.selectors.push(selector)
  74. }
  75. /**
  76. * @description 解析到选择器内容
  77. * @param {string} content 内容
  78. */
  79. Parser.prototype.onContent = function (content) {
  80. // 并集选择器
  81. for (let i = 0; i < this.selectors.length; i++) {
  82. this.selectors[i].style = content
  83. }
  84. this.styles = this.styles.concat(this.selectors)
  85. this.selectors = []
  86. }
  87. /**
  88. * @description css 词法分析器
  89. * @param {object} handler 高层处理器
  90. */
  91. function Lexer (handler) {
  92. this.selector = ''
  93. this.style = ''
  94. this.handler = handler
  95. }
  96. Lexer.prototype.parse = function (content) {
  97. this.i = 0
  98. this.content = content
  99. this.state = this.blank
  100. for (let len = content.length; this.i < len; this.i++) {
  101. this.state(content[this.i])
  102. }
  103. }
  104. Lexer.prototype.comment = function () {
  105. this.i = this.content.indexOf('*/', this.i) + 1
  106. if (!this.i) {
  107. this.i = this.content.length
  108. }
  109. }
  110. Lexer.prototype.blank = function (c) {
  111. if (!blank[c]) {
  112. if (c === '/' && this.content[this.i + 1] === '*') {
  113. this.comment()
  114. return
  115. }
  116. this.selector += c
  117. this.state = this.name
  118. }
  119. }
  120. Lexer.prototype.name = function (c) {
  121. if (c === '/' && this.content[this.i + 1] === '*') {
  122. this.comment()
  123. return
  124. }
  125. if (c === '{' || c === ',' || c === ';') {
  126. this.handler.onSelector(this.selector.trimEnd())
  127. this.selector = ''
  128. if (c !== '{') {
  129. while (blank[this.content[++this.i]]);
  130. }
  131. if (this.content[this.i] === '{') {
  132. this.floor = 1
  133. this.state = this.val
  134. } else {
  135. this.selector += this.content[this.i]
  136. }
  137. } else if (blank[c]) {
  138. this.selector += ' '
  139. } else {
  140. this.selector += c
  141. }
  142. }
  143. Lexer.prototype.val = function (c) {
  144. if (c === '/' && this.content[this.i + 1] === '*') {
  145. this.comment()
  146. return
  147. }
  148. if (c === '{') {
  149. this.floor++
  150. } else if (c === '}') {
  151. this.floor--
  152. if (!this.floor) {
  153. this.handler.onContent(this.style)
  154. this.style = ''
  155. this.state = this.blank
  156. return
  157. }
  158. }
  159. this.style += c
  160. }
  161. module.exports = Parser