Skip to content
This repository has been archived by the owner on May 10, 2023. It is now read-only.

Commit

Permalink
Rewrite module, following PostCSS plugins guidelines
Browse files Browse the repository at this point in the history
  • Loading branch information
iamvdo committed Sep 21, 2015
1 parent 8bbaeaa commit 9d42ccb
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 207 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
language: node_js
node_js:
- "0.10"
- "0.12"
- "io.js"
185 changes: 79 additions & 106 deletions lib/pixrem.js
Original file line number Diff line number Diff line change
@@ -1,124 +1,109 @@
'use strict';
var calc = require('reduce-css-calc');
var vendor = require('postcss/lib/vendor');
var postcss = require('postcss');
var browserslist = require('browserslist');

var _remgex, _PROPS, _VALUES, _rootvalue, _options;
var REGEX = /(\d*\.?\d+)rem/ig;
var BASE_FONT_SIZE = 16;
var PROPS = /^(background-size|border-image|border-radius|box-shadow|clip-path|column|grid|mask|object|perspective|scroll|shape|size|stroke|transform)/;
var VALUES = /(calc|gradient)\(/;

module.exports = postcss.plugin('pixrem', function (opts) {

// Add pixel fallbacks for rem units to a string of CSS
// - css `String`: the contents of a CSS file.
// - rootvalue `String | Null`: The root element font size. Default = 16px.
// - options `Object`
// - replace `Boolean`: Replace rems with pixels instead of providing
// fallbacks. Default = false.
// - atrules `Boolean`: Replace rems in at-rules too. Default = false

function Pixrem (rootvalue, options) {
_remgex = /(\d*\.?\d+)rem/ig;
_PROPS = /^(background-size|border-image|border-radius|box-shadow|clip-path|column|grid|mask|object|perspective|scroll|shape|size|stroke|transform)/;
_VALUES = /(calc|gradient)\(/;
_rootvalue = typeof rootvalue !== 'undefined' ? rootvalue : BASE_FONT_SIZE;
options = options || {};
_options = {};
_options.replace = (options.replace !== undefined) ? options.replace : false;
_options.atrules = (options.atrules !== undefined) ? options.atrules : false;
_options.html = (options.html !== undefined) ? options.html : true;
_options.browsers = (options.browsers !== undefined) ? options.browsers : 'ie <= 8';
}

Pixrem.prototype.process = function (css, options) {
return postcss(this.postcss).process(css, options).css;
};
opts = opts || {};

Pixrem.prototype.postcss = function (css) {
return function (css, result) {

var vendor = require('postcss/lib/vendor');
var browsers = browserslist(_options.browsers);
var options = {};
options.rootValue = (opts.rootValue !== undefined) ? opts.rootValue : BASE_FONT_SIZE;
options.replace = (opts.replace !== undefined) ? opts.replace : false;
options.atrules = (opts.atrules !== undefined) ? opts.atrules : false;
options.html = (opts.html !== undefined) ? opts.html : true;
options.browsers = (opts.browsers !== undefined) ? opts.browsers : 'ie <= 8';
options.browsers = browserslist(options.browsers);

// detect IE versions needed
var isIElte8, isIEgte9, isIE9_10;
if (detectBrowser(browsers, 'ie <= 8')) {
isIElte8 = true;
}
if (detectBrowser(browsers, 'ie >= 9')) {
isIEgte9 = true;
}
if (detectBrowser(browsers, 'ie 9, ie 10')) {
isIE9_10 = true;
}
// no IE versions needed, skip
if (!isIElte8 && !isIEgte9 && !isIE9_10) { return; }
var isIElte8, isIEgte9, isIE9_10;
if (detectBrowser(options.browsers, 'ie <= 8')) {
isIElte8 = true;
}
if (detectBrowser(options.browsers, 'ie >= 9')) {
isIEgte9 = true;
}
if (detectBrowser(options.browsers, 'ie 9, ie 10')) {
isIE9_10 = true;
}
// no IE versions needed, skip
if (!isIElte8 && !isIEgte9 && !isIE9_10) { return; }

if (options.html) {
// First, check root font-size
css.walkRules(function (rule) {
if (rule.parent && rule.parent.type === 'atrule') { return; }
if (/^(html|:root)$/.test(rule.selectors)) {
rule.walkDecls(function (decl) {
if (decl.prop === 'font-size') {
options.rootValue = decl.value;
} else if (decl.prop === 'font' && decl.value.match(/\d/)) {
options.rootValue = decl.value.match(/.*?([\d\.]*(em|px|rem|%|pt|pc))/)[1];
}
});
}
});
}

if (_options.html) {
// First, check root font-size
css.walkRules(function (rule) {
if (rule.parent && rule.parent.type === 'atrule') { return; }
if (/^(html|:root)$/.test(rule.selectors)) {
rule.walkDecls(function (decl) {
if (decl.prop === 'font-size') {
_rootvalue = decl.value;
} else if (decl.prop === 'font' && decl.value.match(/\d/)) {
_rootvalue = decl.value.match(/.*?([\d\.]*(em|px|rem|%|pt|pc))/)[1];
}
});
}
});
}

//Then, for each rules
css.walkRules(function (rule) {

// if options.at-rules is false AND it's not IE9-10: skip @rules
if (!_options.atrules && !isIE9_10) {
if (rule.type === 'atrule' || (rule.parent && rule.parent.type === 'atrule')) { return; }
}
// if options.at-rules is false AND it's not IE9-10: skip @rules
if (!options.atrules && !isIE9_10) {
if (rule.type === 'atrule' || (rule.parent && rule.parent.type === 'atrule')) { return; }
}

var isPseudoElement = (rule.selector.search(/:(after|before)/gi) !== -1);
var isPseudoElement = (rule.selector.search(/:(after|before)/gi) !== -1);

rule.each(function (decl, i) {
rule.each(function (decl, i) {

if (decl.type !== 'decl') { return; }
if (decl.type !== 'decl') { return; }

var value = decl.value;
var value = decl.value;

if (value.indexOf('rem') !== -1) {
if (value.indexOf('rem') !== -1) {

var prop = vendor.unprefixed(decl.prop);
// replace rems only if needed
var isFontShorthand = (prop === 'font');
var isSpecialCaseIE9_10 = (isIE9_10 && (isPseudoElement || isFontShorthand));
var isUseless = (!isIE9_10 && !(_VALUES.test(value) || _PROPS.test(prop)));
var prop = vendor.unprefixed(decl.prop);
var isFontShorthand = (prop === 'font');
var isSpecialCaseIE9_10 = (isIE9_10 && (isPseudoElement || isFontShorthand));
var isUseless = (VALUES.test(value) || PROPS.test(prop));
var isNotUseless = (!isIE9_10 && !isUseless);

if ( isSpecialCaseIE9_10 || isUseless ) {
if ( isSpecialCaseIE9_10 || isNotUseless ) {

value = value.replace(_remgex, function ($1) {
// Round decimal pixels down to match webkit and opera behavior:
// http://tylertate.com/blog/2012/01/05/subpixel-rounding.html
return Math.floor(parseFloat($1) * toPx(_rootvalue)) + 'px';
});
value = value.replace(REGEX, function ($1) {
// Round decimal pixels down to match webkit and opera behavior:
// http://tylertate.com/blog/2012/01/05/subpixel-rounding.html
return Math.floor(parseFloat($1) * toPx(options.rootValue)) + 'px';
});

if (_options.replace) {
decl.value = value;
} else {
var clone = decl.clone({ value: value });
if (decl.raws.before) {
clone.raws.before = decl.raws.before;
decl.raws.before = reduceLineBreaks(decl.raws.before);
if (options.replace) {
decl.value = value;
} else {
var clone = decl.clone({ value: value });
if (decl.raws.before) {
clone.raws.before = decl.raws.before;
decl.raws.before = reduceLineBreaks(decl.raws.before);
}
rule.insertBefore(i, clone);
}
rule.insertBefore(i, clone);

}

}

}
});

});

});
};

};
});

// Detect if one browser from the browserQuery is in browsers
function detectBrowser (browsers, browserQuery) {
Expand All @@ -136,11 +121,6 @@ function detectBrowser (browsers, browserQuery) {
return b;
}

// Reduce line breaks
function reduceLineBreaks (value) {
return value.replace(/(\r*\n|\r)+/g, '$1');
}

// Return a unitless pixel value from any root font-size value.
function toPx (value) {
value = (typeof value === 'string' && value.indexOf('calc(') !== -1) ? calc(value) : value;
Expand All @@ -166,14 +146,7 @@ function toPx (value) {
}
}

var pixrem = function (rootvalue, options) {
return new Pixrem(rootvalue, options);
};
pixrem.process = function (css, rootvalue, options, postcssoptions) {
return new Pixrem(rootvalue, options).process(css, postcssoptions);
};
pixrem.postcss = function (css) {
return new Pixrem().postcss(css);
};

module.exports = pixrem;
// Reduce line breaks
function reduceLineBreaks (value) {
return value.replace(/(\r*\n|\r)+/g, '$1');
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@
"npm": ">=1.2.10"
},
"scripts": {
"test": "jasmine-node spec"
"test": "mocha"
},
"dependencies": {
"browserslist": "^0.5.0",
"postcss": "^5.0.0",
"reduce-css-calc": "^1.2.0"
},
"devDependencies": {
"jasmine-node": "~1.11.0"
"mocha": "^2.3.2"
},
"keywords": [
"css",
Expand Down
Loading

0 comments on commit 9d42ccb

Please sign in to comment.