Skip to content

Commit

Permalink
Merge pull request svg#640 from lahmatiy/remove-unused-styles
Browse files Browse the repository at this point in the history
Remove unused styles based on usage data
  • Loading branch information
GreLI authored Jan 31, 2017
2 parents d4ff31d + 340da36 commit 282c193
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 33 deletions.
169 changes: 142 additions & 27 deletions plugins/minifyStyles.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,160 @@
'use strict';

exports.type = 'perItem';
exports.type = 'full';

exports.active = true;

exports.description = 'minifies styles and removes unused styles based on usage data';

exports.params = {
svgo: {}
};
// ... CSSO options goes here

exports.description = 'minifies existing styles in svg';
// additional
usage: {
force: false, // force to use usage data even if it unsafe (document contains <script> or on* attributes)
ids: true,
classes: true,
tags: true
}
};

var csso = require('csso');

/**
* Minifies styles (<style> element + style attribute) using svgo
*
* @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out
* Minifies styles (<style> element + style attribute) using CSSO
*
* @author strarsis <[email protected]>
*/
exports.fn = function(item, svgoOptions) {

if(item.elem) {
if(item.isElem('style') && !item.isEmpty()) {
var styleCss = item.content[0].text || item.content[0].cdata || [],
DATA = styleCss.indexOf('>') >= 0 || styleCss.indexOf('<') >= 0 ? 'cdata' : 'text';
if(styleCss.length > 0) {
var styleCssMinified = csso.minify(styleCss, svgoOptions);
item.content[0][DATA] = styleCssMinified.css;
exports.fn = function(ast, options) {
options = options || {};

var minifyOptionsForStylesheet = cloneObject(options);
var minifyOptionsForAttribute = cloneObject(options);
var elems = findStyleElems(ast);

minifyOptionsForStylesheet.usage = collectUsageData(ast, options);
minifyOptionsForAttribute.usage = null;

elems.forEach(function(elem) {
if (elem.isElem('style')) {
// <style> element
var styleCss = elem.content[0].text || elem.content[0].cdata || [];
var DATA = styleCss.indexOf('>') >= 0 || styleCss.indexOf('<') >= 0 ? 'cdata' : 'text';

elem.content[0][DATA] = csso.minify(styleCss, minifyOptionsForStylesheet).css;
} else {
// style attribute
var elemStyle = elem.attr('style').value;

elem.attr('style').value = csso.minifyBlock(elemStyle, minifyOptionsForAttribute).css;
}
});

return ast;
};

function cloneObject(obj) {
var result = {};

for (var key in obj) {
result[key] = obj[key];
}

return result;
}

function findStyleElems(ast) {

function walk(items, styles) {
for (var i = 0; i < items.content.length; i++) {
var item = items.content[i];

// go deeper
if (item.content) {
walk(item, styles);
}

if (item.isElem('style') && !item.isEmpty()) {
styles.push(item);
} else if (item.isElem() && item.hasAttr('style')) {
styles.push(item);
}
}
}

if(item.hasAttr('style')) {
var itemCss = item.attr('style').value;
if(itemCss.length > 0) {
var itemCssMinified = csso.minifyBlock(itemCss, svgoOptions);
item.attr('style').value = itemCssMinified.css;
}
}
return styles;
}

return item;
};
return walk(ast, []);
}

function shouldFilter(options, name) {
if ('usage' in options === false) {
return true;
}

if (options.usage && name in options.usage === false) {
return true;
}

return Boolean(options.usage && options.usage[name]);
}

function collectUsageData(ast, options) {

function walk(items, usageData) {
for (var i = 0; i < items.content.length; i++) {
var item = items.content[i];

// go deeper
if (item.content) {
walk(item, usageData);
}

if (item.isElem('script')) {
safe = false;
}

if (item.isElem()) {
usageData.tags[item.elem] = true;

if (item.hasAttr('id')) {
usageData.ids[item.attr('id').value] = true;
}

if (item.hasAttr('class')) {
item.attr('class').value.replace(/^\s+|\s+$/g, '').split(/\s+/).forEach(function(className) {
usageData.classes[className] = true;
});
}

if (item.attrs && Object.keys(item.attrs).some(function(name) { return /^on/i.test(name); })) {
safe = false;
}
}
}

return usageData;
}

var safe = true;
var usageData = {};
var hasData = false;
var rawData = walk(ast, {
ids: Object.create(null),
classes: Object.create(null),
tags: Object.create(null)
});

if (!safe && options.usage && options.usage.force) {
safe = true;
}

for (var key in rawData) {
if (shouldFilter(options, key)) {
usageData[key] = Object.keys(rawData[key]);
hasData = true;
}
}

return safe && hasData ? usageData : null;
}
4 changes: 2 additions & 2 deletions test/plugins/minifyStyles.01.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions test/plugins/minifyStyles.02.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions test/plugins/minifyStyles.03.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions test/plugins/minifyStyles.04.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions test/plugins/minifyStyles.05.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions test/plugins/minifyStyles.06.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions test/plugins/minifyStyles.07.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions test/plugins/minifyStyles.08.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 282c193

Please sign in to comment.