mirror of
https://github.com/wahyd4/cdnjs.git
synced 2026-08-11 05:46:04 +10:00
168 lines
4.9 KiB
JavaScript
168 lines
4.9 KiB
JavaScript
module.exports = function Tokenizer(data, minifyContext) {
|
|
var chunker = new Chunker(data, 128);
|
|
var chunk = chunker.next();
|
|
var flatBlock = /(^@(font\-face|page|\-ms\-viewport|\-o\-viewport|viewport)|\\@.+?)/;
|
|
|
|
var whatsNext = function(context) {
|
|
var cursor = context.cursor;
|
|
var mode = context.mode;
|
|
var closest;
|
|
|
|
if (chunk.length == context.cursor) {
|
|
if (chunker.isEmpty())
|
|
return null;
|
|
|
|
chunk = chunker.next();
|
|
context.cursor = 0;
|
|
}
|
|
|
|
if (mode == 'body') {
|
|
closest = chunk.indexOf('}', cursor);
|
|
return closest > -1 ?
|
|
[closest, 'bodyEnd'] :
|
|
null;
|
|
}
|
|
|
|
var nextSpecial = chunk.indexOf('@', context.cursor);
|
|
var nextEscape = mode == 'top' ? chunk.indexOf('__ESCAPED_COMMENT_CLEAN_CSS', context.cursor) : -1;
|
|
var nextBodyStart = chunk.indexOf('{', context.cursor);
|
|
var nextBodyEnd = chunk.indexOf('}', context.cursor);
|
|
|
|
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'];
|
|
};
|
|
|
|
var tokenize = function(context) {
|
|
var tokenized = [];
|
|
|
|
context = context || { cursor: 0, mode: 'top' };
|
|
|
|
while (true) {
|
|
var next = whatsNext(context);
|
|
if (!next) {
|
|
var whatsLeft = chunk.substring(context.cursor);
|
|
if (whatsLeft.length > 0) {
|
|
tokenized.push(whatsLeft);
|
|
context.cursor += whatsLeft.length;
|
|
}
|
|
break;
|
|
}
|
|
|
|
var nextSpecial = next[0];
|
|
var what = next[1];
|
|
var nextEnd;
|
|
var oldMode;
|
|
|
|
if (what == 'special') {
|
|
var fragment = chunk.substring(nextSpecial, context.cursor + '@font-face'.length + 1);
|
|
var isSingle = fragment.indexOf('@import') === 0 || fragment.indexOf('@charset') === 0;
|
|
if (isSingle) {
|
|
nextEnd = chunk.indexOf(';', nextSpecial + 1);
|
|
tokenized.push(chunk.substring(context.cursor, nextEnd + 1));
|
|
|
|
context.cursor = nextEnd + 1;
|
|
} else {
|
|
nextEnd = chunk.indexOf('{', nextSpecial + 1);
|
|
var block = chunk.substring(context.cursor, nextEnd).trim();
|
|
|
|
var isFlat = flatBlock.test(block);
|
|
oldMode = context.mode;
|
|
context.cursor = nextEnd + 1;
|
|
context.mode = isFlat ? 'body' : 'block';
|
|
var specialBody = tokenize(context);
|
|
context.mode = oldMode;
|
|
|
|
tokenized.push({ block: block, body: specialBody });
|
|
}
|
|
} else if (what == 'escape') {
|
|
nextEnd = chunk.indexOf('__', nextSpecial + 1);
|
|
var escaped = chunk.substring(context.cursor, nextEnd + 2);
|
|
tokenized.push(escaped);
|
|
|
|
context.cursor = nextEnd + 2;
|
|
} else if (what == 'bodyStart') {
|
|
var selector = chunk.substring(context.cursor, nextSpecial).trim();
|
|
|
|
oldMode = context.mode;
|
|
context.cursor = nextSpecial + 1;
|
|
context.mode = 'body';
|
|
var body = tokenize(context);
|
|
context.mode = oldMode;
|
|
|
|
tokenized.push({ selector: selector, body: 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.';
|
|
|
|
minifyContext.warnings.push(warning);
|
|
context.cursor = nextSpecial + 1;
|
|
continue;
|
|
}
|
|
|
|
if (context.mode != 'block')
|
|
tokenized = chunk.substring(context.cursor, nextSpecial);
|
|
|
|
context.cursor = nextSpecial + 1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return tokenized;
|
|
};
|
|
|
|
return {
|
|
process: function() {
|
|
return tokenize();
|
|
}
|
|
};
|
|
};
|
|
|
|
// Divides `data` into chunks of `chunkSize` for faster processing
|
|
var Chunker = function(data, chunkSize) {
|
|
var chunks = [];
|
|
for (var cursor = 0, dataSize = data.length; cursor < dataSize;) {
|
|
var nextCursor = cursor + chunkSize > dataSize ?
|
|
dataSize - 1 :
|
|
cursor + chunkSize;
|
|
|
|
if (data[nextCursor] != '}')
|
|
nextCursor = data.indexOf('}', nextCursor);
|
|
if (nextCursor == -1)
|
|
nextCursor = data.length - 1;
|
|
|
|
chunks.push(data.substring(cursor, nextCursor + 1));
|
|
cursor = nextCursor + 1;
|
|
}
|
|
|
|
return {
|
|
isEmpty: function() {
|
|
return chunks.length === 0;
|
|
},
|
|
|
|
next: function() {
|
|
return chunks.shift() || '';
|
|
}
|
|
};
|
|
};
|