Skip to content

Commit

Permalink
Replace cssom with mensch
Browse files Browse the repository at this point in the history
First step for Automattic#215 , just changed 1 testcase and
altered test comparison to consider multiple newlines as one newline.
  • Loading branch information
bago committed Jun 22, 2016
1 parent 8e16f5b commit 27681e3
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 73 deletions.
22 changes: 13 additions & 9 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,19 @@ function inlineDocument($, css, options) {
// go through the properties
function addProps(style, selector) {
for (var i = 0, l = style.length; i < l; i++) {
var name = style[i];
var value = style[name] + (options.preserveImportant && style._importants[name] ? ' !important' : '');
var prop = new utils.Property(name, value, selector, style._importants[name] ? 2 : 0);
var existing = el.styleProps[name];

// if property name is not in the excluded properties array
if (juiceClient.excludedProperties.indexOf(name) < 0) {
if (existing && existing.compare(prop) === prop || !existing) {
el.styleProps[name] = prop;
if (style[i].type == 'property') {
var name = style[i].name;
var value = style[i].value;
var important = style[i].value.match(/!important$/) !== null;
if (important && !options.preserveImportant) value = value.replace(/\s*!important$/, '');
var prop = new utils.Property(name, value, selector, important ? 2 : 0);
var existing = el.styleProps[name];

// if property name is not in the excluded properties array
if (juiceClient.excludedProperties.indexOf(name) < 0) {
if (existing && existing.compare(prop) === prop || !existing) {
el.styleProps[name] = prop;
}
}
}
}
Expand Down
107 changes: 47 additions & 60 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Module dependencies.
*/

var cssom = require('cssom');
var mensch = require('mensch');
var cheerio = require('cheerio');
var own = {}.hasOwnProperty;
var os = require('os');
Expand Down Expand Up @@ -61,33 +61,36 @@ exports.extract = function extract(selectorText) {
*/

exports.parseCSS = function(css) {
var rules = cssom.parse(css).cssRules || [];
var parsed = mensch.parse(css);
var rules = typeof parsed.stylesheet != 'undefined' && parsed.stylesheet.rules ? parsed.stylesheet.rules : [];
var ret = [];

for (var i = 0, l = rules.length; i < l; i++) {
if (rules[i].selectorText) { // media queries don't have selectorText
if (rules[i].type == 'rule') {
var rule = rules[i];
var selectors = exports.extract(rule.selectorText);
var selectors = rule.selectors;

for (var ii = 0, ll = selectors.length; ii < ll; ii++) {
ret.push([selectors[ii], rule.style]);
ret.push([selectors[ii], rule.declarations]);
}
}
}

return ret;
};


var getStringifiedStyles = function(rule) {
var styles = [];
for (var style = 0; style < rule.style.length; style++) {
var property = rule.style[style];
var value = rule.style[property];
var important = rule.style._importants[property] ? ' !important' : '';
styles.push(' ' + property + ': ' + value + important + ';');
}
return styles;
var removeStyle = function(style, startPos, endPos, skipRows, startOffset, endOffset, insert) {
var styleRows = style.split("\n");
var start = startOffset;
var end = endOffset;
for (var r = 1 + skipRows; r < startPos.line; r++) start += styleRows[r - 1 - skipRows].length + 1;
start += startPos.col;
if (endPos !== null) {
for (var r2 = 1 + skipRows; r2 < endPos.line; r2++) end += styleRows[r2 - 1 - skipRows].length + 1;
end += endPos.col;
} else end += style.length + 1;
var newStyle = style.substr(0, start - 1) + insert + style.substr(end - 1);
return newStyle;
};

/**
Expand All @@ -99,52 +102,35 @@ var getStringifiedStyles = function(rule) {
*/

exports.getPreservedText = function(css, options) {
var rules = cssom.parse(css).cssRules || [];
var preserved = [];

for (var i = 0, l = rules.length; i < l; i++) {
/* CSS types
STYLE: 1,
IMPORT: 3,
MEDIA: 4,
FONT_FACE: 5,
*/

if (options.fontFaces && rules[i].type === cssom.CSSFontFaceRule.prototype.type) {
var fontFace = [ '' ];
fontFace.push('@font-face {');
fontFace = fontFace.concat(getStringifiedStyles(rules[i]));
fontFace.push('}');

if (fontFace.length) {
preserved.push(fontFace.length ? fontFace.join(os.EOL) + os.EOL : '');
}
}

if (options.mediaQueries && rules[i].type === cssom.CSSMediaRule.prototype.type) {
var query = rules[i];
var queryString = [];

queryString.push(os.EOL + '@media ' + query.media[0] + ' {');

for (var ii = 0, ll = query.cssRules.length; ii < ll; ii++) {
var rule = query.cssRules[ii];

if (rule.type === cssom.CSSStyleRule.prototype.type
|| rule.type === cssom.CSSFontFaceRule.prototype.type) {
queryString.push(' '
+ (rule.type === cssom.CSSStyleRule.prototype.type ? rule.selectorText : '@font-face') + ' {');
queryString = queryString.concat(getStringifiedStyles(rule));
queryString.push(' }');
}
}

queryString.push('}');
preserved.push(queryString.length ? queryString.join(os.EOL) + os.EOL : '');
var parsed = mensch.parse(css, {position: true, comments: true});
var rules = typeof parsed.stylesheet != 'undefined' && parsed.stylesheet.rules ? parsed.stylesheet.rules : [];
var preserved = css;
var preserved2 = [];
var lastStart = null;

for (var i = rules.length - 1; i >= 0; i--) {
if (options.fontFaces && rules[i].type === 'font-face') {
// preserve
preserved2.push(mensch.stringify({ stylesheet: { rules: [ rules[i] ] }}, { comments: false, indentation: ' ' }).replace(/}/,os.EOL+'}'));
} else if (options.mediaQueries && rules[i].type === 'media') {
// preserve
preserved2.push(mensch.stringify({ stylesheet: { rules: [ rules[i] ] }}, { comments: false, indentation: ' ' }));
} else {
// remove
preserved = removeStyle(preserved, rules[i].position.start, lastStart, 0, 0, 0, '');
// TODO +os.EOL?
}
lastStart = rules[i].position.start;
}
// preserved works by removing unwanted stuff, preserved2 by generating a new style with "stuff to keep"
// We have to decide what is our strategy (the preserved2 produces the same output of the cssom based version <= 2.0.0)
if (false) {
if (preserved.trim().length === 0) return false;
return preserved;
} else {
if (preserved2.length === 0) return false;
return os.EOL+preserved2.join(os.EOL)+os.EOL;
}

return preserved.join(os.EOL);
};

/**
Expand All @@ -160,7 +146,8 @@ exports.cheerio = function(html, options) {
};

exports.normalizeLineEndings = function(text) {
return text.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
// NOTE this consider multiple newlines the same as a single newline
return text.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n').replace(/\r\n\r\n/g, '\r\n');
};

exports.encodeEJS = function(html) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"cheerio": "0.20.0",
"commander": "2.9.0",
"cross-spawn-async": "^2.1.8",
"cssom": "0.3.1",
"mensch": "brettstimmerman/mensch^0.3.2",
"deep-extend": "^0.4.0",
"slick": "1.12.2",
"web-resource-inliner": "2.0.0"
Expand Down
5 changes: 2 additions & 3 deletions test/juice.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,13 @@ it('parse simple css into a object structure', function() {
var parse = utils.parseCSS;

var actual = parse('a, b { c: e; }');

var a = actual[0];
var b = actual[1];

assert.equal(a[0],'a');
assert.equal(a[1]['0'],'c');
assert.deepEqual(a[1]['0'],{ type: 'property', name: 'c', value: 'e' });
assert.equal(a[1].length,1);
assert.deepEqual(a[1]._importants, { c: '' });
assert.equal(a[1].c,'e');
assert.deepEqual(a[1],b[1]);
});

Expand Down

0 comments on commit 27681e3

Please sign in to comment.