postcss-bgimage
Version:
Plugin for PoscCSS to remove or keep only images by url() to optimize page loading
220 lines (180 loc) • 5.9 kB
JavaScript
;
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var postcss = _interopDefault(require('postcss'));
var cssColorList = _interopDefault(require('css-color-list'));
const PATTERN_IGNORE = /[\s]*bgImage[\s]*:[\s]*ignore[\s]*/i;
/**
* Checks instruction to ignore the node.
*
* @param {Node} node
*
* @returns {boolean}
*/
function isNodeIgnored(node) {
const {
parent
} = node;
if (!parent) {
// Root was reached.
return false;
}
if (parent.some(child => child.type === 'comment' && PATTERN_IGNORE.test(child.text))) {
// Instruction to ignore the node was detected.
return true;
} // Check the instruction on one level above.
return isNodeIgnored(parent);
}
const PATTERN_BACKGROUND = /background(?!-)/i;
const PATTERN_GRADIENT = /[a-z-]*gradient\((?:(?:hls|rgb)a?\([^()]*\)[^,]*[,]?|[^,]+[,]?)+\)/i;
const PATTERN_URL = /url\([^)]+\)[\s]*/i; // @see https://developer.mozilla.org/en-US/docs/Web/CSS/background-color
const colorList = cssColorList();
const PATTERN_COLOR = `(?:(?:rgb|hls)a?\\([^)]*\\)|#[0-9a-f]{3,8}|${colorList.join('|')})`;
const PATTERN_START_BACKGROUND_COLOR = new RegExp(`^${PATTERN_COLOR}[\\s]*`, 'i');
const PATTERN_END_BACKGROUND_COLOR = new RegExp(`[\\s]*${PATTERN_COLOR}$`, 'i');
/**
* Destruction of shorthand 'background' by prop 'background-image' and the rest props.
*
* @param {string} css
*/
function destructShorthand(css) {
css.walkDecls(PATTERN_BACKGROUND, decl => {
if (!isNodeIgnored(decl)) {
const {
value
} = decl;
const hasURL = PATTERN_URL.test(value);
if (hasURL) {
let valueStartBgColor = '';
let valueEndBgColor = '';
const layers = value.replace(PATTERN_START_BACKGROUND_COLOR, match => {
valueStartBgColor = match;
return '';
}).replace(PATTERN_END_BACKGROUND_COLOR, match => {
valueEndBgColor = match;
return '';
}).split(/,/).reduce((result, subLine, index) => {
if (index === 0) {
return [subLine];
}
const indexLast = result.length - 1;
const subLineLast = result[indexLast];
if ((subLineLast.match(/\(/g) || []).length !== (subLineLast.match(/\)/g) || []).length) {
const lastItem = `${result[indexLast]},${subLine}`;
return [...result.slice(0, -1), lastItem];
}
return [...result, subLine];
}, []);
const isMultiple = layers.length > 1; // Calculate 'background-image' value
const valueURL = layers.map(layer => {
const hasURLValue = PATTERN_URL.test(layer);
const valueGradient = layer.match(PATTERN_GRADIENT) ? layer.match(PATTERN_GRADIENT)[0] : null;
if (hasURLValue) {
return layer.replace(/(?:[^\s]+[\s]+)*(url\([^)]*\))(?:[\s]+[^\s]+)*/ig, '$1');
}
if (valueGradient) {
return layer.replace(/([^\s]+(?:[\s]+[^\s]+)*)/ig, valueGradient);
}
return layer.replace(/[^\s]+(?:[\s]+[^\s]+)*/ig, 'initial');
}).join(',');
decl.cloneAfter({
prop: 'background-image',
value: valueURL.trim()
}); // Calculate 'background' value
const valueRest = layers.map(layer => {
const hasURLValue = PATTERN_URL.test(layer);
if (!hasURLValue) {
return layer;
}
return layer.replace(/([^\s]+[\s]+)*(?:url\([^)]*\)(?:[\s]+(?=[^\s]))?)([^\s]+[\s]*[^\s]+)*/ig, (match, group1, group2) => {
const startPart = group1 || '';
const endPart = group2 || '';
const result = `${startPart}${endPart}`;
if (isMultiple && !result.trim()) {
return `${startPart}initial${endPart}`;
}
return result;
});
}).join(',');
if (!valueStartBgColor.trim() && !valueEndBgColor.trim() && !valueRest.replace(/(?:initial)|[\s,]+/ig, '')) {
decl.remove();
} else {
decl.replaceWith(decl.clone({
value: `${valueStartBgColor}${valueRest}${valueEndBgColor}`.trim()
}));
}
}
}
});
}
const PATTERN_URL$1 = /url\([^)]+\)[\s]*/i;
/**
* Checks value for containing of 'url()'.
*
* @param {string} value
*
* @returns {boolean}
*/
function hasValueUrl(value) {
return PATTERN_URL$1.test(value);
}
const PATTERN_BACKGROUND_IMAGE = /background-image/i;
/**
* @param {string} css
*/
function cutter(css) {
destructShorthand(css);
css.walkDecls(PATTERN_BACKGROUND_IMAGE, decl => {
if (hasValueUrl(decl.value) && !isNodeIgnored(decl)) {
decl.remove();
}
});
}
const PATTERN_BACKGROUND_IMAGE$1 = /background-image/i;
const IGNORING_AT_RULES_NAMES = ['charset', 'import', 'namespace'];
/**
* @param {string} css
*/
function cutterInvertor(css) {
destructShorthand(css);
css.walkDecls(decl => {
if (isNodeIgnored(decl) || !hasValueUrl(decl.value) || !PATTERN_BACKGROUND_IMAGE$1.test(decl.prop)) {
decl.remove();
}
});
css.walkAtRules(rule => {
if (IGNORING_AT_RULES_NAMES.indexOf(rule.name) !== -1) {
rule.remove();
}
});
}
/**
* @param {string} mode
*
* @returns {void}
*/
function getProcessor(mode) {
switch (mode) {
case 'cutter':
return cutter;
case 'cutterInvertor':
return cutterInvertor;
default:
throw new Error(`Unknow mode for postcss-bgimage: ${mode}`);
}
}
/**
* @param {object} options - Plugin options.
*
* @returns {void}
*/
var index = postcss.plugin('postcss-bgimage', options => {
if (!options) {
throw new Error('Required params was not passed.');
}
const {
mode
} = options;
return css => getProcessor(mode)(css);
});
module.exports = index;
//# sourceMappingURL=index.js.map