123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- 'use strict'
- /**
- * Cloned from https://github.com/postcss/postcss/blob/master/lib/tokenize.es6
- *
- */
- 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 = /[ \n\t\r\f\{\(\)'"\\;/\[\]#]/g;
- const RE_WORD_END = /[ \n\t\r\f\(\)\{\}:;@!'"\\\]\[#]|\/(?=\*)/g;
- const RE_BAD_BRACKET = /.[\\\/\("'\n]/;
- const RE_HEX_ESCAPE = /[a-f0-9]/i;
- module.exports = function tokenizer(input, options) {
- options = options || {}
- let css = input.css.valueOf();
- let ignore = options.ignoreErrors;
- let code, next, quote, lines, last, content, escape,
- nextLine, nextOffset, escaped, escapePos, prev, n, currentToken;
- let length = css.length;
- let offset = -1;
- let line = 1;
- let pos = 0;
- let buffer = [];
- let returned = [];
- function unclosed(what) {
- throw input.error('Unclosed ' + what, line, pos - offset);
- }
- function endOfFile() {
- return returned.length === 0 && pos >= length;
- }
- function nextToken() {
- if ( returned.length ) return returned.pop();
- if ( pos >= length ) return;
- code = css.charCodeAt(pos);
- if ( code === NEWLINE || code === FEED ||
- code === CR && css.charCodeAt(pos + 1) !== NEWLINE ) {
- offset = pos;
- line += 1;
- }
- switch ( code ) {
- case NEWLINE:
- case SPACE:
- case TAB:
- case CR:
- case FEED:
- next = pos;
- do {
- next += 1;
- code = css.charCodeAt(next);
- if ( code === NEWLINE ) {
- offset = next;
- line += 1;
- }
- } while ( code === SPACE ||
- code === NEWLINE ||
- code === TAB ||
- code === CR ||
- code === FEED );
- currentToken = ['space', css.slice(pos, next)];
- pos = next - 1;
- break;
- case OPEN_SQUARE:
- currentToken = ['[', '[', line, pos - offset];
- break;
- case CLOSE_SQUARE:
- currentToken = [']', ']', line, pos - offset];
- break;
- case OPEN_CURLY:
- currentToken = ['{', '{', line, pos - offset];
- break;
- case CLOSE_CURLY:
- currentToken = ['}', '}', line, pos - offset];
- break;
- case COLON:
- currentToken = [':', ':', line, pos - offset];
- break;
- case SEMICOLON:
- currentToken = [';', ';', line, pos - offset];
- 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 ) {
- 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),
- line, pos - offset,
- line, next - offset
- ];
- pos = next;
- } else {
- next = css.indexOf(')', pos + 1);
- content = css.slice(pos, next + 1);
- if ( next === -1 || RE_BAD_BRACKET.test(content) ) {
- currentToken = ['(', '(', line, pos - offset];
- } else {
- currentToken = ['brackets', content,
- line, pos - offset,
- line, next - offset
- ];
- pos = next;
- }
- }
- break;
- case CLOSE_PARENTHESES:
- currentToken = [')', ')', line, pos - offset];
- 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 ) {
- next = pos + 1;
- break;
- } else {
- unclosed('string');
- }
- }
- escapePos = next;
- while ( css.charCodeAt(escapePos - 1) === BACKSLASH ) {
- escapePos -= 1;
- escaped = !escaped;
- }
- } while ( escaped );
- content = css.slice(pos, next + 1);
- lines = content.split('\n');
- last = lines.length - 1;
- if ( last > 0 ) {
- nextLine = line + last;
- nextOffset = next - lines[last].length;
- } else {
- nextLine = line;
- nextOffset = offset;
- }
- currentToken = ['string', css.slice(pos, next + 1),
- line, pos - offset,
- nextLine, next - nextOffset
- ];
- offset = nextOffset;
- line = nextLine;
- 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),
- line, pos - offset,
- line, next - offset
- ];
- 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),
- line, pos - offset,
- line, next - offset
- ];
- pos = next;
- break;
- default:
- if ( code === SLASH && css.charCodeAt(pos + 1) === ASTERISK ) {
- next = css.indexOf('*/', pos + 2) + 1;
- if ( next === 0 ) {
- if ( ignore ) {
- next = css.length;
- } else {
- unclosed('comment');
- }
- }
- content = css.slice(pos, next + 1);
- lines = content.split('\n');
- last = lines.length - 1;
- if ( last > 0 ) {
- nextLine = line + last;
- nextOffset = next - lines[last].length;
- } else {
- nextLine = line;
- nextOffset = offset;
- }
- currentToken = ['comment', content,
- line, pos - offset,
- nextLine, next - nextOffset
- ];
- offset = nextOffset;
- line = nextLine;
- pos = next;
- } else if ( code === SLASH && css.charCodeAt(pos + 1) === SLASH ) {
- next = css.indexOf('\n', pos + 2);
- if (next === -1) {
- next = css.length - 1
- } else {
- next = next - 1
- }
- content = '/*' + css.slice(pos + 2, next + 1);
- content = content.replace(/\*\//g, '*\\/') + ' */'
- currentToken = ['comment', content,
- line, pos - offset,
- line, next - offset
- ];
- 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),
- line, pos - offset,
- line, next - offset
- ];
- buffer.push(currentToken);
- pos = next;
- }
- break;
- }
- pos++;
- return currentToken;
- }
- function back(token) {
- returned.push(token);
- }
- return {
- back,
- nextToken,
- endOfFile
- };
- }
|