@rain-star/postcss-pxtorem
Version:
✨ a postcss plugin that transform px to rem
130 lines (127 loc) • 4.72 kB
JavaScript
// src/utils/pixel-unit-regex.ts
var pxRegex = /"[^"]+"|'[^']+'|url\([^)]+\)|var\([^)]+\)|(\d*\.?\d+)px/g;
// src/utils/filter-prop-list.ts
var filterPropList = {
exact: (list) => list.filter((m) => m.match(/^[^*!]+$/)),
contain: (list) => list.filter((m) => m.match(/^\*.+\*$/)).map((m) => m.substr(1, m.length - 2)),
endWith: (list) => list.filter((m) => m.match(/^\*[^*]+$/)).map((m) => m.substr(1)),
startWith: (list) => list.filter((m) => m.match(/^[^*!]+\*$/)).map((m) => m.substr(0, m.length - 1)),
notExact: (list) => list.filter((m) => m.match(/^![^*].*$/)).map((m) => m.substr(1)),
notContain: (list) => list.filter((m) => m.match(/^!\*.+\*$/)).map((m) => m.substr(2, m.length - 3)),
notEndWith: (list) => list.filter((m) => m.match(/^!\*[^*]+$/)).map((m) => m.substr(2)),
notStartWith: (list) => list.filter((m) => m.match(/^![^*]+\*$/)).map((m) => m.substr(1, m.length - 2))
};
// src/index.ts
import { isFunction, isRegExp, isString } from "lodash";
var defaults = {
rootValue: 16,
unitPrecision: 5,
propList: ["font", "font-size", "line-height", "letter-spacing"],
replace: true,
mediaQuery: false,
minPixelValue: 0,
exclude: null
};
function toFixed(number, precision) {
const multiplier = Math.pow(10, precision + 1), wholeNumber = Math.floor(number * multiplier);
return Math.round(wholeNumber / 10) * 10 / multiplier;
}
function createPxReplace(rootValue, unitPrecision, minPixelValue) {
return (m, $1) => {
if (!$1)
return m;
const pixels = parseFloat($1);
if (pixels < minPixelValue)
return m;
const fixedVal = toFixed(pixels / rootValue, unitPrecision);
return fixedVal === 0 ? "0" : fixedVal + "rem";
};
}
function declarationExists(decls, prop, value) {
return decls.some((decl) => decl.prop === prop && decl.value === value);
}
function createPropListMatcher(propList) {
const hasWild = propList.indexOf("*") > -1;
const matchAll = hasWild && propList.length === 1;
const 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 (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;
}));
};
}
var pxtorem = (options = {}) => {
const opts = Object.assign({}, defaults, options);
const satisfyPropList = createPropListMatcher(opts.propList);
const exclude = opts.exclude;
let isExcludeFile = false;
let pxReplace;
return {
postcssPlugin: "postcss-pxtorem",
Once(css) {
const filePath = css.source?.input.file;
if (exclude && (isFunction(exclude) && exclude(filePath) || isString(exclude) && filePath.indexOf(exclude) !== -1 || isRegExp(exclude) && filePath.match(exclude) !== null)) {
isExcludeFile = true;
} else {
isExcludeFile = false;
}
const rootValue = isFunction(opts.rootValue) ? opts.rootValue(css.source?.input) : opts.rootValue;
pxReplace = createPxReplace(rootValue, opts.unitPrecision, opts.minPixelValue);
},
Declaration(decl) {
if (isExcludeFile) {
return;
}
if (decl.value.indexOf("px") === -1 || !satisfyPropList(decl.prop)) {
return;
}
const value = decl.value.replace(pxRegex, pxReplace);
if (declarationExists(decl.parent, decl.prop, value)) {
return;
}
if (opts.replace) {
decl.value = value;
} else {
decl.cloneAfter({ value });
}
},
AtRule(atRule) {
if (isExcludeFile) {
return;
}
if (opts.mediaQuery && atRule.name === "media") {
if (atRule.params.indexOf("px") === -1) {
return;
}
atRule.params = atRule.params.replace(pxRegex, pxReplace);
}
}
};
};
pxtorem.postcss = true;
var src_default = pxtorem;
export {
src_default as default,
pxtorem
};