版博士V2.0程序
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

184 строки
4.8 KiB

  1. const { calculateTokenCharactersRange } = require('../helpers')
  2. const {
  3. TOKEN_TEXT,
  4. TOKEN_COMMENT_START
  5. } = require('../constants/token-types')
  6. const {
  7. OPEN_TAG_START_CONTEXT,
  8. CLOSE_TAG_CONTEXT,
  9. DOCTYPE_START_CONTEXT,
  10. COMMENT_CONTENT_CONTEXT
  11. } = require('../constants/tokenizer-contexts')
  12. const COMMENT_START = '<!--'
  13. function generateTextToken (state) {
  14. const range = calculateTokenCharactersRange(state, { keepBuffer: false })
  15. return {
  16. type: TOKEN_TEXT,
  17. content: state.accumulatedContent,
  18. startPosition: range.startPosition,
  19. endPosition: range.endPosition
  20. }
  21. }
  22. function openingCornerBraceWithText (state, tokens) {
  23. if (state.accumulatedContent.length !== 0) {
  24. tokens.push(generateTextToken(state))
  25. }
  26. state.accumulatedContent = state.decisionBuffer
  27. state.decisionBuffer = ''
  28. state.currentContext = OPEN_TAG_START_CONTEXT
  29. state.caretPosition++
  30. }
  31. function openingCornerBraceWithSlash (state, tokens) {
  32. if (state.accumulatedContent.length !== 0) {
  33. tokens.push(generateTextToken(state))
  34. }
  35. state.accumulatedContent = state.decisionBuffer
  36. state.decisionBuffer = ''
  37. state.currentContext = CLOSE_TAG_CONTEXT
  38. state.caretPosition++
  39. }
  40. function doctypeStart (state, tokens) {
  41. if (state.accumulatedContent.length !== 0) {
  42. tokens.push(generateTextToken(state))
  43. }
  44. state.accumulatedContent = state.decisionBuffer
  45. state.decisionBuffer = ''
  46. state.currentContext = DOCTYPE_START_CONTEXT
  47. state.caretPosition++
  48. }
  49. function commentStart (state, tokens) {
  50. if (state.accumulatedContent.length !== 0) {
  51. tokens.push(generateTextToken(state))
  52. }
  53. const commentStartRange = {
  54. startPosition: state.caretPosition - (COMMENT_START.length - 1),
  55. endPosition: state.caretPosition
  56. }
  57. tokens.push({
  58. type: TOKEN_COMMENT_START,
  59. content: state.decisionBuffer,
  60. startPosition: commentStartRange.startPosition,
  61. endPosition: commentStartRange.endPosition
  62. })
  63. state.accumulatedContent = ''
  64. state.decisionBuffer = ''
  65. state.currentContext = COMMENT_CONTENT_CONTEXT
  66. state.caretPosition++
  67. }
  68. function handleContentEnd (state, tokens) {
  69. const textContent = state.accumulatedContent + state.decisionBuffer
  70. if (textContent.length !== 0) {
  71. const range = calculateTokenCharactersRange(state, { keepBuffer: false })
  72. tokens.push({
  73. type: TOKEN_TEXT,
  74. content: textContent,
  75. startPosition: range.startPosition,
  76. endPosition: range.endPosition
  77. })
  78. }
  79. }
  80. function isIncompleteDoctype (chars) {
  81. const charsUpperCase = chars.toUpperCase()
  82. return (
  83. charsUpperCase === '<!'
  84. || charsUpperCase === '<!D'
  85. || charsUpperCase === '<!DO'
  86. || charsUpperCase === '<!DOC'
  87. || charsUpperCase === '<!DOCT'
  88. || charsUpperCase === '<!DOCTY'
  89. || charsUpperCase === '<!DOCTYP'
  90. )
  91. }
  92. const OPEN_TAG_START_PATTERN = /^<\w/
  93. function parseSyntax (chars, state, tokens, nextChar, nextNoWhiteChar, fullChars, charIndex) {
  94. if (OPEN_TAG_START_PATTERN.test(chars)) {
  95. return openingCornerBraceWithText(state, tokens)
  96. }
  97. if (chars === '</') {
  98. return openingCornerBraceWithSlash(state, tokens)
  99. }
  100. // 这种方法不行,会误伤属性里的<,如<dd class="1<5">1<5</dd> <view>我是打酱油</view>
  101. // if (chars === '<') {
  102. // const restChars = fullChars ? fullChars.slice(charIndex + 1) : ''
  103. // if (nextChar !== '!' && restChars.indexOf('>') == -1) {
  104. // // 1 < 5 后面没有其他的 > ,则 当前 < 不是标签
  105. // } else if (nextChar !== '!' && restChars.indexOf('<') > -1 && restChars.indexOf('<') < restChars.indexOf('>')) {
  106. // // <div> a < 1 </div> 如果 < 后面先出现 < 后出现 > 就代表当前的 < 不是标签
  107. // } else {
  108. // state.caretPosition++
  109. // return
  110. // }
  111. // }
  112. if (chars === '<') {
  113. // <a>{{i<b}}</a> 判断前后片段,如果被{{}}包裹,就不认为是标签
  114. const preChars = fullChars ? fullChars.slice(0, charIndex - 1) : ''
  115. const restChars = fullChars ? fullChars.slice(charIndex + 1) : ''
  116. const restStartIndex = restChars.indexOf('{{')
  117. const restEndIndex = restChars.indexOf('}}')
  118. if (preChars.lastIndexOf('{{') > preChars.lastIndexOf('}}')
  119. && restEndIndex > -1
  120. && restStartIndex == -1 || restEndIndex < restStartIndex) {
  121. // not open tag
  122. } else {
  123. if (nextNoWhiteChar.match(/[a-z]|[A-Z]|\/|\!|\-/)) {
  124. state.caretPosition++
  125. return
  126. }
  127. }
  128. }
  129. if (chars === '<!' || chars === '<!-') {
  130. state.caretPosition++
  131. return
  132. }
  133. if (chars === COMMENT_START) {
  134. return commentStart(state, tokens)
  135. }
  136. if (isIncompleteDoctype(chars)) {
  137. state.caretPosition++
  138. return
  139. }
  140. if (chars.toUpperCase() === '<!DOCTYPE') {
  141. return doctypeStart(state, tokens)
  142. }
  143. state.accumulatedContent += state.decisionBuffer
  144. state.decisionBuffer = ''
  145. state.caretPosition++
  146. }
  147. module.exports = {
  148. parseSyntax,
  149. handleContentEnd
  150. }