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;
|