From 009d9ed8fb1370135cb65237c9a925bcc6004751 Mon Sep 17 00:00:00 2001 From: Ivan Bunin Date: Thu, 24 Jan 2019 13:30:55 +0300 Subject: [PATCH 1/4] add prop list --- index.js | 12 ++-- spec/px-to-viewport.spec.js | 51 +++++++++++++++++ src/prop-list-matcher.js | 106 ++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 src/prop-list-matcher.js diff --git a/index.js b/index.js index 7bcc46a..344762b 100755 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ var postcss = require('postcss'); var objectAssign = require('object-assign'); +var { createPropListMatcher } = require('./src/prop-list-matcher'); var defaults = { unitToConvert: 'px', @@ -11,6 +12,7 @@ var defaults = { viewportUnit: 'vw', fontViewportUnit: 'vw', // vmin is more suitable. selectorBlackList: [], + propList: ['*'], minPixelValue: 1, mediaQuery: false }; @@ -27,14 +29,16 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { // Any digit followed by px // !singlequotes|!doublequotes|!url()|pixelunit var pxRegex = new RegExp('"[^"]+"|\'[^\']+\'|url\\([^\\)]+\\)|(\\d*\\.?\\d+)' + opts.unitToConvert, 'ig') + 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(opts.unitToConvert) === -1) return; - - if (blacklistedSelector(opts.selectorBlackList, decl.parent.selector)) return; + if ( + decl.value.indexOf(opts.unitToConvert) === -1 || + !satisfyPropList(decl.prop) || + blacklistedSelector(opts.selectorBlackList, decl.parent.selector) + ) return; var unit = getUnit(decl.prop, opts) diff --git a/spec/px-to-viewport.spec.js b/spec/px-to-viewport.spec.js index 23632e2..4b3f26e 100644 --- a/spec/px-to-viewport.spec.js +++ b/spec/px-to-viewport.spec.js @@ -9,6 +9,7 @@ var postcss = require('postcss'); var pxToViewport = require('..'); var basicCSS = '.rule { font-size: 15px }'; +var { filterPropList } = require('../src/prop-list-matcher'); describe('px-to-viewport', function() { it('should work on the readme example', function () { @@ -196,3 +197,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.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); + }); + + 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); + }); + + 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.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); + }); +}); diff --git a/src/prop-list-matcher.js b/src/prop-list-matcher.js new file mode 100644 index 0000000..294830e --- /dev/null +++ b/src/prop-list-matcher.js @@ -0,0 +1,106 @@ +var filterPropList = { + exact: function (list) { + return list.filter(function (m) { + return m.match(/^[^\*\!]+$/); + }); + }, + contain: function (list) { + return list.filter(function (m) { + return m.match(/^\*.+\*$/); + }).map(function (m) { + return m.substr(1, m.length - 2); + }); + }, + endWith: function (list) { + return list.filter(function (m) { + return m.match(/^\*[^\*]+$/); + }).map(function (m) { + return m.substr(1); + }); + }, + startWith: function (list) { + return list.filter(function (m) { + return m.match(/^[^\*\!]+\*$/); + }).map(function (m) { + return m.substr(0, m.length - 1); + }); + }, + notExact: function (list) { + return list.filter(function (m) { + return m.match(/^\![^\*].*$/); + }).map(function (m) { + return m.substr(1); + }); + }, + notContain: function (list) { + return list.filter(function (m) { + return m.match(/^\!\*.+\*$/); + }).map(function (m) { + return m.substr(2, m.length - 3); + }); + }, + notEndWith: function (list) { + return list.filter(function (m) { + return m.match(/^\!\*[^\*]+$/); + }).map(function (m) { + return m.substr(2); + }); + }, + notStartWith: function (list) { + return list.filter(function (m) { + return m.match(/^\![^\*]+\*$/); + }).map(function (m) { + return m.substr(1, m.length - 2); + }); + } +}; + +function createPropListMatcher(propList) { + var hasWild = propList.indexOf('*') > -1; + var matchAll = (hasWild && propList.length === 1); + var lists = { + exact: filterPropList.exact(propList), + contain: filterPropList.contain(propList), + startWith: filterPropList.startWith(propList), + endWith: filterPropList.endWith(propList), + notExact: filterPropList.notExact(propList), + notContain: filterPropList.notContain(propList), + notStartWith: filterPropList.notStartWith(propList), + notEndWith: filterPropList.notEndWith(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.startWith.some(function (m) { + return prop.indexOf(m) === 0; + }) || + lists.endWith.some(function (m) { + return prop.indexOf(m) === prop.length - m.length; + }) + ) && + !( + lists.notExact.indexOf(prop) > -1 || + lists.notContain.some(function (m) { + return prop.indexOf(m) > -1; + }) || + lists.notStartWith.some(function (m) { + return prop.indexOf(m) === 0; + }) || + lists.notEndWith.some(function (m) { + return prop.indexOf(m) === prop.length - m.length; + }) + ) + ); + }; +} + +module.exports = { + filterPropList, + createPropListMatcher +}; From 5f3c1b2a0fbffeb923841f9bb7511289442fd9aa Mon Sep 17 00:00:00 2001 From: Ivan Bunin Date: Thu, 24 Jan 2019 15:38:06 +0300 Subject: [PATCH 2/4] add tests on propList --- index.js | 2 +- spec/px-to-viewport.spec.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 344762b..2b3dd11 100755 --- a/index.js +++ b/index.js @@ -18,7 +18,7 @@ var defaults = { }; module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { - + var opts = objectAssign({}, defaults, options); var pxReplace = createPxReplace(opts.viewportWidth, opts.minPixelValue, opts.unitPrecision, opts.viewportUnit); diff --git a/spec/px-to-viewport.spec.js b/spec/px-to-viewport.spec.js index 4b3f26e..eaaa21c 100644 --- a/spec/px-to-viewport.spec.js +++ b/spec/px-to-viewport.spec.js @@ -184,6 +184,38 @@ describe('mediaQuery', function () { }); }); +describe('propList', function () { + 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: 5vw; margin: 5vw; margin-left: 5px; padding: 5px; padding-right: 5vw }'; + var options = { + propList: ['*font*', 'margin*', '!margin-left', '*-right', 'pad'] + }; + var processed = postcss(pxToViewport(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: 5vw; margin-left: 5px; padding: 5px; padding-right: 16px }'; + var options = { + propList: ['*', '!margin-left', '!*padding*', '!font*'] + }; + var processed = postcss(pxToViewport(options)).process(css).css; + + expect(processed).toBe(expected); + }); + + it('should replace all properties when prop list is not given', function () { + var rules = '.rule { margin: 16px; font-size: 15px }'; + var expected = '.rule { margin: 5vw; font-size: 4.6875vw }'; + var processed = postcss(pxToViewport()).process(rules).css; + + expect(processed).toBe(expected); + }); +}); + describe('minPixelValue', function () { it('should not replace values below minPixelValue', function () { var options = { From 757643587c59533b57dbbafd35ffd2da058e33f5 Mon Sep 17 00:00:00 2001 From: Ivan Bunin Date: Fri, 25 Jan 2019 14:39:49 +0300 Subject: [PATCH 3/4] update readme --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 21a16c5..72e4028 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ Default: viewportWidth: 320, viewportHeight: 568, // not now used; TODO: need for different units and math for different properties unitPrecision: 5, + propList: ['*'], viewportUnit: 'vw', fontViewportUnit: 'vw', // vmin is more suitable. selectorBlackList: [], @@ -101,7 +102,13 @@ Default: - `unitToConvert` (String) unit to convert, by default, it is px. - `viewportWidth` (Number) The width of the viewport. - `viewportHeight` (Number) The height of the viewport. -- `unitPrecision` (Number) The decimal numbers to allow the REM units to grow to. +- `unitPrecision` (Number) The decimal numbers to allow the vw units to grow to. +- `propList` (Array) The properties that can change from px to vw. + - Values need to be exact matches. + - Use wildcard * to enable all properties. Example: ['*'] + - Use * at the start or end of a word. (['*position*'] will match background-position-y) + - Use ! to not match a property. Example: ['*', '!letter-spacing'] + - Combine the "not" prefix with the other prefixes. Example: ['*', '!font*'] - `viewportUnit` (String) Expected units. - `fontViewportUnit` (String) Expected units for font. - `selectorBlackList` (Array) The selectors to ignore and leave as px. From 18eea3cf1ca2b866d5da0847bc4d1d316788e091 Mon Sep 17 00:00:00 2001 From: Ivan Bunin Date: Mon, 28 Jan 2019 16:47:48 +0300 Subject: [PATCH 4/4] remove ignorecase --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 29b4c51..88273bf 100755 --- a/index.js +++ b/index.js @@ -28,7 +28,7 @@ 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, 'g'); var satisfyPropList = createPropListMatcher(opts.propList); return function (css) {