|
|
var sortSelectors = require('./sort-selectors'); var tidyRules = require('./tidy-rules'); var tidyBlock = require('./tidy-block'); var tidyAtRule = require('./tidy-at-rule');
var Hack = require('../hack'); var removeUnused = require('../remove-unused'); var restoreFromOptimizing = require('../restore-from-optimizing'); var wrapForOptimizing = require('../wrap-for-optimizing').all;
var configuration = require('../configuration'); var optimizers = require('./value-optimizers');
var OptimizationLevel = require('../../options/optimization-level').OptimizationLevel;
var Token = require('../../tokenizer/token'); var Marker = require('../../tokenizer/marker');
var formatPosition = require('../../utils/format-position');
var serializeRules = require('../../writer/one-time').rules;
var CHARSET_TOKEN = '@charset'; var CHARSET_REGEXP = new RegExp('^' + CHARSET_TOKEN, 'i');
var DEFAULT_ROUNDING_PRECISION = require('../../options/rounding-precision').DEFAULT;
var VARIABLE_PROPERTY_NAME_PATTERN = /^--\S+$/; var PROPERTY_NAME_PATTERN = /^(?:-chrome-|-[\w-]+\w|\w[\w-]+\w|\w{1,})$/; var IMPORT_PREFIX_PATTERN = /^@import/i; var URL_PREFIX_PATTERN = /^url\(/i;
function startsAsUrl(value) { return URL_PREFIX_PATTERN.test(value); }
function isImport(token) { return IMPORT_PREFIX_PATTERN.test(token[1]); }
function isLegacyFilter(property) { var value;
if (property.name == 'filter' || property.name == '-ms-filter') { value = property.value[0][1];
return value.indexOf('progid') > -1 || value.indexOf('alpha') === 0 || value.indexOf('chroma') === 0; } return false; }
function noop() {}
function noopValueOptimizer(_name, value, _options) { return value; }
function optimizeBody(rule, properties, context) { var options = context.options; var valueOptimizers; var property, name, type, value; var propertyToken; var propertyOptimizer; var serializedRule = serializeRules(rule); var _properties = wrapForOptimizing(properties); var pluginValueOptimizers = context.options.plugins.level1Value; var pluginPropertyOptimizers = context.options.plugins.level1Property; var isVariable; var i, l;
for (i = 0, l = _properties.length; i < l; i++) { var j, k, m, n;
property = _properties[i]; name = property.name; propertyOptimizer = configuration[name] && configuration[name].propertyOptimizer || noop; valueOptimizers = configuration[name] && configuration[name].valueOptimizers || [optimizers.whiteSpace]; isVariable = VARIABLE_PROPERTY_NAME_PATTERN.test(name);
if (isVariable) { valueOptimizers = options.variableOptimizers.length > 0 ? options.variableOptimizers : [optimizers.whiteSpace]; }
if (!isVariable && !PROPERTY_NAME_PATTERN.test(name)) { propertyToken = property.all[property.position]; context.warnings.push('Invalid property name \'' + name + '\' at ' + formatPosition(propertyToken[1][2][0]) + '. Ignoring.'); property.unused = true; continue; }
if (property.value.length === 0) { propertyToken = property.all[property.position]; context.warnings.push('Empty property \'' + name + '\' at ' + formatPosition(propertyToken[1][2][0]) + '. Ignoring.'); property.unused = true; continue; }
if (property.hack && ( (property.hack[0] == Hack.ASTERISK || property.hack[0] == Hack.UNDERSCORE) && !options.compatibility.properties.iePrefixHack || property.hack[0] == Hack.BACKSLASH && !options.compatibility.properties.ieSuffixHack || property.hack[0] == Hack.BANG && !options.compatibility.properties.ieBangHack)) { property.unused = true; continue; }
if (!options.compatibility.properties.ieFilters && isLegacyFilter(property)) { property.unused = true; continue; }
if (property.block) { optimizeBody(rule, property.value[0][1], context); continue; }
for (j = 0, m = property.value.length; j < m; j++) { type = property.value[j][0]; value = property.value[j][1];
if (type == Token.PROPERTY_BLOCK) { property.unused = true; context.warnings.push('Invalid value token at ' + formatPosition(value[0][1][2][0]) + '. Ignoring.'); break; }
if (startsAsUrl(value) && !context.validator.isUrl(value)) { property.unused = true; context.warnings.push('Broken URL \'' + value + '\' at ' + formatPosition(property.value[j][2][0]) + '. Ignoring.'); break; }
for (k = 0, n = valueOptimizers.length; k < n; k++) { value = valueOptimizers[k](name, value, options); }
for (k = 0, n = pluginValueOptimizers.length; k < n; k++) { value = pluginValueOptimizers[k](name, value, options); }
property.value[j][1] = value; }
propertyOptimizer(serializedRule, property, options);
for (j = 0, m = pluginPropertyOptimizers.length; j < m; j++) { pluginPropertyOptimizers[j](serializedRule, property, options); } }
restoreFromOptimizing(_properties); removeUnused(_properties); removeComments(properties, options); }
function removeComments(tokens, options) { var token; var i;
for (i = 0; i < tokens.length; i++) { token = tokens[i];
if (token[0] != Token.COMMENT) { continue; }
optimizeComment(token, options);
if (token[1].length === 0) { tokens.splice(i, 1); i--; } } }
function optimizeComment(token, options) { if (token[1][2] == Marker.EXCLAMATION && (options.level[OptimizationLevel.One].specialComments == 'all' || options.commentsKept < options.level[OptimizationLevel.One].specialComments)) { options.commentsKept++; return; }
token[1] = []; }
function cleanupCharsets(tokens) { var hasCharset = false;
for (var i = 0, l = tokens.length; i < l; i++) { var token = tokens[i];
if (token[0] != Token.AT_RULE) { continue; }
if (!CHARSET_REGEXP.test(token[1])) { continue; }
if (hasCharset || token[1].indexOf(CHARSET_TOKEN) == -1) { tokens.splice(i, 1); i--; l--; } else { hasCharset = true; tokens.splice(i, 1); tokens.unshift([Token.AT_RULE, token[1].replace(CHARSET_REGEXP, CHARSET_TOKEN)]); } } }
function buildUnitRegexp(options) { var units = ['px', 'em', 'ex', 'cm', 'mm', 'in', 'pt', 'pc', '%']; var otherUnits = ['ch', 'rem', 'vh', 'vm', 'vmax', 'vmin', 'vw'];
otherUnits.forEach(function(unit) { if (options.compatibility.units[unit]) { units.push(unit); } });
return new RegExp('(^|\\s|\\(|,)0(?:' + units.join('|') + ')(\\W|$)', 'g'); }
function buildPrecisionOptions(roundingPrecision) { var precisionOptions = { matcher: null, units: {} }; var optimizable = []; var unit; var value;
for (unit in roundingPrecision) { value = roundingPrecision[unit];
if (value != DEFAULT_ROUNDING_PRECISION) { precisionOptions.units[unit] = {}; precisionOptions.units[unit].value = value; precisionOptions.units[unit].multiplier = 10 ** value;
optimizable.push(unit); } }
if (optimizable.length > 0) { precisionOptions.enabled = true; precisionOptions.decimalPointMatcher = new RegExp('(\\d)\\.($|' + optimizable.join('|') + ')($|\\W)', 'g'); precisionOptions.zeroMatcher = new RegExp('(\\d*)(\\.\\d+)(' + optimizable.join('|') + ')', 'g'); }
return precisionOptions; }
function buildVariableOptimizers(options) { return options.level[OptimizationLevel.One].variableValueOptimizers.map(function(optimizer) { if (typeof (optimizer) == 'string') { return optimizers[optimizer] || noopValueOptimizer; }
return optimizer; }); }
function level1Optimize(tokens, context) { var options = context.options; var levelOptions = options.level[OptimizationLevel.One]; var ie7Hack = options.compatibility.selectors.ie7Hack; var adjacentSpace = options.compatibility.selectors.adjacentSpace; var spaceAfterClosingBrace = options.compatibility.properties.spaceAfterClosingBrace; var format = options.format; var mayHaveCharset = false; var afterRules = false;
options.unitsRegexp = options.unitsRegexp || buildUnitRegexp(options); options.precision = options.precision || buildPrecisionOptions(levelOptions.roundingPrecision); options.commentsKept = options.commentsKept || 0; options.variableOptimizers = options.variableOptimizers || buildVariableOptimizers(options);
for (var i = 0, l = tokens.length; i < l; i++) { var token = tokens[i];
switch (token[0]) { case Token.AT_RULE: token[1] = isImport(token) && afterRules ? '' : token[1]; token[1] = levelOptions.tidyAtRules ? tidyAtRule(token[1]) : token[1]; mayHaveCharset = true; break; case Token.AT_RULE_BLOCK: optimizeBody(token[1], token[2], context); afterRules = true; break; case Token.NESTED_BLOCK: token[1] = levelOptions.tidyBlockScopes ? tidyBlock(token[1], spaceAfterClosingBrace) : token[1]; level1Optimize(token[2], context); afterRules = true; break; case Token.COMMENT: optimizeComment(token, options); break; case Token.RULE: token[1] = levelOptions.tidySelectors ? tidyRules(token[1], !ie7Hack, adjacentSpace, format, context.warnings) : token[1]; token[1] = token[1].length > 1 ? sortSelectors(token[1], levelOptions.selectorsSortingMethod) : token[1]; optimizeBody(token[1], token[2], context); afterRules = true; break; }
if (token[0] == Token.COMMENT && token[1].length === 0 || levelOptions.removeEmpty && (token[1].length === 0 || (token[2] && token[2].length === 0))) { tokens.splice(i, 1); i--; l--; } }
if (levelOptions.cleanupCharsets && mayHaveCharset) { cleanupCharsets(tokens); }
return tokens; }
module.exports = level1Optimize;
|