From 024e815815b44c9d4b284fc8b663bb9cc00f1463 Mon Sep 17 00:00:00 2001 From: MiaHu Date: Tue, 14 Dec 2021 20:47:40 +0800 Subject: [PATCH] feat: support array format for unitToConvert option --- README.md | 2 +- README_CN.md | 2 +- index.js | 143 ++++++--- package.json | 4 +- spec/px-to-viewport.spec.js | 562 ++++++++++++++++++++++-------------- src/pixel-unit-regexp.js | 19 +- 6 files changed, 460 insertions(+), 272 deletions(-) diff --git a/README.md b/README.md index 234df59..7492715 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ Default Options: landscapeWidth: 568 } ``` -- `unitToConvert` (String) unit to convert, by default, it is px. +- `unitToConvert` (String) unit to convert, by default, it is px.Support Array, e.g: ["px","PX"] - `viewportWidth` (Number) The width of the viewport. - `unitPrecision` (Number) The decimal numbers to allow the vw units to grow to. - `propList` (Array) The properties that can change from px to vw. diff --git a/README_CN.md b/README_CN.md index 158f2e4..e8801c4 100644 --- a/README_CN.md +++ b/README_CN.md @@ -103,7 +103,7 @@ $ yarn add -D postcss-px-to-viewport landscapeWidth: 568 } ``` -- `unitToConvert` (String) 需要转换的单位,默认为"px" +- `unitToConvert` (String) 需要转换的单位,默认为"px",支持Array, e.g: ["px","PX"] - `viewportWidth` (Number) 设计稿的视口宽度 - `unitPrecision` (Number) 单位转换后保留的精度 - `propList` (Array) 能转化为vw的属性列表 diff --git a/index.js b/index.js index 6419322..6340c8b 100755 --- a/index.js +++ b/index.js @@ -1,35 +1,48 @@ -'use strict'; +"use strict"; -var postcss = require('postcss'); -var objectAssign = require('object-assign'); -var { createPropListMatcher } = require('./src/prop-list-matcher'); -var { getUnitRegexp } = require('./src/pixel-unit-regexp'); +var postcss = require("postcss"); +var objectAssign = require("object-assign"); +var { createPropListMatcher } = require("./src/prop-list-matcher"); +var { getUnitRegexp } = require("./src/pixel-unit-regexp"); var defaults = { - unitToConvert: 'px', + unitToConvert: "px", // support array and string viewportWidth: 320, viewportHeight: 568, // not now used; TODO: need for different units and math for different properties unitPrecision: 5, - viewportUnit: 'vw', - fontViewportUnit: 'vw', // vmin is more suitable. + viewportUnit: "vw", + fontViewportUnit: "vw", // vmin is more suitable. selectorBlackList: [], - propList: ['*'], + propList: ["*"], minPixelValue: 1, mediaQuery: false, replace: true, landscape: false, - landscapeUnit: 'vw', - landscapeWidth: 568 + landscapeUnit: "vw", + landscapeWidth: 568, }; -var ignoreNextComment = 'px-to-viewport-ignore-next'; -var ignorePrevComment = 'px-to-viewport-ignore'; +var ignoreNextComment = "px-to-viewport-ignore-next"; +var ignorePrevComment = "px-to-viewport-ignore"; + +function isValueIncludeUnitToConvert(strValue, unitToConvert) { + var unitList = + typeof unitToConvert === "string" ? [unitToConvert] : unitToConvert; + var i = 0, + len = unitList.length; + for (; i < len; i++) { + if (strValue.indexOf(unitList[i]) !== -1) { + return true; + } + } + return false; +} -module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { +module.exports = postcss.plugin("postcss-px-to-viewport", function (options) { var opts = objectAssign({}, defaults, options); - checkRegExpOrArray(opts, 'exclude'); - checkRegExpOrArray(opts, 'include'); + checkRegExpOrArray(opts, "exclude"); + checkRegExpOrArray(opts, "include"); var pxRegex = getUnitRegexp(opts.unitToConvert); var satisfyPropList = createPropListMatcher(opts.propList); @@ -41,9 +54,13 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { var file = rule.source && rule.source.input.file; if (opts.include && file) { - if (Object.prototype.toString.call(opts.include) === '[object RegExp]') { + if ( + Object.prototype.toString.call(opts.include) === "[object RegExp]" + ) { if (!opts.include.test(file)) return; - } else if (Object.prototype.toString.call(opts.include) === '[object Array]') { + } else if ( + Object.prototype.toString.call(opts.include) === "[object Array]" + ) { var flag = false; for (var i = 0; i < opts.include.length; i++) { if (opts.include[i].test(file)) { @@ -56,9 +73,13 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { } if (opts.exclude && file) { - if (Object.prototype.toString.call(opts.exclude) === '[object RegExp]') { + if ( + Object.prototype.toString.call(opts.exclude) === "[object RegExp]" + ) { if (opts.exclude.test(file)) return; - } else if (Object.prototype.toString.call(opts.exclude) === '[object Array]') { + } else if ( + Object.prototype.toString.call(opts.exclude) === "[object Array]" + ) { for (var i = 0; i < opts.exclude.length; i++) { if (opts.exclude[i].test(file)) return; } @@ -70,13 +91,19 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { if (opts.landscape && !rule.parent.params) { var landscapeRule = rule.clone().removeAll(); - rule.walkDecls(function(decl) { - if (decl.value.indexOf(opts.unitToConvert) === -1) return; + rule.walkDecls(function (decl) { + if (!isValueIncludeUnitToConvert(decl.value, opts.unitToConvert)) + return; if (!satisfyPropList(decl.prop)) return; - landscapeRule.append(decl.clone({ - value: decl.value.replace(pxRegex, createPxReplace(opts, opts.landscapeUnit, opts.landscapeWidth)) - })); + landscapeRule.append( + decl.clone({ + value: decl.value.replace( + pxRegex, + createPxReplace(opts, opts.landscapeUnit, opts.landscapeWidth) + ), + }) + ); }); if (landscapeRule.nodes.length > 0) { @@ -86,22 +113,36 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { if (!validateParams(rule.parent.params, opts.mediaQuery)) return; - rule.walkDecls(function(decl, i) { - if (decl.value.indexOf(opts.unitToConvert) === -1) return; + rule.walkDecls(function (decl, i) { + if (!isValueIncludeUnitToConvert(decl.value, opts.unitToConvert)) + return; if (!satisfyPropList(decl.prop)) return; var prev = decl.prev(); // prev declaration is ignore conversion comment at same line - if (prev && prev.type === 'comment' && prev.text === ignoreNextComment) { + if ( + prev && + prev.type === "comment" && + prev.text === ignoreNextComment + ) { // remove comment prev.remove(); return; } var next = decl.next(); // next declaration is ignore conversion comment at same line - if (next && next.type === 'comment' && next.text === ignorePrevComment) { + if ( + next && + next.type === "comment" && + next.text === ignorePrevComment + ) { if (/\n/.test(next.raws.before)) { - result.warn('Unexpected comment /* ' + ignorePrevComment + ' */ must be after declaration at same line.', { node: next }); + result.warn( + "Unexpected comment /* " + + ignorePrevComment + + " */ must be after declaration at same line.", + { node: next } + ); } else { // remove comment next.remove(); @@ -113,7 +154,7 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { var size; var params = rule.parent.params; - if (opts.landscape && params && params.indexOf('landscape') !== -1) { + if (opts.landscape && params && params.indexOf("landscape") !== -1) { unit = opts.landscapeUnit; size = opts.landscapeWidth; } else { @@ -121,7 +162,10 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { size = opts.viewportWidth; } - var value = decl.value.replace(pxRegex, createPxReplace(opts, unit, size)); + var value = decl.value.replace( + pxRegex, + createPxReplace(opts, unit, size) + ); if (declarationExists(decl.parent, decl.prop, value)) return; @@ -134,9 +178,12 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { }); if (landscapeRules.length > 0) { - var landscapeRoot = new postcss.atRule({ params: '(orientation: landscape)', name: 'media' }); + var landscapeRoot = new postcss.atRule({ + params: "(orientation: landscape)", + name: "media", + }); - landscapeRules.forEach(function(rule) { + landscapeRules.forEach(function (rule) { landscapeRoot.append(rule); }); css.append(landscapeRoot); @@ -145,7 +192,9 @@ module.exports = postcss.plugin('postcss-px-to-viewport', function (options) { }); function getUnit(prop, opts) { - return prop.indexOf('font') === -1 ? opts.viewportUnit : opts.fontViewportUnit; + return prop.indexOf("font") === -1 + ? opts.viewportUnit + : opts.fontViewportUnit; } function createPxReplace(opts, viewportUnit, viewportSize) { @@ -153,49 +202,51 @@ function createPxReplace(opts, viewportUnit, viewportSize) { if (!$1) return m; var pixels = parseFloat($1); if (pixels <= opts.minPixelValue) return m; - var parsedVal = toFixed((pixels / viewportSize * 100), opts.unitPrecision); - return parsedVal === 0 ? '0' : parsedVal + viewportUnit; + var parsedVal = toFixed((pixels / viewportSize) * 100, opts.unitPrecision); + return parsedVal === 0 ? "0" : parsedVal + viewportUnit; }; } function error(decl, message) { - throw decl.error(message, { plugin: 'postcss-px-to-viewport' }); + throw decl.error(message, { plugin: "postcss-px-to-viewport" }); } function checkRegExpOrArray(options, optionName) { var option = options[optionName]; if (!option) return; - if (Object.prototype.toString.call(option) === '[object RegExp]') return; - if (Object.prototype.toString.call(option) === '[object Array]') { + if (Object.prototype.toString.call(option) === "[object RegExp]") return; + if (Object.prototype.toString.call(option) === "[object Array]") { var bad = false; for (var i = 0; i < option.length; i++) { - if (Object.prototype.toString.call(option[i]) !== '[object RegExp]') { + if (Object.prototype.toString.call(option[i]) !== "[object RegExp]") { bad = true; break; } } if (!bad) return; } - throw new Error('options.' + optionName + ' should be RegExp or Array of RegExp.'); + throw new Error( + "options." + optionName + " should be RegExp or Array of RegExp." + ); } function toFixed(number, precision) { var multiplier = Math.pow(10, precision + 1), wholeNumber = Math.floor(number * multiplier); - return Math.round(wholeNumber / 10) * 10 / multiplier; + return (Math.round(wholeNumber / 10) * 10) / multiplier; } function blacklistedSelector(blacklist, selector) { - if (typeof selector !== 'string') return; + if (typeof selector !== "string") return; return blacklist.some(function (regex) { - if (typeof regex === 'string') return selector.indexOf(regex) !== -1; + if (typeof regex === "string") return selector.indexOf(regex) !== -1; return selector.match(regex); }); } function declarationExists(decls, prop, value) { return decls.some(function (decl) { - return (decl.prop === prop && decl.value === value); + return decl.prop === prop && decl.value === value; }); } diff --git a/package.json b/package.json index deec944..d8be4f6 100755 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "postcss-plugin" ], "scripts": { - "test": "jest spec/*.spec.js" + "test": "jest spec/*.js" }, "dependencies": { "object-assign": ">=4.0.1" @@ -37,4 +37,4 @@ "peerDependencies": { "postcss": ">=5.0.2" } -} +} \ No newline at end of file diff --git a/spec/px-to-viewport.spec.js b/spec/px-to-viewport.spec.js index 0e47c10..1c72b07 100644 --- a/spec/px-to-viewport.spec.js +++ b/spec/px-to-viewport.spec.js @@ -4,102 +4,123 @@ /* global describe, it, expect */ -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 () { - var input = 'h1 { margin: 0 0 20px; font-size: 32px; line-height: 2; letter-spacing: 1px; }'; - var output = 'h1 { margin: 0 0 6.25vw; font-size: 10vw; line-height: 2; letter-spacing: 1px; }'; +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 () { + var input = + "h1 { margin: 0 0 20px; font-size: 32px; line-height: 2; letter-spacing: 1px; }"; + var output = + "h1 { margin: 0 0 6.25vw; font-size: 10vw; line-height: 2; letter-spacing: 1px; }"; var processed = postcss(pxToViewport()).process(input).css; expect(processed).toBe(output); }); - it('should replace the px unit with vw', function () { + it("should replace the px unit with vw", function () { var processed = postcss(pxToViewport()).process(basicCSS).css; - var expected = '.rule { font-size: 4.6875vw }'; + var expected = ".rule { font-size: 4.6875vw }"; 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.15625vw -0.0625vw -.2em }'; + 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.15625vw -0.0625vw -.2em }"; var options = { - minPixelValue: 0 + minPixelValue: 0, }; var processed = postcss(pxToViewport(options)).process(rules).css; expect(processed).toBe(expected); }); - it('should remain unitless if 0', function () { - var expected = '.rule { font-size: 0px; font-size: 0; }'; + 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); }); - it('should not add properties that already exist', function () { - var expected = '.rule { font-size: 16px; font-size: 5vw; }'; + it("should not add properties that already exist", function () { + var expected = ".rule { font-size: 16px; font-size: 5vw; }"; var processed = postcss(pxToViewport()).process(expected).css; expect(processed).toBe(expected); }); - it('should not replace units inside mediaQueries by default', function() { - var expected = '@media (min-width: 500px) { .rule { font-size: 16px } }'; - var processed = postcss(pxToViewport()).process('@media (min-width: 500px) { .rule { font-size: 16px } }').css; + it("should not replace units inside mediaQueries by default", function () { + var expected = "@media (min-width: 500px) { .rule { font-size: 16px } }"; + var processed = postcss(pxToViewport()).process( + "@media (min-width: 500px) { .rule { font-size: 16px } }" + ).css; expect(processed).toBe(expected); - }) + }); }); -describe('value parsing', function() { - it('should not replace values in double quotes or single quotes', function () { +describe("value parsing", function () { + it("should not replace values in double quotes or single quotes", function () { var options = { - propList: ['*'] + propList: ["*"], }; - var rules = '.rule { content: \'16px\'; font-family: "16px"; font-size: 16px; }'; - var expected = '.rule { content: \'16px\'; font-family: "16px"; font-size: 5vw; }'; + var rules = + ".rule { content: '16px'; font-family: \"16px\"; font-size: 16px; }"; + var expected = + ".rule { content: '16px'; font-family: \"16px\"; font-size: 5vw; }"; var processed = postcss(pxToViewport(options)).process(rules).css; expect(processed).toBe(expected); }); - it('should not replace values in `url()`', function () { - var rules = '.rule { background: url(16px.jpg); font-size: 16px; }'; - var expected = '.rule { background: url(16px.jpg); font-size: 5vw; }'; + it("should not replace values in `url()`", function () { + var rules = ".rule { background: url(16px.jpg); font-size: 16px; }"; + var expected = ".rule { background: url(16px.jpg); font-size: 5vw; }"; var processed = postcss(pxToViewport()).process(rules).css; 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; }'; + 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() { - it('should ignore non px values by default', function () { - var expected = '.rule { font-size: 2em }'; +describe("unitToConvert", function () { + it("should ignore non px values by default", function () { + var expected = ".rule { font-size: 2em }"; var processed = postcss(pxToViewport()).process(expected).css; expect(processed).toBe(expected); }); - it('should convert only values described in options', function () { - var rules = '.rule { font-size: 5em; line-height: 2px }'; - var expected = '.rule { font-size: 1.5625vw; line-height: 2px }'; + it("should convert only values described in options", function () { + var rules = ".rule { font-size: 5em; width: 10EM; line-height: 2px }"; + var expected = + ".rule { font-size: 1.5625vw; width: 10EM; line-height: 2px }"; + var options = { + unitToConvert: "em", + }; + var processed = postcss(pxToViewport(options)).process(rules).css; + + expect(processed).toBe(expected); + }); + + it("should convert only values described in array options", function () { + var rules = ".rule { font-size: 5em; width: 10EM; line-height: 2px }"; + var expected = + ".rule { font-size: 1.5625vw; width: 3.125vw; line-height: 2px }"; var options = { - unitToConvert: 'em' + unitToConvert: ["em", "EM"], }; var processed = postcss(pxToViewport(options)).process(rules).css; @@ -107,30 +128,30 @@ describe('unitToConvert', function() { }); }); -describe('viewportWidth', function() { - it('should should replace using 320px by default', function() { - var expected = '.rule { font-size: 4.6875vw }'; +describe("viewportWidth", function () { + it("should should replace using 320px by default", function () { + var expected = ".rule { font-size: 4.6875vw }"; var processed = postcss(pxToViewport()).process(basicCSS).css; expect(processed).toBe(expected); }); - it('should replace using viewportWidth from options', function() { - var expected = '.rule { font-size: 3.125vw }'; + it("should replace using viewportWidth from options", function () { + var expected = ".rule { font-size: 3.125vw }"; var options = { - viewportWidth: 480 + viewportWidth: 480, }; var processed = postcss(pxToViewport(options)).process(basicCSS).css; expect(processed).toBe(expected); - }) + }); }); -describe('unitPrecision', function () { - it('should replace using a decimal of 2 places', function () { - var expected = '.rule { font-size: 4.69vw }'; +describe("unitPrecision", function () { + it("should replace using a decimal of 2 places", function () { + var expected = ".rule { font-size: 4.69vw }"; var options = { - unitPrecision: 2 + unitPrecision: 2, }; var processed = postcss(pxToViewport(options)).process(basicCSS).css; @@ -138,12 +159,12 @@ describe('unitPrecision', function () { }); }); -describe('viewportUnit', function() { - it('should replace using unit from options', function() { - var rules = '.rule { margin-top: 15px }'; - var expected = '.rule { margin-top: 4.6875vh }'; +describe("viewportUnit", function () { + it("should replace using unit from options", function () { + var rules = ".rule { margin-top: 15px }"; + var expected = ".rule { margin-top: 4.6875vh }"; var options = { - viewportUnit: 'vh' + viewportUnit: "vh", }; var processed = postcss(pxToViewport(options)).process(rules).css; @@ -151,12 +172,12 @@ describe('viewportUnit', function() { }); }); -describe('fontViewportUnit', function() { - it('should replace only font-size using unit from options', function() { - var rules = '.rule { margin-top: 15px; font-size: 8px; }'; - var expected = '.rule { margin-top: 4.6875vw; font-size: 2.5vmax; }'; +describe("fontViewportUnit", function () { + it("should replace only font-size using unit from options", function () { + var rules = ".rule { margin-top: 15px; font-size: 8px; }"; + var expected = ".rule { margin-top: 4.6875vw; font-size: 2.5vmax; }"; var options = { - fontViewportUnit: 'vmax' + fontViewportUnit: "vmax", }; var processed = postcss(pxToViewport(options)).process(rules).css; @@ -164,34 +185,38 @@ describe('fontViewportUnit', function() { }); }); -describe('selectorBlackList', function () { - it('should ignore selectors in the selector black list', function () { - var rules = '.rule { font-size: 15px } .rule2 { font-size: 15px }'; - var expected = '.rule { font-size: 4.6875vw } .rule2 { font-size: 15px }'; +describe("selectorBlackList", function () { + it("should ignore selectors in the selector black list", function () { + var rules = ".rule { font-size: 15px } .rule2 { font-size: 15px }"; + var expected = ".rule { font-size: 4.6875vw } .rule2 { font-size: 15px }"; var options = { - selectorBlackList: ['.rule2'] + selectorBlackList: [".rule2"], }; var processed = postcss(pxToViewport(options)).process(rules).css; expect(processed).toBe(expected); }); - it('should ignore every selector with `body$`', function () { - var rules = 'body { font-size: 16px; } .class-body$ { font-size: 16px; } .simple-class { font-size: 16px; }'; - var expected = 'body { font-size: 5vw; } .class-body$ { font-size: 16px; } .simple-class { font-size: 5vw; }'; + it("should ignore every selector with `body$`", function () { + var rules = + "body { font-size: 16px; } .class-body$ { font-size: 16px; } .simple-class { font-size: 16px; }"; + var expected = + "body { font-size: 5vw; } .class-body$ { font-size: 16px; } .simple-class { font-size: 5vw; }"; var options = { - selectorBlackList: ['body$'] + selectorBlackList: ["body$"], }; var processed = postcss(pxToViewport(options)).process(rules).css; expect(processed).toBe(expected); }); - it('should only ignore exactly `body`', function () { - var rules = 'body { font-size: 16px; } .class-body { font-size: 16px; } .simple-class { font-size: 16px; }'; - var expected = 'body { font-size: 16px; } .class-body { font-size: 5vw; } .simple-class { font-size: 5vw; }'; + it("should only ignore exactly `body`", function () { + var rules = + "body { font-size: 16px; } .class-body { font-size: 16px; } .simple-class { font-size: 16px; }"; + var expected = + "body { font-size: 16px; } .class-body { font-size: 5vw; } .simple-class { font-size: 5vw; }"; var options = { - selectorBlackList: [/^body$/] + selectorBlackList: [/^body$/], }; var processed = postcss(pxToViewport(options)).process(rules).css; @@ -199,437 +224,534 @@ describe('selectorBlackList', function () { }); }); -describe('mediaQuery', function () { - it('should replace px inside media queries if opts.mediaQuery', function() { +describe("mediaQuery", function () { + it("should replace px inside media queries if opts.mediaQuery", function () { var options = { - mediaQuery: true + mediaQuery: true, }; - var processed = postcss(pxToViewport(options)).process('@media (min-width: 500px) { .rule { font-size: 16px } }').css; - var expected = '@media (min-width: 500px) { .rule { font-size: 5vw } }'; + var processed = postcss(pxToViewport(options)).process( + "@media (min-width: 500px) { .rule { font-size: 16px } }" + ).css; + var expected = "@media (min-width: 500px) { .rule { font-size: 5vw } }"; expect(processed).toBe(expected); }); - it('should not replace px inside media queries if not opts.mediaQuery', function() { + it("should not replace px inside media queries if not opts.mediaQuery", function () { var options = { - mediaQuery: false + mediaQuery: false, }; - var processed = postcss(pxToViewport(options)).process('@media (min-width: 500px) { .rule { font-size: 16px } }').css; - var expected = '@media (min-width: 500px) { .rule { font-size: 16px } }'; + var processed = postcss(pxToViewport(options)).process( + "@media (min-width: 500px) { .rule { font-size: 16px } }" + ).css; + var expected = "@media (min-width: 500px) { .rule { font-size: 16px } }"; expect(processed).toBe(expected); }); - it('should replace px inside media queries if it has params orientation landscape and landscape option', function() { + it("should replace px inside media queries if it has params orientation landscape and landscape option", function () { var options = { mediaQuery: true, - landscape: true + landscape: true, }; - var processed = postcss(pxToViewport(options)).process('@media (orientation-landscape) and (min-width: 500px) { .rule { font-size: 16px } }').css; - var expected = '@media (orientation-landscape) and (min-width: 500px) { .rule { font-size: 2.8169vw } }'; + var processed = postcss(pxToViewport(options)).process( + "@media (orientation-landscape) and (min-width: 500px) { .rule { font-size: 16px } }" + ).css; + var expected = + "@media (orientation-landscape) and (min-width: 500px) { .rule { font-size: 2.8169vw } }"; expect(processed).toBe(expected); }); }); -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 }'; +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'] + 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 }'; + 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*'] + 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 }'; + 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 () { +describe("minPixelValue", function () { + it("should not replace values below minPixelValue", function () { var options = { - propWhiteList: [], - minPixelValue: 2 + propWhiteList: [], + minPixelValue: 2, }; - var rules = '.rule { border: 1px solid #000; font-size: 16px; margin: 1px 10px; }'; - var expected = '.rule { border: 1px solid #000; font-size: 5vw; margin: 1px 3.125vw; }'; + var rules = + ".rule { border: 1px solid #000; font-size: 16px; margin: 1px 10px; }"; + var expected = + ".rule { border: 1px solid #000; font-size: 5vw; margin: 1px 3.125vw; }"; var processed = postcss(pxToViewport(options)).process(rules).css; expect(processed).toBe(expected); }); }); -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 () { +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\// + exclude: /\/node_modules\//, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/node_modules/main.css' + from: "/node_modules/main.css", }).css; expect(processed).toBe(rules); }); - it('when using regex at the time, the style should be overwritten.', function () { + it("when using regex at the time, the style should be overwritten.", function () { var options = { - exclude: /\/node_modules\// + exclude: /\/node_modules\//, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/example/main.css' + from: "/example/main.css", }).css; expect(processed).toBe(covered); }); - it('when using array at the time, the style should not be overwritten.', function () { + it("when using array at the time, the style should not be overwritten.", function () { var options = { - exclude: [/\/node_modules\//, /\/exclude\//] + exclude: [/\/node_modules\//, /\/exclude\//], }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/exclude/main.css' + from: "/exclude/main.css", }).css; expect(processed).toBe(rules); }); - it('when using array at the time, the style should be overwritten.', function () { + it("when using array at the time, the style should be overwritten.", function () { var options = { - exclude: [/\/node_modules\//, /\/exclude\//] + exclude: [/\/node_modules\//, /\/exclude\//], }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/example/main.css' + from: "/example/main.css", }).css; expect(processed).toBe(covered); }); }); -describe('include', 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 () { +describe("include", 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 = { - include: /\/mobile\// + include: /\/mobile\//, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/pc/main.css' + from: "/pc/main.css", }).css; expect(processed).toBe(rules); }); - it('when using regex at the time, the style should be overwritten.', function () { + it("when using regex at the time, the style should be overwritten.", function () { var options = { - include: /\/mobile\// + include: /\/mobile\//, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/mobile/main.css' + from: "/mobile/main.css", }).css; expect(processed).toBe(covered); }); - it('when using array at the time, the style should not be overwritten.', function () { + it("when using array at the time, the style should not be overwritten.", function () { var options = { - include: [/\/flexible\//, /\/mobile\//] + include: [/\/flexible\//, /\/mobile\//], }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/pc/main.css' + from: "/pc/main.css", }).css; expect(processed).toBe(rules); }); - it('when using array at the time, the style should be overwritten.', function () { + it("when using array at the time, the style should be overwritten.", function () { var options = { - include: [/\/flexible\//, /\/mobile\//] + include: [/\/flexible\//, /\/mobile\//], }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/flexible/main.css' + from: "/flexible/main.css", }).css; expect(processed).toBe(covered); }); }); -describe('include-and-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; }'; +describe("include-and-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 () { + it("when using regex at the time, the style should not be overwritten.", function () { var options = { include: /\/mobile\//, - exclude: /\/not-transform\// + exclude: /\/not-transform\//, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/mobile/not-transform/main.css' + from: "/mobile/not-transform/main.css", }).css; expect(processed).toBe(rules); }); - it('when using regex at the time, the style should be overwritten.', function () { + it("when using regex at the time, the style should be overwritten.", function () { var options = { include: /\/mobile\//, - exclude: /\/not-transform\// + exclude: /\/not-transform\//, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/mobile/style/main.css' + from: "/mobile/style/main.css", }).css; expect(processed).toBe(covered); }); - it('when using array at the time, the style should not be overwritten.', function () { + it("when using array at the time, the style should not be overwritten.", function () { var options = { include: [/\/flexible\//, /\/mobile\//], - exclude: [/\/not-transform\//, /pc/] + exclude: [/\/not-transform\//, /pc/], }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/flexible/not-transform/main.css' + from: "/flexible/not-transform/main.css", }).css; expect(processed).toBe(rules); }); - it('when using regex at the time, the style should be overwritten.', function () { + it("when using regex at the time, the style should be overwritten.", function () { var options = { include: [/\/flexible\//, /\/mobile\//], - exclude: [/\/not-transform\//, /pc/] + exclude: [/\/not-transform\//, /pc/], }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/mobile/style/main.css' + from: "/mobile/style/main.css", }).css; expect(processed).toBe(covered); }); }); -describe('regex', 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; }'; +describe("regex", 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 () { + it("when using regex at the time, the style should not be overwritten.", function () { var options = { - exclude: /pc/ + exclude: /pc/, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/pc-project/main.css' + from: "/pc-project/main.css", }).css; expect(processed).toBe(rules); }); - it('when using regex at the time, the style should be overwritten.', function () { + it("when using regex at the time, the style should be overwritten.", function () { var options = { - exclude: /\/pc\// + exclude: /\/pc\//, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/pc-project/main.css' + from: "/pc-project/main.css", }).css; expect(processed).toBe(covered); }); - it('when using regex at the time, the style should not be overwritten.', function () { + it("when using regex at the time, the style should not be overwritten.", function () { var options = { - include: /\/pc\// + include: /\/pc\//, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/pc-project/main.css' + from: "/pc-project/main.css", }).css; expect(processed).toBe(rules); }); - it('when using regex at the time, the style should be overwritten.', function () { + it("when using regex at the time, the style should be overwritten.", function () { var options = { - include: /pc/ + include: /pc/, }; var processed = postcss(pxToViewport(options)).process(rules, { - from: '/pc-project/main.css' + from: "/pc-project/main.css", }).css; expect(processed).toBe(covered); }); }); -describe('replace', function () { - it('should leave fallback pixel unit with root em value', function () { +describe("replace", function () { + it("should leave fallback pixel unit with root em value", function () { var options = { - replace: false + replace: false, }; var processed = postcss(pxToViewport(options)).process(basicCSS).css; - var expected = '.rule { font-size: 15px; font-size: 4.6875vw }'; + var expected = ".rule { font-size: 15px; font-size: 4.6875vw }"; expect(processed).toBe(expected); }); }); -describe('filter-prop-list', function () { +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'; + 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'; + 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'; + 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'; + 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'; + 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'; + 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'; + 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'; + var propList = [ + "font-size", + "*margin*", + "!padding", + "!border*", + "*", + "!*y", + "!*font*", + ]; + var expected = "y"; expect(filterPropList.notEndWith(propList).join()).toBe(expected); }); }); -describe('landscape', function() { - it('should add landscape atRule', 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: 1.5625vw; padding: 1.5625vw; padding-right: 5vw }@media (orientation: landscape) {.rule { font-size: 2.8169vw; margin: 2.8169vw; margin-left: 0.88028vw; padding: 0.88028vw; padding-right: 2.8169vw } }'; +describe("landscape", function () { + it("should add landscape atRule", 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: 1.5625vw; padding: 1.5625vw; padding-right: 5vw }@media (orientation: landscape) {.rule { font-size: 2.8169vw; margin: 2.8169vw; margin-left: 0.88028vw; padding: 0.88028vw; padding-right: 2.8169vw } }"; var options = { - landscape: true + landscape: true, }; var processed = postcss(pxToViewport(options)).process(css).css; expect(processed).toBe(expected); }); - it('should add landscape atRule with specified landscapeUnits', 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: 1.5625vw; padding: 1.5625vw; padding-right: 5vw }@media (orientation: landscape) {.rule { font-size: 2.8169vh; margin: 2.8169vh; margin-left: 0.88028vh; padding: 0.88028vh; padding-right: 2.8169vh } }'; + it("should add landscape atRule with specified landscapeUnits", 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: 1.5625vw; padding: 1.5625vw; padding-right: 5vw }@media (orientation: landscape) {.rule { font-size: 2.8169vh; margin: 2.8169vh; margin-left: 0.88028vh; padding: 0.88028vh; padding-right: 2.8169vh } }"; var options = { landscape: true, - landscapeUnit: 'vh' + landscapeUnit: "vh", }; var processed = postcss(pxToViewport(options)).process(css).css; expect(processed).toBe(expected); }); - it('should not add landscape atRule in mediaQueries', function() { - var css = '@media (min-width: 500px) { .rule { font-size: 16px } }'; - var expected = '@media (min-width: 500px) { .rule { font-size: 5vw } }'; + it("should not add landscape atRule in mediaQueries", function () { + var css = "@media (min-width: 500px) { .rule { font-size: 16px } }"; + var expected = "@media (min-width: 500px) { .rule { font-size: 5vw } }"; var options = { landscape: true, - mediaQuery: true + mediaQuery: true, }; var processed = postcss(pxToViewport(options)).process(css).css; expect(processed).toBe(expected); }); - it('should not replace values inside landscape atRule', function() { + it("should not replace values inside landscape atRule", function () { var options = { replace: false, - landscape: true + landscape: true, }; var processed = postcss(pxToViewport(options)).process(basicCSS).css; - var expected = '.rule { font-size: 15px; font-size: 4.6875vw }@media (orientation: landscape) {.rule { font-size: 2.64085vw } }'; + var expected = + ".rule { font-size: 15px; font-size: 4.6875vw }@media (orientation: landscape) {.rule { font-size: 2.64085vw } }"; expect(processed).toBe(expected); }); - it('should add landscape atRule with specified landscapeWidth', function() { + it("should add landscape atRule with specified landscapeWidth", function () { var options = { landscape: true, - landscapeWidth: 768 + landscapeWidth: 768, }; var processed = postcss(pxToViewport(options)).process(basicCSS).css; - var expected = '.rule { font-size: 4.6875vw }@media (orientation: landscape) {.rule { font-size: 1.95313vw } }'; + var expected = + ".rule { font-size: 4.6875vw }@media (orientation: landscape) {.rule { font-size: 1.95313vw } }"; expect(processed).toBe(expected); }); - it('should not add landscape atRule if it has no nodes', function() { - var css = '.rule { font-size: 15vw }'; + it("should not add landscape atRule if it has no nodes", function () { + var css = ".rule { font-size: 15vw }"; var options = { - landscape: true + landscape: true, }; var processed = postcss(pxToViewport(options)).process(css).css; - var expected = '.rule { font-size: 15vw }'; + var expected = ".rule { font-size: 15vw }"; expect(processed).toBe(expected); }); }); -describe('/* px-to-viewport-ignore */ & /* px-to-viewport-ignore-next */', function() { - it('should ignore right-commented', function() { - var css = '.rule { font-size: 15px; /* simple comment */ width: 100px; /* px-to-viewport-ignore */ height: 50px; }'; - var expected = '.rule { font-size: 4.6875vw; /* simple comment */ width: 100px; height: 15.625vw; }'; +describe("/* px-to-viewport-ignore */ & /* px-to-viewport-ignore-next */", function () { + it("should ignore right-commented", function () { + var css = + ".rule { font-size: 15px; /* simple comment */ width: 100px; /* px-to-viewport-ignore */ height: 50px; }"; + var expected = + ".rule { font-size: 4.6875vw; /* simple comment */ width: 100px; height: 15.625vw; }"; var processed = postcss(pxToViewport()).process(css).css; expect(processed).toBe(expected); }); - it('should ignore right-commented in multiline-css', function() { - var css = '.rule {\n font-size: 15px;\n width: 100px; /*px-to-viewport-ignore*/\n height: 50px;\n}'; - var expected = '.rule {\n font-size: 4.6875vw;\n width: 100px;\n height: 15.625vw;\n}'; + it("should ignore right-commented in multiline-css", function () { + var css = + ".rule {\n font-size: 15px;\n width: 100px; /*px-to-viewport-ignore*/\n height: 50px;\n}"; + var expected = + ".rule {\n font-size: 4.6875vw;\n width: 100px;\n height: 15.625vw;\n}"; var processed = postcss(pxToViewport()).process(css).css; expect(processed).toBe(expected); }); - it('should ignore before-commented in multiline-css', function() { - var css = '.rule {\n font-size: 15px;\n /*px-to-viewport-ignore-next*/\n width: 100px;\n /*px-to-viewport-ignore*/\n height: 50px;\n}'; - var expected = '.rule {\n font-size: 4.6875vw;\n width: 100px;\n /*px-to-viewport-ignore*/\n height: 15.625vw;\n}'; + it("should ignore before-commented in multiline-css", function () { + var css = + ".rule {\n font-size: 15px;\n /*px-to-viewport-ignore-next*/\n width: 100px;\n /*px-to-viewport-ignore*/\n height: 50px;\n}"; + var expected = + ".rule {\n font-size: 4.6875vw;\n width: 100px;\n /*px-to-viewport-ignore*/\n height: 15.625vw;\n}"; var processed = postcss(pxToViewport()).process(css).css; diff --git a/src/pixel-unit-regexp.js b/src/pixel-unit-regexp.js index f166fb0..501e9c9 100644 --- a/src/pixel-unit-regexp.js +++ b/src/pixel-unit-regexp.js @@ -5,10 +5,25 @@ // Not anything inside url() // Any digit followed by px // !singlequotes|!doublequotes|!url()|pixelunit + +function getUnitPartPatternStr(unit) { + if (typeof unit === "string") { + return unit; + } + if (unit.length === 1) { + return unit[0]; + } + return "(" + unit.join("|") + ")"; +} function getUnitRegexp(unit) { - return new RegExp('"[^"]+"|\'[^\']+\'|url\\([^\\)]+\\)|(\\d*\\.?\\d+)' + unit, 'g'); + var unitRegStr = getUnitPartPatternStr(unit); + + return new RegExp( + "\"[^\"]+\"|'[^']+'|url\\([^\\)]+\\)|(\\d*\\.?\\d+)" + unitRegStr, + "g" + ); } module.exports = { - getUnitRegexp + getUnitRegexp, };