* Uses arrays instead of hashes.
* Gets rid of cached metadata in favor to live-calculated values.
* Adds `removeEmpty` step in advanced optimizer to improve empty
elements removal.
* Cleans up simple optimizer iteration loop.
* Cleans up source map tracking.
function extract(token) {
var properties = [];
- if (token.kind == 'selector') {
- var inSimpleSelector = !/[\.\+#>~\s]/.test(token.metadata.selector);
- for (var i = 0, l = token.metadata.bodiesList.length; i < l; i++) {
- var property = token.metadata.bodiesList[i];
+ if (token[0] == 'selector') {
+ // TODO: stringifySelector
+ var inSimpleSelector = !/[\.\+#>~\s]/.test(token[1].join(','));
+ for (var i = 0, l = token[2].length; i < l; i++) {
+ var property = token[2][i][0];
if (property.indexOf('__ESCAPED') === 0)
continue;
name,
property.substring(splitAt + 1),
nameRoot,
- property,
- token.metadata.selectorsList,
+ token[2][i],
+ token[1],
inSimpleSelector
]);
}
- } else if (token.kind == 'block') {
- for (var j = 0, k = token.body.length; j < k; j++) {
- properties = properties.concat(extract(token.body[j]));
+ } else if (token[0] == 'block') {
+ for (var j = 0, k = token[2].length; j < k; j++) {
+ properties = properties.concat(extract(token[2][j]));
}
}
var overrideCompactor = require('./override-compactor');
var shorthandCompactor = require('./shorthand-compactor');
-function valueMapper(object) { return object.value; }
-
module.exports = function Optimizer(options, context) {
var overridable = {
'animation-delay': ['animation'],
}
}
- var tokenize = function(body, selector) {
- var keyValues = [];
+ var tokenize = function(properties, selector) {
+ var tokenized = [];
- for (var i = 0, l = body.length; i < l; i++) {
- var token = body[i];
- var firstColon = token.value.indexOf(':');
- var property = token.value.substring(0, firstColon);
- var value = token.value.substring(firstColon + 1);
+ for (var i = 0, l = properties.length; i < l; i++) {
+ var property = properties[i];
+ var firstColon = property[0].indexOf(':');
+ var name = property[0].substring(0, firstColon);
+ var value = property[0].substring(firstColon + 1);
if (value === '') {
- context.warnings.push('Empty property \'' + property + '\' inside \'' + selector.map(valueMapper).join(',') + '\' selector. Ignoring.');
+ context.warnings.push('Empty property \'' + name + '\' inside \'' + selector.join(',') + '\' selector. Ignoring.');
continue;
}
- keyValues.push([
- property,
+ tokenized.push([
+ name,
value,
- token.value.indexOf('!important') > -1,
- token.value.indexOf(IE_BACKSLASH_HACK, firstColon + 1) === token.value.length - IE_BACKSLASH_HACK.length,
- token.metadata
+ value.indexOf('!important') > -1,
+ property[0].indexOf(IE_BACKSLASH_HACK, firstColon + 1) === property[0].length - IE_BACKSLASH_HACK.length,
+ property.slice(1)
]);
}
- return keyValues;
+ return tokenized;
};
- var optimize = function(tokens, allowAdjacent) {
+ var optimize = function(properties, allowAdjacent) {
var merged = [];
- var properties = [];
- var lastProperty = null;
+ var names = [];
+ var lastName = null;
var rescanTrigger = {};
var removeOverridenBy = function(property, isImportant) {
var overrided = overrides[property];
for (var i = 0, l = overrided.length; i < l; i++) {
- for (var j = 0; j < properties.length; j++) {
- if (properties[j] != overrided[i] || (merged[j][2] && !isImportant))
+ for (var j = 0; j < names.length; j++) {
+ if (names[j] != overrided[i] || (merged[j][2] && !isImportant))
continue;
merged.splice(j, 1);
- properties.splice(j, 1);
+ names.splice(j, 1);
j -= 1;
}
}
return allowAdjacent.indexOf(position) > -1;
};
- tokensLoop:
- for (var i = 0, l = tokens.length; i < l; i++) {
- var token = tokens[i];
- var property = token[0];
- var value = token[1];
- var isImportant = token[2];
- var isIEHack = token[3];
- var _property = (property == '-ms-filter' || property == 'filter') ?
- (lastProperty == 'background' || lastProperty == 'background-image' ? lastProperty : property) :
- property;
+ propertiesLoop:
+ for (var i = 0, l = properties.length; i < l; i++) {
+ var property = properties[i];
+ var name = property[0];
+ var value = property[1];
+ var isImportant = property[2];
+ var isIEHack = property[3];
+ var _name = (name == '-ms-filter' || name == 'filter') ?
+ (lastName == 'background' || lastName == 'background-image' ? lastName : name) :
+ name;
var toOverridePosition = 0;
if (isIEHack && !compatibility.properties.ieSuffixHack)
// e.g. a{display:inline-block;display:-moz-inline-box}
// however if `mergeablePosition` yields true then the rule does not apply
// (e.g merging two adjacent selectors: `a{display:block}a{display:block}`)
- if (aggressiveMerging && property !== '' && _property != lastProperty || mergeablePosition(i)) {
+ if (aggressiveMerging && name !== '' && _name != lastName || mergeablePosition(i)) {
while (true) {
- toOverridePosition = properties.indexOf(_property, toOverridePosition);
+ toOverridePosition = names.indexOf(_name, toOverridePosition);
if (toOverridePosition == -1)
break;
var wasIEHack = lastToken[3];
if (wasImportant && !isImportant)
- continue tokensLoop;
+ continue propertiesLoop;
if (compatibility.properties.ieSuffixHack && !wasIEHack && isIEHack)
break;
- var _info = processable[_property];
- if (!isIEHack && !wasIEHack && _info && _info.canOverride && !_info.canOverride(tokens[toOverridePosition][1], value))
+ var _info = processable[_name];
+ if (!isIEHack && !wasIEHack && _info && _info.canOverride && !_info.canOverride(properties[toOverridePosition][1], value))
break;
merged.splice(toOverridePosition, 1);
- properties.splice(toOverridePosition, 1);
+ names.splice(toOverridePosition, 1);
}
}
- merged.push(token);
- properties.push(_property);
+ merged.push(property);
+ names.push(_name);
// certain properties (see values of `overridable`) should trigger removal of
// more granular properties (see keys of `overridable`)
- if (rescanTrigger[_property])
- removeOverridenBy(_property, isImportant);
+ if (rescanTrigger[_name])
+ removeOverridenBy(_name, isImportant);
// add rescan triggers - if certain property appears later in the list a rescan needs
// to be triggered, e.g 'border-top' triggers a rescan after 'border-top-width' and
// 'border-top-color' as they can be removed
- for (var j = 0, list = overridable[_property] || [], m = list.length; j < m; j++)
+ for (var j = 0, list = overridable[_name] || [], m = list.length; j < m; j++)
rescanTrigger[list[j]] = true;
- lastProperty = _property;
+ lastName = _name;
}
return merged;
};
- var rebuild = function(tokens) {
- var tokenized = [];
- var list = [];
+ var rebuild = function(properties) {
+ var rebuilt = [];
var eligibleForCompacting = false;
- for (var i = 0, l = tokens.length; i < l; i++) {
- if (!eligibleForCompacting && processableInfo.implementedFor.test(tokens[i][0]))
+ for (var i = 0, l = properties.length; i < l; i++) {
+ if (!eligibleForCompacting && processableInfo.implementedFor.test(properties[i][0]))
eligibleForCompacting = true;
// FIXME: the check should be gone with #407
- var property = !tokens[i][0] && tokens[i][1].indexOf('__ESCAPED_') === 0 ?
- tokens[i][1] :
- tokens[i][0] + ':' + tokens[i][1];
- tokenized.push({ value: property, metadata: tokens[i][4] });
- list.push(property);
+ var property = !properties[i][0] && properties[i][1].indexOf('__ESCAPED_') === 0 ?
+ properties[i][1] :
+ properties[i][0] + ':' + properties[i][1];
+ var metadata = properties[i].pop();
+
+ rebuilt.push([property].concat(metadata));
}
return {
compactFurther: eligibleForCompacting,
- list: list,
- tokenized: tokenized
+ list: rebuilt
};
};
};
return {
- process: function(selector, body, allowAdjacent, compactProperties) {
- var tokenized = tokenize(body, selector);
+ process: function(selector, properties, allowAdjacent, compactProperties) {
+ var tokenized = tokenize(properties, selector);
var optimized = optimize(tokenized, allowAdjacent);
var rebuilt = rebuild(optimized);
return shorthandCompacting && compactProperties && rebuilt.compactFurther ?
- compact(rebuilt.tokenized) :
- rebuilt;
+ compact(rebuilt.list) :
+ rebuilt.list;
}
};
};
return canOverride.sameFunctionOrValue(val1, val2);
},
border: function(val1, val2) {
- var brokenUp1 = breakUp.border(Token.tokenizeOne({ value: val1 }));
- var brokenUp2 = breakUp.border(Token.tokenizeOne({ value: val2 }));
+ var brokenUp1 = breakUp.border(Token.tokenizeOne([val1]));
+ var brokenUp2 = breakUp.border(Token.tokenizeOne([val2]));
return canOverride.color(brokenUp1[2].value, brokenUp2[2].value);
}
function selectorsDoNotOverlap(s1, s2) {
for (var i = 0, l = s1.length; i < l; i++) {
- if (s2.indexOf(s1[i]) > -1)
- return false;
+ for (var j = 0, m = s2.length; j < m; j++) {
+ if (s1[i][0] == s2[j][0])
+ return false;
+ }
}
return true;
// Parses one CSS property declaration into a token
Token.tokenizeOne = function (fullProp) {
// Find first colon
- var colonPos = fullProp.value.indexOf(':');
+ var colonPos = fullProp[0].indexOf(':');
if (colonPos < 0) {
// This property doesn't have a colon, it's invalid. Let's keep it intact anyway.
- return new Token('', fullProp.value);
+ return new Token('', fullProp[0]);
}
// Parse parts of the property
- var prop = fullProp.value.substr(0, colonPos).trim();
- var value = fullProp.value.substr(colonPos + 1).trim();
+ var prop = fullProp[0].substr(0, colonPos).trim();
+ var value = fullProp[0].substr(colonPos + 1).trim();
var isImportant = false;
var importantPos = value.indexOf(important);
tokens = [tokens];
}
- var tokenized = [];
var list = [];
// This step takes care of putting together the components of shorthands
// FIXME: to be fixed with #429
property = property.replace(/\) /g, ')');
- tokenized.push({ value: property, metadata: t.metadata || {} });
- list.push(property);
+ list.push([property]);
}
- return {
- list: list,
- tokenized: tokenized
- };
+ return list;
};
// Gets the final (detokenized) length of the given tokens
}
SelectorsOptimizer.prototype.process = function (data, stringifier) {
- var tokens = new Tokenizer(this.context, this.options.advanced, this.options.sourceMap).toTokens(data);
+ var tokens = new Tokenizer(this.context, this.options.sourceMap).toTokens(data);
new SimpleOptimizer(this.options).optimize(tokens);
if (this.options.advanced)
var extractProperties = require('../../properties/extractor');
var canReorder = require('../../properties/reorderable').canReorder;
var canReorderSingle = require('../../properties/reorderable').canReorderSingle;
+var stringifyBody = require('../../utils/stringify-tokens').body;
+var stringifySelector = require('../../utils/stringify-tokens').selector;
function AdvancedOptimizer(options, context) {
this.options = options;
- this.minificationsMade = [];
this.propertyOptimizer = new PropertyOptimizer(this.options, context);
}
-function changeBodyOf(token, newBody) {
- token.body = newBody.tokenized;
- token.metadata.body = newBody.list.join(';');
- token.metadata.bodiesList = newBody.list;
-}
-
-function changeSelectorOf(token, newSelectors) {
- token.value = newSelectors.tokenized;
- token.metadata.selector = newSelectors.list.join(',');
- token.metadata.selectorsList = newSelectors.list;
-}
-
function unsafeSelector(value) {
return /\.|\*| :/.test(value);
}
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
- if (token.kind != 'selector')
+ if (token[0] != 'selector')
continue;
- var id = token.metadata.body + '@' + token.metadata.selector;
+ var id = stringifyBody(token[2]) + '@' + stringifySelector(token[1]);
var alreadyMatched = matched[id];
if (alreadyMatched) {
for (var j = 0, n = forRemoval.length; j < n; j++) {
tokens.splice(forRemoval[j] - j, 1);
}
-
- this.minificationsMade.unshift(forRemoval.length > 0);
};
AdvancedOptimizer.prototype.mergeAdjacent = function (tokens) {
var forRemoval = [];
- var lastToken = { selector: null, body: null };
+ var lastToken = [null, [], []];
var adjacentSpace = this.options.compatibility.selectors.adjacentSpace;
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
- if (token.kind != 'selector') {
- lastToken = { selector: null, body: null };
+ if (token[0] != 'selector') {
+ lastToken = [null, [], []];
continue;
}
- if (lastToken.kind == 'selector' && token.metadata.selector == lastToken.metadata.selector) {
- var joinAt = [lastToken.body.length];
- changeBodyOf(
- lastToken,
- this.propertyOptimizer.process(token.value, lastToken.body.concat(token.body), joinAt, true)
- );
+ if (lastToken[0] == 'selector' && stringifySelector(token[1]) == stringifySelector(lastToken[1])) {
+ var joinAt = [lastToken[2].length];
+ lastToken[2] = this.propertyOptimizer.process(token[1], lastToken[2].concat(token[2]), joinAt, true);
forRemoval.push(i);
- } else if (lastToken.body && token.metadata.body == lastToken.metadata.body &&
- !this.isSpecial(token.metadata.selector) && !this.isSpecial(lastToken.metadata.selector)) {
- changeSelectorOf(
- lastToken,
- CleanUp.selectors(lastToken.value.concat(token.value), false, adjacentSpace)
- );
+ } else if (lastToken[0] == 'selector' && stringifyBody(token[2]) == stringifyBody(lastToken[2]) &&
+ !this.isSpecial(stringifySelector(token[1])) && !this.isSpecial(stringifySelector(lastToken[1]))) {
+ lastToken[1] = CleanUp.selectors(lastToken[1].concat(token[1]), false, adjacentSpace);
forRemoval.push(i);
} else {
lastToken = token;
for (var j = 0, m = forRemoval.length; j < m; j++) {
tokens.splice(forRemoval[j] - j, 1);
}
-
- this.minificationsMade.unshift(forRemoval.length > 0);
};
AdvancedOptimizer.prototype.reduceNonAdjacent = function (tokens) {
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
- if (token.kind != 'selector')
+ if (token[0] != 'selector')
continue;
- var isComplexAndNotSpecial = token.value.length > 1 && !this.isSpecial(token.metadata.selector);
+ var isComplexAndNotSpecial = token[1].length > 1 && !this.isSpecial(stringifySelector(token[1]));
var selectors = isComplexAndNotSpecial ?
- [token.metadata.selector].concat(token.metadata.selectorsList) :
- [token.metadata.selector];
+ [stringifySelector(token[1])].concat(token[1]) :
+ [stringifySelector(token[1])];
for (var j = 0, m = selectors.length; j < m; j++) {
var selector = selectors[j];
candidates[selector].push({
where: i,
- list: token.metadata.selectorsList,
+ list: token[1],
isPartial: isComplexAndNotSpecial && j > 0,
isComplex: isComplexAndNotSpecial && j === 0
});
}
}
- var reducedInSimple = this.reduceSimpleNonAdjacentCases(tokens, repeated, candidates);
- var reducedInComplex = this.reduceComplexNonAdjacentCases(tokens, candidates);
-
- this.minificationsMade.unshift(reducedInSimple || reducedInComplex);
+ this.reduceSimpleNonAdjacentCases(tokens, repeated, candidates);
+ this.reduceComplexNonAdjacentCases(tokens, candidates);
};
AdvancedOptimizer.prototype.reduceSimpleNonAdjacentCases = function (tokens, repeated, candidates) {
- var reduced = false;
-
function filterOut(idx, bodies) {
return data[idx].isPartial && bodies.length === 0;
}
function reduceBody(token, newBody, processedCount, tokenIdx) {
- if (!data[processedCount - tokenIdx - 1].isPartial) {
- changeBodyOf(token, newBody);
- reduced = true;
- }
+ if (!data[processedCount - tokenIdx - 1].isPartial)
+ token[2] = newBody;
}
for (var i = 0, l = repeated.length; i < l; i++) {
callback: reduceBody
});
}
-
- return reduced;
};
AdvancedOptimizer.prototype.reduceComplexNonAdjacentCases = function (tokens, candidates) {
- var reduced = false;
var localContext = {};
function filterOut(idx) {
callback: collectReducedBodies
});
- if (reducedBodies[reducedBodies.length - 1].list.join(';') != reducedBodies[0].list.join(';'))
+ if (stringifyBody(reducedBodies[reducedBodies.length - 1]) != stringifyBody(reducedBodies[0]))
continue allSelectors;
}
- intoToken.body = reducedBodies[0].tokenized;
- reduced = true;
+ intoToken[2] = reducedBodies[0];
}
-
- return reduced;
};
AdvancedOptimizer.prototype.reduceSelector = function (tokens, selector, data, options) {
var where = data[j].where;
var token = tokens[where];
- bodies = bodies.concat(token.body);
- bodiesAsList.push(token.metadata.bodiesList);
+ bodies = bodies.concat(token[2]);
+ bodiesAsList.push(token[2]);
processedTokens.push(where);
}
var optimizedBody = this.propertyOptimizer.process(selector, bodies, joinsAt, false);
var processedCount = processedTokens.length;
- var propertyIdx = optimizedBody.tokenized.length - 1;
+ var propertyIdx = optimizedBody.length - 1;
var tokenIdx = processedCount - 1;
while (tokenIdx >= 0) {
- if ((tokenIdx === 0 || (optimizedBody.tokenized[propertyIdx] && bodiesAsList[tokenIdx].indexOf(optimizedBody.tokenized[propertyIdx].value) > -1)) && propertyIdx > -1) {
+ if ((tokenIdx === 0 || (optimizedBody[propertyIdx] && stringifyBody(bodiesAsList[tokenIdx]).indexOf(optimizedBody[propertyIdx]) > -1)) && propertyIdx > -1) {
propertyIdx--;
continue;
}
- var newBody = {
- list: optimizedBody.list.splice(propertyIdx + 1),
- tokenized: optimizedBody.tokenized.splice(propertyIdx + 1)
- };
+ var newBody = optimizedBody.splice(propertyIdx + 1);
options.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
tokenIdx--;
var i;
for (i = tokens.length - 1; i >= 0; i--) {
- if (tokens[i].kind != 'selector')
+ if (tokens[i][0] != 'selector')
continue;
- if (tokens[i].body.length === 0)
+ if (tokens[i][2].length === 0)
continue;
- var selector = tokens[i].metadata.selector;
+ var selector = stringifySelector(tokens[i][1]);
allSelectors[selector] = [i].concat(allSelectors[selector] || []);
if (allSelectors[selector].length == 2)
continue directionIterator;
}
- var joinAt = topToBottom ? [target.body.length] : [moved.body.length];
- var joinedBodies = topToBottom ? moved.body.concat(target.body) : target.body.concat(moved.body);
- var newBody = this.propertyOptimizer.process(target.value, joinedBodies, joinAt, true);
- changeBodyOf(target, newBody);
- changeBodyOf(moved, { tokenized: [], list: [] });
+ var joinAt = topToBottom ? [target[2].length] : [moved[2].length];
+ var joinedBodies = topToBottom ? moved[2].concat(target[2]) : target[2].concat(moved[2]);
+ target[2] = this.propertyOptimizer.process(target[1], joinedBodies, joinAt, true);
+ moved[2] = [];
}
}
}
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
- if (token.kind != 'selector')
+ if (token[0] != 'selector')
continue;
- if (token.body.length > 0 && unsafeSelector(token.metadata.selector))
+ if (token[2].length > 0 && unsafeSelector(stringifySelector(token[1])))
candidates = {};
- var oldToken = candidates[token.metadata.body];
- if (oldToken && !this.isSpecial(token.metadata.selector) && !this.isSpecial(oldToken.metadata.selector)) {
- changeSelectorOf(
- token,
- CleanUp.selectors(oldToken.value.concat(token.value), false, adjacentSpace)
- );
+ var oldToken = candidates[stringifyBody(token[2])];
+ if (oldToken && !this.isSpecial(stringifySelector(token[1])) && !this.isSpecial(stringifySelector(oldToken[1]))) {
+ token[1] = CleanUp.selectors(oldToken[1].concat(token[1]), false, adjacentSpace);
- changeBodyOf(oldToken, { tokenized: [], list: [] });
- candidates[token.metadata.body] = null;
+ oldToken[2] = [];
+ candidates[stringifyBody(token[2])] = null;
}
- candidates[token.metadata.body] = token;
+ candidates[stringifyBody(token[2])] = token;
}
};
function cacheId(cachedTokens) {
var id = [];
for (var i = 0, l = cachedTokens.length; i < l; i++) {
- id.push(cachedTokens[i].metadata.selector);
+ id.push(stringifySelector(cachedTokens[i][1]));
}
return id.join(ID_JOIN_CHARACTER);
}
var mergeableTokens = [];
for (var i = sourceTokens.length - 1; i >= 0; i--) {
- if (self.isSpecial(sourceTokens[i].metadata.selector))
+ if (self.isSpecial(stringifySelector(sourceTokens[i][1])))
continue;
mergeableTokens.unshift(sourceTokens[i]);
- if (sourceTokens[i].body.length > 0 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1)
+ if (sourceTokens[i][2].length > 0 && uniqueTokensWithBody.indexOf(sourceTokens[i]) == -1)
uniqueTokensWithBody.push(sourceTokens[i]);
}
function shortenIfPossible(position, movedProperty) {
var name = movedProperty[0];
var value = movedProperty[1];
- var key = movedProperty[3];
+ var key = movedProperty[3][0];
var valueSize = name.length + value.length + 1;
var allSelectors = [];
var qualifiedTokens = [];
return sendToMultiPropertyMoveCache(position, movedProperty, allFits);
for (var i = bestFit[0].length - 1; i >=0; i--) {
- allSelectors = bestFit[0][i].value.concat(allSelectors);
+ allSelectors = bestFit[0][i][1].concat(allSelectors);
qualifiedTokens.unshift(bestFit[0][i]);
}
function sizeDifference(tokensVariant, propertySize, propertiesCount) {
var allSelectorsSize = 0;
for (var i = tokensVariant.length - 1; i >= 0; i--) {
- allSelectorsSize += tokensVariant[i].body.length > propertiesCount ? tokensVariant[i].metadata.selector.length : -1;
+ allSelectorsSize += tokensVariant[i][2].length > propertiesCount ? stringifySelector(tokensVariant[i][1]).length : -1;
}
return allSelectorsSize - (tokensVariant.length - 1) * propertySize + 1;
}
function dropAsNewTokenAt(position, properties, allSelectors, mergeableTokens) {
- var bodyMetadata = {};
var i, j, k, m;
+ var allProperties = [];
for (i = mergeableTokens.length - 1; i >= 0; i--) {
var mergeableToken = mergeableTokens[i];
- for (j = mergeableToken.body.length - 1; j >= 0; j--) {
+ for (j = mergeableToken[2].length - 1; j >= 0; j--) {
for (k = 0, m = properties.length; k < m; k++) {
var property = properties[k];
- if (mergeableToken.body[j].value === property[3]) {
- bodyMetadata[property[3]] = mergeableToken.body[j].metadata;
-
- mergeableToken.body.splice(j, 1);
- mergeableToken.metadata.bodiesList.splice(j, 1);
- mergeableToken.metadata.body = mergeableToken.metadata.bodiesList.join(';');
+ if (mergeableToken[2][j][0] === property[3][0]) {
+ mergeableToken[2].splice(j, 1);
break;
}
}
}
}
- var newToken = { kind: 'selector', metadata: {} };
- var allBodies = { tokenized: [], list: [] };
-
for (i = properties.length - 1; i >= 0; i--) {
- allBodies.tokenized.push({ value: properties[i][3] });
- allBodies.list.push(properties[i][3]);
- }
-
- changeSelectorOf(newToken, allSelectors);
- changeBodyOf(newToken, allBodies);
-
- for (i = properties.length - 1; i >= 0; i--) {
- newToken.body[i].metadata = bodyMetadata[properties[i][3]];
+ allProperties.push(properties[i][3]);
}
+ var newToken = ['selector', allSelectors, allProperties];
tokens.splice(position, 0, newToken);
}
function dropPropertiesAt(position, movedProperty) {
- var key = movedProperty[3];
+ var key = movedProperty[3][0];
if (movableTokens[key] && movableTokens[key].length > 1)
shortenIfPossible(position, movedProperty);
for (var i = propertiesAndMergableTokens.length - 1; i >= 0; i--) {
property = propertiesAndMergableTokens[i][0];
- var fullValue = property[3];
+ var fullValue = property[3][0];
valueSize += fullValue.length + (i > 0 ? 1 : 0);
properties.push(property);
var allSelectors = [];
var qualifiedTokens = [];
for (i = bestFit[0].length - 1; i >= 0; i--) {
- allSelectors = bestFit[0][i].value.concat(allSelectors);
+ allSelectors = bestFit[0][i][1].concat(allSelectors);
qualifiedTokens.unshift(bestFit[0][i]);
}
var isSelector;
var j, k, m;
- if (token.kind == 'selector') {
+ if (token[0] == 'selector') {
isSelector = true;
- } else if (token.kind == 'block' && !token.isFlatBlock) {
+ } else if (token[0] == 'block') {
isSelector = false;
} else {
continue;
if (movedToBeDropped.indexOf(k) == -1 && !canReorderSingle(property, movedProperty)) {
dropPropertiesAt(i + 1, movedProperty);
movedToBeDropped.push(k);
- delete movableTokens[movedProperty[3]];
+ delete movableTokens[movedProperty[3][0]];
}
if (!movedSameProperty)
if (!isSelector || unmovableInCurrentToken.indexOf(j) > -1)
continue;
- var key = property[3];
+ var key = property[3][0];
movableTokens[key] = movableTokens[key] || [];
movableTokens[key].push(token);
}
}
- var position = tokens[0] && tokens[0].kind == 'at-rule' && tokens[0].value.indexOf('@charset') === 0 ? 1 : 0;
+ var position = tokens[0] && tokens[0][0] == 'at-rule' && tokens[0][1][0].indexOf('@charset') === 0 ? 1 : 0;
for (; position < tokens.length - 1; position++) {
- var isImportRule = tokens[position].kind === 'at-rule' && tokens[position].value.indexOf('@import') === 0;
- var isEscapedCommentSpecial = tokens[position].kind === 'text' && tokens[position].value.indexOf('__ESCAPED_COMMENT_SPECIAL') === 0;
+ var isImportRule = tokens[position][0] === 'at-rule' && tokens[position][1][0].indexOf('@import') === 0;
+ var isEscapedCommentSpecial = tokens[position][0] === 'text' && tokens[position][1][0].indexOf('__ESCAPED_COMMENT_SPECIAL') === 0;
if (!(isImportRule || isEscapedCommentSpecial))
break;
}
for (var i = tokens.length - 1; i >= 0; i--) {
var token = tokens[i];
- if (token.kind != 'block' || token.isFlatBlock === true)
+ if (token[0] != 'block')
continue;
- var candidate = candidates[token.value];
+ var candidate = candidates[token[1][0]];
if (!candidate) {
candidate = [];
- candidates[token.value] = candidate;
+ candidates[token[1][0]] = candidate;
}
candidate.push(i);
continue positionLoop;
}
- target.body = source.body.concat(target.body);
- source.body = [];
+ target[2] = source[2].concat(target[2]);
+ source[2] = [];
reduced.push(target);
}
return reduced;
};
+AdvancedOptimizer.prototype.removeEmpty = function (tokens) {
+ for (var i = 0, l = tokens.length; i < l; i++) {
+ var token = tokens[i];
+
+ if (token[0] == 'selector' && (token[1].length === 0 || token[2].length === 0)) {
+ tokens.splice(i, 1);
+ i--;
+ l--;
+ } else if (token[1] == 'block') {
+ this.removeEmpty(token[2]);
+ }
+ }
+};
+
function optimizeProperties(tokens, propertyOptimizer) {
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
- if (token.kind == 'selector') {
- changeBodyOf(
- token,
- propertyOptimizer.process(token.value, token.body, false, true)
- );
- } else if (token.kind == 'block') {
- optimizeProperties(token.body, propertyOptimizer);
+ switch (token[0]) {
+ case 'selector':
+ token[2] = propertyOptimizer.process(token[1], token[2], false, true);
+ break;
+ case 'block':
+ optimizeProperties(token[2], propertyOptimizer);
}
}
}
function _optimize(tokens, withRestructuring) {
tokens.forEach(function (token) {
- if (token.kind == 'block') {
- var isKeyframes = /@(-moz-|-o-|-webkit-)?keyframes/.test(token.value);
- _optimize(token.body, !isKeyframes);
+ if (token[0] == 'block') {
+ var isKeyframes = /@(-moz-|-o-|-webkit-)?keyframes/.test(token[1][0]);
+ _optimize(token[2], !isKeyframes);
}
});
if (self.options.mediaMerging) {
var reduced = self.mergeMediaQueries(tokens);
for (var i = reduced.length - 1; i >= 0; i--) {
- _optimize(reduced[i].body);
+ _optimize(reduced[i][2]);
}
}
+
+ self.removeEmpty(tokens);
}
_optimize(tokens, true);
}
function selectorSorter(s1, s2) {
- return s1.value > s2.value ? 1 : -1;
+ return s1[0] > s2[0] ? 1 : -1;
}
var CleanUp = {
selectors: function (selectors, removeUnsupported, adjacentSpace) {
- var plain = [];
- var tokenized = [];
+ var list = [];
+ var repeated = [];
for (var i = 0, l = selectors.length; i < l; i++) {
var selector = selectors[i];
- var reduced = selector.value
+ var reduced = selector[0]
.replace(/\s+/g, ' ')
.replace(/ ?, ?/g, ',')
.replace(/\s*([>\+\~])\s*/g, '$1')
if (reduced.indexOf('[') > -1)
reduced = reduced.replace(/\[([^\]]+)\]/g, removeWhitespace);
- if (plain.indexOf(reduced) == -1) {
- plain.push(reduced);
- selector.value = reduced;
- tokenized.push(selector);
+ if (repeated.indexOf(reduced) == -1) {
+ selector[0] = reduced;
+ repeated.push(reduced);
+ list.push(selector);
}
}
- return {
- list: plain.sort(),
- tokenized: tokenized.sort(selectorSorter)
- };
+ return list.sort(selectorSorter);
},
selectorDuplicates: function (selectors) {
- var plain = [];
- var tokenized = [];
+ var list = [];
+ var repeated = [];
for (var i = 0, l = selectors.length; i < l; i++) {
var selector = selectors[i];
- if (plain.indexOf(selector.value) == -1) {
- plain.push(selector.value);
- tokenized.push(selector);
+ if (repeated.indexOf(selector[0]) == -1) {
+ repeated.push(selector[0]);
+ list.push(selector);
}
}
- return {
- list: plain.sort(),
- tokenized: tokenized.sort(selectorSorter)
- };
+ return list.sort(selectorSorter);
},
- block: function (block) {
- return block
+ block: function (values) {
+ values[0] = values[0]
.replace(/\s+/g, ' ')
.replace(/(,|:|\() /g, '$1')
.replace(/ ?\) ?/g, ')');
},
- atRule: function (block) {
- return block
+ atRule: function (values) {
+ values[0] = values[0]
.replace(/\s+/g, ' ')
.trim();
}
options.roundingPrecision;
options.precision.multiplier = Math.pow(10, options.precision.value);
options.precision.regexp = new RegExp('(\\d*\\.\\d{' + (options.precision.value + 1) + ',})px', 'g');
-
- options.updateMetadata = this.options.advanced;
}
var valueMinifiers = {
return value.replace(unitsRegexp, '$1' + '0');
}
-function multipleZerosMinifier(property, value) {
+function multipleZerosMinifier(name, value) {
if (value.indexOf('0 0 0 0') == -1)
return value;
- if (property.indexOf('box-shadow') > -1)
+ if (name.indexOf('box-shadow') > -1)
return value == '0 0 0 0' ? '0 0' : value;
return value.replace(/^0 0 0 0$/, '0');
}
-function colorMininifier(property, value, compatibility) {
+function colorMininifier(_, value, compatibility) {
if (value.indexOf('#') === -1 && value.indexOf('rgb') == -1 && value.indexOf('hsl') == -1)
return HexNameShortener.shorten(value);
return HexNameShortener.shorten(value);
}
-function spaceMinifier(property, value) {
- if (property == 'filter' || value.indexOf(') ') == -1 || processable.implementedFor.test(property))
+function spaceMinifier(name, value) {
+ if (name == 'filter' || value.indexOf(') ') == -1 || processable.implementedFor.test(name))
return value;
return value.replace(/\) ((?![\+\-] )|$)/g, ')$1');
}
-function reduce(body, options) {
- var reduced = [];
- var properties = [];
- var newProperty;
+function optimizeBody(properties, options) {
+ var property, firstColon, name, value, important;
- for (var i = 0, l = body.length; i < l; i++) {
- var token = body[i];
+ for (var i = 0, l = properties.length; i < l; i++) {
+ property = properties[i];
// FIXME: the check should be gone with #407
- if (token.value.indexOf('__ESCAPED_') === 0) {
- reduced.push(token);
- properties.push(token.value);
+ if (property[0].indexOf('__ESCAPED_') === 0)
continue;
- }
- var firstColon = token.value.indexOf(':');
- var property = token.value.substring(0, firstColon);
- var value = token.value.substring(firstColon + 1);
- var important = false;
+ firstColon = property[0].indexOf(':');
+ name = property[0].substring(0, firstColon);
+ value = property[0].substring(firstColon + 1);
+ important = false;
- if (!options.compatibility.properties.iePrefixHack && (property[0] == '_' || property[0] == '*'))
+ if ((!options.compatibility.properties.iePrefixHack && (name[0] == '_' || name[0] == '*')) ||
+ (name.indexOf('padding') === 0 && isNegative(value))) {
+ properties.splice(i, 1);
+ i--;
+ l--;
continue;
+ }
if (value.indexOf('!important') > 0 || value.indexOf('! important') > 0) {
value = value.substring(0, value.indexOf('!')).trim();
important = true;
}
- if (property.indexOf('padding') === 0 && isNegative(value))
- continue;
-
- if (property.indexOf('border') === 0 && property.indexOf('radius') > 0)
+ if (name.indexOf('border') === 0 && name.indexOf('radius') > 0)
value = valueMinifiers['border-*-radius'](value);
- if (valueMinifiers[property])
- value = valueMinifiers[property](value);
+ if (valueMinifiers[name])
+ value = valueMinifiers[name](value);
- value = precisionMinifier(property, value, options.precision);
- value = zeroMinifier(property, value);
+ value = precisionMinifier(name, value, options.precision);
+ value = zeroMinifier(name, value);
if (options.compatibility.properties.zeroUnits) {
- value = zeroDegMinifier(property, value);
- value = unitMinifier(property, value, options.unitsRegexp);
+ value = zeroDegMinifier(name, value);
+ value = unitMinifier(name, value, options.unitsRegexp);
}
- value = multipleZerosMinifier(property, value);
- value = colorMininifier(property, value, options.compatibility);
+ value = multipleZerosMinifier(name, value);
+ value = colorMininifier(name, value, options.compatibility);
if (!options.compatibility.properties.spaceAfterClosingBrace)
- value = spaceMinifier(property, value);
+ value = spaceMinifier(name, value);
- newProperty = property + ':' + value + (important ? '!important' : '');
- reduced.push({ value: newProperty, metadata: token.metadata });
- properties.push(newProperty);
+ property[0] = name + ':' + value + (important ? '!important' : '');
}
-
- return {
- tokenized: reduced,
- list: properties
- };
}
SimpleOptimizer.prototype.optimize = function(tokens) {
var self = this;
var hasCharset = false;
var options = this.options;
+ var ie7Hack = options.compatibility.selectors.ie7Hack;
+ var adjacentSpace = options.compatibility.selectors.adjacentSpace;
+ var token;
- function _optimize(tokens) {
+ function _cleanupCharsets(tokens) {
for (var i = 0, l = tokens.length; i < l; i++) {
- var token = tokens[i];
- // FIXME: why it's so?
- if (!token)
- break;
+ token = tokens[i];
- if (token.kind == 'selector') {
- var newSelectors = CleanUp.selectors(token.value, !options.compatibility.selectors.ie7Hack, options.compatibility.selectors.adjacentSpace);
- token.value = newSelectors.tokenized;
+ if (token[0] != 'at-rule')
+ continue;
- if (token.value.length === 0) {
+ if (CHARSET_REGEXP.test(token[1][0])) {
+ if (hasCharset || token[1][0].indexOf(CHARSET_TOKEN) == -1) {
tokens.splice(i, 1);
i--;
- continue;
- }
- var newBody = reduce(token.body, self.options);
- token.body = newBody.tokenized;
-
- if (options.updateMetadata) {
- token.metadata.body = newBody.list.join(';');
- token.metadata.bodiesList = newBody.list;
- token.metadata.selector = newSelectors.list.join(',');
- token.metadata.selectorsList = newSelectors.list;
- }
- } else if (token.kind == 'block') {
- token.value = CleanUp.block(token.value);
- if (token.isFlatBlock)
- token.body = reduce(token.body, self.options).tokenized;
- else
- _optimize(token.body);
- } else if (token.kind == 'at-rule') {
- token.value = CleanUp.atRule(token.value);
-
- if (CHARSET_REGEXP.test(token.value)) {
- if (hasCharset || token.value.indexOf(CHARSET_TOKEN) == -1) {
- tokens.splice(i, 1);
- i--;
- } else {
- hasCharset = true;
- tokens.splice(i, 1);
- tokens.unshift({ kind: 'at-rule', value: token.value.replace(CHARSET_REGEXP, CHARSET_TOKEN) });
- }
+ l--;
+ } else {
+ hasCharset = true;
+ tokens.splice(i, 1);
+ tokens.unshift(['at-rule', [token[1][0].replace(CHARSET_REGEXP, CHARSET_TOKEN)]]);
}
}
}
}
+ function _optimize(tokens) {
+ var mayHaveCharset = false;
+
+ for (var i = 0, l = tokens.length; i < l; i++) {
+ token = tokens[i];
+
+ switch (token[0]) {
+ case 'selector':
+ token[1] = CleanUp.selectors(token[1], !ie7Hack, adjacentSpace);
+ optimizeBody(token[2], self.options);
+ break;
+ case 'block':
+ CleanUp.block(token[1]);
+ _optimize(token[2]);
+ break;
+ case 'flat-block':
+ CleanUp.block(token[1]);
+ optimizeBody(token[2], self.options);
+ break;
+ case 'at-rule':
+ CleanUp.atRule(token[1]);
+ mayHaveCharset = true;
+ }
+
+ if (token[1].length === 0 || (token[2] && token[2].length === 0)) {
+ tokens.splice(i, 1);
+ i--;
+ l--;
+ }
+ }
+
+ if (mayHaveCharset)
+ _cleanupCharsets(tokens);
+ }
+
_optimize(tokens);
};
var SourceMapGenerator = require('source-map').SourceMapGenerator;
-var SourceMap = require('../utils/source-maps');
var lineBreak = require('os').EOL;
+var unknownSource = '$stdin';
function Rebuilder(options, restoreCallback, inputMapTracker) {
this.column = 0;
this.outputMap = new SourceMapGenerator();
}
-Rebuilder.prototype.rebuildValue = function (list, separator) {
+Rebuilder.prototype.rebuildValue = function (elements, separator) {
var escaped = 0;
- for (var i = 0, l = list.length; i < l; i++) {
- var el = list[i];
+ for (var i = 0, l = elements.length; i < l; i++) {
+ var element = elements[i];
- if (el.value.indexOf('__ESCAPED_') === 0) {
- this.store(el);
+ if (element[0].indexOf('__ESCAPED_') === 0) {
+ this.store(element[0]);
escaped++;
if (i === l - 1 && escaped > 0)
this.output.splice(this.output.length - escaped - 1, 1);
} else {
- this.store(el);
+ this.store(element);
this.store(i < l - 1 ? separator : '');
escaped = 0;
}
}
};
-Rebuilder.prototype.store = function (token) {
- var value = typeof token == 'string' ?
- token :
- token.value.indexOf('_') > -1 ? this.restore(token.value) : token.value;
+Rebuilder.prototype.store = function (element) {
+ var fromString = typeof element == 'string';
+ var value = fromString ? element : element[0];
- this.track(value, token.metadata);
+ if (value.indexOf('_') > -1)
+ value = this.restore(value);
+
+ this.track(value, fromString ? null : element);
this.output.push(value);
};
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
- if (token.kind === 'text' || token.kind == 'at-rule') {
- this.store(token);
- continue;
- }
-
- // FIXME: broken due to joining/splitting
- if (token.body && (token.body.length === 0 || (token.body.length == 1 && token.body[0].value === '')))
- continue;
-
- if (token.kind == 'block') {
- if (token.body.length > 0) {
- this.rebuildValue([{ value: token.value, metadata: token.metadata }], '');
+ switch (token[0]) {
+ case 'at-rule':
+ case 'text':
+ this.store(token[1][0]);
+ break;
+ case 'block':
+ this.rebuildValue([token[1]], '');
this.store('{');
- if (token.isFlatBlock)
- this.rebuildValue(token.body, ';');
- else
- this.rebuildList(token.body, false);
+ this.rebuildList(token[2], false);
this.store('}');
- }
- } else {
- this.rebuildValue(token.value, ',');
- this.store('{');
- this.rebuildValue(token.body, ';');
- this.store('}');
+ this.store(joinCharacter);
+ break;
+ case 'flat-block':
+ this.rebuildValue([token[1]], '');
+ this.store('{');
+ this.rebuildValue(token[2], ';');
+ this.store('}');
+ this.store(joinCharacter);
+ break;
+ default:
+ this.rebuildValue(token[1], ',');
+ this.store('{');
+ this.rebuildValue(token[2], ';');
+ this.store('}');
+ this.store(joinCharacter);
}
-
- this.store(joinCharacter);
}
};
-Rebuilder.prototype.track = function (value, metadata) {
- if (metadata)
- this.trackMetadata(metadata);
+Rebuilder.prototype.track = function (value, element) {
+ if (element)
+ this.trackMetadata(element);
var parts = value.split('\n');
this.line += parts.length - 1;
this.column = parts.length > 1 ? 0 : (this.column + parts.pop().length);
};
-Rebuilder.prototype.trackMetadata = function (metadata) {
- var source = metadata.source || SourceMap.unknownSource;
+Rebuilder.prototype.trackMetadata = function (element) {
+ var source = element[3] || unknownSource;
this.outputMap.addMapping({
generated: {
column: this.column
},
source: source,
- original: metadata.original
+ original: {
+ line: element[1],
+ column: element[2]
+ }
});
- if (metadata.sourcesContent)
- this.outputMap.setSourceContent(source, metadata.sourcesContent[metadata.source]);
+ if (element[4])
+ this.outputMap.setSourceContent(source, element[4][element[3]]);
};
function SourceMapStringifier(options, restoreCallback, inputMapTracker) {
this.restoreCallback = restoreCallback;
}
-function valueRebuilder(list, separator) {
+function valueRebuilder(elements, separator) {
var merged = '';
+ var element;
- for (var i = 0, l = list.length; i < l; i++) {
- var el = list[i];
+ for (var i = 0, l = elements.length; i < l; i++) {
+ element = elements[i];
- if (el.value.indexOf('__ESCAPED_') === 0) {
- merged += el.value;
+ if (element[0].indexOf('__ESCAPED_') === 0) {
+ merged += element[0];
if (i === l - 1) {
var lastSemicolonAt = merged.lastIndexOf(';');
merged = merged.substring(0, lastSemicolonAt) + merged.substring(lastSemicolonAt + 1);
}
} else {
- merged += list[i].value + (i < l - 1 ? separator : '');
+ merged += element[0] + (i < l - 1 ? separator : '');
}
}
for (var i = 0, l = tokens.length; i < l; i++) {
var token = tokens[i];
- if (token.kind === 'text' || token.kind == 'at-rule') {
- parts.push(token.value);
- continue;
- }
-
- // FIXME: broken due to joining/splitting
- if (token.body && (token.body.length === 0 || (token.body.length == 1 && token.body[0].value === '')))
- continue;
-
- if (token.kind == 'block') {
- body = token.isFlatBlock ?
- valueRebuilder(token.body, ';') :
- rebuild(token.body, keepBreaks, token.isFlatBlock);
- if (body.length > 0)
- parts.push(token.value + '{' + body + '}');
- } else {
- selector = valueRebuilder(token.value, ',');
- body = valueRebuilder(token.body, ';');
- parts.push(selector + '{' + body + '}');
+ switch (token[0]) {
+ case 'at-rule':
+ case 'text':
+ parts.push(token[1][0]);
+ break;
+ case 'block':
+ body = rebuild(token[2], keepBreaks, false);
+ if (body.length > 0)
+ parts.push(token[1][0] + '{' + body + '}');
+ break;
+ case 'flat-block':
+ body = valueRebuilder(token[2], ';');
+ if (body.length > 0)
+ parts.push(token[1][0] + '{' + body + '}');
+ break;
+ default:
+ selector = valueRebuilder(token[1], ',');
+ body = valueRebuilder(token[2], ';');
+ parts.push(selector + '{' + body + '}');
}
}
var Chunker = require('../utils/chunker');
var Extract = require('../utils/extractors');
-var SourceMaps = require('../utils/source-maps');
+var track = require('../utils/source-maps');
var path = require('path');
var flatBlock = /(^@(font\-face|page|\-ms\-viewport|\-o\-viewport|viewport|counter\-style)|\\@.+?)/;
-function Tokenizer(minifyContext, addMetadata, addSourceMap) {
+function Tokenizer(minifyContext, sourceMaps) {
this.minifyContext = minifyContext;
- this.addMetadata = addMetadata;
- this.addSourceMap = addSourceMap;
+ this.sourceMaps = sourceMaps;
}
Tokenizer.prototype.toTokens = function (data) {
chunker: chunker,
chunk: chunker.next(),
outer: this.minifyContext,
- addMetadata: this.addMetadata,
- addSourceMap: this.addSourceMap,
+ track: this.sourceMaps ?
+ function (data, snapshotMetadata, fallbacks) { return track(data, context, snapshotMetadata, fallbacks); } :
+ function () { return []; },
+ sourceMaps: this.sourceMaps,
state: [],
line: 1,
column: 0,
var tokenized = [];
var newToken;
var value;
- var addSourceMap = context.addSourceMap;
while (true) {
var next = whatsNext(context);
if (context.mode == 'body') {
context.outer.warnings.push('Missing \'}\' after \'' + whatsLeft + '\'. Ignoring.');
} else {
- tokenized.push({ kind: 'text', value: whatsLeft });
+ tokenized.push(['text', [whatsLeft]]);
}
context.cursor += whatsLeft.length;
}
if (leadingWhitespace) {
context.cursor += leadingWhitespace[0].length;
-
- if (addSourceMap)
- SourceMaps.track(leadingWhitespace[0], context);
+ context.track(leadingWhitespace[0]);
}
}
context.cursor = chunk.length;
} else if (isSingle) {
nextEnd = chunk.indexOf(';', nextSpecial + 1);
-
value = chunk.substring(context.cursor, nextEnd + 1);
- newToken = { kind: 'at-rule', value: value };
- tokenized.push(newToken);
- if (addSourceMap)
- newToken.metadata = SourceMaps.saveAndTrack(value, context, true);
+ tokenized.push([
+ 'at-rule',
+ [value].concat(context.track(value, true))
+ ]);
+ context.track(';');
context.cursor = nextEnd + 1;
} else {
nextEnd = chunk.indexOf('{', nextSpecial + 1);
context.cursor = nextEnd + 1;
context.mode = isFlat ? 'body' : 'block';
- newToken = { kind: 'block', value: trimmedValue, isFlatBlock: isFlat };
+ newToken = [
+ isFlat ? 'flat-block' : 'block'
+ ];
- if (addSourceMap)
- newToken.metadata = SourceMaps.saveAndTrack(value, context, true);
+ newToken.push([trimmedValue].concat(context.track(value, true)));
+ context.track('{');
+ newToken.push(tokenize(context));
- newToken.body = tokenize(context);
- if (typeof newToken.body == 'string')
- newToken.body = Extract.properties(newToken.body, context).tokenized;
+ if (typeof newToken[2] == 'string')
+ newToken[2] = Extract.properties(newToken[2], context);
context.mode = oldMode;
-
- if (addSourceMap)
- SourceMaps.suffix(context);
+ context.track('}');
tokenized.push(newToken);
}
var isEndSourceMarker = !!context.outer.sourceTracker.nextEnd(escaped);
if (isStartSourceMarker) {
- if (addSourceMap)
- SourceMaps.track(escaped, context);
-
+ context.track(escaped);
context.state.push({
source: context.source,
line: context.line,
context.source = oldState.source;
context.line = oldState.line;
context.column = oldState.column;
-
- if (addSourceMap)
- SourceMaps.track(escaped, context);
+ context.track(escaped);
} else {
if (escaped.indexOf('__ESCAPED_COMMENT_SPECIAL') === 0)
- tokenized.push({ kind: 'text', value: escaped });
+ tokenized.push(['text', [escaped]]);
- if (addSourceMap)
- SourceMaps.track(escaped, context);
+ context.track(escaped);
}
context.cursor = nextEnd + 2;
} else if (what == 'bodyStart') {
- var selectorData = Extract.selectors(chunk.substring(context.cursor, nextSpecial), context);
+ var selectors = Extract.selectors(chunk.substring(context.cursor, nextSpecial), context);
oldMode = context.mode;
context.cursor = nextSpecial + 1;
context.mode = 'body';
- var bodyData = Extract.properties(tokenize(context), context);
-
- if (addSourceMap)
- SourceMaps.suffix(context);
+ var body = Extract.properties(tokenize(context), context);
+ context.track('{');
context.mode = oldMode;
- newToken = {
- kind: 'selector',
- value: selectorData.tokenized,
- body: bodyData.tokenized
- };
- if (context.addMetadata) {
- newToken.metadata = {
- body: bodyData.list.join(','),
- bodiesList: bodyData.list,
- selector: selectorData.list.join(','),
- selectorsList: selectorData.list
- };
- }
- tokenized.push(newToken);
+ tokenized.push([
+ 'selector',
+ selectors,
+ body
+ ]);
} else if (what == 'bodyEnd') {
// extra closing brace at the top level can be safely ignored
if (context.mode == 'top') {
continue;
}
- if (context.mode == 'block' && context.addSourceMap)
- SourceMaps.track(chunk.substring(context.cursor, nextSpecial), context);
+ if (context.mode == 'block')
+ context.track(chunk.substring(context.cursor, nextSpecial));
if (context.mode != 'block')
tokenized = chunk.substring(context.cursor, nextSpecial);
var Splitter = require('./splitter');
-var SourceMaps = require('../utils/source-maps');
var Extractors = {
properties: function (string, context) {
- var tokenized = [];
var list = [];
var buffer = [];
var all = [];
var secondToLast;
var wasCloseParenthesis;
var isEscape;
- var token;
- var addSourceMap = context.addSourceMap;
+ var metadata;
if (string.replace && string.indexOf(')') > 0)
- string = string.replace(/\)([^\s_;:,\)])/g, context.addSourceMap ? ') __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__$1' : ') $1');
+ string = string.replace(/\)([^\s_;:,\)])/g, context.sourceMaps ? ') __ESCAPED_COMMENT_CLEAN_CSS(0,-1)__$1' : ') $1');
for (var i = 0, l = string.length; i < l; i++) {
current = string[i];
i = endOfEscape - 1;
if (comment.indexOf('__ESCAPED_COMMENT_SPECIAL') === -1) {
- if (addSourceMap)
- SourceMaps.track(comment, context, true);
+ context.track(comment);
continue;
}
else {
if (buffer.length > 0) {
property = buffer.join('');
if (property.indexOf('{') === -1) {
- token = { value: property };
- tokenized.push(token);
- list.push(property);
+ metadata = context.track(all.join(''), true);
+ list.push([property].concat(metadata));
- if (addSourceMap)
- token.metadata = SourceMaps.saveAndTrack(all.join(''), context, !isEscape);
+ if (!isEscape)
+ context.track(';');
}
}
buffer = [];
if (buffer.length > 0) {
property = buffer.join('');
if (property.indexOf('{') === -1) {
- token = { value: property };
- tokenized.push(token);
- list.push(property);
-
- if (addSourceMap)
- token.metadata = SourceMaps.saveAndTrack(all.join(''), context, false);
+ metadata = context.track(all.join(''), true);
+ list.push([property].concat(metadata));
}
} else if (all.indexOf('\n') > -1) {
- SourceMaps.track(all.join(''), context);
+ context.track(all.join(''));
}
- return {
- list: list,
- tokenized: tokenized
- };
+ return list;
},
selectors: function (string, context) {
- var tokenized = [];
var list = [];
+ var metadata;
var selectors = new Splitter(',').split(string);
- var addSourceMap = context.addSourceMap;
- for (var i = 0, l = selectors.length; i < l; i++) {
- var selector = selectors[i];
-
- list.push(selector);
- var token = { value: selector };
- tokenized.push(token);
-
- if (addSourceMap)
- token.metadata = SourceMaps.saveAndTrack(selector, context, true, i);
+ for (var i = 0, l = selectors.length; i < l; i++) {
+ metadata = context.track(selectors[i], true, i);
+ context.track(',');
+ list.push([selectors[i]].concat(metadata));
}
- return {
- list: list,
- tokenized: tokenized
- };
+ return list;
}
};
.setTimeout(self.timeout);
}
-function originalPositionIn(trackedSource, sourceInfo, token, allowNFallbacks) {
+function originalPositionIn(trackedSource, line, column, token, allowNFallbacks) {
var originalPosition;
var maxRange = token.length;
var position = {
- line: sourceInfo.line,
- column: sourceInfo.column + maxRange
+ line: line,
+ column: column + maxRange
};
while (maxRange-- > 0) {
break;
}
- if (originalPosition.line === null && sourceInfo.line > 1 && allowNFallbacks > 0)
- return originalPositionIn(trackedSource, { line: sourceInfo.line - 1, column: sourceInfo.column }, token, allowNFallbacks - 1);
+ if (originalPosition.line === null && line > 1 && allowNFallbacks > 0)
+ return originalPositionIn(trackedSource, line - 1, column, token, allowNFallbacks - 1);
if (trackedSource.path) {
originalPosition.source = REMOTE_RESOURCE.test(trackedSource.path) ?
};
InputSourceMapStore.prototype.originalPositionFor = function (sourceInfo, token, allowNFallbacks) {
- return originalPositionIn(this.maps[sourceInfo.source], sourceInfo.original, token, allowNFallbacks);
+ return originalPositionIn(this.maps[sourceInfo.source], sourceInfo.line, sourceInfo.column, token, allowNFallbacks);
};
InputSourceMapStore.prototype.sourcesContentFor = function (contextSource) {
-function trimLeft(value, context) {
- var withoutContent;
- var total;
+var escapePrefix = '__ESCAPED_';
+
+function trackPrefix(value, context, interestingContent) {
+ if (!interestingContent && value.indexOf('\n') == -1) {
+ if (value.indexOf(escapePrefix) === 0) {
+ return value;
+ } else {
+ context.column += value.length;
+ return;
+ }
+ }
+
+ var withoutContent = 0;
var split = value.split('\n');
+ var total = split.length;
var shift = 0;
- for (withoutContent = 0, total = split.length; withoutContent < total; withoutContent++) {
+
+ while (true) {
+ if (withoutContent == total - 1)
+ break;
+
var part = split[withoutContent];
if (/\S/.test(part))
break;
shift += part.length + 1;
+ withoutContent++;
}
context.line += withoutContent;
return source;
}
-var SourceMaps = {
- unknownSource: '$stdin',
-
- saveAndTrack: function (data, context, hasSuffix, allowNFallbacks) {
- var trimmedValue = trimLeft(data, context);
-
- var contextMetadata = {
- original: {
- line: context.line,
- column: context.column
- },
- source: context.source
- };
- var sourceMetadata = context.outer.inputSourceMapTracker.isTracking(contextMetadata.source) ?
- context.outer.inputSourceMapTracker.originalPositionFor(contextMetadata, trimmedValue, allowNFallbacks || 0) :
- {};
-
- contextMetadata.original.line = sourceMetadata.line || contextMetadata.original.line;
- contextMetadata.original.column = sourceMetadata.column || contextMetadata.original.column;
- contextMetadata.source = sourceMetadata.sourceResolved ?
- sourceMetadata.source :
- sourceFor(sourceMetadata, contextMetadata, context);
-
- if (context.outer.options.sourceMapInlineSources) {
- var sourceMapSourcesContent = context.outer.inputSourceMapTracker.sourcesContentFor(context.source);
- var source = sourceMapSourcesContent && sourceMapSourcesContent[contextMetadata.source] ?
- sourceMapSourcesContent :
- context.outer.sourceReader.sourceAt(context.source);
-
- if (source)
- contextMetadata.sourcesContent = source;
- }
-
- this.track(trimmedValue, context);
+function snapshot(data, context, fallbacks) {
+ var metadata = {
+ line: context.line,
+ column: context.column,
+ source: context.source
+ };
+ var sourceContent = null;
+ var sourceMetadata = context.outer.inputSourceMapTracker.isTracking(metadata.source) ?
+ context.outer.inputSourceMapTracker.originalPositionFor(metadata, data, fallbacks || 0) :
+ {};
+
+ metadata.line = sourceMetadata.line || metadata.line;
+ metadata.column = sourceMetadata.column || metadata.column;
+ metadata.source = sourceMetadata.sourceResolved ?
+ sourceMetadata.source :
+ sourceFor(sourceMetadata, metadata, context);
+
+ if (context.outer.options.sourceMapInlineSources) {
+ var sourceMapSourcesContent = context.outer.inputSourceMapTracker.sourcesContentFor(context.source);
+ sourceContent = sourceMapSourcesContent && sourceMapSourcesContent[metadata.source] ?
+ sourceMapSourcesContent :
+ context.outer.sourceReader.sourceAt(context.source);
+ }
- if (hasSuffix)
- context.column++;
+ return sourceContent ?
+ [metadata.line, metadata.column, metadata.source, sourceContent] :
+ [metadata.line, metadata.column, metadata.source];
+}
- return contextMetadata;
- },
+function trackSuffix(data, context) {
+ var parts = data.split('\n');
- suffix: function (context) {
- context.column++;
- },
+ for (var i = 0, l = parts.length; i < l; i++) {
+ var part = parts[i];
+ var cursor = 0;
- track: function (data, context) {
- var parts = data.split('\n');
+ if (i > 0) {
+ context.line++;
+ context.column = 0;
+ }
- for (var i = 0, l = parts.length; i < l; i++) {
- var part = parts[i];
- var cursor = 0;
+ while (true) {
+ var next = part.indexOf(escapePrefix, cursor);
- if (i > 0) {
- context.line++;
- context.column = 0;
+ if (next == -1) {
+ context.column += part.substring(cursor).length;
+ break;
}
- while (true) {
- var next = part.indexOf('__ESCAPED_', cursor);
-
- if (next == -1) {
- context.column += part.substring(cursor).length;
- break;
- }
-
- context.column += next - cursor;
- cursor += next - cursor;
+ context.column += next - cursor;
+ cursor += next - cursor;
- var escaped = part.substring(next, part.indexOf('__', next + 1) + 2);
- var encodedValues = escaped.substring(escaped.indexOf('(') + 1, escaped.indexOf(')')).split(',');
- context.line += ~~encodedValues[0];
- context.column = (~~encodedValues[0] === 0 ? context.column : 0) + ~~encodedValues[1];
- cursor += escaped.length;
- }
+ var escaped = part.substring(next, part.indexOf('__', next + 1) + 2);
+ var encodedValues = escaped.substring(escaped.indexOf('(') + 1, escaped.indexOf(')')).split(',');
+ context.line += ~~encodedValues[0];
+ context.column = (~~encodedValues[0] === 0 ? context.column : 0) + ~~encodedValues[1];
+ cursor += escaped.length;
}
}
-};
+}
+
+function track(data, context, snapshotMetadata, fallbacks) {
+ var untracked = trackPrefix(data, context, snapshotMetadata);
+ var metadata = snapshotMetadata ?
+ snapshot(untracked, context, fallbacks) :
+ [];
+
+ if (untracked)
+ trackSuffix(untracked, context);
+
+ return metadata;
+}
-module.exports = SourceMaps;
+module.exports = track;
--- /dev/null
+function stringify(values, separator) {
+ var i = 0;
+ var result = [];
+
+ while (values[i]) {
+ result.push(values[i][0]);
+ i++;
+ }
+
+ return result.join(separator);
+}
+
+function stringifyBody(properties) {
+ return stringify(properties, ';');
+}
+
+function stringifySelector(list) {
+ return stringify(list, ',');
+}
+
+module.exports = {
+ body: stringifyBody,
+ selector: stringifySelector
+};
var extractor = require('../../lib/properties/extractor');
function buildToken(source) {
- return new SelectorTokenizer({ options: {} }, true, false).toTokens(source)[0];
+ return new SelectorTokenizer({ options: {} }, false).toTokens(source)[0];
}
vows.describe(extractor)
'one property': {
'topic': extractor(buildToken('a{color:red}')),
'has no properties': function (tokens) {
- assert.deepEqual(tokens, [['color', 'red', 'color', 'color:red', ['a'], true]]);
+ assert.deepEqual(tokens, [['color', 'red', 'color', ['color:red'], [['a']], true]]);
}
},
'one property - complex selector': {
'topic': extractor(buildToken('.one{color:red}')),
'has no properties': function (tokens) {
- assert.deepEqual(tokens, [['color', 'red', 'color', 'color:red', ['.one'], false]]);
+ assert.deepEqual(tokens, [['color', 'red', 'color', ['color:red'], [['.one']], false]]);
}
},
'two properties': {
'topic': extractor(buildToken('a{color:red;display:block}')),
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
- ['color', 'red', 'color', 'color:red', ['a'], true],
- ['display', 'block', 'display', 'display:block', ['a'], true]
+ ['color', 'red', 'color', ['color:red'], [['a']], true],
+ ['display', 'block', 'display', ['display:block'], [['a']], true]
]);
}
},
'topic': extractor(buildToken('@media{a{color:red;display:block}p{color:red}}')),
'has no properties': function (tokens) {
assert.deepEqual(tokens, [
- ['color', 'red', 'color', 'color:red', ['a'], true],
- ['display', 'block', 'display', 'display:block', ['a'], true],
- ['color', 'red', 'color', 'color:red', ['p'], true]
+ ['color', 'red', 'color', ['color:red'], [['a']], true],
+ ['display', 'block', 'display', ['display:block'], [['a']], true],
+ ['color', 'red', 'color', ['color:red'], [['p']], true]
]);
}
}
'vendor prefix': {
'topic': extractor(buildToken('a{-moz-transform:none}')),
'has no properties': function (tokens) {
- assert.deepEqual(tokens, [['-moz-transform', 'none', 'transform', '-moz-transform:none', ['a'], true]]);
+ assert.deepEqual(tokens, [['-moz-transform', 'none', 'transform', ['-moz-transform:none'], [['a']], true]]);
}
},
'list-style': {
'topic': extractor(buildToken('a{list-style:none}')),
'has no properties': function (tokens) {
- assert.deepEqual(tokens, [['list-style', 'none', 'list-style', 'list-style:none', ['a'], true]]);
+ assert.deepEqual(tokens, [['list-style', 'none', 'list-style', ['list-style:none'], [['a']], true]]);
}
},
'border-radius': {
'topic': extractor(buildToken('a{border-top-left-radius:none}')),
'has no properties': function (tokens) {
- assert.deepEqual(tokens, [['border-top-left-radius', 'none', 'border-radius', 'border-top-left-radius:none', ['a'], true]]);
+ assert.deepEqual(tokens, [['border-top-left-radius', 'none', 'border-radius', ['border-top-left-radius:none'], [['a']], true]]);
}
},
'vendor prefixed border-radius': {
'topic': extractor(buildToken('a{-webkit-border-top-left-radius:none}')),
'has no properties': function (tokens) {
- assert.deepEqual(tokens, [['-webkit-border-top-left-radius', 'none', 'border-radius', '-webkit-border-top-left-radius:none', ['a'], true]]);
+ assert.deepEqual(tokens, [['-webkit-border-top-left-radius', 'none', 'border-radius', ['-webkit-border-top-left-radius:none'], [['a']], true]]);
}
},
'border-image': {
'topic': extractor(buildToken('a{border-image-width:2px}')),
'has no properties': function (tokens) {
- assert.deepEqual(tokens, [['border-image-width', '2px', 'border-image', 'border-image-width:2px', ['a'], true]]);
+ assert.deepEqual(tokens, [['border-image-width', '2px', 'border-image', ['border-image-width:2px'], [['a']], true]]);
}
},
'border-top': {
'topic': extractor(buildToken('a{border-top-style:none}')),
'has no properties': function (tokens) {
- assert.deepEqual(tokens, [['border-top-style', 'none', 'border-top', 'border-top-style:none', ['a'], true]]);
+ assert.deepEqual(tokens, [['border-top-style', 'none', 'border-top', ['border-top-style:none'], [['a']], true]]);
}
},
'text-shadow': {
'topic': extractor(buildToken('a{text-shadow:none}')),
'has no properties': function (tokens) {
- assert.deepEqual(tokens, [['text-shadow', 'none', 'text-shadow', 'text-shadow:none', ['a'], true]]);
+ assert.deepEqual(tokens, [['text-shadow', 'none', 'text-shadow', ['text-shadow:none'], [['a']], true]]);
}
}
}
var canReorderSingle = require('../../lib/properties/reorderable').canReorderSingle;
function propertiesIn(source) {
- return extractProperties(new SelectorTokenizer({ options: {} }, true, false).toTokens(source)[0]);
+ return extractProperties(new SelectorTokenizer({ options: {} }, false).toTokens(source)[0]);
}
vows.describe(canReorder)
var tokens = new Tokenizer({ options: {} }).toTokens(source);
new SimpleOptimizer(options).optimize(tokens);
- assert.deepEqual(tokens[0] ? tokens[0].value : null, selectors);
+ assert.deepEqual(tokens[0] ? tokens[0][1] : null, selectors);
};
}
return function (source) {
var tokens = new Tokenizer({ options: {} }).toTokens(source);
new SimpleOptimizer(options).optimize(tokens);
- var value = tokens[0].body.map(function (property) { return property.value; });
+ var value = tokens[0] ? tokens[0][2].map(function (property) { return property[0]; }) : null;
assert.deepEqual(value, selectors);
};
selectorContext('default', {
'optimized': [
'a{}',
- [{ value: 'a' }]
+ null
],
'whitespace': [
- ' div > span{}',
- [{ value: 'div>span' }]
+ ' div > span{color:red}',
+ [['div>span']]
],
'line breaks': [
- ' div >\n\r\n span{}',
- [{ value: 'div>span' }]
+ ' div >\n\r\n span{color:red}',
+ [['div>span']]
],
'+html': [
'*+html .foo{display:inline}',
null
],
'adjacent nav': [
- 'div + nav{}',
- [{ value: 'div+nav' }]
+ 'div + nav{color:red}',
+ [['div+nav']]
]
})
)
],
'+html - complex': [
'*+html .foo,.bar{display:inline}',
- [{ value: '.bar' }]
+ [['.bar']]
]
}, { compatibility: 'ie8' })
)
selectorContext('ie7', {
'+html': [
'*+html .foo{display:inline}',
- [{ value: '*+html .foo' }]
+ [['*+html .foo']]
],
'+html - complex': [
'*+html .foo,.bar{display:inline}',
- [{ value: '*+html .foo' }, { value: '.bar' }]
+ [['*+html .foo'], ['.bar']]
]
}, { compatibility: 'ie7' })
)
.addBatch(
selectorContext('+adjacentSpace', {
'with whitespace': [
- 'div + nav{}',
- [{ value: 'div+ nav' }]
+ 'div + nav{color:red}',
+ [['div+ nav']]
],
'without whitespace': [
- 'div+nav{}',
- [{ value: 'div+ nav' }]
+ 'div+nav{color:red}',
+ [['div+ nav']]
]
}, { compatibility: { selectors: { adjacentSpace: true } } })
)
propertyContext('ie hacks', {
'underscore': [
'a{_width:100px}',
- []
+ null
],
'star': [
'a{*width:100px}',
- []
+ null
]
})
)
function sourceMapContext(group, specs) {
var ctx = {};
- function tokenizedContext(target, index) {
+ function tokenizedContext(target) {
return function (tokenized) {
- assert.deepEqual(tokenized[index], target);
+ assert.deepEqual(tokenized, target);
+ };
+ }
+
+ function toTokens(source) {
+ return function () {
+ return new Tokenizer({
+ sourceTracker: sourceTracker,
+ sourceReader: sourceReader,
+ inputSourceMapTracker: inputSourceMapTracker,
+ options: {}
+ }, true).toTokens(source);
};
}
for (var test in specs) {
- for (var i = 0; i < specs[test][1].length; i++) {
- var target = specs[test][1][i];
- var sourceTracker = new SourceTracker();
- var sourceReader = new SourceReader();
- var inputSourceMapTracker = new InputSourceMapTracker({
- options: { inliner: {} },
- errors: {},
- sourceTracker: sourceTracker
- });
+ var target = specs[test][1];
+ var sourceTracker = new SourceTracker();
+ var sourceReader = new SourceReader();
+ var inputSourceMapTracker = new InputSourceMapTracker({
+ options: { inliner: {} },
+ errors: {},
+ sourceTracker: sourceTracker
+ });
- ctx[group + ' ' + test + ' - #' + (i + 1)] = {
- topic: typeof specs[test][0] == 'function' ?
- specs[test][0]() :
- new Tokenizer({
- sourceTracker: sourceTracker,
- sourceReader: sourceReader,
- inputSourceMapTracker: inputSourceMapTracker,
- options: {}
- }, false, true).toTokens(specs[test][0]),
- tokenized: tokenizedContext(target, i)
- };
- }
+ ctx[group + ' ' + test] = {
+ topic: typeof specs[test][0] == 'function' ?
+ specs[test][0] :
+ toTokens(specs[test][0]),
+ tokenized: tokenizedContext(target)
+ };
}
return ctx;
sourceMapContext('selectors', {
'single': [
'a{}',
- [{
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: []
- }]
+ [
+ [
+ 'selector',
+ [['a', 1, 0, undefined]],
+ []
+ ]
+ ]
],
'double': [
'a,div{}',
- [{
- kind: 'selector',
- value: [
- { value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } },
- { value: 'div', metadata: { original: { line: 1, column: 2 }, source: undefined } }
- ],
- body: []
- }]
+ [
+ [
+ 'selector',
+ [
+ ['a', 1, 0, undefined],
+ ['div', 1, 2, undefined]
+ ],
+ []
+ ]
+ ]
],
'double with whitespace': [
' a,\n\ndiv{}',
- [{
- kind: 'selector',
- value: [
- { value: 'a', metadata: { original: { line: 1, column: 1 }, source: undefined } },
- { value: '\n\ndiv', metadata: { original: { line: 3, column: 0 }, source: undefined } }
- ],
- body: []
- }]
+ [
+ [
+ 'selector',
+ [['a', 1, 1, undefined], ['\n\ndiv', 3, 0, undefined]],
+ []
+ ]
+ ]
],
'triple': [
'a,div,p{}',
- [{
- kind: 'selector',
- value: [
- { value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } },
- { value: 'div', metadata: { original: { line: 1, column: 2 }, source: undefined } },
- { value: 'p', metadata: { original: { line: 1, column: 6 }, source: undefined } }
- ],
- body: []
- }]
+ [
+ [
+ 'selector',
+ [['a', 1, 0, undefined], ['div', 1, 2, undefined], ['p', 1, 6, undefined]],
+ []
+ ]
+ ]
],
'triple with whitespace': [
' a,\n\ndiv\na,\n p{}',
- [{
- kind: 'selector',
- value: [
- { value: 'a', metadata: { original: { line: 1, column: 1 }, source: undefined } },
- { value: '\n\ndiv\na', metadata: { original: { line: 3, column: 0 }, source: undefined } },
- { value: '\n p', metadata: { original: { line: 5, column: 1 }, source: undefined } }
- ],
- body: []
- }]
+ [
+ [
+ 'selector',
+ [['a', 1, 1, undefined], ['\n\ndiv\na', 3, 0, undefined], ['\n p', 5, 1, undefined]],
+ []
+ ]
+ ]
],
'two': [
'a{}div{}',
[
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: []
- },
- {
- kind: 'selector',
- value: [{ value: 'div', metadata: { original: { line: 1, column: 3 }, source: undefined } }],
- body: []
- }
+ [
+ 'selector',
+ [['a', 1, 0, undefined]],
+ []
+ ],
+ [
+ 'selector',
+ [['div', 1, 3, undefined]],
+ []
+ ]
]
],
'three with whitespace and breaks': [
'a {}\n\ndiv{}\n \n p{}',
[
- {
- kind: 'selector',
- value: [{ value: 'a ', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: []
- },
- {
- kind: 'selector',
- value: [{ value: 'div', metadata: { original: { line: 3, column: 0 }, source: undefined } }],
- body: []
- },
- {
- kind: 'selector',
- value: [{ value: 'p', metadata: { original: { line: 5, column: 2 }, source: undefined } }],
- body: []
- }
+ [
+ 'selector',
+ [['a ', 1, 0, undefined]],
+ []
+ ],
+ [
+ 'selector',
+ [['div', 3, 0, undefined]],
+ []
+ ],
+ [
+ 'selector',
+ [['p', 5, 2, undefined]],
+ []
+ ]
]
]
})
sourceMapContext('properties', {
'single': [
'a{color:red}',
- [{
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 1, column: 2 }, source: undefined } }]
- }]
+ [
+ [
+ 'selector',
+ [['a', 1, 0, undefined]],
+ [['color:red', 1, 2, undefined]]
+ ]
+ ]
],
'double': [
'a{color:red;border:none}',
- [{
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: [
- { value: 'color:red', metadata: { original: { line: 1, column: 2 }, source: undefined } },
- { value: 'border:none', metadata: { original: { line: 1, column: 12 }, source: undefined } }
+ [
+ [
+ 'selector',
+ [['a', 1, 0, undefined]],
+ [
+ ['color:red', 1, 2, undefined],
+ ['border:none', 1, 12, undefined]
+ ]
]
- }]
+ ]
],
'triple with whitespace': [
'a{color:red;\nborder:\nnone;\n\n display:block}',
- [{
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: [
- { value: 'color:red', metadata: { original: { line: 1, column: 2 }, source: undefined } },
- { value: 'border:none', metadata: { original: { line: 2, column: 0 }, source: undefined } },
- { value: 'display:block', metadata: { original: { line: 5, column: 2 }, source: undefined } }
+ [
+ [
+ 'selector',
+ [['a', 1, 0, undefined]],
+ [
+ ['color:red', 1, 2, undefined],
+ ['border:none', 2, 0, undefined],
+ ['display:block', 5, 2, undefined]
+ ]
]
- }]
+ ]
],
'two declarations': [
'a{color:red}div{color:blue}',
[
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 1, column: 2 }, source: undefined } }]
- },
- {
- kind: 'selector',
- value: [{ value: 'div', metadata: { original: { line: 1, column: 12 }, source: undefined } }],
- body: [{ value: 'color:blue', metadata: { original: { line: 1, column: 16 }, source: undefined } }]
- }
+ [
+ 'selector',
+ [['a', 1, 0, undefined]],
+ [['color:red', 1, 2, undefined]]
+ ],
+ [
+ 'selector',
+ [['div', 1, 12, undefined]],
+ [['color:blue', 1, 16, undefined]]
+ ]
]
],
'two declarations with whitespace': [
'a{color:red}\n div{color:blue}',
[
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 1, column: 2 }, source: undefined } }]
- },
- {
- kind: 'selector',
- value: [{ value: 'div', metadata: { original: { line: 2, column: 1 }, source: undefined } }],
- body: [{ value: 'color:blue', metadata: { original: { line: 2, column: 5 }, source: undefined } }]
- }
+ [
+ 'selector',
+ [['a', 1, 0, undefined]],
+ [['color:red', 1, 2, undefined]]
+ ],
+ [
+ 'selector',
+ [['div', 2, 1, undefined]],
+ [['color:blue', 2, 5, undefined]]
+ ]
]
],
'two declarations with whitespace and ending semicolon': [
'a{color:red;\n}\n div{color:blue}',
[
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 1, column: 2 }, source: undefined } }]
- },
- {
- kind: 'selector',
- value: [{ value: 'div', metadata: { original: { line: 3, column: 1 }, source: undefined } }],
- body: [{ value: 'color:blue', metadata: { original: { line: 3, column: 5 }, source: undefined } }]
- }
+ [
+ 'selector',
+ [['a', 1, 0, undefined]],
+ [['color:red', 1, 2, undefined]]
+ ],
+ [
+ 'selector',
+ [['div', 3, 1, undefined]],
+ [['color:blue', 3, 5, undefined]]
+ ]
]
]
})
'@import': [
'a{}@import \n"test.css";\n\na{color:red}',
[
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: []
- },
- {
- kind: 'at-rule',
- value: '@import \n"test.css";',
- metadata: { original: { line: 1, column: 3 }, source: undefined }
- },
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 4, column: 0 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 4, column: 2 }, source: undefined } }]
- }
+ [
+ 'selector',
+ [['a', 1, 0, undefined]],
+ []
+ ],
+ [
+ 'at-rule',
+ ['@import \n"test.css";', 1, 3, undefined]
+ ],
+ [
+ 'selector',
+ [['a', 4, 0, undefined]],
+ [['color:red', 4, 2, undefined]]
+ ]
]
],
'@charset': [
'@charset "utf-8";a{color:red}',
[
- {
- kind: 'at-rule',
- value: '@charset "utf-8";',
- metadata: { original: { line: 1, column: 0 }, source: undefined }
- },
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 18 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 1, column: 20 }, source: undefined } }]
- }
+ [
+ 'at-rule',
+ ['@charset "utf-8";', 1, 0, undefined]
+ ],
+ [
+ 'selector',
+ [['a', 1, 18, undefined]],
+ [['color:red', 1, 20, undefined]]
+ ]
]
]
})
'@media - simple': [
'@media (min-width:980px){a{color:red}}',
[
- {
- kind: 'block',
- value: '@media (min-width:980px)',
- metadata: { original: { line: 1, column: 0 }, source: undefined },
- isFlatBlock: false,
- body: [{
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 25 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 1, column: 27 }, source: undefined } }]
- }]
- }
+ [
+ 'block',
+ ['@media (min-width:980px)', 1, 0, undefined],
+ [
+ [
+ 'selector',
+ [['a', 1, 25, undefined]],
+ [['color:red', 1, 27, undefined]]
+ ]
+ ]
+ ]
]
],
'@media - with whitespace': [
'@media (\nmin-width:980px)\n{\na{\ncolor:\nred}p{}}',
[
- {
- kind: 'block',
- value: '@media (\nmin-width:980px)',
- metadata: { original: { line: 1, column: 0 }, source: undefined },
- isFlatBlock: false,
- body: [
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 4, column: 0 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 5, column: 0 }, source: undefined } }]
- },
- {
- kind: 'selector',
- value: [{ value: 'p', metadata: { original: { line: 6, column: 4 }, source: undefined } }],
- body: []
- }
+ [
+ 'block',
+ ['@media (\nmin-width:980px)', 1, 0, undefined],
+ [
+ [
+ 'selector',
+ [['a', 4, 0, undefined]],
+ [['color:red', 5, 0, undefined]]
+ ],
+ [
+ 'selector',
+ [['p', 6, 4, undefined]],
+ []
+ ]
]
- }
+ ]
]
],
'@media - stray whitespace at end': [
'@media (min-width:980px){a{color:red} }p{color:red}',
[
- {
- kind: 'block',
- value: '@media (min-width:980px)',
- metadata: { original: { line: 1, column: 0 }, source: undefined },
- isFlatBlock: false,
- body: [
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 25 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 1, column: 27 }, source: undefined } }]
- }
+ [
+ 'block',
+ ['@media (min-width:980px)', 1, 0, undefined],
+ [
+ [
+ 'selector',
+ [['a', 1, 25, undefined]],
+ [['color:red', 1, 27, undefined]]
+ ]
]
- },
- {
- kind: 'selector',
- value: [{ value: 'p', metadata: { original: { line: 1, column: 39 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 1, column: 41 }, source: undefined } }]
- }
+ ],
+ [
+ 'selector',
+ [['p', 1, 39, undefined]],
+ [['color:red', 1, 41, undefined]]
+ ]
]
],
'@font-face': [
'@font-face{font-family: "Font";\nsrc: url("font.ttf");\nfont-weight: normal;font-style: normal}a{}',
[
- {
- kind: 'block',
- value: '@font-face',
- metadata: { original: { line: 1, column: 0 }, source: undefined },
- isFlatBlock: true,
- body: [
- { value: 'font-family:"Font"', metadata: { original: { line: 1, column: 11 }, source: undefined } },
- { value: 'src:url("font.ttf")', metadata: { original: { line: 2, column: 0 }, source: undefined } },
- { value: 'font-weight:normal', metadata: { original: { line: 3, column: 0 }, source: undefined } },
- { value: 'font-style:normal', metadata: { original: { line: 3, column: 20 }, source: undefined } }
+ [
+ 'flat-block',
+ ['@font-face', 1, 0, undefined],
+ [
+ ['font-family:"Font"', 1, 11, undefined],
+ ['src:url("font.ttf")', 2, 0, undefined],
+ ['font-weight:normal', 3, 0, undefined],
+ ['font-style:normal', 3, 20, undefined]
]
- },
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 3, column: 39 }, source: undefined } }],
- body: []
- }
+ ],
+ [
+ 'selector',
+ [['a', 3, 39, undefined]],
+ []
+ ]
]
],
'@font-face with breaks': [
'\n@font-face\n{font-family: "Font"}',
[
- {
- kind: 'block',
- value: '@font-face',
- metadata: { original: { line: 2, column: 0 }, source: undefined },
- isFlatBlock: true,
- body: [
- { value: 'font-family:"Font"', metadata: { original: { line: 3, column: 1 }, source: undefined } }
+ [
+ 'flat-block',
+ ['@font-face', 2, 0, undefined],
+ [
+ ['font-family:"Font"', 3, 1, undefined]
]
- }
+ ]
]
]
})
'top-level': [
'__ESCAPED_COMMENT_CLEAN_CSS0(0, 5)__a{}',
[
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 5 }, source: undefined } }],
- body: []
- }
+ [
+ 'selector',
+ [['a', 1, 5, undefined]],
+ []
+ ]
]
],
'top-level with line breaks': [
'__ESCAPED_COMMENT_CLEAN_CSS0(2, 5)__a{}',
[
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 3, column: 5 }, source: undefined } }],
- body: []
- }
+ [
+ 'selector',
+ [['a', 3, 5, undefined]],
+ []
+ ]
]
],
'in selectors': [
'div[data-type=__ESCAPED_FREE_TEXT_CLEAN_CSS0(1,3)__],div[data-id=__ESCAPED_FREE_TEXT_CLEAN_CSS1(0,7)__]{color:red}',
- [{
- kind: 'selector',
- value: [
- { value: 'div[data-type=__ESCAPED_FREE_TEXT_CLEAN_CSS0(1,3)__]', metadata: { original: { line: 1, column: 0 }, source: undefined } },
- { value: 'div[data-id=__ESCAPED_FREE_TEXT_CLEAN_CSS1(0,7)__]', metadata: { original: { line: 2, column: 5 }, source: undefined } }
- ],
- body: [{ value: 'color:red', metadata: { original: { line: 2, column: 26 }, source: undefined } }]
- }]
+ [
+ [
+ 'selector',
+ [
+ ['div[data-type=__ESCAPED_FREE_TEXT_CLEAN_CSS0(1,3)__]', 1, 0, undefined],
+ ['div[data-id=__ESCAPED_FREE_TEXT_CLEAN_CSS1(0,7)__]', 2, 5, undefined]
+ ],
+ [['color:red', 2, 26, undefined]]
+ ]
+ ]
],
'in properties': [
'div{__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(2,5)__background:url(__ESCAPED_URL_CLEAN_CSS0(0,20)__);color:blue}a{font-family:__ESCAPED_FREE_TEXT_CLEAN_CSS0(1,3)__;color:red}',
[
- {
- kind: 'selector',
- value: [{ value: 'div', metadata: { original: { line: 1, column: 0 }, source: undefined } }],
- body: [
- { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(2,5)__', metadata: { original: { line: 1, column: 4 }, source: undefined }},
- { value: 'background:url(__ESCAPED_URL_CLEAN_CSS0(0,20)__)', metadata: { original: { line: 3, column: 5 }, source: undefined } },
- { value: 'color:blue', metadata: { original: { line: 3, column: 42 }, source: undefined } }
+ [
+ 'selector',
+ [['div', 1, 0, undefined]],
+ [
+ ['__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(2,5)__', 1, 4, undefined],
+ ['background:url(__ESCAPED_URL_CLEAN_CSS0(0,20)__)', 3, 5, undefined],
+ ['color:blue', 3, 42, undefined]
]
- },
- {
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 3, column: 53 }, source: undefined } }],
- body: [
- { value: 'font-family:__ESCAPED_FREE_TEXT_CLEAN_CSS0(1,3)__', metadata: { original: { line: 3, column: 55 }, source: undefined } },
- { value: 'color:red', metadata: { original: { line: 4, column: 4 }, source: undefined } }
+ ],
+ [
+ 'selector',
+ [['a', 3, 53, undefined]],
+ [
+ ['font-family:__ESCAPED_FREE_TEXT_CLEAN_CSS0(1,3)__', 3, 55, undefined],
+ ['color:red', 4, 4, undefined]
]
- }
+ ]
]
],
'in at-rules': [
'@charset __ESCAPED_FREE_TEXT_CLEAN_CSS0(1, 5)__;div{}',
[
- {
- kind: 'at-rule',
- value: '@charset __ESCAPED_FREE_TEXT_CLEAN_CSS0(1, 5)__;',
- metadata: { original: { line: 1, column: 0 }, source: undefined }
- },
- {
- kind: 'selector',
- value: [{ value: 'div', metadata: { original: { line: 2, column: 7 }, source: undefined } }],
- body: []
- }
+ [
+ 'at-rule',
+ ['@charset __ESCAPED_FREE_TEXT_CLEAN_CSS0(1, 5)__;', 1, 0, undefined]
+ ],
+ [
+ 'selector',
+ [['div', 2, 7, undefined]],
+ []
+ ]
]
],
'in blocks': [
'@media (__ESCAPED_COMMENT_CLEAN_CSS0(2, 1)__min-width:980px){a{color:red}}',
[
- {
- kind: 'block',
- value: '@media (__ESCAPED_COMMENT_CLEAN_CSS0(2, 1)__min-width:980px)',
- metadata: { original: { line: 1, column: 0 }, source: undefined },
- isFlatBlock: false,
- body: [{
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 3, column: 18 }, source: undefined } }],
- body: [{ value: 'color:red', metadata: { original: { line: 3, column: 20 }, source: undefined } }]
- }]
- }
+ [
+ 'block',
+ ['@media (__ESCAPED_COMMENT_CLEAN_CSS0(2, 1)__min-width:980px)', 1, 0, undefined],
+ [
+ [
+ 'selector',
+ [['a', 3, 18, undefined]],
+ [['color:red', 3, 20, undefined]]
+ ]
+ ]
+ ]
]
]
})
var tracker = new SourceTracker();
var reader = new SourceReader();
var inputTracker = new InputSourceMapTracker({ options: { inliner: {} }, errors: {}, sourceTracker: tracker });
- var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, false, true);
+ var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, true);
var data = tracker.store('one.css', 'a{}');
return tokenizer.toTokens(data);
},
- [{
- kind: 'selector',
- value: [{ value: 'a', metadata: { original: { line: 1, column: 0 }, source: 'one.css' } }],
- body: []
- }]
+ [
+ [
+ 'selector',
+ [['a', 1, 0, 'one.css']],
+ []
+ ]
+ ]
],
'two': [
function () {
var tracker = new SourceTracker();
var reader = new SourceReader();
var inputTracker = new InputSourceMapTracker({ options: { inliner: {} }, errors: {}, sourceTracker: tracker });
- var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, false, true);
+ var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, true);
var data1 = tracker.store('one.css', 'a{}');
var data2 = tracker.store('two.css', '\na{color:red}');
return tokenizer.toTokens(data1 + data2);
},
[
- {
- kind: 'selector',
- value: [
- { value: 'a', metadata: { original: { line: 1, column: 0 }, source: 'one.css' } }
- ],
- body: []
- },
- {
- kind: 'selector',
- value: [
- { value: 'a', metadata: { original: { line: 2, column: 0 }, source: 'two.css' } }
- ],
- body: [{ value: 'color:red', metadata: { original: { line: 2, column: 2 }, source: 'two.css' } }]
- }
+ [
+ 'selector',
+ [['a', 1, 0, 'one.css']],
+ []
+ ],
+ [
+ 'selector',
+ [['a', 2, 0, 'two.css']],
+ [['color:red', 2, 2, 'two.css']]
+ ]
]
]
})
var inputTracker = new InputSourceMapTracker({ options: { inliner: {}, sourceMap: inputMap, options: {} }, errors: {}, sourceTracker: tracker });
inputTracker.track('', function () {});
- var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, false, true);
+ var tokenizer = new Tokenizer({ sourceTracker: tracker, sourceReader: reader, inputSourceMapTracker: inputTracker, options: {} }, true);
return tokenizer.toTokens('div > a {\n color: red;\n}');
},
- [{
- kind: 'selector',
- value: [{ value: 'div > a ', metadata: { original: { line: 1, column: 4 }, source: 'styles.less' } }],
- body: [{ value: 'color:red', metadata: { original: { line: 2, column: 2 }, source: 'styles.less' } }]
- }]
+ [
+ [
+ 'selector',
+ [['div > a ', 1, 4, 'styles.less']],
+ [['color:red', 2, 2, 'styles.less']]
+ ]
+ ]
]
})
)
var Tokenizer = require('../../lib/selectors/tokenizer');
var SourceTracker = require('../../lib/utils/source-tracker');
-function tokenizerContext(name, specs, addMetadata) {
+function tokenizerContext(name, specs) {
var ctx = {};
function tokenized(target) {
return function (source) {
- var tokenized = new Tokenizer({ options: {}, sourceTracker: new SourceTracker(), warnings: [] }, addMetadata).toTokens(source);
- assert.deepEqual(target, tokenized);
+ var tokenized = new Tokenizer({
+ options: {},
+ sourceTracker: new SourceTracker(),
+ warnings: []
+ }).toTokens(source);
+
+ assert.deepEqual(tokenized, target);
};
}
],
'an empty selector': [
'a{}',
- [{
- kind: 'selector',
- value: [{ value: 'a' }],
- body: []
- }]
+ [
+ ['selector', [['a']], []]
+ ]
],
'an empty selector with whitespace': [
'a{ \n }',
- [{
- kind: 'selector',
- value: [{ value: 'a' }],
- body: []
- }]
+ [
+ ['selector', [['a']], []]
+ ]
],
'a selector': [
'a{color:red}',
- [{
- kind: 'selector',
- value: [{ value: 'a' }],
- body: [{ value: 'color:red' }]
- }]
+ [
+ ['selector', [['a']], [['color:red']]]
+ ]
],
'a selector with whitespace': [
'a {color:red;\n\ndisplay :\r\n block }',
- [{
- kind: 'selector',
- value: [{ value: 'a ' }],
- body: [
- { value: 'color:red' },
- { value: 'display:block'
- }]
- }]
+ [
+ ['selector', [['a ']], [['color:red'], ['display:block']]]
+ ]
],
'a selector with suffix whitespace': [
'div a{color:red\r\n}',
- [{ kind: 'selector', value: [{ value: 'div a' }], body: [{ value: 'color:red' }] }]
+ [
+ ['selector', [['div a']], [['color:red']]]
+ ]
],
'a selector with whitespace in functions': [
'a{color:rgba( 255, 255, 0, 0.5 )}',
- [{
- kind: 'selector',
- value: [{ value: 'a' }],
- body: [{ value: 'color:rgba(255,255,0,0.5)' }]
- }]
+ [
+ ['selector', [['a']], [['color:rgba(255,255,0,0.5)']]]
+ ]
],
'a selector with empty properties': [
'a{color:red; ; ; ;}',
- [{
- kind: 'selector',
- value: [{ value: 'a' }],
- body: [{ value: 'color:red' }]
- }]
+ [
+ ['selector', [['a']], [['color:red']]]
+ ]
],
'a selector with quoted attribute': [
'a[data-kind=__ESCAPED_FREE_TEXT_CLEAN_CSS0__]{color:red}',
- [{
- kind: 'selector',
- value: [{ value: 'a[data-kind=__ESCAPED_FREE_TEXT_CLEAN_CSS0__]' }],
- body: [{ value: 'color:red' }]
- }]
+ [
+ ['selector', [['a[data-kind=__ESCAPED_FREE_TEXT_CLEAN_CSS0__]']], [['color:red']]]
+ ]
],
'a selector with escaped quote': [
'.this-class\\\'s-got-an-apostrophe{}',
- [{
- kind: 'selector',
- value: [{ value: '.this-class\\\'s-got-an-apostrophe' }],
- body: []
- }]
+ [
+ ['selector', [['.this-class\\\'s-got-an-apostrophe']], []]
+ ]
],
'a double selector': [
'a,\n\ndiv.class > p {color:red}',
- [{
- kind: 'selector',
- value: [
- { value: 'a' },
- { value: '\n\ndiv.class > p ' }
- ],
- body: [{ value: 'color:red' }]
- }]
+ [
+ ['selector', [['a'], ['\n\ndiv.class > p ']], [['color:red']]]
+ ]
],
'two selectors': [
'a{color:red}div{color:blue}',
[
- {
- kind: 'selector',
- value: [{ value: 'a' }],
- body: [{ value: 'color:red' }]
- },
- {
- kind: 'selector',
- value: [{ value: 'div' }],
- body: [{ value: 'color:blue' }]
- }
+ ['selector', [['a']], [['color:red']]],
+ ['selector', [['div']], [['color:blue']]],
]
],
'two comments and a selector separated by newline': [
'__ESCAPED_COMMENT_CLEAN_CSS0__\n__ESCAPED_COMMENT_CLEAN_CSS1__\ndiv{}',
[
- {
- kind: 'selector',
- value: [{ value: 'div' }],
- body: []
- }
+ ['selector', [['div']], []]
]
],
'two properties wrapped between comments': [
'div{__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__color:red__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__}',
- [{
- kind: 'selector',
- value: [{ value: 'div' }],
- body: [
- { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__' },
- { value: 'color:red' },
- { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__' }
- ]
- }]
+ [
+ ['selector', [['div']], [['__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__'], ['color:red'], ['__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1__']]]
+ ]
],
'pseudoselector after an argument one': [
'div:nth-child(2n):not(.test){}',
- [{
- kind: 'selector',
- value: [{ value: 'div:nth-child(2n):not(.test)' }],
- body: []
- }]
+ [
+ ['selector', [['div:nth-child(2n):not(.test)']], []]
+ ]
],
'media query': [
'@media (min-width:980px){}',
- [{
- kind: 'block',
- value: '@media (min-width:980px)',
- body: [],
- isFlatBlock: false
- }]
+ [
+ ['block', ['@media (min-width:980px)'], []]
+ ]
],
'media query with selectors': [
'@media (min-width:980px){a{color:red}}',
- [{
- kind: 'block',
- value: '@media (min-width:980px)',
- body: [{
- kind: 'selector',
- value: [{ value: 'a' }],
- body: [{ value: 'color:red' }]
- }],
- isFlatBlock: false
- }]
+ [
+ ['block', ['@media (min-width:980px)'], [
+ ['selector', [['a']], [['color:red']]]
+ ]]
+ ]
],
'media query spanning more than one chunk': [
'@media only screen and (max-width:1319px) and (min--moz-device-pixel-ratio:1.5),only screen and (max-width:1319px) and (-moz-min-device-pixel-ratio:1.5){a{color:#000}}',
- [{
- kind: 'block',
- value: '@media only screen and (max-width:1319px) and (min--moz-device-pixel-ratio:1.5),only screen and (max-width:1319px) and (-moz-min-device-pixel-ratio:1.5)',
- body: [{
- kind: 'selector',
- value: [{ value: 'a' }],
- body: [{ value: 'color:#000' }]
- }],
- isFlatBlock: false
- }]
+ [
+ ['block', ['@media only screen and (max-width:1319px) and (min--moz-device-pixel-ratio:1.5),only screen and (max-width:1319px) and (-moz-min-device-pixel-ratio:1.5)'], [
+ ['selector', [['a']], [['color:#000']]]
+ ]]
+ ]
],
'font-face': [
'@font-face{font-family: fontName;font-size:12px}',
- [{
- kind: 'block',
- value: '@font-face',
- body: [
- { value: 'font-family:fontName' },
- { value: 'font-size:12px' }
- ],
- isFlatBlock: true
- }]
+ [
+ ['flat-block', ['@font-face'], [['font-family:fontName'], ['font-size:12px']]]
+ ]
],
'charset': [
'@charset \'utf-8\';a{color:red}',
[
- {
- kind: 'at-rule',
- value: '@charset \'utf-8\';'
- },
- {
- kind: 'selector',
- value: [{ value: 'a' }],
- body: [{ value: 'color:red' }]
- }
+ ['at-rule', ['@charset \'utf-8\';']],
+ ['selector', [['a']], [['color:red']]]
]
],
'charset after a line break': [
'\n@charset \n\'utf-8\';',
[
- {
- kind: 'at-rule',
- value: '@charset \n\'utf-8\';'
- }
+ ['at-rule', ['@charset \n\'utf-8\';']]
]
],
'keyframes with quoted attribute': [
'@keyframes __ESCAPED_FREE_TEXT_CLEAN_CSS0__{}',
- [{
- kind: 'block',
- value: '@keyframes __ESCAPED_FREE_TEXT_CLEAN_CSS0__',
- body: [],
- isFlatBlock: false
- }]
+ [
+ ['block', ['@keyframes __ESCAPED_FREE_TEXT_CLEAN_CSS0__'], []]
+ ]
+ ],
+ 'variables': [
+ 'a{border:var(--width)var(--style)var(--color)}',
+ [
+ [
+ 'selector',
+ [[ 'a' ]],
+ [['border:var(--width) var(--style) var(--color)']]
+ ]
+ ]
]
})
)
tokenizerContext('broken', {
'missing end brace': [
'a{display:block',
- [{
- kind: 'selector',
- value: [{ value: 'a' }],
- body: []
- }]
+ [
+ ['selector', [['a']], []]
+ ]
],
'missing end brace in the middle': [
'body{color:red;a{color:blue;}',
- [{
- kind: 'selector',
- value: [{ value: 'body' }],
- body: [{ value: 'color:red' }]
- }]
- ]
- })
- )
- .addBatch(
- tokenizerContext('metadata', {
- 'no content': [
- '',
- []
- ],
- 'an escaped comment': [
- '__ESCAPED_COMMENT_CLEAN_CSS0__',
- []
- ],
- 'an escaped special comment': [
- '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__',
- [{ kind: 'text', value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0__' }]
- ],
- 'an empty selector': [
- 'a{}',
- [{
- kind: 'selector',
- value: [{ value: 'a' }],
- body: [],
- metadata: {
- body: '',
- bodiesList: [],
- selector: 'a',
- selectorsList: ['a']
- }
- }]
- ],
- 'a double selector': [
- 'a,\n\ndiv.class > p {color:red}',
- [{
- kind: 'selector',
- value: [{ value: 'a' }, { value: '\n\ndiv.class > p ' }],
- body: [{ value: 'color:red' }],
- metadata: {
- body: 'color:red',
- bodiesList: ['color:red'],
- selector: 'a,\n\ndiv.class > p ',
- selectorsList: ['a', '\n\ndiv.class > p ']
- }
- }]
- ],
- 'two selectors': [
- 'a{color:red}div{color:blue}',
[
- {
- kind: 'selector',
- value: [{ value: 'a' }],
- body: [{ value: 'color:red' }],
- metadata: {
- body: 'color:red',
- bodiesList: ['color:red'],
- selector: 'a',
- selectorsList: ['a']
- }
- },
- {
- kind: 'selector',
- value: [{ value: 'div' }],
- body: [{ value: 'color:blue' }],
- metadata: {
- body: 'color:blue',
- bodiesList: ['color:blue'],
- selector: 'div',
- selectorsList: ['div']
- }
- }
+ ['selector', [['body']], [['color:red']]]
]
- ],
- 'two properties wrapped between comments': [
- 'div{__ESCAPED_COMMENT_CLEAN_CSS0(0, 5)__color:red__ESCAPED_COMMENT_CLEAN_CSS1(0, 5)__}',
- [{
- kind: 'selector',
- value: [{ value: 'div' }],
- body: [
- { value: 'color:red' }
- ],
- metadata: {
- body: 'color:red',
- bodiesList: ['color:red'],
- selector: 'div',
- selectorsList: ['div']
- }
- }]
- ],
- 'two properties wrapped between special comments': [
- 'div{__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0, 5)__color:red__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(0, 5)__}',
- [{
- kind: 'selector',
- value: [{ value: 'div' }],
- body: [
- { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0, 5)__' },
- { value: 'color:red' },
- { value: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(0, 5)__' }
- ],
- metadata: {
- body: '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0, 5)__,color:red,__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(0, 5)__',
- bodiesList: ['__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS0(0, 5)__', 'color:red', '__ESCAPED_COMMENT_SPECIAL_CLEAN_CSS1(0, 5)__'],
- selector: 'div',
- selectorsList: ['div']
- }
- }]
]
- }, true)
+ })
)
.export(module);
})
.addBatch({
'advanced optimizations': {
- 'new property in smart sort': {
+ 'new property in restructuring': {
'topic': function () {
return new CleanCSS({ sourceMap: true }).minify('a{color:#000}div{color:red}.one{display:block}.two{display:inline;color:red}');
},