@minko-fe/postcss-pxtorem
Version:
A postcss plugin that converts px to rem.
606 lines (595 loc) • 20.2 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
DEFAULT_OPTIONS: () => DEFAULT_OPTIONS,
default: () => src_default,
pxtorem: () => pxtorem
});
module.exports = __toCommonJS(src_exports);
var import_lodash_pro3 = require("@minko-fe/lodash-pro");
// src/utils/index.ts
var import_lodash_pro2 = require("@minko-fe/lodash-pro");
// src/utils/constant.ts
var MAYBE_REGEXP = ["selectorBlackList", "exclude", "include"];
var DISABLE_NEXT_COMMENT = "pxtorem-disable-next-line";
// src/utils/parse-query.ts
var import_lodash_pro = require("@minko-fe/lodash-pro");
// node_modules/.pnpm/decode-uri-component@0.4.1/node_modules/decode-uri-component/index.js
var token = "%[a-f0-9]{2}";
var singleMatcher = new RegExp("(" + token + ")|([^%]+?)", "gi");
var multiMatcher = new RegExp("(" + token + ")+", "gi");
function decodeComponents(components, split) {
try {
return [decodeURIComponent(components.join(""))];
} catch {
}
if (components.length === 1) {
return components;
}
split = split || 1;
const left = components.slice(0, split);
const right = components.slice(split);
return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right));
}
function decode(input) {
try {
return decodeURIComponent(input);
} catch {
let tokens = input.match(singleMatcher) || [];
for (let i = 1; i < tokens.length; i++) {
input = decodeComponents(tokens, i).join("");
tokens = input.match(singleMatcher) || [];
}
return input;
}
}
function customDecodeURIComponent(input) {
const replaceMap = {
"%FE%FF": "\uFFFD\uFFFD",
"%FF%FE": "\uFFFD\uFFFD"
};
let match = multiMatcher.exec(input);
while (match) {
try {
replaceMap[match[0]] = decodeURIComponent(match[0]);
} catch {
const result = decode(match[0]);
if (result !== match[0]) {
replaceMap[match[0]] = result;
}
}
match = multiMatcher.exec(input);
}
replaceMap["%C2"] = "\uFFFD";
const entries = Object.keys(replaceMap);
for (const key of entries) {
input = input.replace(new RegExp(key, "g"), replaceMap[key]);
}
return input;
}
function decodeUriComponent(encodedURI) {
if (typeof encodedURI !== "string") {
throw new TypeError("Expected `encodedURI` to be of type `string`, got `" + typeof encodedURI + "`");
}
try {
return decodeURIComponent(encodedURI);
} catch {
return customDecodeURIComponent(encodedURI);
}
}
// node_modules/.pnpm/split-on-first@3.0.0/node_modules/split-on-first/index.js
function splitOnFirst(string, separator) {
if (!(typeof string === "string" && typeof separator === "string")) {
throw new TypeError("Expected the arguments to be of type `string`");
}
if (string === "" || separator === "") {
return [];
}
const separatorIndex = string.indexOf(separator);
if (separatorIndex === -1) {
return [];
}
return [
string.slice(0, separatorIndex),
string.slice(separatorIndex + separator.length)
];
}
// src/utils/parse-query.ts
function validateArrayFormatSeparator(value) {
if (typeof value !== "string" || value.length !== 1) {
throw new TypeError("arrayFormatSeparator must be single character string");
}
}
function parse(query, options) {
options = {
decode: true,
sort: true,
arrayFormat: "none",
arrayFormatSeparator: ",",
parseNumbers: false,
parseBooleans: false,
...options
};
validateArrayFormatSeparator(options.arrayFormatSeparator);
const formatter = parserForArrayFormat(options);
const returnValue = /* @__PURE__ */ Object.create(null);
if (typeof query !== "string") {
return returnValue;
}
query = query.trim().replace(/^[#&?]/, "");
if (!query) {
return returnValue;
}
for (const parameter of query.split("&")) {
if (parameter === "") {
continue;
}
const parameter_ = options.decode ? parameter.replaceAll("+", " ") : parameter;
let [key, value] = splitOnFirst(parameter_, "=");
if (key === void 0) {
key = parameter_;
}
value = value === void 0 ? null : ["comma", "separator", "bracket-separator"].includes(options.arrayFormat) ? value : decode2(value, options);
formatter(decode2(key, options), value, returnValue);
}
for (const [key, value] of Object.entries(returnValue)) {
if (typeof value === "object" && value !== null) {
for (const [key2, value2] of Object.entries(value)) {
value[key2] = parseValue(value2, options);
}
} else {
returnValue[key] = parseValue(value, options);
}
}
if (options.sort === false) {
return returnValue;
}
return (options.sort === true ? Object.keys(returnValue).sort() : Object.keys(returnValue).sort(options.sort)).reduce(
(result, key) => {
const value = returnValue[key];
if (Boolean(value) && (0, import_lodash_pro.isObject)(value) && !(0, import_lodash_pro.isArray)(value)) {
result[key] = keysSorter(value);
} else {
result[key] = value;
}
return result;
},
/* @__PURE__ */ Object.create(null)
);
}
function parserForArrayFormat(options) {
let result;
switch (options.arrayFormat) {
case "index": {
return (key, value, accumulator) => {
result = /\[(\d*)]$/.exec(key);
key = key.replace(/\[\d*]$/, "");
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === void 0) {
accumulator[key] = {};
}
accumulator[key][result[1]] = value;
};
}
case "bracket": {
return (key, value, accumulator) => {
result = /(\[])$/.exec(key);
key = key.replace(/\[]$/, "");
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === void 0) {
accumulator[key] = [value];
return;
}
accumulator[key] = [...accumulator[key], value];
};
}
case "colon-list-separator": {
return (key, value, accumulator) => {
result = /(:list)$/.exec(key);
key = key.replace(/:list$/, "");
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === void 0) {
accumulator[key] = [value];
return;
}
accumulator[key] = [...accumulator[key], value];
};
}
case "comma":
case "separator": {
return (key, value, accumulator) => {
const isArray4 = typeof value === "string" && value.includes(options.arrayFormatSeparator);
const isEncodedArray = typeof value === "string" && !isArray4 && decode2(value, options).includes(options.arrayFormatSeparator);
value = isEncodedArray ? decode2(value, options) : value;
const newValue = isArray4 || isEncodedArray ? value.split(options.arrayFormatSeparator).map((item) => decode2(item, options)) : value === null ? value : decode2(value, options);
accumulator[key] = newValue;
};
}
case "bracket-separator": {
return (key, value, accumulator) => {
const isArray4 = /(\[])$/.test(key);
key = key.replace(/\[]$/, "");
if (!isArray4) {
accumulator[key] = value ? decode2(value, options) : value;
return;
}
const arrayValue = value === null ? [] : value.split(options.arrayFormatSeparator).map((item) => decode2(item, options));
if (accumulator[key] === void 0) {
accumulator[key] = arrayValue;
return;
}
accumulator[key] = [...accumulator[key], ...arrayValue];
};
}
default: {
return (key, value, accumulator) => {
if (accumulator[key] === void 0) {
accumulator[key] = value;
return;
}
accumulator[key] = [...[accumulator[key]].flat(), value];
};
}
}
}
function decode2(value, options) {
if (options.decode) {
return decodeUriComponent(value);
}
return value;
}
function parseValue(value, options) {
if (options.parseNumbers && !Number.isNaN(Number(value)) && typeof value === "string" && value.trim() !== "") {
value = Number(value);
} else if (options.parseBooleans && value !== null && (value.toLowerCase() === "true" || value.toLowerCase() === "false")) {
value = value.toLowerCase() === "true";
}
return value;
}
function keysSorter(input) {
if ((0, import_lodash_pro.isArray)(input)) {
return input.sort();
}
if (typeof input === "object") {
return keysSorter(Object.keys(input)).sort((a, b) => Number(a) - Number(b)).map((key) => input[key]);
}
return input;
}
// src/utils/index.ts
function reRegExp() {
return /^\/((?:\\\/|[^/])+)\/([gimy]*)$/;
}
var filterPropList = Object.freeze({
exact(list) {
return list.filter((m) => m.match(/^[^!*]+$/));
},
contain(list) {
return list.filter((m) => m.match(/^\*.+\*$/)).map((m) => m.slice(1, -1));
},
endWith(list) {
return list.filter((m) => m.match(/^\*[^*]+$/)).map((m) => m.slice(1));
},
startWith(list) {
return list.filter((m) => m.match(/^[^!*]+\*$/)).map((m) => m.slice(0, Math.max(0, m.length - 1)));
},
notExact(list) {
return list.filter((m) => m.match(/^![^*].*$/)).map((m) => m.slice(1));
},
notContain(list) {
return list.filter((m) => m.match(/^!\*.+\*$/)).map((m) => m.slice(2, -1));
},
notEndWith(list) {
return list.filter((m) => m.match(/^!\*[^*]+$/)).map((m) => m.slice(2));
},
notStartWith(list) {
return list.filter((m) => m.match(/^![^*]+\*$/)).map((m) => m.slice(1, -1));
}
});
function initOptions(options) {
return Object.assign({}, DEFAULT_OPTIONS, options);
}
function isOptionComment(node) {
return (node == null ? void 0 : node.type) === "comment";
}
var processd = Symbol("processed");
function isRepeatRun(r) {
if (!r)
return false;
if (Reflect.get(r, processd)) {
return true;
}
Reflect.set(r, processd, true);
return false;
}
function parseRegExp(maybeRegExpArg) {
var _a, _b;
const RE_REGEXP = reRegExp();
if ((0, import_lodash_pro2.isString)(maybeRegExpArg) && RE_REGEXP.test(maybeRegExpArg)) {
return new RegExp(((_a = RE_REGEXP.exec(maybeRegExpArg)) == null ? void 0 : _a[1]) || "", (_b = RE_REGEXP.exec(maybeRegExpArg)) == null ? void 0 : _b[2]);
}
return maybeRegExpArg;
}
var isPxtoremReg = /(?<=^pxtorem\?).+/g;
function getOptionsFromComment(comment, parseOptions) {
var _a, _b;
try {
const index = comment.text.search(isPxtoremReg);
const ret = {};
let query = comment.text.slice(index);
if (!query || index === -1)
return ret;
query = query.replace(/\s+/g, "");
const defaultKeys = Object.keys(DEFAULT_OPTIONS);
const parsed = parse(query, {
parseBooleans: true,
parseNumbers: true,
arrayFormat: "bracket-separator",
arrayFormatSeparator: "|",
...parseOptions
});
const RE_REGEXP = reRegExp();
for (const k of Object.keys(parsed)) {
if (defaultKeys.includes(k)) {
let cur = parsed[k];
if (MAYBE_REGEXP.includes(k)) {
if ((0, import_lodash_pro2.isArray)(cur)) {
cur = cur.map((t) => {
return parseRegExp(t);
});
} else if ((0, import_lodash_pro2.isString)(cur) && RE_REGEXP.test(cur)) {
cur = parseRegExp(cur);
}
}
ret[k] = cur;
}
}
return ret;
} catch {
console.warn("Unexpected comment", { start: (_a = comment.source) == null ? void 0 : _a.start, end: (_b = comment.source) == null ? void 0 : _b.end });
}
}
function createPropListMatcher(filterList) {
const hasWild = filterList.includes("*");
const matchAll = hasWild && filterList.length === 1;
const lists = {
exact: filterPropList.exact(filterList),
contain: filterPropList.contain(filterList),
startWith: filterPropList.startWith(filterList),
endWith: filterPropList.endWith(filterList),
notExact: filterPropList.notExact(filterList),
notContain: filterPropList.notContain(filterList),
notStartWith: filterPropList.notStartWith(filterList),
notEndWith: filterPropList.notEndWith(filterList)
};
return function(prop) {
if (matchAll)
return true;
return (hasWild || lists.exact.includes(prop) || lists.contain.some((m) => prop.includes(m)) || lists.startWith.some((m) => prop.indexOf(m) === 0) || lists.endWith.some((m) => prop.indexOf(m) === prop.length - m.length)) && !(lists.notExact.includes(prop) || lists.notContain.some((m) => prop.includes(m)) || lists.notStartWith.some((m) => prop.indexOf(m) === 0) || lists.notEndWith.some((m) => prop.indexOf(m) === prop.length - m.length));
};
}
function toFixed(number, precision) {
const multiplier = 10 ** (precision + 1);
const 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 = Number.parseFloat($1);
if (pixels <= minPixelValue)
return m;
const fixedVal = toFixed(pixels / rootValue, unitPrecision);
return fixedVal === 0 ? "0" : `${fixedVal}rem`;
};
}
function blacklistedSelector(blacklist, selector) {
if (!(0, import_lodash_pro2.isString)(selector))
return;
return blacklist.some((t) => {
if ((0, import_lodash_pro2.isString)(t)) {
return selector.includes(t);
}
return selector.match(t);
});
}
function declarationExists(decls, prop, value) {
return decls.some((decl) => {
return decl.prop === prop && decl.value === value;
});
}
function isXClude(Xclude, filePath) {
return Xclude && filePath && ((0, import_lodash_pro2.isFunction)(Xclude) && Xclude(filePath) || (0, import_lodash_pro2.isString)(Xclude) && filePath.includes(Xclude) || (0, import_lodash_pro2.isRegExp)(Xclude) && Xclude.test(filePath));
}
function judgeIsExclude(exclude, include, filePath) {
if (isXClude(exclude, filePath)) {
return true;
}
if (include) {
if (isXClude(include, filePath)) {
return false;
}
return true;
}
return false;
}
function convertUnitFn(value, convert) {
if (typeof convert.source === "string") {
return value.replace(new RegExp(`${convert.source}$`), convert.target);
} else if ((0, import_lodash_pro2.isRegExp)(convert.source)) {
return value.replace(new RegExp(convert.source), convert.target);
}
throw new Error("convertUnit source must be string or RegExp");
}
function checkIfDisable(p) {
const { disable, isExcludeFile, r } = p;
return disable || isExcludeFile || isRepeatRun(r);
}
var OPTION_SYMBOL = Symbol("OPTION_SYMBOL");
function setupCurrentOptions(h, {
node,
comment
}) {
var _a;
if (isOptionComment(comment)) {
h[OPTION_SYMBOL].originOpts = {
...h[OPTION_SYMBOL].originOpts,
...getOptionsFromComment(comment, h[OPTION_SYMBOL].originOpts.parseOptions)
};
}
const { originOpts } = h[OPTION_SYMBOL];
const { include, exclude, rootValue, disable } = originOpts;
h[OPTION_SYMBOL].isExcludeFile = judgeIsExclude(exclude, include, (_a = node == null ? void 0 : node.source) == null ? void 0 : _a.input.file);
if (checkIfDisable({ disable, isExcludeFile: h[OPTION_SYMBOL].isExcludeFile })) {
return;
}
h[OPTION_SYMBOL].originOpts.rootValue = (0, import_lodash_pro2.isFunction)(rootValue) ? rootValue(node == null ? void 0 : node.source.input) : rootValue;
h[OPTION_SYMBOL].pxReplace = createPxReplace(
h[OPTION_SYMBOL].originOpts.rootValue,
originOpts.unitPrecision,
originOpts.minPixelValue
);
}
// src/utils/pixel-unit-regex.ts
function getUnitRegexp(unit) {
return new RegExp(`"[^"]+"|'[^']+'|url\\([^\\)]+\\)|--[\\w-]+|(\\d*\\.?\\d+)${unit}`, "g");
}
// src/index.ts
var DEFAULT_OPTIONS = {
rootValue: 16,
unitToConvert: "px",
unitPrecision: 5,
selectorBlackList: [],
propList: ["*"],
replace: true,
atRules: false,
minPixelValue: 0,
include: null,
exclude: null,
disable: false,
convertUnit: false,
parseOptions: {}
};
var postcssPlugin = "postcss-pxtorem";
function pxtorem(options) {
const RAW_OPTIONS = initOptions(options);
const plugin = {
postcssPlugin,
Once(r, h) {
const node = r.root();
const firstNode = node.nodes[0];
h[OPTION_SYMBOL] = {
isExcludeFile: false,
pxReplace: void 0,
originOpts: (0, import_lodash_pro3.cloneDeep)(RAW_OPTIONS)
// avoid reference pollution
};
setupCurrentOptions(h, { node, comment: firstNode });
},
Comment(node, h) {
setupCurrentOptions(h, { node, comment: node });
},
CommentExit(comment) {
var _a;
if ((_a = comment.text.match(isPxtoremReg)) == null ? void 0 : _a.length) {
comment.remove();
}
},
Declaration(decl, h) {
const opts = h[OPTION_SYMBOL].originOpts;
if (checkIfDisable({ disable: opts.disable, isExcludeFile: h[OPTION_SYMBOL].isExcludeFile, r: decl })) {
return;
}
const satisfyPropList = createPropListMatcher(opts.propList);
if (!decl.value.includes(opts.unitToConvert) || !satisfyPropList(decl.prop) || blacklistedSelector(opts.selectorBlackList, decl.parent.selector)) {
return;
}
const prev = decl.prev();
if ((prev == null ? void 0 : prev.type) === "comment" && prev.text === DISABLE_NEXT_COMMENT) {
prev.remove();
return;
}
const pxRegex = getUnitRegexp(opts.unitToConvert);
const value = h[OPTION_SYMBOL].pxReplace ? decl.value.replace(pxRegex, h[OPTION_SYMBOL].pxReplace) : decl.value;
if (declarationExists(decl.parent, decl.prop, value))
return;
if (opts.replace) {
decl.value = value;
} else {
decl.cloneAfter({ value });
}
},
DeclarationExit(decl, h) {
const opts = h[OPTION_SYMBOL].originOpts;
const { convertUnit } = opts;
if (convertUnit) {
if ((0, import_lodash_pro3.isArray)(convertUnit)) {
decl.value = convertUnit.reduce((c, conv) => {
return convertUnitFn(c, conv);
}, decl.value);
} else {
decl.value = convertUnitFn(decl.value, convertUnit);
}
}
},
AtRule(atRule, h) {
const opts = h[OPTION_SYMBOL].originOpts;
if (checkIfDisable({ disable: opts.disable, isExcludeFile: h[OPTION_SYMBOL].isExcludeFile, r: atRule })) {
return;
}
function replacePxInRules() {
if (!atRule.params.includes(opts.unitToConvert))
return;
const pxRegex = getUnitRegexp(opts.unitToConvert);
atRule.params = h[OPTION_SYMBOL].pxReplace ? atRule.params.replace(pxRegex, h[OPTION_SYMBOL].pxReplace) : atRule.params;
}
if ((0, import_lodash_pro3.isBoolean)(opts.atRules) && opts.atRules) {
replacePxInRules();
}
if ((0, import_lodash_pro3.isArray)(opts.atRules) && opts.atRules.length > 0 && opts.atRules.includes(atRule.name)) {
replacePxInRules();
}
}
};
if (options == null ? void 0 : options.disable) {
return {
postcssPlugin
};
}
return plugin;
}
pxtorem.postcss = true;
var src_default = pxtorem;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
DEFAULT_OPTIONS,
pxtorem
});
module.exports = module.exports.default;