123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- var resolveProperty = require('../../utils/names.js').property;
- var resolveKeyword = require('../../utils/names.js').keyword;
- var walkRulesRight = require('../../utils/walk.js').rulesRight;
- var translate = require('../../utils/translate.js');
- var dontRestructure = {
- 'src': 1 // https://github.com/afelix/csso/issues/50
- };
- var DONT_MIX_VALUE = {
- // https://developer.mozilla.org/en-US/docs/Web/CSS/display#Browser_compatibility
- 'display': /table|ruby|flex|-(flex)?box$|grid|contents|run-in/i,
- // https://developer.mozilla.org/en/docs/Web/CSS/text-align
- 'text-align': /^(start|end|match-parent|justify-all)$/i
- };
- var CURSOR_SAFE_VALUE = [
- 'auto', 'crosshair', 'default', 'move', 'text', 'wait', 'help',
- 'n-resize', 'e-resize', 's-resize', 'w-resize',
- 'ne-resize', 'nw-resize', 'se-resize', 'sw-resize',
- 'pointer', 'progress', 'not-allowed', 'no-drop', 'vertical-text', 'all-scroll',
- 'col-resize', 'row-resize'
- ];
- var NEEDLESS_TABLE = {
- 'border-width': ['border'],
- 'border-style': ['border'],
- 'border-color': ['border'],
- 'border-top': ['border'],
- 'border-right': ['border'],
- 'border-bottom': ['border'],
- 'border-left': ['border'],
- 'border-top-width': ['border-top', 'border-width', 'border'],
- 'border-right-width': ['border-right', 'border-width', 'border'],
- 'border-bottom-width': ['border-bottom', 'border-width', 'border'],
- 'border-left-width': ['border-left', 'border-width', 'border'],
- 'border-top-style': ['border-top', 'border-style', 'border'],
- 'border-right-style': ['border-right', 'border-style', 'border'],
- 'border-bottom-style': ['border-bottom', 'border-style', 'border'],
- 'border-left-style': ['border-left', 'border-style', 'border'],
- 'border-top-color': ['border-top', 'border-color', 'border'],
- 'border-right-color': ['border-right', 'border-color', 'border'],
- 'border-bottom-color': ['border-bottom', 'border-color', 'border'],
- 'border-left-color': ['border-left', 'border-color', 'border'],
- 'margin-top': ['margin'],
- 'margin-right': ['margin'],
- 'margin-bottom': ['margin'],
- 'margin-left': ['margin'],
- 'padding-top': ['padding'],
- 'padding-right': ['padding'],
- 'padding-bottom': ['padding'],
- 'padding-left': ['padding'],
- 'font-style': ['font'],
- 'font-variant': ['font'],
- 'font-weight': ['font'],
- 'font-size': ['font'],
- 'font-family': ['font'],
- 'list-style-type': ['list-style'],
- 'list-style-position': ['list-style'],
- 'list-style-image': ['list-style']
- };
- function getPropertyFingerprint(propertyName, declaration, fingerprints) {
- var realName = resolveProperty(propertyName).name;
- if (realName === 'background' ||
- (realName === 'filter' && declaration.value.sequence.first().type === 'Progid')) {
- return propertyName + ':' + translate(declaration.value);
- }
- var declarationId = declaration.id;
- var fingerprint = fingerprints[declarationId];
- if (!fingerprint) {
- var vendorId = '';
- var iehack = '';
- var special = {};
- declaration.value.sequence.each(function walk(node) {
- switch (node.type) {
- case 'Argument':
- case 'Value':
- case 'Braces':
- node.sequence.each(walk);
- break;
- case 'Identifier':
- var name = node.name;
- if (!vendorId) {
- vendorId = resolveKeyword(name).vendor;
- }
- if (/\\[09]/.test(name)) {
- iehack = RegExp.lastMatch;
- }
- if (realName === 'cursor') {
- if (CURSOR_SAFE_VALUE.indexOf(name) === -1) {
- special[name] = true;
- }
- } else if (DONT_MIX_VALUE.hasOwnProperty(realName)) {
- if (DONT_MIX_VALUE[realName].test(name)) {
- special[name] = true;
- }
- }
- break;
- case 'Function':
- var name = node.name;
- if (!vendorId) {
- vendorId = resolveKeyword(name).vendor;
- }
- if (name === 'rect') {
- // there are 2 forms of rect:
- // rect(<top>, <right>, <bottom>, <left>) - standart
- // rect(<top> <right> <bottom> <left>) – backwards compatible syntax
- // only the same form values can be merged
- if (node.arguments.size < 4) {
- name = 'rect-backward';
- }
- }
- special[name + '()'] = true;
- // check nested tokens too
- node.arguments.each(walk);
- break;
- case 'Dimension':
- var unit = node.unit;
- switch (unit) {
- // is not supported until IE11
- case 'rem':
- // v* units is too buggy across browsers and better
- // don't merge values with those units
- case 'vw':
- case 'vh':
- case 'vmin':
- case 'vmax':
- case 'vm': // IE9 supporting "vm" instead of "vmin".
- special[unit] = true;
- break;
- }
- break;
- }
- });
- fingerprint = '|' + Object.keys(special).sort() + '|' + iehack + vendorId;
- fingerprints[declarationId] = fingerprint;
- }
- return propertyName + fingerprint;
- }
- function needless(props, declaration, fingerprints) {
- var property = resolveProperty(declaration.property.name);
- if (NEEDLESS_TABLE.hasOwnProperty(property.name)) {
- var table = NEEDLESS_TABLE[property.name];
- for (var i = 0; i < table.length; i++) {
- var ppre = getPropertyFingerprint(property.prefix + table[i], declaration, fingerprints);
- var prev = props[ppre];
- if (prev && (!declaration.value.important || prev.item.data.value.important)) {
- return prev;
- }
- }
- }
- }
- function processRuleset(ruleset, item, list, props, fingerprints) {
- var declarations = ruleset.block.declarations;
- declarations.eachRight(function(declaration, declarationItem) {
- var property = declaration.property.name;
- var fingerprint = getPropertyFingerprint(property, declaration, fingerprints);
- var prev = props[fingerprint];
- if (prev && !dontRestructure.hasOwnProperty(property)) {
- if (declaration.value.important && !prev.item.data.value.important) {
- props[fingerprint] = {
- block: declarations,
- item: declarationItem
- };
- prev.block.remove(prev.item);
- declaration.info = {
- primary: declaration.info,
- merged: prev.item.data.info
- };
- } else {
- declarations.remove(declarationItem);
- prev.item.data.info = {
- primary: prev.item.data.info,
- merged: declaration.info
- };
- }
- } else {
- var prev = needless(props, declaration, fingerprints);
- if (prev) {
- declarations.remove(declarationItem);
- prev.item.data.info = {
- primary: prev.item.data.info,
- merged: declaration.info
- };
- } else {
- declaration.fingerprint = fingerprint;
- props[fingerprint] = {
- block: declarations,
- item: declarationItem
- };
- }
- }
- });
- if (declarations.isEmpty()) {
- list.remove(item);
- }
- };
- module.exports = function restructBlock(ast) {
- var stylesheetMap = {};
- var fingerprints = Object.create(null);
- walkRulesRight(ast, function(node, item, list) {
- if (node.type !== 'Ruleset') {
- return;
- }
- var stylesheet = this.stylesheet;
- var rulesetId = (node.pseudoSignature || '') + '|' + node.selector.selectors.first().id;
- var rulesetMap;
- var props;
- if (!stylesheetMap.hasOwnProperty(stylesheet.id)) {
- rulesetMap = {};
- stylesheetMap[stylesheet.id] = rulesetMap;
- } else {
- rulesetMap = stylesheetMap[stylesheet.id];
- }
- if (rulesetMap.hasOwnProperty(rulesetId)) {
- props = rulesetMap[rulesetId];
- } else {
- props = {};
- rulesetMap[rulesetId] = props;
- }
- processRuleset.call(this, node, item, list, props, fingerprints);
- });
- };
|