Skip to content

Commit aa52ad5

Browse files
committed
Change propWhiteList to propList
1 parent e1a3bff commit aa52ad5

File tree

5 files changed

+245
-13
lines changed

5 files changed

+245
-13
lines changed

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Default:
5858
{
5959
rootValue: 16,
6060
unitPrecision: 5,
61-
propWhiteList: ['font', 'font-size', 'line-height', 'letter-spacing'],
61+
propList: ['font', 'font-size', 'line-height', 'letter-spacing'],
6262
selectorBlackList: [],
6363
replace: true,
6464
mediaQuery: false,
@@ -68,9 +68,14 @@ Default:
6868

6969
- `rootValue` (Number) The root element font size.
7070
- `unitPrecision` (Number) The decimal numbers to allow the REM units to grow to.
71-
- `propWhiteList` (Array) The properties that can change from px to rem.
72-
- Set this to an empty array to disable the white list and enable all properties.
71+
- `propList` (Array) The properties that can change from px to rem.
7372
- Values need to be exact matches.
73+
- Use wildcard `*` to enable all properties. Example: `['*']`
74+
- Use `~` to match any part of the property. (`['~position']` will match `background-position-y`)
75+
- Use `^` to match the start of the property. (`['^font']` will match `font-weight`)
76+
- Use `$` to match the end of the property. (`['$-radius']` will match `border-top-right-radius`)
77+
- Use `!` to not match a property. Example: `['*', '!letter-spacing']`
78+
- Combine the "not" prefix with the other prefixes. Example: `['*', '!~margin']`
7479
- `selectorBlackList` (Array) The selectors to ignore and leave as px.
7580
- If value is string, it checks to see if selector contains the string.
7681
- `['body']` will match `.body-class`

index.js

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
'use strict';
22

33
var postcss = require('postcss');
4-
var pxRegex = require('./lib/pixel-unit-regex');
54
var objectAssign = require('object-assign');
5+
var pxRegex = require('./lib/pixel-unit-regex');
6+
var filterPropList = require('./lib/filter-prop-list');
67

78
var defaults = {
89
rootValue: 16,
910
unitPrecision: 5,
1011
selectorBlackList: [],
11-
propWhiteList: ['font', 'font-size', 'line-height', 'letter-spacing'],
12+
propList: ['font', 'font-size', 'line-height', 'letter-spacing'],
1213
replace: true,
1314
mediaQuery: false,
1415
minPixelValue: 0
@@ -18,8 +19,9 @@ var legacyOptions = {
1819
'root_value': 'rootValue',
1920
'unit_precision': 'unitPrecision',
2021
'selector_black_list': 'selectorBlackList',
21-
'prop_white_list': 'propWhiteList',
22-
'media_query': 'mediaQuery'
22+
'prop_white_list': 'propList',
23+
'media_query': 'mediaQuery',
24+
'propWhiteList': 'propList'
2325
};
2426

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

34+
var satisfyPropList = createPropListMatcher(opts.propList);
35+
3236
return function (css) {
3337

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

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

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

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

6468
function convertLegacyOptions(options) {
6569
if (typeof options !== 'object') return;
70+
if (
71+
(
72+
(typeof options['prop_white_list'] !== 'undefined' && options['prop_white_list'].length === 0) ||
73+
(typeof options.propWhiteList !== 'undefined' && options.propWhiteList.length === 0)
74+
) &&
75+
typeof options.propList === 'undefined'
76+
) {
77+
options.propList = ['*'];
78+
delete options['prop_white_list'];
79+
delete options.propWhiteList;
80+
}
6681
Object.keys(legacyOptions).forEach(function (key) {
6782
if (options.hasOwnProperty(key)) {
6883
options[legacyOptions[key]] = options[key];
@@ -99,3 +114,48 @@ function blacklistedSelector(blacklist, selector) {
99114
return selector.match(regex);
100115
});
101116
}
117+
118+
function createPropListMatcher(propList) {
119+
var hasWild = propList.indexOf('*') > -1;
120+
var matchAll = (hasWild && propList.length === 1);
121+
var lists = {
122+
exact: filterPropList.exact(propList),
123+
contain: filterPropList.contain(propList),
124+
start: filterPropList.start(propList),
125+
end: filterPropList.end(propList),
126+
not: filterPropList.not(propList),
127+
notContain: filterPropList.notContain(propList),
128+
notStart: filterPropList.notStart(propList),
129+
notEnd: filterPropList.notEnd(propList)
130+
};
131+
return function (prop) {
132+
if (matchAll) return true;
133+
return (
134+
(
135+
hasWild ||
136+
lists.exact.indexOf(prop) > -1 ||
137+
lists.contain.some(function (m) {
138+
return prop.indexOf(m) > -1;
139+
}) ||
140+
lists.start.some(function (m) {
141+
return prop.indexOf(m) === 0;
142+
}) ||
143+
lists.end.some(function (m) {
144+
return prop.indexOf(m) === prop.length - m.length;
145+
})
146+
) &&
147+
!(
148+
lists.not.indexOf(prop) > -1 ||
149+
lists.notContain.some(function (m) {
150+
return prop.indexOf(m) > -1;
151+
}) ||
152+
lists.notStart.some(function (m) {
153+
return prop.indexOf(m) === 0;
154+
}) ||
155+
lists.notEnd.some(function (m) {
156+
return prop.indexOf(m) === prop.length - m.length;
157+
})
158+
)
159+
);
160+
};
161+
}

lib/filter-prop-list.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
module.exports = {
2+
exact: function (list) {
3+
return list.filter(function (m) {
4+
return m.match(/^[^\*\~\^\$\!]+/);
5+
});
6+
},
7+
contain: function (list) {
8+
return list.filter(function (m) {
9+
return m.indexOf('~') === 0;
10+
}).map(trimFirstCharacter);
11+
},
12+
start: function (list) {
13+
return list.filter(function (m) {
14+
return m.indexOf('^') === 0;
15+
}).map(trimFirstCharacter);
16+
},
17+
end: function (list) {
18+
return list.filter(function (m) {
19+
return m.indexOf('$') === 0;
20+
}).map(trimFirstCharacter);
21+
},
22+
not: function (list) {
23+
return list.filter(function (m) {
24+
return m.match(/^\![^\~\^\$]+/);
25+
}).map(trimFirstCharacter);
26+
},
27+
notContain: function (list) {
28+
return list.filter(function (m) {
29+
return m.indexOf('!~') === 0;
30+
}).map(trimFirstTwoCharacters);
31+
},
32+
notStart: function (list) {
33+
return list.filter(function (m) {
34+
return m.indexOf('!^') === 0;
35+
}).map(trimFirstTwoCharacters);
36+
},
37+
notEnd: function (list) {
38+
return list.filter(function (m) {
39+
return m.indexOf('!$') === 0;
40+
}).map(trimFirstTwoCharacters);
41+
}
42+
};
43+
44+
function trimFirstCharacter(str) {
45+
return str.substring(1);
46+
}
47+
48+
function trimFirstTwoCharacters(str) {
49+
return str.substring(2);
50+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "postcss-pxtorem",
33
"description": "A CSS post-processor that converts px to rem.",
4-
"version": "3.3.1",
4+
"version": "3.4.0",
55
"author": "cuth",
66
"license": "MIT",
77
"repository": {

spec/pxtorem-spec.js

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
var postcss = require('postcss');
1010
var pxtorem = require('..');
1111
var basicCSS = '.rule { font-size: 15px }';
12+
var filterPropList = require('../lib/filter-prop-list');
1213

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

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

48+
it('should handle < 1 values and values without a leading 0', function () {
49+
var rules = '.rule { margin: 0.5rem .5px -0.2px -.2em }';
50+
var expected = '.rule { margin: 0.5rem 0.03125rem -0.0125rem -.2em }';
51+
var options = {
52+
propList: ['margin']
53+
};
54+
var processed = postcss(pxtorem(options)).process(rules).css;
55+
56+
expect(processed).toBe(expected);
57+
});
58+
4759
it('should not add properties that already exist', function () {
4860
var expected = '.rule { font-size: 16px; font-size: 1rem; }';
4961
var processed = postcss(pxtorem()).process(expected).css;
@@ -53,7 +65,7 @@ describe('pxtorem', function () {
5365
});
5466

5567
describe('value parsing', function () {
56-
it('should not replace values in double quotes or single quotes', function () {
68+
it('should not replace values in double quotes or single quotes - legacy', function () {
5769
var options = {
5870
propWhiteList: []
5971
};
@@ -64,7 +76,18 @@ describe('value parsing', function () {
6476
expect(processed).toBe(expected);
6577
});
6678

67-
it('should not replace values in `url()`', function () {
79+
it('should not replace values in double quotes or single quotes', function () {
80+
var options = {
81+
propList: ['*']
82+
};
83+
var rules = '.rule { content: \'16px\'; font-family: "16px"; font-size: 16px; }';
84+
var expected = '.rule { content: \'16px\'; font-family: "16px"; font-size: 1rem; }';
85+
var processed = postcss(pxtorem(options)).process(rules).css;
86+
87+
expect(processed).toBe(expected);
88+
});
89+
90+
it('should not replace values in `url()` - legacy', function () {
6891
var options = {
6992
propWhiteList: []
7093
};
@@ -74,6 +97,17 @@ describe('value parsing', function () {
7497

7598
expect(processed).toBe(expected);
7699
});
100+
101+
it('should not replace values in `url()`', function () {
102+
var options = {
103+
propList: ['*']
104+
};
105+
var rules = '.rule { background: url(16px.jpg); font-size: 16px; }';
106+
var expected = '.rule { background: url(16px.jpg); font-size: 1rem; }';
107+
var processed = postcss(pxtorem(options)).process(rules).css;
108+
109+
expect(processed).toBe(expected);
110+
});
77111
});
78112

79113
describe('rootValue', function () {
@@ -134,7 +168,7 @@ describe('propWhiteList', function () {
134168
expect(processed).toBe(expected);
135169
});
136170

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

181+
it('should only replace properties in the white list - legacy', function () {
182+
var css = '.rule { margin: 16px; margin-left: 10px }';
183+
var expected = '.rule { margin: 1rem; margin-left: 10px }';
184+
var options = {
185+
propWhiteList: ['margin']
186+
};
187+
var processed = postcss(pxtorem(options)).process(css).css;
188+
189+
expect(processed).toBe(expected);
190+
});
191+
192+
it('should only replace properties in the prop list', function () {
193+
var css = '.rule { font-size: 16px; margin: 16px; margin-left: 5px; padding: 5px; padding-right: 16px }';
194+
var expected = '.rule { font-size: 1rem; margin: 1rem; margin-left: 5px; padding: 5px; padding-right: 1rem }';
195+
var options = {
196+
propWhiteList: ['~font', '^margin', '!margin-left', '$-right', 'pad']
197+
};
198+
var processed = postcss(pxtorem(options)).process(css).css;
199+
200+
expect(processed).toBe(expected);
201+
});
202+
203+
it('should only replace properties in the prop list with wildcard', function () {
204+
var css = '.rule { font-size: 16px; margin: 16px; margin-left: 5px; padding: 5px; padding-right: 16px }';
205+
var expected = '.rule { font-size: 16px; margin: 1rem; margin-left: 5px; padding: 5px; padding-right: 16px }';
206+
var options = {
207+
propWhiteList: ['*', '!margin-left', '!~padding', '!^font']
208+
};
209+
var processed = postcss(pxtorem(options)).process(css).css;
210+
211+
expect(processed).toBe(expected);
212+
});
213+
147214
it('should replace all properties when white list is empty', function () {
148215
var rules = '.rule { margin: 16px; font-size: 15px }';
149216
var expected = '.rule { margin: 1rem; font-size: 0.9375rem }';
@@ -251,3 +318,53 @@ describe('minPixelValue', function () {
251318
expect(processed).toBe(expected);
252319
});
253320
});
321+
322+
describe('filter-prop-list', function () {
323+
it('should find "exact" matches from propList', function () {
324+
var propList = ['font-size', 'margin', '!padding', '~border', '*', '$y', '!~font'];
325+
var expected = 'font-size,margin';
326+
expect(filterPropList.exact(propList).join()).toBe(expected);
327+
});
328+
329+
it('should find "contain" matches from propList and reduce to string', function () {
330+
var propList = ['font-size', '~margin', '!padding', '~border', '*', '$y', '!~font'];
331+
var expected = 'margin,border';
332+
expect(filterPropList.contain(propList).join()).toBe(expected);
333+
});
334+
335+
it('should find "start" matches from propList and reduce to string', function () {
336+
var propList = ['font-size', '~margin', '!padding', '^border', '*', '$y', '!~font'];
337+
var expected = 'border';
338+
expect(filterPropList.start(propList).join()).toBe(expected);
339+
});
340+
341+
it('should find "end" matches from propList and reduce to string', function () {
342+
var propList = ['font-size', '~margin', '!padding', '^border', '*', '$y', '!~font'];
343+
var expected = 'y';
344+
expect(filterPropList.end(propList).join()).toBe(expected);
345+
});
346+
347+
it('should find "not" matches from propList and reduce to string', function () {
348+
var propList = ['font-size', '~margin', '!padding', '^border', '*', '$y', '!~font'];
349+
var expected = 'padding';
350+
expect(filterPropList.not(propList).join()).toBe(expected);
351+
});
352+
353+
it('should find "not contain" matches from propList and reduce to string', function () {
354+
var propList = ['font-size', '~margin', '!padding', '!^border', '*', '$y', '!~font'];
355+
var expected = 'font';
356+
expect(filterPropList.notContain(propList).join()).toBe(expected);
357+
});
358+
359+
it('should find "not start" matches from propList and reduce to string', function () {
360+
var propList = ['font-size', '~margin', '!padding', '!^border', '*', '$y', '!~font'];
361+
var expected = 'border';
362+
expect(filterPropList.notStart(propList).join()).toBe(expected);
363+
});
364+
365+
it('should find "not end" matches from propList and reduce to string', function () {
366+
var propList = ['font-size', '~margin', '!padding', '!^border', '*', '!$y', '!~font'];
367+
var expected = 'y';
368+
expect(filterPropList.notEnd(propList).join()).toBe(expected);
369+
});
370+
});

0 commit comments

Comments
 (0)