Skip to content

feat: support array format for unitToConvert option #122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
@@ -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的属性列表
143 changes: 97 additions & 46 deletions index.js
Original file line number Diff line number Diff line change
@@ -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,15 +154,18 @@ 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 {
unit = getUnit(decl.prop, opts);
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,57 +192,61 @@ 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) {
return function (m, $1) {
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;
});
}

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
562 changes: 342 additions & 220 deletions spec/px-to-viewport.spec.js

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions src/pixel-unit-regexp.js
Original file line number Diff line number Diff line change
@@ -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,
};