UNPKG

@mapcss/preset-svg

Version:
236 lines (235 loc) 8.4 kB
const SINGLE_QUOTE = "'".charCodeAt(0); const DOUBLE_QUOTE = '"'.charCodeAt(0); const BACKSLASH = "\\".charCodeAt(0); const SLASH = "/".charCodeAt(0); const NEWLINE = "\n".charCodeAt(0); const SPACE = " ".charCodeAt(0); const FEED = "\f".charCodeAt(0); const TAB = "\t".charCodeAt(0); const CR = "\r".charCodeAt(0); const OPEN_SQUARE = "[".charCodeAt(0); const CLOSE_SQUARE = "]".charCodeAt(0); const OPEN_PARENTHESES = "(".charCodeAt(0); const CLOSE_PARENTHESES = ")".charCodeAt(0); const OPEN_CURLY = "{".charCodeAt(0); const CLOSE_CURLY = "}".charCodeAt(0); const SEMICOLON = ";".charCodeAt(0); const ASTERISK = "*".charCodeAt(0); const COLON = ":".charCodeAt(0); const AT = "@".charCodeAt(0); const RE_AT_END = /[\t\n\f\r "#'()/;[\\\]{}]/g; const RE_WORD_END = /[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g; const RE_BAD_BRACKET = /.[\n"'(/\\]/; const RE_HEX_ESCAPE = /[\da-f]/i; export default function tokenizer(input, options = {}) { let css = input.css.valueOf(); let ignore = options.ignoreErrors; let code, next, quote, content, escape; let escaped, escapePos, prev, n, currentToken; let length = css.length; let pos = 0; let buffer = []; let returned = []; function position() { return pos; } function unclosed(what) { throw input.error("Unclosed " + what, pos); } function endOfFile() { return returned.length === 0 && pos >= length; } function nextToken(opts) { if (returned.length) return returned.pop(); if (pos >= length) return; let ignoreUnclosed = opts ? opts.ignoreUnclosed : false; code = css.charCodeAt(pos); switch (code) { case NEWLINE: case SPACE: case TAB: case CR: case FEED: { next = pos; do { next += 1; code = css.charCodeAt(next); } while (code === SPACE || code === NEWLINE || code === TAB || code === CR || code === FEED); currentToken = ["space", css.slice(pos, next)]; pos = next - 1; break; } case OPEN_SQUARE: case CLOSE_SQUARE: case OPEN_CURLY: case CLOSE_CURLY: case COLON: case SEMICOLON: case CLOSE_PARENTHESES: { let controlChar = String.fromCharCode(code); currentToken = [controlChar, controlChar, pos]; break; } case OPEN_PARENTHESES: { prev = buffer.length ? buffer.pop()[1] : ""; n = css.charCodeAt(pos + 1); if (prev === "url" && n !== SINGLE_QUOTE && n !== DOUBLE_QUOTE && n !== SPACE && n !== NEWLINE && n !== TAB && n !== FEED && n !== CR) { next = pos; do { escaped = false; next = css.indexOf(")", next + 1); if (next === -1) { if (ignore || ignoreUnclosed) { next = pos; break; } else { unclosed("bracket"); } } escapePos = next; while (css.charCodeAt(escapePos - 1) === BACKSLASH) { escapePos -= 1; escaped = !escaped; } } while (escaped); currentToken = ["brackets", css.slice(pos, next + 1), pos, next]; pos = next; } else { next = css.indexOf(")", pos + 1); content = css.slice(pos, next + 1); if (next === -1 || RE_BAD_BRACKET.test(content)) { currentToken = ["(", "(", pos]; } else { currentToken = ["brackets", content, pos, next]; pos = next; } } break; } case SINGLE_QUOTE: case DOUBLE_QUOTE: { quote = code === SINGLE_QUOTE ? "'" : '"'; next = pos; do { escaped = false; next = css.indexOf(quote, next + 1); if (next === -1) { if (ignore || ignoreUnclosed) { next = pos + 1; break; } else { unclosed("string"); } } escapePos = next; while (css.charCodeAt(escapePos - 1) === BACKSLASH) { escapePos -= 1; escaped = !escaped; } } while (escaped); currentToken = ["string", css.slice(pos, next + 1), pos, next]; pos = next; break; } case AT: { RE_AT_END.lastIndex = pos + 1; RE_AT_END.test(css); if (RE_AT_END.lastIndex === 0) { next = css.length - 1; } else { next = RE_AT_END.lastIndex - 2; } currentToken = ["at-word", css.slice(pos, next + 1), pos, next]; pos = next; break; } case BACKSLASH: { next = pos; escape = true; while (css.charCodeAt(next + 1) === BACKSLASH) { next += 1; escape = !escape; } code = css.charCodeAt(next + 1); if (escape && code !== SLASH && code !== SPACE && code !== NEWLINE && code !== TAB && code !== CR && code !== FEED) { next += 1; if (RE_HEX_ESCAPE.test(css.charAt(next))) { while (RE_HEX_ESCAPE.test(css.charAt(next + 1))) { next += 1; } if (css.charCodeAt(next + 1) === SPACE) { next += 1; } } } currentToken = ["word", css.slice(pos, next + 1), pos, next]; pos = next; break; } default: { if (code === SLASH && css.charCodeAt(pos + 1) === ASTERISK) { next = css.indexOf("*/", pos + 2) + 1; if (next === 0) { if (ignore || ignoreUnclosed) { next = css.length; } else { unclosed("comment"); } } currentToken = ["comment", css.slice(pos, next + 1), pos, next]; pos = next; } else { RE_WORD_END.lastIndex = pos + 1; RE_WORD_END.test(css); if (RE_WORD_END.lastIndex === 0) { next = css.length - 1; } else { next = RE_WORD_END.lastIndex - 2; } currentToken = ["word", css.slice(pos, next + 1), pos, next]; buffer.push(currentToken); pos = next; } break; } } pos++; return currentToken; } function back(token) { returned.push(token); } return { back, nextToken, endOfFile, position, }; }