250 lines
6.6 KiB
JavaScript
250 lines
6.6 KiB
JavaScript
var populateComponents = require('./properties/populate-components');
|
|
|
|
var wrapForOptimizing = require('../wrap-for-optimizing').single;
|
|
var restoreFromOptimizing = require('../restore-from-optimizing');
|
|
|
|
var Token = require('../../tokenizer/token');
|
|
|
|
var animationNameRegex = /^(\-moz\-|\-o\-|\-webkit\-)?animation-name$/;
|
|
var animationRegex = /^(\-moz\-|\-o\-|\-webkit\-)?animation$/;
|
|
var keyframeRegex = /^@(\-moz\-|\-o\-|\-webkit\-)?keyframes /;
|
|
var importantRegex = /\s{0,31}!important$/;
|
|
var optionalMatchingQuotesRegex = /^(['"]?)(.*)\1$/;
|
|
|
|
function normalize(value) {
|
|
return value
|
|
.replace(optionalMatchingQuotesRegex, '$2')
|
|
.replace(importantRegex, '');
|
|
}
|
|
|
|
function removeUnusedAtRules(tokens, context) {
|
|
removeUnusedAtRule(tokens, matchCounterStyle, markCounterStylesAsUsed, context);
|
|
removeUnusedAtRule(tokens, matchFontFace, markFontFacesAsUsed, context);
|
|
removeUnusedAtRule(tokens, matchKeyframe, markKeyframesAsUsed, context);
|
|
removeUnusedAtRule(tokens, matchNamespace, markNamespacesAsUsed, context);
|
|
}
|
|
|
|
function removeUnusedAtRule(tokens, matchCallback, markCallback, context) {
|
|
var atRules = {};
|
|
var atRule;
|
|
var atRuleTokens;
|
|
var atRuleToken;
|
|
var zeroAt;
|
|
var i, l;
|
|
|
|
for (i = 0, l = tokens.length; i < l; i++) {
|
|
matchCallback(tokens[i], atRules);
|
|
}
|
|
|
|
if (Object.keys(atRules).length === 0) {
|
|
return;
|
|
}
|
|
|
|
markUsedAtRules(tokens, markCallback, atRules, context);
|
|
|
|
for (atRule in atRules) {
|
|
atRuleTokens = atRules[atRule];
|
|
|
|
for (i = 0, l = atRuleTokens.length; i < l; i++) {
|
|
atRuleToken = atRuleTokens[i];
|
|
zeroAt = atRuleToken[0] == Token.AT_RULE ? 1 : 2;
|
|
atRuleToken[zeroAt] = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
function markUsedAtRules(tokens, markCallback, atRules, context) {
|
|
var boundMarkCallback = markCallback(atRules);
|
|
var i, l;
|
|
|
|
for (i = 0, l = tokens.length; i < l; i++) {
|
|
switch (tokens[i][0]) {
|
|
case Token.RULE:
|
|
boundMarkCallback(tokens[i], context);
|
|
break;
|
|
case Token.NESTED_BLOCK:
|
|
markUsedAtRules(tokens[i][2], markCallback, atRules, context);
|
|
}
|
|
}
|
|
}
|
|
|
|
function matchCounterStyle(token, atRules) {
|
|
var match;
|
|
|
|
if (token[0] == Token.AT_RULE_BLOCK && token[1][0][1].indexOf('@counter-style') === 0) {
|
|
match = token[1][0][1].split(' ')[1];
|
|
atRules[match] = atRules[match] || [];
|
|
atRules[match].push(token);
|
|
}
|
|
}
|
|
|
|
function markCounterStylesAsUsed(atRules) {
|
|
return function (token, context) {
|
|
var property;
|
|
var wrappedProperty;
|
|
var i, l;
|
|
|
|
for (i = 0, l = token[2].length; i < l; i++) {
|
|
property = token[2][i];
|
|
|
|
if (property[1][1] == 'list-style') {
|
|
wrappedProperty = wrapForOptimizing(property);
|
|
populateComponents([wrappedProperty], context.validator, context.warnings);
|
|
|
|
if (wrappedProperty.components[0].value[0][1] in atRules) {
|
|
delete atRules[property[2][1]];
|
|
}
|
|
|
|
restoreFromOptimizing([wrappedProperty]);
|
|
}
|
|
|
|
if (property[1][1] == 'list-style-type' && property[2][1] in atRules) {
|
|
delete atRules[property[2][1]];
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function matchFontFace(token, atRules) {
|
|
var property;
|
|
var match;
|
|
var i, l;
|
|
|
|
if (token[0] == Token.AT_RULE_BLOCK && token[1][0][1] == '@font-face') {
|
|
for (i = 0, l = token[2].length; i < l; i++) {
|
|
property = token[2][i];
|
|
|
|
if (property[1][1] == 'font-family') {
|
|
match = normalize(property[2][1].toLowerCase());
|
|
atRules[match] = atRules[match] || [];
|
|
atRules[match].push(token);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function markFontFacesAsUsed(atRules) {
|
|
return function (token, context) {
|
|
var property;
|
|
var wrappedProperty;
|
|
var component;
|
|
var normalizedMatch;
|
|
var i, l;
|
|
var j, m;
|
|
|
|
for (i = 0, l = token[2].length; i < l; i++) {
|
|
property = token[2][i];
|
|
|
|
if (property[1][1] == 'font') {
|
|
wrappedProperty = wrapForOptimizing(property);
|
|
populateComponents([wrappedProperty], context.validator, context.warnings);
|
|
component = wrappedProperty.components[6];
|
|
|
|
for (j = 0, m = component.value.length; j < m; j++) {
|
|
normalizedMatch = normalize(component.value[j][1].toLowerCase());
|
|
|
|
if (normalizedMatch in atRules) {
|
|
delete atRules[normalizedMatch];
|
|
}
|
|
}
|
|
|
|
restoreFromOptimizing([wrappedProperty]);
|
|
}
|
|
|
|
if (property[1][1] == 'font-family') {
|
|
for (j = 2, m = property.length; j < m; j++) {
|
|
normalizedMatch = normalize(property[j][1].toLowerCase());
|
|
|
|
if (normalizedMatch in atRules) {
|
|
delete atRules[normalizedMatch];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function matchKeyframe(token, atRules) {
|
|
var match;
|
|
|
|
if (token[0] == Token.NESTED_BLOCK && keyframeRegex.test(token[1][0][1])) {
|
|
match = token[1][0][1].split(' ')[1];
|
|
atRules[match] = atRules[match] || [];
|
|
atRules[match].push(token);
|
|
}
|
|
}
|
|
|
|
function markKeyframesAsUsed(atRules) {
|
|
return function (token, context) {
|
|
var property;
|
|
var wrappedProperty;
|
|
var component;
|
|
var i, l;
|
|
var j, m;
|
|
|
|
for (i = 0, l = token[2].length; i < l; i++) {
|
|
property = token[2][i];
|
|
|
|
if (animationRegex.test(property[1][1])) {
|
|
wrappedProperty = wrapForOptimizing(property);
|
|
populateComponents([wrappedProperty], context.validator, context.warnings);
|
|
component = wrappedProperty.components[7];
|
|
|
|
for (j = 0, m = component.value.length; j < m; j++) {
|
|
if (component.value[j][1] in atRules) {
|
|
delete atRules[component.value[j][1]];
|
|
}
|
|
}
|
|
|
|
restoreFromOptimizing([wrappedProperty]);
|
|
}
|
|
|
|
if (animationNameRegex.test(property[1][1])) {
|
|
for (j = 2, m = property.length; j < m; j++) {
|
|
if (property[j][1] in atRules) {
|
|
delete atRules[property[j][1]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function matchNamespace(token, atRules) {
|
|
var match;
|
|
|
|
if (token[0] == Token.AT_RULE && token[1].indexOf('@namespace') === 0) {
|
|
match = token[1].split(' ')[1];
|
|
atRules[match] = atRules[match] || [];
|
|
atRules[match].push(token);
|
|
}
|
|
}
|
|
|
|
function markNamespacesAsUsed(atRules) {
|
|
var namespaceRegex = new RegExp(Object.keys(atRules).join('\\\||') + '\\\|', 'g');
|
|
|
|
return function (token) {
|
|
var match;
|
|
var scope;
|
|
var normalizedMatch;
|
|
var i, l;
|
|
var j, m;
|
|
|
|
for (i = 0, l = token[1].length; i < l; i++) {
|
|
scope = token[1][i];
|
|
match = scope[1].match(namespaceRegex);
|
|
|
|
for (j = 0, m = match.length; j < m; j++) {
|
|
normalizedMatch = match[j].substring(0, match[j].length - 1);
|
|
|
|
if (normalizedMatch in atRules) {
|
|
delete atRules[normalizedMatch];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
module.exports = removeUnusedAtRules;
|