Spaces:
Running
Running
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; | |