AK-21/Graphite-Industrial-Intelligence
0
1'use strict'2 3const SINGLE_QUOTE = "'".charCodeAt(0)4const DOUBLE_QUOTE = '"'.charCodeAt(0)5const BACKSLASH = '\\'.charCodeAt(0)6const SLASH = '/'.charCodeAt(0)7const NEWLINE = '\n'.charCodeAt(0)8const SPACE = ' '.charCodeAt(0)9const FEED = '\f'.charCodeAt(0)10const TAB = '\t'.charCodeAt(0)11const CR = '\r'.charCodeAt(0)12const OPEN_SQUARE = '['.charCodeAt(0)13const CLOSE_SQUARE = ']'.charCodeAt(0)14const OPEN_PARENTHESES = '('.charCodeAt(0)15const CLOSE_PARENTHESES = ')'.charCodeAt(0)16const OPEN_CURLY = '{'.charCodeAt(0)17const CLOSE_CURLY = '}'.charCodeAt(0)18const SEMICOLON = ';'.charCodeAt(0)19const ASTERISK = '*'.charCodeAt(0)20const COLON = ':'.charCodeAt(0)21const AT = '@'.charCodeAt(0)22 23const RE_AT_END = /[\t\n\f\r "#'()/;[\\\]{}]/g24const RE_WORD_END = /[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g25const RE_BAD_BRACKET = /.[\r\n"'(/\\]/26const RE_HEX_ESCAPE = /[\da-f]/i27 28module.exports = function tokenizer(input, options = {}) {29 let css = input.css.valueOf()30 let ignore = options.ignoreErrors31 32 let code, content, escape, next, quote33 let currentToken, escaped, escapePos, n, prev34 35 let length = css.length36 let pos = 037 let buffer = []38 let returned = []39 let lastBadParen = -140 41 function position() {42 return pos43 }44 45 function unclosed(what) {46 throw input.error('Unclosed ' + what, pos)47 }48 49 function endOfFile() {50 return returned.length === 0 && pos >= length51 }52 53 function nextToken(opts) {54 if (returned.length) return returned.pop()55 if (pos >= length) return56 57 let ignoreUnclosed = opts ? opts.ignoreUnclosed : false58 59 code = css.charCodeAt(pos)60 61 switch (code) {62 case NEWLINE:63 case SPACE:64 case TAB:65 case CR:66 case FEED: {67 next = pos68 do {69 next += 170 code = css.charCodeAt(next)71 } while (72 code === SPACE ||73 code === NEWLINE ||74 code === TAB ||75 code === CR ||76 code === FEED77 )78 79 currentToken = ['space', css.slice(pos, next)]80 pos = next - 181 break82 }83 84 case OPEN_SQUARE:85 case CLOSE_SQUARE:86 case OPEN_CURLY:87 case CLOSE_CURLY:88 case COLON:89 case SEMICOLON:90 case CLOSE_PARENTHESES: {91 let controlChar = String.fromCharCode(code)92 currentToken = [controlChar, controlChar, pos]93 break94 }95 96 case OPEN_PARENTHESES: {97 prev = buffer.length ? buffer.pop()[1] : ''98 n = css.charCodeAt(pos + 1)99 if (100 prev === 'url' &&101 n !== SINGLE_QUOTE &&102 n !== DOUBLE_QUOTE &&103 n !== SPACE &&104 n !== NEWLINE &&105 n !== TAB &&106 n !== FEED &&107 n !== CR108 ) {109 next = pos110 do {111 escaped = false112 next = css.indexOf(')', next + 1)113 if (next === -1) {114 if (ignore || ignoreUnclosed) {115 next = pos116 break117 } else {118 unclosed('bracket')119 }120 }121 escapePos = next122 while (css.charCodeAt(escapePos - 1) === BACKSLASH) {123 escapePos -= 1124 escaped = !escaped125 }126 } while (escaped)127 128 currentToken = ['brackets', css.slice(pos, next + 1), pos, next]129 130 pos = next131 } else if (pos <= lastBadParen) {132 currentToken = ['(', '(', pos]133 } else {134 next = css.indexOf(')', pos + 1)135 content = css.slice(pos, next + 1)136 137 if (next === -1 || RE_BAD_BRACKET.test(content)) {138 lastBadParen = next === -1 ? length : next139 currentToken = ['(', '(', pos]140 } else {141 currentToken = ['brackets', content, pos, next]142 pos = next143 }144 }145 146 break147 }148 149 case SINGLE_QUOTE:150 case DOUBLE_QUOTE: {151 quote = code === SINGLE_QUOTE ? "'" : '"'152 next = pos153 do {154 escaped = false155 next = css.indexOf(quote, next + 1)156 if (next === -1) {157 if (ignore || ignoreUnclosed) {158 next = pos + 1159 break160 } else {161 unclosed('string')162 }163 }164 escapePos = next165 while (css.charCodeAt(escapePos - 1) === BACKSLASH) {166 escapePos -= 1167 escaped = !escaped168 }169 } while (escaped)170 171 currentToken = ['string', css.slice(pos, next + 1), pos, next]172 pos = next173 break174 }175 176 case AT: {177 RE_AT_END.lastIndex = pos + 1178 RE_AT_END.test(css)179 if (RE_AT_END.lastIndex === 0) {180 next = css.length - 1181 } else {182 next = RE_AT_END.lastIndex - 2183 }184 185 currentToken = ['at-word', css.slice(pos, next + 1), pos, next]186 187 pos = next188 break189 }190 191 case BACKSLASH: {192 next = pos193 escape = true194 while (css.charCodeAt(next + 1) === BACKSLASH) {195 next += 1196 escape = !escape197 }198 code = css.charCodeAt(next + 1)199 if (200 escape &&201 code !== SLASH &&202 code !== SPACE &&203 code !== NEWLINE &&204 code !== TAB &&205 code !== CR &&206 code !== FEED207 ) {208 next += 1209 if (RE_HEX_ESCAPE.test(css.charAt(next))) {210 while (RE_HEX_ESCAPE.test(css.charAt(next + 1))) {211 next += 1212 }213 if (css.charCodeAt(next + 1) === SPACE) {214 next += 1215 }216 }217 }218 219 currentToken = ['word', css.slice(pos, next + 1), pos, next]220 221 pos = next222 break223 }224 225 default: {226 if (code === SLASH && css.charCodeAt(pos + 1) === ASTERISK) {227 next = css.indexOf('*/', pos + 2) + 1228 if (next === 0) {229 if (ignore || ignoreUnclosed) {230 next = css.length231 } else {232 unclosed('comment')233 }234 }235 236 currentToken = ['comment', css.slice(pos, next + 1), pos, next]237 pos = next238 } else {239 RE_WORD_END.lastIndex = pos + 1240 RE_WORD_END.test(css)241 if (RE_WORD_END.lastIndex === 0) {242 next = css.length - 1243 } else {244 next = RE_WORD_END.lastIndex - 2245 }246 247 currentToken = ['word', css.slice(pos, next + 1), pos, next]248 buffer.push(currentToken)249 pos = next250 }251 252 break253 }254 }255 256 pos++257 return currentToken258 }259 260 function back(token) {261 returned.push(token)262 }263 264 return {265 back,266 endOfFile,267 nextToken,268 position269 }270}271 