Skip to content

Commit

Permalink
Change propWhiteList to propList
Browse files Browse the repository at this point in the history
  • Loading branch information
cuth committed Jul 18, 2016
1 parent e1a3bff commit aa52ad5
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 13 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Default:
{
rootValue: 16,
unitPrecision: 5,
propWhiteList: ['font', 'font-size', 'line-height', 'letter-spacing'],
propList: ['font', 'font-size', 'line-height', 'letter-spacing'],
selectorBlackList: [],
replace: true,
mediaQuery: false,
Expand All @@ -68,9 +68,14 @@ Default:

- `rootValue` (Number) The root element font size.
- `unitPrecision` (Number) The decimal numbers to allow the REM units to grow to.
- `propWhiteList` (Array) The properties that can change from px to rem.
- Set this to an empty array to disable the white list and enable all properties.
- `propList` (Array) The properties that can change from px to rem.
- Values need to be exact matches.
- Use wildcard `*` to enable all properties. Example: `['*']`
- Use `~` to match any part of the property. (`['~position']` will match `background-position-y`)
- Use `^` to match the start of the property. (`['^font']` will match `font-weight`)
- Use `$` to match the end of the property. (`['$-radius']` will match `border-top-right-radius`)
- Use `!` to not match a property. Example: `['*', '!letter-spacing']`
- Combine the "not" prefix with the other prefixes. Example: `['*', '!~margin']`
- `selectorBlackList` (Array) The selectors to ignore and leave as px.
- If value is string, it checks to see if selector contains the string.
- `['body']` will match `.body-class`
Expand Down
70 changes: 65 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
'use strict';

var postcss = require('postcss');
var pxRegex = require('./lib/pixel-unit-regex');
var objectAssign = require('object-assign');
var pxRegex = require('./lib/pixel-unit-regex');
var filterPropList = require('./lib/filter-prop-list');

var defaults = {
rootValue: 16,
unitPrecision: 5,
selectorBlackList: [],
propWhiteList: ['font', 'font-size', 'line-height', 'letter-spacing'],
propList: ['font', 'font-size', 'line-height', 'letter-spacing'],
replace: true,
mediaQuery: false,
minPixelValue: 0
Expand All @@ -18,8 +19,9 @@ var legacyOptions = {
'root_value': 'rootValue',
'unit_precision': 'unitPrecision',
'selector_black_list': 'selectorBlackList',
'prop_white_list': 'propWhiteList',
'media_query': 'mediaQuery'
'prop_white_list': 'propList',
'media_query': 'mediaQuery',
'propWhiteList': 'propList'
};

module.exports = postcss.plugin('postcss-pxtorem', function (options) {
Expand All @@ -29,13 +31,15 @@ module.exports = postcss.plugin('postcss-pxtorem', function (options) {
var opts = objectAssign({}, defaults, options);
var pxReplace = createPxReplace(opts.rootValue, opts.unitPrecision, opts.minPixelValue);

var satisfyPropList = createPropListMatcher(opts.propList);

return function (css) {

css.walkDecls(function (decl, i) {
// This should be the fastest test and will remove most declarations
if (decl.value.indexOf('px') === -1) return;

if (opts.propWhiteList.length && opts.propWhiteList.indexOf(decl.prop) === -1) return;
if (!satisfyPropList(decl.prop)) return;

if (blacklistedSelector(opts.selectorBlackList, decl.parent.selector)) return;

Expand Down Expand Up @@ -63,6 +67,17 @@ module.exports = postcss.plugin('postcss-pxtorem', function (options) {

function convertLegacyOptions(options) {
if (typeof options !== 'object') return;
if (
(
(typeof options['prop_white_list'] !== 'undefined' && options['prop_white_list'].length === 0) ||
(typeof options.propWhiteList !== 'undefined' && options.propWhiteList.length === 0)
) &&
typeof options.propList === 'undefined'
) {
options.propList = ['*'];
delete options['prop_white_list'];
delete options.propWhiteList;
}
Object.keys(legacyOptions).forEach(function (key) {
if (options.hasOwnProperty(key)) {
options[legacyOptions[key]] = options[key];
Expand Down Expand Up @@ -99,3 +114,48 @@ function blacklistedSelector(blacklist, selector) {
return selector.match(regex);
});
}

function createPropListMatcher(propList) {
var hasWild = propList.indexOf('*') > -1;
var matchAll = (hasWild && propList.length === 1);
var lists = {
exact: filterPropList.exact(propList),
contain: filterPropList.contain(propList),
start: filterPropList.start(propList),
end: filterPropList.end(propList),
not: filterPropList.not(propList),
notContain: filterPropList.notContain(propList),
notStart: filterPropList.notStart(propList),
notEnd: filterPropList.notEnd(propList)
};
return function (prop) {
if (matchAll) return true;
return (
(
hasWild ||
lists.exact.indexOf(prop) > -1 ||
lists.contain.some(function (m) {
return prop.indexOf(m) > -1;
}) ||
lists.start.some(function (m) {
return prop.indexOf(m) === 0;
}) ||
lists.end.some(function (m) {
return prop.indexOf(m) === prop.length - m.length;
})
) &&
!(
lists.not.indexOf(prop) > -1 ||
lists.notContain.some(function (m) {
return prop.indexOf(m) > -1;
}) ||
lists.notStart.some(function (m) {
return prop.indexOf(m) === 0;
}) ||
lists.notEnd.some(function (m) {
return prop.indexOf(m) === prop.length - m.length;
})
)
);
};
}
50 changes: 50 additions & 0 deletions lib/filter-prop-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module.exports = {
exact: function (list) {
return list.filter(function (m) {
return m.match(/^[^\*\~\^\$\!]+/);
});
},
contain: function (list) {
return list.filter(function (m) {
return m.indexOf('~') === 0;
}).map(trimFirstCharacter);
},
start: function (list) {
return list.filter(function (m) {
return m.indexOf('^') === 0;
}).map(trimFirstCharacter);
},
end: function (list) {
return list.filter(function (m) {
return m.indexOf('$') === 0;
}).map(trimFirstCharacter);
},
not: function (list) {
return list.filter(function (m) {
return m.match(/^\![^\~\^\$]+/);
}).map(trimFirstCharacter);
},
notContain: function (list) {
return list.filter(function (m) {
return m.indexOf('!~') === 0;
}).map(trimFirstTwoCharacters);
},
notStart: function (list) {
return list.filter(function (m) {
return m.indexOf('!^') === 0;
}).map(trimFirstTwoCharacters);
},
notEnd: function (list) {
return list.filter(function (m) {
return m.indexOf('!$') === 0;
}).map(trimFirstTwoCharacters);
}
};

function trimFirstCharacter(str) {
return str.substring(1);
}

function trimFirstTwoCharacters(str) {
return str.substring(2);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "postcss-pxtorem",
"description": "A CSS post-processor that converts px to rem.",
"version": "3.3.1",
"version": "3.4.0",
"author": "cuth",
"license": "MIT",
"repository": {
Expand Down
125 changes: 121 additions & 4 deletions spec/pxtorem-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
var postcss = require('postcss');
var pxtorem = require('..');
var basicCSS = '.rule { font-size: 15px }';
var filterPropList = require('../lib/filter-prop-list');

describe('pxtorem', function () {
it('should work on the readme example', function () {
Expand All @@ -33,7 +34,7 @@ describe('pxtorem', function () {
expect(processed).toBe(expected);
});

it('should handle < 1 values and values without a leading 0', function () {
it('should handle < 1 values and values without a leading 0 - legacy', function () {
var rules = '.rule { margin: 0.5rem .5px -0.2px -.2em }';
var expected = '.rule { margin: 0.5rem 0.03125rem -0.0125rem -.2em }';
var options = {
Expand All @@ -44,6 +45,17 @@ describe('pxtorem', function () {
expect(processed).toBe(expected);
});

it('should handle < 1 values and values without a leading 0', function () {
var rules = '.rule { margin: 0.5rem .5px -0.2px -.2em }';
var expected = '.rule { margin: 0.5rem 0.03125rem -0.0125rem -.2em }';
var options = {
propList: ['margin']
};
var processed = postcss(pxtorem(options)).process(rules).css;

expect(processed).toBe(expected);
});

it('should not add properties that already exist', function () {
var expected = '.rule { font-size: 16px; font-size: 1rem; }';
var processed = postcss(pxtorem()).process(expected).css;
Expand All @@ -53,7 +65,7 @@ describe('pxtorem', function () {
});

describe('value parsing', function () {
it('should not replace values in double quotes or single quotes', function () {
it('should not replace values in double quotes or single quotes - legacy', function () {
var options = {
propWhiteList: []
};
Expand All @@ -64,7 +76,18 @@ describe('value parsing', function () {
expect(processed).toBe(expected);
});

it('should not replace values in `url()`', function () {
it('should not replace values in double quotes or single quotes', function () {
var options = {
propList: ['*']
};
var rules = '.rule { content: \'16px\'; font-family: "16px"; font-size: 16px; }';
var expected = '.rule { content: \'16px\'; font-family: "16px"; font-size: 1rem; }';
var processed = postcss(pxtorem(options)).process(rules).css;

expect(processed).toBe(expected);
});

it('should not replace values in `url()` - legacy', function () {
var options = {
propWhiteList: []
};
Expand All @@ -74,6 +97,17 @@ describe('value parsing', function () {

expect(processed).toBe(expected);
});

it('should not replace values in `url()`', function () {
var options = {
propList: ['*']
};
var rules = '.rule { background: url(16px.jpg); font-size: 16px; }';
var expected = '.rule { background: url(16px.jpg); font-size: 1rem; }';
var processed = postcss(pxtorem(options)).process(rules).css;

expect(processed).toBe(expected);
});
});

describe('rootValue', function () {
Expand Down Expand Up @@ -134,7 +168,7 @@ describe('propWhiteList', function () {
expect(processed).toBe(expected);
});

it('should only replace properties in the white list', function () {
it('should only replace properties in the white list - legacy', function () {
var expected = '.rule { font-size: 15px }';
var options = {
propWhiteList: ['font']
Expand All @@ -144,6 +178,39 @@ describe('propWhiteList', function () {
expect(processed).toBe(expected);
});

it('should only replace properties in the white list - legacy', function () {
var css = '.rule { margin: 16px; margin-left: 10px }';
var expected = '.rule { margin: 1rem; margin-left: 10px }';
var options = {
propWhiteList: ['margin']
};
var processed = postcss(pxtorem(options)).process(css).css;

expect(processed).toBe(expected);
});

it('should only replace properties in the prop list', function () {
var css = '.rule { font-size: 16px; margin: 16px; margin-left: 5px; padding: 5px; padding-right: 16px }';
var expected = '.rule { font-size: 1rem; margin: 1rem; margin-left: 5px; padding: 5px; padding-right: 1rem }';
var options = {
propWhiteList: ['~font', '^margin', '!margin-left', '$-right', 'pad']
};
var processed = postcss(pxtorem(options)).process(css).css;

expect(processed).toBe(expected);
});

it('should only replace properties in the prop list with wildcard', function () {
var css = '.rule { font-size: 16px; margin: 16px; margin-left: 5px; padding: 5px; padding-right: 16px }';
var expected = '.rule { font-size: 16px; margin: 1rem; margin-left: 5px; padding: 5px; padding-right: 16px }';
var options = {
propWhiteList: ['*', '!margin-left', '!~padding', '!^font']
};
var processed = postcss(pxtorem(options)).process(css).css;

expect(processed).toBe(expected);
});

it('should replace all properties when white list is empty', function () {
var rules = '.rule { margin: 16px; font-size: 15px }';
var expected = '.rule { margin: 1rem; font-size: 0.9375rem }';
Expand Down Expand Up @@ -251,3 +318,53 @@ describe('minPixelValue', function () {
expect(processed).toBe(expected);
});
});

describe('filter-prop-list', function () {
it('should find "exact" matches from propList', function () {
var propList = ['font-size', 'margin', '!padding', '~border', '*', '$y', '!~font'];
var expected = 'font-size,margin';
expect(filterPropList.exact(propList).join()).toBe(expected);
});

it('should find "contain" matches from propList and reduce to string', function () {
var propList = ['font-size', '~margin', '!padding', '~border', '*', '$y', '!~font'];
var expected = 'margin,border';
expect(filterPropList.contain(propList).join()).toBe(expected);
});

it('should find "start" matches from propList and reduce to string', function () {
var propList = ['font-size', '~margin', '!padding', '^border', '*', '$y', '!~font'];
var expected = 'border';
expect(filterPropList.start(propList).join()).toBe(expected);
});

it('should find "end" matches from propList and reduce to string', function () {
var propList = ['font-size', '~margin', '!padding', '^border', '*', '$y', '!~font'];
var expected = 'y';
expect(filterPropList.end(propList).join()).toBe(expected);
});

it('should find "not" matches from propList and reduce to string', function () {
var propList = ['font-size', '~margin', '!padding', '^border', '*', '$y', '!~font'];
var expected = 'padding';
expect(filterPropList.not(propList).join()).toBe(expected);
});

it('should find "not contain" matches from propList and reduce to string', function () {
var propList = ['font-size', '~margin', '!padding', '!^border', '*', '$y', '!~font'];
var expected = 'font';
expect(filterPropList.notContain(propList).join()).toBe(expected);
});

it('should find "not start" matches from propList and reduce to string', function () {
var propList = ['font-size', '~margin', '!padding', '!^border', '*', '$y', '!~font'];
var expected = 'border';
expect(filterPropList.notStart(propList).join()).toBe(expected);
});

it('should find "not end" matches from propList and reduce to string', function () {
var propList = ['font-size', '~margin', '!padding', '!^border', '*', '!$y', '!~font'];
var expected = 'y';
expect(filterPropList.notEnd(propList).join()).toBe(expected);
});
});

0 comments on commit aa52ad5

Please sign in to comment.