| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 | var extractProperties = require('./extract-properties');var extractSelectors = require('./extract-selectors');var track = require('../source-maps/track');var split = require('../utils/split');var path = require('path');var flatBlock = /(@(font\-face|page|\-ms\-viewport|\-o\-viewport|viewport|counter\-style)|\\@.+?)/;var BACKSLASH = '\\';function tokenize(data, outerContext) {  var chunks = split(normalize(data), '}', true, '{', '}');  if (chunks.length === 0)    return [];  var context = {    chunk: chunks.shift(),    chunks: chunks,    column: 0,    cursor: 0,    line: 1,    mode: 'top',    resolvePath: outerContext.options.explicitTarget ?      relativePathResolver(outerContext.options.root, outerContext.options.target) :      null,    source: undefined,    sourceMap: outerContext.options.sourceMap,    sourceMapInlineSources: outerContext.options.sourceMapInlineSources,    sourceMapTracker: outerContext.inputSourceMapTracker,    sourceReader: outerContext.sourceReader,    sourceTracker: outerContext.sourceTracker,    state: [],    track: outerContext.options.sourceMap ?      function (data, snapshotMetadata, fallbacks) { return [[track(data, context, snapshotMetadata, fallbacks)]]; } :      function () { return []; },    warnings: outerContext.warnings  };  return intoTokens(context);}function normalize(data) {  return data.replace(/\r\n/g, '\n');}function relativePathResolver(root, target) {  var rebaseTo = path.relative(root, target);  return function (relativeTo, sourcePath) {    return relativeTo != sourcePath ?      path.normalize(path.join(path.relative(rebaseTo, path.dirname(relativeTo)), sourcePath)) :      sourcePath;  };}function whatsNext(context) {  var mode = context.mode;  var chunk = context.chunk;  var closest;  if (chunk.length == context.cursor) {    if (context.chunks.length === 0)      return null;    context.chunk = chunk = context.chunks.shift();    context.cursor = 0;  }  if (mode == 'body') {    if (chunk[context.cursor] == '}')      return [context.cursor, 'bodyEnd'];    if (chunk.indexOf('}', context.cursor) == -1)      return null;    closest = context.cursor + split(chunk.substring(context.cursor - 1), '}', true, '{', '}')[0].length - 2;    return [closest, 'bodyEnd'];  }  var nextSpecial = nextAt(context, '@');  var nextEscape = chunk.indexOf('__ESCAPED_', context.cursor);  var nextBodyStart = nextAt(context, '{');  var nextBodyEnd = nextAt(context, '}');  if (nextSpecial > -1 && context.cursor > 0 && !/\s|\{|\}|\/|_|,|;/.test(chunk.substring(nextSpecial - 1, nextSpecial))) {    nextSpecial = -1;  }  if (nextEscape > -1 && /\S/.test(chunk.substring(context.cursor, nextEscape)))    nextEscape = -1;  closest = nextSpecial;  if (closest == -1 || (nextEscape > -1 && nextEscape < closest))    closest = nextEscape;  if (closest == -1 || (nextBodyStart > -1 && nextBodyStart < closest))    closest = nextBodyStart;  if (closest == -1 || (nextBodyEnd > -1 && nextBodyEnd < closest))    closest = nextBodyEnd;  if (closest == -1)    return;  if (nextEscape === closest)    return [closest, 'escape'];  if (nextBodyStart === closest)    return [closest, 'bodyStart'];  if (nextBodyEnd === closest)    return [closest, 'bodyEnd'];  if (nextSpecial === closest)    return [closest, 'special'];}function nextAt(context, character) {  var startAt = context.cursor;  var chunk = context.chunk;  var position;  while ((position = chunk.indexOf(character, startAt)) > -1) {    if (isEscaped(chunk, position)) {      startAt = position + 1;    } else {      return position;    }  }  return -1;}function isEscaped(chunk, position) {  var startAt = position;  var backslashCount = 0;  while (startAt > 0 && chunk[startAt - 1] == BACKSLASH) {    backslashCount++;    startAt--;  }  return backslashCount % 2 !== 0;}function intoTokens(context) {  var chunk = context.chunk;  var tokenized = [];  var newToken;  var value;  while (true) {    var next = whatsNext(context);    if (!next) {      var whatsLeft = context.chunk.substring(context.cursor);      if (whatsLeft.trim().length > 0) {        if (context.mode == 'body') {          context.warnings.push('Missing \'}\' after \'' + whatsLeft + '\'. Ignoring.');        } else {          tokenized.push(['text', [whatsLeft]]);        }        context.cursor += whatsLeft.length;      }      break;    }    var nextSpecial = next[0];    var what = next[1];    var nextEnd;    var oldMode;    chunk = context.chunk;    if (context.cursor != nextSpecial && what != 'bodyEnd') {      var spacing = chunk.substring(context.cursor, nextSpecial);      var leadingWhitespace = /^\s+/.exec(spacing);      if (leadingWhitespace) {        context.cursor += leadingWhitespace[0].length;        context.track(leadingWhitespace[0]);      }    }    if (what == 'special') {      var firstOpenBraceAt = chunk.indexOf('{', nextSpecial);      var firstSemicolonAt = chunk.indexOf(';', nextSpecial);      var isSingle = firstSemicolonAt > -1 && (firstOpenBraceAt == -1 || firstSemicolonAt < firstOpenBraceAt);      var isBroken = firstOpenBraceAt == -1 && firstSemicolonAt == -1;      if (isBroken) {        context.warnings.push('Broken declaration: \'' + chunk.substring(context.cursor) +  '\'.');        context.cursor = chunk.length;      } else if (isSingle) {        nextEnd = chunk.indexOf(';', nextSpecial + 1);        value = chunk.substring(context.cursor, nextEnd + 1);        tokenized.push([          'at-rule',          [value].concat(context.track(value, true))        ]);        context.track(';');        context.cursor = nextEnd + 1;      } else {        nextEnd = chunk.indexOf('{', nextSpecial + 1);        value = chunk.substring(context.cursor, nextEnd);        var trimmedValue = value.trim();        var isFlat = flatBlock.test(trimmedValue);        oldMode = context.mode;        context.cursor = nextEnd + 1;        context.mode = isFlat ? 'body' : 'block';        newToken = [          isFlat ? 'flat-block' : 'block'        ];        newToken.push([trimmedValue].concat(context.track(value, true)));        context.track('{');        newToken.push(intoTokens(context));        if (typeof newToken[2] == 'string')          newToken[2] = extractProperties(newToken[2], [[trimmedValue]], context);        context.mode = oldMode;        context.track('}');        tokenized.push(newToken);      }    } else if (what == 'escape') {      nextEnd = chunk.indexOf('__', nextSpecial + 1);      var escaped = chunk.substring(context.cursor, nextEnd + 2);      var isStartSourceMarker = !!context.sourceTracker.nextStart(escaped);      var isEndSourceMarker = !!context.sourceTracker.nextEnd(escaped);      if (isStartSourceMarker) {        context.track(escaped);        context.state.push({          source: context.source,          line: context.line,          column: context.column        });        context.source = context.sourceTracker.nextStart(escaped).filename;        context.line = 1;        context.column = 0;      } else if (isEndSourceMarker) {        var oldState = context.state.pop();        context.source = oldState.source;        context.line = oldState.line;        context.column = oldState.column;        context.track(escaped);      } else {        if (escaped.indexOf('__ESCAPED_COMMENT_SPECIAL') === 0)          tokenized.push(['text', [escaped]]);        context.track(escaped);      }      context.cursor = nextEnd + 2;    } else if (what == 'bodyStart') {      var selectors = extractSelectors(chunk.substring(context.cursor, nextSpecial), context);      oldMode = context.mode;      context.cursor = nextSpecial + 1;      context.mode = 'body';      var body = extractProperties(intoTokens(context), selectors, context);      context.track('{');      context.mode = oldMode;      tokenized.push([        'selector',        selectors,        body      ]);    } else if (what == 'bodyEnd') {      // extra closing brace at the top level can be safely ignored      if (context.mode == 'top') {        var at = context.cursor;        var warning = chunk[context.cursor] == '}' ?          'Unexpected \'}\' in \'' + chunk.substring(at - 20, at + 20) + '\'. Ignoring.' :          'Unexpected content: \'' + chunk.substring(at, nextSpecial + 1) + '\'. Ignoring.';        context.warnings.push(warning);        context.cursor = nextSpecial + 1;        continue;      }      if (context.mode == 'block')        context.track(chunk.substring(context.cursor, nextSpecial));      if (context.mode != 'block')        tokenized = chunk.substring(context.cursor, nextSpecial);      context.cursor = nextSpecial + 1;      break;    }  }  return tokenized;}module.exports = tokenize;
 |