@cherrywind/postcss
Version:
A collection of PostCSS plugins for modern CSS development.
1 lines • 10.9 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../src/px-to-local-var/lib/black-list.ts","../src/px-to-local-var/lib/constant.ts","../src/px-to-local-var/lib/filter-prop-list.ts","../src/px-to-local-var/lib/pixel-unit-regex.ts","../src/px-to-local-var/index.ts","../src/rem-to-local-var/index.ts"],"sourcesContent":["export function createSelectorBlackList(blackList: (string | RegExp)[]) {\n return (selector: string) => {\n return blackList.some((item) => {\n if (typeof item === 'string') return selector.includes(item);\n return item.test(selector);\n });\n };\n}\n","export const DISABLE_COMMENT_REG = /pxtovar-disable-next-line/;\n","export function createPropListMatcher(propList: string[]) {\n const hasWildcard = propList.includes('*');\n const matchList = propList.filter((p) => !p.startsWith('!'));\n const ignoreList = propList.filter((p) => p.startsWith('!')).map((p) => p.slice(1));\n\n function matches(prop: string, list: string[]) {\n return list.some((pattern) => {\n if (pattern === '*') return true;\n if (pattern.endsWith('*')) return prop.startsWith(pattern.slice(0, -1));\n return prop === pattern;\n });\n }\n\n return (prop: string) => {\n if (hasWildcard && !matches(prop, ignoreList)) return true;\n return matches(prop, matchList) && !matches(prop, ignoreList);\n };\n}\n","// 匹配 px 单位,跳过字符串和 url,支持负数\nconst pixelUnitRegex = /\"[^\"]+\"|'[^']+'|url\\([^)]+\\)|(-?\\d*\\.?\\d+)px/g;\nexport default pixelUnitRegex;\n","import type { AtRule, Declaration, Plugin, Root } from 'postcss';\nimport { createSelectorBlackList } from './lib/black-list';\nimport { DISABLE_COMMENT_REG } from './lib/constant';\nimport { createPropListMatcher } from './lib/filter-prop-list';\nimport pixelUnitRegex from './lib/pixel-unit-regex';\n\nexport interface PxToLocalVarOptions {\n /**\n * scope rem的根元素字体大小,计算 scope rem 值的基准 (vw = px / rootValue)。\n * @default 16\n */\n rootValue?: number;\n /**\n * 转换后 scope rem 值的小数点位数。\n * @default 5\n */\n unitPrecision?: number;\n /**\n * 选择器黑名单,数组内的选择器不会被转换。支持字符串和正则。\n * @default []\n */\n selectorBlackList?: (string | RegExp)[];\n /**\n * 要转换的 CSS 属性列表,['*'] 表示所有属性。支持通配符和排除(如 '!margin')。\n * @default ['*']\n */\n propList?: string[];\n /**\n * 是否直接替换 px 值,true 为直接替换,false 为在 px 声明后添加 rem 声明作为回退。\n * 也可以在开发模式传入 'replace',为false,便于调试\n * @default true\n */\n replace?: boolean;\n /**\n * 是否转换媒体查询中的 px。\n * @default false\n */\n mediaQuery?: boolean;\n /**\n * 小于或等于这个值的 px 不会被转换。\n * @default 0\n */\n minPixelValue?: number;\n /**\n * 排除某些文件或文件夹,支持正则或函数。\n * @default null\n */\n exclude?: RegExp | ((file: string) => boolean) | null;\n /**\n * 自定义生成的 rem 变量名,默认为 --local-scope-rem\n */\n varName?: string;\n}\n\nconst defaults: Required<PxToLocalVarOptions> = {\n rootValue: 16,\n unitPrecision: 5,\n selectorBlackList: [],\n propList: ['*'],\n replace: true,\n mediaQuery: false,\n minPixelValue: 0,\n exclude: null,\n varName: '--local-scope-rem',\n};\n\nfunction shouldExclude(exclude: PxToLocalVarOptions['exclude'], file?: string) {\n if (!exclude || !file) return false;\n if (typeof exclude === 'function') return exclude(file);\n if (exclude instanceof RegExp) return exclude.test(file);\n return false;\n}\n\nfunction toFixed(number: number, precision: number) {\n return parseFloat(number.toFixed(precision)).toString();\n}\n\nfunction createPxReplace(opts: Required<PxToLocalVarOptions>) {\n return function (m: string, $1: string) {\n if (!$1) return m;\n const pixels = parseFloat($1);\n if (Math.abs(pixels) <= opts.minPixelValue) return m;\n const remValue = toFixed(pixels / opts.rootValue, opts.unitPrecision);\n return `calc(var(${opts.varName}, 1rem) * ${remValue})`;\n };\n}\n\nfunction hasDisableNextLineComment(decl: Declaration): boolean {\n let node = decl;\n while (node) {\n const parent = node.parent;\n if (!parent || !Array.isArray(parent.nodes)) break;\n const idx = parent.nodes.indexOf(node);\n if (idx > 0) {\n // 查找前一个兄弟节点\n const prev = parent.nodes[idx - 1];\n if (prev.type === 'comment' && DISABLE_COMMENT_REG.test(prev.text)) {\n return true;\n }\n }\n //@ts-ignore\n node = parent;\n }\n // 兼容 raws.before\n if (decl.raws && typeof decl.raws.before === 'string') {\n return DISABLE_COMMENT_REG.test(decl.raws.before);\n }\n return false;\n}\n\n/**\n * px 转换为 局部 rem 插件\n */\nexport const postcssPxToLocalVar = (options: PxToLocalVarOptions = {}): Plugin => {\n const opts = { ...defaults, ...options };\n const propListMatch = createPropListMatcher(opts.propList);\n const isBlackSelector = createSelectorBlackList(opts.selectorBlackList);\n\n return {\n postcssPlugin: 'postcss-px-to-local-var',\n Once(root: Root) {\n const file = root.source?.input.file;\n if (shouldExclude(opts.exclude, file)) return;\n\n root.walkDecls((decl: Declaration) => {\n if (hasDisableNextLineComment(decl)) return;\n if (!propListMatch(decl.prop)) return;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n if (isBlackSelector((decl.parent as any)?.selector || '')) return;\n if (decl.value.indexOf('px') === -1) return;\n\n const newValue = decl.value.replace(pixelUnitRegex, createPxReplace(opts));\n if (newValue === decl.value) return;\n\n if (opts.replace) {\n decl.value = newValue;\n } else {\n decl.cloneAfter({ value: newValue });\n }\n });\n\n if (opts.mediaQuery) {\n root.walkAtRules('media', (rule: AtRule) => {\n if (rule.params.indexOf('px') === -1) return;\n rule.params = rule.params.replace(pixelUnitRegex, createPxReplace(opts));\n });\n }\n },\n };\n};\npostcssPxToLocalVar.postcss = true;\n","import type { Plugin } from 'postcss';\n\n/**\n * Options for the PostCSS rem-to-css-vars plugin.\n */\nexport interface PostcssRemToCssVarsOptions {\n /**\n * The CSS variable name to use for the base rem value.\n * Defaults to \"--local-scope-rem\".\n */\n varName?: string;\n}\n\n/**\n * A PostCSS plugin that converts rem units to CSS variables via calc().\n *\n * @param opts - Plugin options.\n * @returns A PostCSS plugin instance.\n * @deprecated Use `postcss-px-to-local-var` instead.\n */\nexport const postcssRemToCssVars = (opts: PostcssRemToCssVarsOptions = {}): Plugin => {\n const { varName = '--local-scope-rem' } = opts;\n\n return {\n postcssPlugin: 'postcss-rem-to-local-var',\n Declaration(decl) {\n if ((decl as any)._remToVarProcessed) return;\n // If the value has already been converted to calc(var(--local-scope-rem, 1rem) * 1), it should not be converted again\n if (decl.value.includes(`calc(var(${varName}, 1rem) * `)) return;\n const regex = /(-?(?:\\d+|\\d*\\.\\d+))rem\\b/g;\n decl.value = decl.value.replace(regex, (_, n) => `calc(var(${varName}, 1rem) * ${n})`);\n // Mark as processed\n (decl as any)._remToVarProcessed = true;\n },\n };\n};\n\n// Mark the plugin as a PostCSS plugin\npostcssRemToCssVars.postcss = true;\n"],"names":["createSelectorBlackList","blackList","selector","item","DISABLE_COMMENT_REG","createPropListMatcher","propList","hasWildcard","matchList","p","ignoreList","matches","prop","list","pattern","pixelUnitRegex","defaults","shouldExclude","exclude","file","toFixed","number","precision","createPxReplace","opts","m","$1","pixels","remValue","hasDisableNextLineComment","decl","node","parent","idx","prev","postcssPxToLocalVar","options","propListMatch","isBlackSelector","root","_a","newValue","rule","postcssRemToCssVars","varName","regex","_"],"mappings":"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"}