diff --git a/README.md b/README.md index 72e4028..955d819 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ A plugin for [PostCSS](https://github.com/ai/postcss) that generates viewport units (vw, vh, vmin, vmax) from pixel units. +## Install +``` +$ npm install postcss-px-to-viewport --save-dev +``` + ## Usage If your project involves a fixed width, this script will help to convert pixels into viewport units. @@ -96,7 +101,8 @@ Default: fontViewportUnit: 'vw', // vmin is more suitable. selectorBlackList: [], minPixelValue: 1, - mediaQuery: false + mediaQuery: false, + exclude: [] // ignore some files } ``` - `unitToConvert` (String) unit to convert, by default, it is px. @@ -118,9 +124,12 @@ Default: - `[/^body$/]` will match `body` but not `.body` - `minPixelValue` (Number) Set the minimum pixel value to replace. - `mediaQuery` (Boolean) Allow px to be converted in media queries. +- `exclude` (Array or Regexp) Ignore some files like 'node_modules' + - If value is regexp, will ignore the matches files. + - If value is array, the elements of the array are regexp. ### Use with gulp-postcss - +add to your gulp config: ```js var gulp = require('gulp'); var postcss = require('gulp-postcss'); @@ -140,3 +149,15 @@ gulp.task('css', function () { .pipe(gulp.dest('build/css')); }); ``` +### Use with Postcss configuration file +add to postcss.config.js +```js +module.exports = { + plugins: { + ... + 'postcss-px-to-viewport': { + // options + } + } +} +``` diff --git a/index.js b/index.js index 2b3dd11..29b4c51 100755 --- a/index.js +++ b/index.js @@ -28,17 +28,31 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { // Not anything inside url() // Any digit followed by px // !singlequotes|!doublequotes|!url()|pixelunit - var pxRegex = new RegExp('"[^"]+"|\'[^\']+\'|url\\([^\\)]+\\)|(\\d*\\.?\\d+)' + opts.unitToConvert, 'ig') + var pxRegex = new RegExp('"[^"]+"|\'[^\']+\'|url\\([^\\)]+\\)|(\\d*\\.?\\d+)' + opts.unitToConvert, 'ig'); var satisfyPropList = createPropListMatcher(opts.propList); return function (css) { - + css.walkDecls(function (decl, i) { + + // Add exlclude option to ignore some files like 'node_modules' + if (opts.exclude && decl.source.input.file) { + if (Object.prototype.toString.call(opts.exclude) === '[object RegExp]') { + if (!handleExclude(opts.exclude, decl.source.input.file)) return; + } else if (Object.prototype.toString.call(opts.exclude) === '[object Array]') { + for (let i = 0; i < opts.exclude.length; ++i) { + if (!handleExclude(opts.exclude[i], decl.source.input.file)) return; + } + } else { + throw new Error('options.exclude should be RegExp or Array!'); + } + } + if ( decl.value.indexOf(opts.unitToConvert) === -1 || - !satisfyPropList(decl.prop) || + !satisfyPropList(decl.prop) || blacklistedSelector(opts.selectorBlackList, decl.parent.selector) - ) return; + ) return; var unit = getUnit(decl.prop, opts) @@ -55,6 +69,14 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { }; }); +function handleExclude (reg, file) { + if (Object.prototype.toString.call(reg) !== '[object RegExp]') { + throw new Error('options.exclude should be RegExp!'); + } + if (file.match(reg) !== null) return false; + return true; +} + function getUnit(prop, opts) { return prop.indexOf('font') === -1 ? opts.viewportUnit : opts.fontViewportUnit; } @@ -64,7 +86,8 @@ function createPxReplace(viewportSize, minPixelValue, unitPrecision, viewportUni if (!$1) return m; var pixels = parseFloat($1); if (pixels <= minPixelValue) return m; - return toFixed((pixels / viewportSize * 100), unitPrecision) + viewportUnit; + var parsedVal = toFixed((pixels / viewportSize * 100), unitPrecision); + return parsedVal === 0 ? '0' : parsedVal + viewportUnit; }; } diff --git a/spec/px-to-viewport.spec.js b/spec/px-to-viewport.spec.js index eaaa21c..81ed2d6 100644 --- a/spec/px-to-viewport.spec.js +++ b/spec/px-to-viewport.spec.js @@ -37,6 +37,13 @@ describe('px-to-viewport', function() { expect(processed).toBe(expected); }); + + it('should remain unitless if 0', function () { + var expected = '.rule { font-size: 0px; font-size: 0; }'; + var processed = postcss(pxToViewport()).process(expected).css; + + expect(processed).toBe(expected); + }); }); describe('value parsing', function() { @@ -58,6 +65,14 @@ describe('value parsing', function() { expect(processed).toBe(expected); }); + + it('should not replace values with an uppercase P or X', function () { + var rules = '.rule { margin: 12px calc(100% - 14PX); height: calc(100% - 20px); font-size: 12Px; line-height: 16px; }'; + var expected = '.rule { margin: 3.75vw calc(100% - 14PX); height: calc(100% - 6.25vw); font-size: 12Px; line-height: 5vw; }'; + var processed = postcss(pxToViewport()).process(rules).css; + + expect(processed).toBe(expected); + }); }); describe('unitToConvert', function() { @@ -230,52 +245,100 @@ describe('minPixelValue', function () { }); }); +describe('exclude', function () { + var rules = '.rule { border: 1px solid #000; font-size: 16px; margin: 1px 10px; }'; + var covered = '.rule { border: 1px solid #000; font-size: 5vw; margin: 1px 3.125vw; }' + it('when using regex at the time, the style should not be overwritten.', function () { + var options = { + exclude: /node_modules/ + } + var processed = postcss(pxToViewport(options)).process(rules, { + from: '/node_modules/main.css' + }).css; + + expect(processed).toBe(rules); + }); + + it('when using regex at the time, the style should be overwritten.', function () { + var options = { + exclude: /node_modules/ + } + var processed = postcss(pxToViewport(options)).process(rules, { + from: '/example/main.css' + }).css; + + expect(processed).toBe(covered); + }); + + it('when using array at the time, the style should not be overwritten.', function () { + var options = { + exclude: [/node_modules/, /exclude/] + } + var processed = postcss(pxToViewport(options)).process(rules, { + from: '/exclude/main.css' + }).css; + + expect(processed).toBe(rules); + }); + + it('when using array at the time, the style should be overwritten.', function () { + var options = { + exclude: [/node_modules/, /exclude/] + } + var processed = postcss(pxToViewport(options)).process(rules, { + from: '/example/main.css' + }).css; + + expect(processed).toBe(covered); + }); +}); + 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); + 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); + 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.startWith(propList).join()).toBe(expected); + var propList = ['font-size', '*margin*', '!padding', 'border*', '*', '*y', '!*font*']; + var expected = 'border'; + expect(filterPropList.startWith(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.endWith(propList).join()).toBe(expected); + var propList = ['font-size', '*margin*', '!padding', 'border*', '*', '*y', '!*font*']; + var expected = 'y'; + expect(filterPropList.endWith(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.notExact(propList).join()).toBe(expected); + var propList = ['font-size', '*margin*', '!padding', 'border*', '*', '*y', '!*font*']; + var expected = 'padding'; + expect(filterPropList.notExact(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); + 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.notStartWith(propList).join()).toBe(expected); + var propList = ['font-size', '*margin*', '!padding', '!border*', '*', '*y', '!*font*']; + var expected = 'border'; + expect(filterPropList.notStartWith(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.notEndWith(propList).join()).toBe(expected); + var propList = ['font-size', '*margin*', '!padding', '!border*', '*', '!*y', '!*font*']; + var expected = 'y'; + expect(filterPropList.notEndWith(propList).join()).toBe(expected); }); });