css-isolate
Version:
Isolate CSS declaration differences between 2 files/strings within the context of their rules in order to produce a set of overrides
112 lines (89 loc) • 2.99 kB
JavaScript
/*
* cssDeclarationDiff
* http://github.com/webxl/css-isolate
*
* Copyright (c) 2014 Matt Motherway
* Licensed under the MIT license.
*/
;
var parse = require('css').parse,
stringify = require('css').stringify;
// returns hash map of selector+prop -> value
function hashRules (rules) {
var hash = {};
rules.forEach(function (rule) {
if (!rule.selectors) { return; }
rule.selectors.forEach(function (selector) {
if (!rule.declarations) { return; }
rule.declarations.forEach(function (declaration) {
if (declaration.value) { // needed for bootstrap ie8-9 hack
hash[selector + declaration.property] = declaration.value;
}
});
});
});
return hash;
}
exports.diff = function(cssStr1, cssStr2, opts) {
if (!cssStr1 || !cssStr2) {
throw 'Must provide 2 CSS strings in order to diff';
}
opts = opts || {};
var rules1 = parse(cssStr1).stylesheet.rules,
rules2 = parse(cssStr2).stylesheet.rules,
rules1Hash = hashRules(rules1),
rules2Hash = hashRules(rules2),
diffRules = [];
rules2.forEach(function (rule) {
if (!rule.selectors) { return; }
var newRule, lastPropFound;
rule.selectors.forEach(function (selector) {
if (!rule.declarations) { return; }
rule.declarations.forEach(function (declaration) {
// ignore declarations that have been overridden or ie hacks
if (rules2Hash[selector + declaration.property] !== declaration.value || !declaration.value) {
return;
}
if (rules1Hash[selector + declaration.property] !== declaration.value || !rules1Hash[selector + declaration.property] ||
(lastPropFound && declaration.property.indexOf(lastPropFound + '-') === 0)) { // include subproperties so specificity is maintained (e.g. border & border-bottom-color)
if (!newRule) {
newRule = JSON.parse(JSON.stringify(rule));
// ensure all unique rules in 2nd string are preserved
// TODO: flatten rules first so that there are no redundant declarations
// can possibly do this within the rule.selectors.forEach loop
if (rules1Hash[selector + declaration.property]) {
delete newRule.declarations;
}
}
if (!newRule.declarations) {
newRule.declarations = [];
}
var found = false;
newRule.declarations.forEach(function (dec) {
if (dec.property === declaration.property) {
found = true;
}
});
if (!found) {
newRule.declarations.push(declaration);
lastPropFound = declaration.property;
}
}
});
});
if (newRule) {
diffRules.push(newRule);
}
});
return stringify(
{
type: "stylesheet",
stylesheet: {
rules: diffRules
}
},
{
compress: opts.compress
}
);
};