├── .babelrc
├── .eslintrc
├── .gitignore
├── .travis.yml
├── HISTORY.md
├── Makefile
├── README.md
├── index.js
├── package.json
├── src
└── index.js
└── test
└── index-test.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "stage-0",
4 | "es2015"
5 | ],
6 | "plugins": [
7 | "add-module-exports"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": "eslint-config-airbnb/base",
4 | "rules": {
5 | "no-console": [0],
6 | "max-len" : [0]
7 | },
8 | "ecmaFeatures": {
9 | "experimentalObjectRestSpread": true
10 | },
11 | "env": {
12 | "node": true,
13 | "mocha": true
14 | }
15 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | npm-debug.log
4 | node_modules/*
5 | coverage
6 | lib
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "4"
5 | - "5"
6 |
7 |
8 | after_success:
9 | - npm run coveralls
10 |
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | HISTORY
2 |
3 | ---------
4 |
5 | ### 0.7.0 - 2016/6/23
6 |
7 | - feat: support multi(px/rpx) units transform to rem unit
8 |
9 | ### 0.6.0 - 2016/6/23
10 |
11 | - feat: add option propBlackList
12 |
13 | ### 0.5.0 - 2016/6/21
14 |
15 | :sparkles:
16 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | publish:
2 | npm run compile
3 | npm publish
4 |
5 | publish-sync: publish
6 | cnpm sync
7 | tnpm sync
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # postcss-plugin-px2rem
2 |
3 | [](https://npmjs.org/package/postcss-plugin-px2rem)
4 | [](https://travis-ci.org/ant-tool/postcss-plugin-px2rem)
5 | [](https://coveralls.io/r/ant-tool/postcss-plugin-px2rem)
6 | [](https://npmjs.org/package/postcss-plugin-px2rem)
7 | [](https://david-dm.org/ant-tool/postcss-plugin-px2rem)
8 |
9 | postcss plugin px2rem.
10 |
11 |
14 |
15 | ## Features
16 |
17 | A plugin for PostCSS that generates rem units from pixel units.
18 |
19 | ## Installation
20 |
21 | ```bash
22 | $ npm i --save postcss-plugin-px2rem
23 | ```
24 |
25 | ## Usage
26 |
27 | ### input and output
28 |
29 | ```css
30 | // input
31 | h1 {
32 | margin: 0 0 20px;
33 | font-size: 32px;
34 | line-height: 1.2;
35 | letter-spacing: 1px;
36 | }
37 |
38 | // output
39 | h1 {
40 | margin: 0 0 0.2rem;
41 | font-size: 0.32rem;
42 | line-height: 1.2;
43 | letter-spacing: 0.01rem;
44 | }
45 | ```
46 |
47 | ### original
48 |
49 | ```javascript
50 | import { writeFile, readFileSync } from 'fs';
51 | import postcss from 'postcss';
52 | import pxtorem from 'postcss-plugin-px2rem';
53 |
54 | const css = readFileSync('/path/to/test.css', 'utf8');
55 | const options = {
56 | replace: false,
57 | };
58 | const processedCss = postcss(pxtorem(options)).process(css).css;
59 |
60 | writeFile('/path/to/test.rem.css', processedCss, err => {
61 | if (err) throw err;
62 | console.log('Rem file written.');
63 | });
64 | ```
65 |
66 | ### with webpack
67 |
68 | ```javascript
69 | import px2rem from 'postcss-plugin-px2rem';
70 | const px2remOpts = {
71 | ......
72 | };
73 |
74 | export default {
75 | module: {
76 | loaders: [
77 | {
78 | test: /\.css$/,
79 | loader: 'style-loader!css-loader!postcss-loader',
80 | },
81 | ],
82 | },
83 | postcss: [px2rem(px2remOpts)],
84 | }
85 | ```
86 |
87 | ### with [atool-build](https://github.com/ant-tool/atool-build)
88 |
89 | `webpack.connfig.js`
90 |
91 | ```javascript
92 | import webpack from 'atool-build/lib/webpack';
93 | import px2rem from 'postcss-plugin-px2rem';
94 |
95 | export default webpackConfig => {
96 | const px2remOpts = {
97 | ......
98 | };
99 | webpackConfig.postcss.push(px2rem(px2remOpts));
100 |
101 | return webpackConfig;
102 | };
103 | ```
104 |
105 | ## Configuration
106 |
107 | Default:
108 | ```js
109 | {
110 | rootValue: 100,
111 | unitPrecision: 5,
112 | propWhiteList: [],
113 | propBlackList: [],
114 | exclude:false,
115 | selectorBlackList: [],
116 | ignoreIdentifier: false,
117 | replace: true,
118 | mediaQuery: false,
119 | minPixelValue: 0
120 | }
121 | ```
122 |
123 | - `rootValue` (Number|Object) The root element font size. Default is 100.
124 | - If rootValue is an object, for example `{ px: 50, rpx: 100 }`, it will
125 | replace rpx to 1/100 rem , and px to 1/50 rem.
126 | - `unitPrecision` (Number) The decimal numbers to allow the REM units to grow to.
127 | - `propWhiteList` (Array) The properties that can change from px to rem.
128 | - Default is an empty array that means disable the white list and enable all properties.
129 | - Values need to be exact matches.
130 | - `propBlackList` (Array) The properties that should not change from px to rem.
131 | - Values need to be exact matches.
132 | - `exclude` (Reg) a way to exclude some folder,eg. /(node_module)/.
133 | - `selectorBlackList` (Array) The selectors to ignore and leave as px.
134 | - If value is string, it checks to see if selector contains the string.
135 | - `['body']` will match `.body-class`
136 | - If value is regexp, it checks to see if the selector matches the regexp.
137 | - `[/^body$/]` will match `body` but not `.body`
138 | - `ignoreIdentifier` (Boolean/String) a way to have a single property ignored, when ignoreIdentifier enabled, then `replace` would be set to `true` automatically.
139 | - `replace` (Boolean) replaces rules containing rems instead of adding fallbacks.
140 | - `mediaQuery` (Boolean) Allow px to be converted in media queries.
141 | - `minPixelValue` (Number) Set the minimum pixel value to replace.
142 |
143 | ### License
144 | MIT
145 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib');
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "postcss-plugin-px2rem",
3 | "version": "0.8.1",
4 | "description": "A plugin for PostCSS that generates rem units from multi units.",
5 | "main": "index.js",
6 | "scripts": {
7 | "compile": "rm -rf lib && babel -d lib src",
8 | "compile:watch": "npm run compile -- --watch",
9 | "lint": "eslint src test",
10 | "prepublish": "npm run compile",
11 | "test": "babel-node $(npm bin)/babel-istanbul cover $(npm bin)/_mocha -- --no-timeouts",
12 | "coveralls": "cat ./coverage/lcov.info | coveralls"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/ant-tool/postcss-plugin-px2rem.git"
17 | },
18 | "keywords": [
19 | "px",
20 | "rem",
21 | "postcss",
22 | "plugin"
23 | ],
24 | "author": "pigcan ",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/ant-tool/postcss-plugin-px2rem/issues"
28 | },
29 | "homepage": "https://github.com/ant-tool/postcss-plugin-px2rem#readme",
30 | "dependencies": {
31 | "postcss": "^5.0.21"
32 | },
33 | "devDependencies": {
34 | "babel-cli": "^6.7.5",
35 | "babel-core": "^6.7.6",
36 | "babel-eslint": "^6.0.2",
37 | "babel-istanbul": "^0.7.0",
38 | "babel-plugin-add-module-exports": "^0.1.2",
39 | "babel-preset-es2015": "^6.6.0",
40 | "babel-preset-stage-0": "^6.5.0",
41 | "coveralls": "^2.11.9",
42 | "eslint": "^2.7.0",
43 | "eslint-config-airbnb": "^6.2.0",
44 | "expect": "^1.20.1",
45 | "mocha": "^2.4.5"
46 | },
47 | "files": [
48 | "lib",
49 | "index.js",
50 | "package.json",
51 | "README.md"
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * respect to https://github.com/cuth/postcss-pxtorem/
3 | **/
4 | import postcss from 'postcss';
5 |
6 | const defaultOpts = {
7 | rootValue: 100,
8 | unitPrecision: 5,
9 | selectorBlackList: [],
10 | propWhiteList: [],
11 | propBlackList: [],
12 | ignoreIdentifier: false,
13 | replace: true,
14 | mediaQuery: false,
15 | minPixelValue: 0,
16 | };
17 |
18 | const toFixed = (number, precision) => {
19 | const multiplier = Math.pow(10, precision + 1);
20 | const wholeNumber = Math.floor(number * multiplier);
21 |
22 | return Math.round(wholeNumber / 10) * 10 / multiplier;
23 | };
24 | const isObject = o => typeof o === 'object' && o !== null;
25 |
26 | const createPxReplace = (rootValue, identifier, unitPrecision, minPixelValue) => (m, $1, $2) => {
27 | if (!$1) return m;
28 | if (identifier && m.indexOf(identifier) === 0) return m.replace(identifier, '');
29 | const pixels = parseFloat($1);
30 | if (pixels < minPixelValue) return m;
31 | // { px: 100, rpx: 50 }
32 | const baseValue = isObject(rootValue) ? rootValue[$2] : rootValue;
33 | const fixedVal = toFixed((pixels / baseValue), unitPrecision);
34 |
35 | return `${fixedVal}rem`;
36 | };
37 |
38 | const declarationExists = (decls, prop, value) => decls.some(decl =>
39 | decl.prop === prop && decl.value === value
40 | );
41 |
42 | const blacklistedSelector = (blacklist, selector) => {
43 | if (typeof selector !== 'string') return false;
44 |
45 | return blacklist.some(regex => {
46 | if (typeof regex === 'string') return selector.indexOf(regex) !== -1;
47 |
48 | return selector.match(regex);
49 | });
50 | };
51 |
52 | const blacklistedProp = (blacklist, prop) => {
53 | if (typeof prop !== 'string') return false;
54 |
55 | return blacklist.some(regex => {
56 | if (typeof regex === 'string') return prop.indexOf(regex) !== -1;
57 |
58 | return prop.match(regex);
59 | });
60 | };
61 |
62 | const handleIgnoreIdentifierRegx = (identifier, unit) => {
63 | const _identifier = identifier;
64 | let backslashfy = _identifier.split('').join('\\');
65 | backslashfy = `\\${backslashfy}`;
66 | const pattern = `"[^"]+"|'[^']+'|url\\([^\\)]+\\)|((?:${backslashfy}|\\d*)\\.?\\d+)(${unit})`;
67 |
68 | return new RegExp(pattern, 'ig');
69 | };
70 |
71 | export default postcss.plugin('postcss-plugin-px2rem', options => {
72 | const opts = { ...defaultOpts, ...options };
73 | let unit = 'px';
74 | if (isObject(opts.rootValue)) {
75 | unit = Object.keys(opts.rootValue).join('|');
76 | }
77 |
78 | const regText = `"[^"]+"|'[^']+'|url\\([^\\)]+\\)|(\\d*\\.?\\d+)(${unit})`;
79 | let pxRegex = new RegExp(regText, 'ig');
80 | let identifier = opts.ignoreIdentifier;
81 | if (identifier && typeof identifier === 'string') {
82 | identifier = identifier.replace(/\s+/g, '');
83 | opts.replace = true;
84 | pxRegex = handleIgnoreIdentifierRegx(identifier, unit);
85 | } else {
86 | identifier = false;
87 | }
88 | const pxReplace = createPxReplace(opts.rootValue, identifier, opts.unitPrecision, opts.minPixelValue);
89 |
90 | return css => {
91 | css.walkDecls((decl, i) => {
92 | const _decl = decl;
93 | // 1st check exclude
94 | if (opts.exclude && css.source.input.file && css.source.input.file.match(opts.exclude) !== null) return;
95 | // 2st check 'px'
96 | if (_decl.value.indexOf('px') === -1) return;
97 | // 3nd check property black list
98 | if (blacklistedProp(opts.propBlackList, _decl.prop)) return;
99 | // 4rd check property white list
100 | if (opts.propWhiteList.length && opts.propWhiteList.indexOf(_decl.prop) === -1) return;
101 | // 5th check seletor black list
102 | if (blacklistedSelector(opts.selectorBlackList, _decl.parent.selector)) return;
103 |
104 | const value = _decl.value.replace(pxRegex, pxReplace);
105 |
106 | // if rem unit already exists, do not add or replace
107 | if (declarationExists(_decl.parent, _decl.prop, value)) return;
108 |
109 | if (opts.replace) {
110 | _decl.value = value;
111 | } else {
112 | _decl.parent.insertAfter(i, _decl.clone({
113 | value,
114 | }));
115 | }
116 | });
117 |
118 | if (opts.mediaQuery) {
119 | css.walkAtRules('media', rule => {
120 | const _rule = rule;
121 | if (_rule.params.indexOf('px') === -1) return;
122 | _rule.params = _rule.params.replace(pxRegex, pxReplace);
123 | });
124 | }
125 | };
126 | });
127 |
--------------------------------------------------------------------------------
/test/index-test.js:
--------------------------------------------------------------------------------
1 | import postcss from 'postcss';
2 | import expect from 'expect';
3 | import pxtorem from '../src/';
4 |
5 | const basicCSS = '.rule { font-size: 15px }';
6 |
7 | describe('px2rem', () => {
8 | it('should work on the readme example', () => {
9 | const input = 'h1 { margin: 0 0 20px 20px; font-size: 32px; line-height: 1.2; letter-spacing: 1px; }';
10 | const output = 'h1 { margin: 0 0 0.2rem 0.2rem; font-size: 0.32rem; line-height: 1.2; letter-spacing: 0.01rem; }';
11 | const processed = postcss(pxtorem()).process(input).css;
12 |
13 | expect(processed).toBe(output);
14 | });
15 |
16 | it('should replace the px unit with rem', () => {
17 | const processed = postcss(pxtorem()).process(basicCSS).css;
18 | const expected = '.rule { font-size: 0.15rem }';
19 |
20 | expect(processed).toBe(expected);
21 | });
22 |
23 | it('should ignore non px properties', () => {
24 | const expected = '.rule { font-size: 2em }';
25 | const processed = postcss(pxtorem()).process(expected).css;
26 |
27 | expect(processed).toBe(expected);
28 | });
29 |
30 | it('should handle < 1 values and values without a leading 0', () => {
31 | const rules = '.rule { margin: 0.5rem .5px -0.2px -.2em }';
32 | const expected = '.rule { margin: 0.5rem 0.005rem -0.002rem -.2em }';
33 | const options = {
34 | propWhiteList: ['margin'],
35 | };
36 | const processed = postcss(pxtorem(options)).process(rules).css;
37 |
38 | expect(processed).toBe(expected);
39 | });
40 |
41 | it('should not add properties that already exist', () => {
42 | const expected = '.rule { font-size: 16px; font-size: 0.16rem; }';
43 | const processed = postcss(pxtorem()).process(expected).css;
44 |
45 | expect(processed).toBe(expected);
46 | });
47 | });
48 |
49 | describe('value parsing', () => {
50 | it('should not replace values in double quotes or single quotes', () => {
51 | const options = {
52 | propWhiteList: [],
53 | };
54 | const rules = '.rule { content: \'16px\'; font-family: "16px"; font-size: 16px; }';
55 | const expected = '.rule { content: \'16px\'; font-family: "16px"; font-size: 0.16rem; }';
56 | const processed = postcss(pxtorem(options)).process(rules).css;
57 |
58 | expect(processed).toBe(expected);
59 | });
60 |
61 | it('should not replace values in `url()`', () => {
62 | const options = {
63 | propWhiteList: [],
64 | };
65 | const rules = '.rule { background: url(16px.jpg); font-size: 16px; }';
66 | const expected = '.rule { background: url(16px.jpg); font-size: 0.16rem; }';
67 | const processed = postcss(pxtorem(options)).process(rules).css;
68 |
69 | expect(processed).toBe(expected);
70 | });
71 | });
72 |
73 | describe('rootValue', () => {
74 | it('should replace using a root value of 10', () => {
75 | const expected = '.rule { font-size: 1.5rem }';
76 | const options = {
77 | rootValue: 10,
78 | };
79 | const processed = postcss(pxtorem(options)).process(basicCSS).css;
80 |
81 | expect(processed).toBe(expected);
82 | });
83 | });
84 |
85 | describe('unitPrecision', () => {
86 | it('should replace using a decimal of 2 places', () => {
87 | const expected = '.rule { font-size: 0.94rem }';
88 | const options = {
89 | rootValue: 16,
90 | unitPrecision: 2,
91 | };
92 | const processed = postcss(pxtorem(options)).process(basicCSS).css;
93 |
94 | expect(processed).toBe(expected);
95 | });
96 | });
97 |
98 | describe('propWhiteList', () => {
99 | it('should only replace properties in the white list', () => {
100 | const expected = '.rule { font-size: 15px }';
101 | const options = {
102 | propWhiteList: ['font'],
103 | };
104 | const processed = postcss(pxtorem(options)).process(basicCSS).css;
105 |
106 | expect(processed).toBe(expected);
107 | });
108 |
109 | it('should replace all properties when white list is empty', () => {
110 | const rules = '.rule { margin: 16px; font-size: 15px }';
111 | const expected = '.rule { margin: 0.16rem; font-size: 0.15rem }';
112 | const options = {
113 | propWhiteList: [],
114 | };
115 | const processed = postcss(pxtorem(options)).process(rules).css;
116 |
117 | expect(processed).toBe(expected);
118 | });
119 | });
120 |
121 | describe('propBlackList', () => {
122 | it('should not replace properties in the black list', () => {
123 | const expected = '.rule { font-size: 15px }';
124 | const options = {
125 | propBlackList: ['font'],
126 | };
127 | const processed = postcss(pxtorem(options)).process(basicCSS).css;
128 |
129 | expect(processed).toBe(expected);
130 | });
131 | });
132 |
133 | describe('selectorBlackList', () => {
134 | it('should ignore selectors in the selector black list', () => {
135 | const rules = '.rule { font-size: 15px } .rule2 { font-size: 15px }';
136 | const expected = '.rule { font-size: 0.15rem } .rule2 { font-size: 15px }';
137 | const options = {
138 | selectorBlackList: ['.rule2'],
139 | };
140 | const processed = postcss(pxtorem(options)).process(rules).css;
141 |
142 | expect(processed).toBe(expected);
143 | });
144 |
145 | it('should ignore every selector with `body$`', () => {
146 | const rules = 'body { font-size: 16px; } .class-body$ { font-size: 16px; } .simple-class { font-size: 16px; }';
147 | const expected = 'body { font-size: 0.16rem; } .class-body$ { font-size: 16px; } .simple-class { font-size: 0.16rem; }';
148 | const options = {
149 | selectorBlackList: ['body$'],
150 | };
151 | const processed = postcss(pxtorem(options)).process(rules).css;
152 |
153 | expect(processed).toBe(expected);
154 | });
155 |
156 | it('should only ignore exactly `body`', () => {
157 | const rules = 'body { font-size: 16px; } .class-body { font-size: 16px; } .simple-class { font-size: 16px; }';
158 | const expected = 'body { font-size: 16px; } .class-body { font-size: 0.16rem; } .simple-class { font-size: 0.16rem; }';
159 | const options = {
160 | selectorBlackList: [/^body$/],
161 | };
162 | const processed = postcss(pxtorem(options)).process(rules).css;
163 |
164 | expect(processed).toBe(expected);
165 | });
166 | });
167 |
168 | describe('ignoreIdentifier', () => {
169 | it('should not replace px when ignoreIdentifier enabled', () => {
170 | const options = {
171 | ignoreIdentifier: '00',
172 | };
173 | const input = 'h1 { margin: 0 0 00.5px 16px; border-width: 001px; font-size: 32px; font-family: "16px"; }';
174 | const output = 'h1 { margin: 0 0 .5px 0.16rem; border-width: 1px; font-size: 0.32rem; font-family: "16px"; }';
175 | const processed = postcss(pxtorem(options)).process(input).css;
176 |
177 | expect(processed).toBe(output);
178 | });
179 | });
180 |
181 | describe('replace', () => {
182 | it('should leave fallback pixel unit with root em value', () => {
183 | const options = {
184 | replace: false,
185 | };
186 | const processed = postcss(pxtorem(options)).process(basicCSS).css;
187 | const expected = '.rule { font-size: 15px; font-size: 0.15rem }';
188 |
189 | expect(processed).toBe(expected);
190 | });
191 | });
192 |
193 | describe('mediaQuery', () => {
194 | it('should replace px in media queries', () => {
195 | const options = {
196 | mediaQuery: true,
197 | };
198 | const processed = postcss(pxtorem(options)).process('@media (min-width: 500px) { .rule { font-size: 16px } }').css;
199 | const expected = '@media (min-width: 5rem) { .rule { font-size: 0.16rem } }';
200 |
201 | expect(processed).toBe(expected);
202 | });
203 | });
204 |
205 | describe('minPixelValue', () => {
206 | it('should not replace values below minPixelValue', () => {
207 | const options = {
208 | propWhiteList: [],
209 | minPixelValue: 2,
210 | };
211 | const rules = '.rule { border: 1px solid #000; font-size: 16px; margin: 1px 10px; }';
212 | const expected = '.rule { border: 1px solid #000; font-size: 0.16rem; margin: 1px 0.1rem; }';
213 | const processed = postcss(pxtorem(options)).process(rules).css;
214 |
215 | expect(processed).toBe(expected);
216 | });
217 | });
218 |
219 | describe('rpx support', function () {
220 | it('should work on the readme example', () => {
221 | const input = 'h1 { margin: 0 0 20rpx 20rpx; font-size: 32px; line-height: 1.2; letter-spacing: 1rpx; }';
222 | const output = 'h1 { margin: 0 0 0.2rem 0.2rem; font-size: 0.64rem; line-height: 1.2; letter-spacing: 0.01rem; }';
223 | const processed = postcss(pxtorem({
224 | rootValue: { px: 50, rpx: 100 },
225 | })).process(input).css;
226 |
227 | expect(processed).toBe(output);
228 | });
229 |
230 | it('should replace rpx in media queries', () => {
231 | const options = {
232 | mediaQuery: true,
233 | rootValue: { px: 50, rpx: 100 },
234 | };
235 | const processed = postcss(pxtorem(options)).process('@media (min-width: 500rpx) { .rule { font-size: 16px } }').css;
236 | const expected = '@media (min-width: 5rem) { .rule { font-size: 0.32rem } }';
237 |
238 | expect(processed).toBe(expected);
239 | });
240 |
241 | it('should ignore selectors in the selector black list', () => {
242 | const rules = '.rule { font-size: 15rpx } .rule2 { font-size: 15px }';
243 | const expected = '.rule { font-size: 0.15rem } .rule2 { font-size: 15px }';
244 | const options = {
245 | selectorBlackList: ['.rule2'],
246 | rootValue: { px: 50, rpx: 100 },
247 | };
248 | const processed = postcss(pxtorem(options)).process(rules).css;
249 |
250 | expect(processed).toBe(expected);
251 | });
252 |
253 | it('should not replace px when ignoreIdentifier enabled', () => {
254 | const options = {
255 | ignoreIdentifier: '00',
256 | rootValue: { px: 100, rpx: 100 },
257 | };
258 | const input = 'h1 { margin: 0 0 00.5px 16rpx; border-width: 001px; font-size: 32px; font-family: "16px"; }';
259 | const output = 'h1 { margin: 0 0 .5px 0.16rem; border-width: 1px; font-size: 0.32rem; font-family: "16px"; }';
260 | const processed = postcss(pxtorem(options)).process(input).css;
261 |
262 | expect(processed).toBe(output);
263 | });
264 | });
265 |
266 | describe('exclude support', () => {
267 | it('should work on the readme example', () => {
268 | const input = 'h1 { margin: 0 0 20px 20px; font-size: 32px; line-height: 1.2; letter-spacing: 1px; }';
269 | const output = 'h1 { margin: 0 0 20px 20px; font-size: 32px; line-height: 1.2; letter-spacing: 1px; }';
270 | const processed = postcss(pxtorem({
271 | exclude: /(node_modules)/,
272 | })).process(input, {
273 | from: 'node_modules/third.css',
274 | }).css;
275 | expect(processed).toBe(output);
276 | });
277 |
278 | it('should work when exclude option range doesn\'t cover', () => {
279 | const input = 'h1 { margin: 0 0 20px 20px; font-size: 32px; line-height: 1.2; letter-spacing: 1px; }';
280 | const output = 'h1 { margin: 0 0 0.2rem 0.2rem; font-size: 0.32rem; line-height: 1.2; letter-spacing: 0.01rem; }';
281 | const processed = postcss(pxtorem({
282 | exclude: /(node_modules)/,
283 | })).process(input, {
284 | from: 'lib/own.css',
285 | }).css;
286 | expect(processed).toBe(output);
287 | });
288 | });
289 |
--------------------------------------------------------------------------------