postcss-units-to-px 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +132 -0
- package/README.zh-CN.md +132 -0
- package/dist/index.cjs +137 -0
- package/dist/index.d.cts +34 -0
- package/dist/index.d.mts +34 -0
- package/dist/index.mjs +137 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ice breaker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# postcss-units-to-px
|
|
2
|
+
|
|
3
|
+
Convert multiple CSS units to `px` with PostCSS. The default unit map covers `rem`, `em`, `vw`, `vh`, `vmin`, `vmax`, and `rpx`, and you can override or extend the rules as needed.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add postcss-units-to-px postcss
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import postcss from 'postcss'
|
|
15
|
+
import unitsToPx from 'postcss-units-to-px'
|
|
16
|
+
|
|
17
|
+
const input = '.rule { margin: 1rem 1vw; }'
|
|
18
|
+
const output = postcss(unitsToPx()).process(input).css
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Options
|
|
22
|
+
|
|
23
|
+
Type: `Object | Null`
|
|
24
|
+
Default:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const defaultOptions = {
|
|
28
|
+
unitMap: {
|
|
29
|
+
rem: 16,
|
|
30
|
+
em: 16,
|
|
31
|
+
vw: 3.75,
|
|
32
|
+
vh: 6.67,
|
|
33
|
+
vmin: 3.75,
|
|
34
|
+
vmax: 6.67,
|
|
35
|
+
rpx: 0.5,
|
|
36
|
+
},
|
|
37
|
+
unitPrecision: 5,
|
|
38
|
+
selectorBlackList: [],
|
|
39
|
+
propList: ['*'],
|
|
40
|
+
replace: true,
|
|
41
|
+
mediaQuery: false,
|
|
42
|
+
minValue: 0,
|
|
43
|
+
exclude: [/node_modules/i],
|
|
44
|
+
disabled: false,
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### unitMap
|
|
49
|
+
|
|
50
|
+
Type: `Record<string, number | (value, context) => number | null>`
|
|
51
|
+
|
|
52
|
+
Per-unit conversion rules. A numeric value is treated as a multiplier (e.g. `1rem * 16 = 16px`). A function should return the final px value. If the rule is `null`, the plugin will fall back to the global `transform` (if provided).
|
|
53
|
+
|
|
54
|
+
Note: `unitMap` merges with defaults. To override a unit, set that key explicitly.
|
|
55
|
+
|
|
56
|
+
### transform
|
|
57
|
+
|
|
58
|
+
Type: `(value, unit, context) => number`
|
|
59
|
+
|
|
60
|
+
Global conversion function used when a unit does not have a per-unit rule (or when that rule is `null`).
|
|
61
|
+
|
|
62
|
+
### unitPrecision
|
|
63
|
+
|
|
64
|
+
Type: `number`
|
|
65
|
+
Default: `5`
|
|
66
|
+
|
|
67
|
+
Decimal precision for generated px values.
|
|
68
|
+
|
|
69
|
+
### minValue
|
|
70
|
+
|
|
71
|
+
Type: `number`
|
|
72
|
+
Default: `0`
|
|
73
|
+
|
|
74
|
+
Minimum source value to convert. Values below this are left unchanged.
|
|
75
|
+
|
|
76
|
+
### propList
|
|
77
|
+
|
|
78
|
+
Type: `(string | RegExp)[]`
|
|
79
|
+
|
|
80
|
+
Only declarations with matching properties are processed. Supports `'*'` to match all properties.
|
|
81
|
+
|
|
82
|
+
### selectorBlackList
|
|
83
|
+
|
|
84
|
+
Type: `(string | RegExp)[]`
|
|
85
|
+
|
|
86
|
+
Selectors to ignore and leave unchanged.
|
|
87
|
+
|
|
88
|
+
### replace
|
|
89
|
+
|
|
90
|
+
Type: `boolean`
|
|
91
|
+
|
|
92
|
+
Replace the original declaration value instead of adding a fallback declaration.
|
|
93
|
+
|
|
94
|
+
### mediaQuery
|
|
95
|
+
|
|
96
|
+
Type: `boolean`
|
|
97
|
+
|
|
98
|
+
Allow unit conversion inside `@media` params.
|
|
99
|
+
|
|
100
|
+
### exclude
|
|
101
|
+
|
|
102
|
+
Type: `(string | RegExp)[] | ((filePath: string) => boolean)`
|
|
103
|
+
|
|
104
|
+
Exclude files from processing based on their file path.
|
|
105
|
+
|
|
106
|
+
### disabled
|
|
107
|
+
|
|
108
|
+
Type: `boolean`
|
|
109
|
+
|
|
110
|
+
Disable this plugin.
|
|
111
|
+
|
|
112
|
+
## Transform Context
|
|
113
|
+
|
|
114
|
+
The conversion functions receive a context object with:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
interface TransformContext {
|
|
118
|
+
root: Root
|
|
119
|
+
input: Input
|
|
120
|
+
filePath?: string
|
|
121
|
+
decl?: Declaration
|
|
122
|
+
rule?: Rule
|
|
123
|
+
atRule?: AtRule
|
|
124
|
+
prop?: string
|
|
125
|
+
selector?: string
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Notes
|
|
130
|
+
|
|
131
|
+
- The unit regex skips quoted strings, `url(...)`, and `var(...)` to avoid accidental replacements.
|
|
132
|
+
- Absolute units (`in/cm/mm/pt/pc/q`) are not converted by default, but you can add them in `unitMap`.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# postcss-units-to-px
|
|
2
|
+
|
|
3
|
+
将多种 CSS 单位转换为 `px` 的 PostCSS 插件。默认支持 `rem`、`em`、`vw`、`vh`、`vmin`、`vmax`、`rpx`,并可按需覆盖或扩展规则。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add postcss-units-to-px postcss
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 使用
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import postcss from 'postcss'
|
|
15
|
+
import unitsToPx from 'postcss-units-to-px'
|
|
16
|
+
|
|
17
|
+
const input = '.rule { margin: 1rem 1vw; }'
|
|
18
|
+
const output = postcss(unitsToPx()).process(input).css
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 配置
|
|
22
|
+
|
|
23
|
+
类型: `Object | Null`
|
|
24
|
+
默认值:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
const defaultOptions = {
|
|
28
|
+
unitMap: {
|
|
29
|
+
rem: 16,
|
|
30
|
+
em: 16,
|
|
31
|
+
vw: 3.75,
|
|
32
|
+
vh: 6.67,
|
|
33
|
+
vmin: 3.75,
|
|
34
|
+
vmax: 6.67,
|
|
35
|
+
rpx: 0.5,
|
|
36
|
+
},
|
|
37
|
+
unitPrecision: 5,
|
|
38
|
+
selectorBlackList: [],
|
|
39
|
+
propList: ['*'],
|
|
40
|
+
replace: true,
|
|
41
|
+
mediaQuery: false,
|
|
42
|
+
minValue: 0,
|
|
43
|
+
exclude: [/node_modules/i],
|
|
44
|
+
disabled: false,
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### unitMap
|
|
49
|
+
|
|
50
|
+
类型: `Record<string, number | (value, context) => number | null>`
|
|
51
|
+
|
|
52
|
+
单位转换规则。数值表示倍率(例如 `1rem * 16 = 16px`)。函数需要返回最终 px 值。若某个单位规则为 `null`,则会回退使用全局 `transform`。
|
|
53
|
+
|
|
54
|
+
注意:`unitMap` 会与默认值合并,覆盖时请显式写出对应的单位。
|
|
55
|
+
|
|
56
|
+
### transform
|
|
57
|
+
|
|
58
|
+
类型: `(value, unit, context) => number`
|
|
59
|
+
|
|
60
|
+
当某个单位未定义规则(或规则为 `null`)时使用的全局转换函数。
|
|
61
|
+
|
|
62
|
+
### unitPrecision
|
|
63
|
+
|
|
64
|
+
类型: `number`
|
|
65
|
+
默认值: `5`
|
|
66
|
+
|
|
67
|
+
输出 px 的小数精度。
|
|
68
|
+
|
|
69
|
+
### minValue
|
|
70
|
+
|
|
71
|
+
类型: `number`
|
|
72
|
+
默认值: `0`
|
|
73
|
+
|
|
74
|
+
小于该阈值的单位值不会被转换。
|
|
75
|
+
|
|
76
|
+
### propList
|
|
77
|
+
|
|
78
|
+
类型: `(string | RegExp)[]`
|
|
79
|
+
|
|
80
|
+
只处理匹配的属性。支持 `'*'` 表示全部属性。
|
|
81
|
+
|
|
82
|
+
### selectorBlackList
|
|
83
|
+
|
|
84
|
+
类型: `(string | RegExp)[]`
|
|
85
|
+
|
|
86
|
+
匹配到的选择器将被忽略。
|
|
87
|
+
|
|
88
|
+
### replace
|
|
89
|
+
|
|
90
|
+
类型: `boolean`
|
|
91
|
+
|
|
92
|
+
替换原声明值而不是追加一个新声明。
|
|
93
|
+
|
|
94
|
+
### mediaQuery
|
|
95
|
+
|
|
96
|
+
类型: `boolean`
|
|
97
|
+
|
|
98
|
+
允许在 `@media` 参数中转换单位。
|
|
99
|
+
|
|
100
|
+
### exclude
|
|
101
|
+
|
|
102
|
+
类型: `(string | RegExp)[] | ((filePath: string) => boolean)`
|
|
103
|
+
|
|
104
|
+
根据文件路径排除处理。
|
|
105
|
+
|
|
106
|
+
### disabled
|
|
107
|
+
|
|
108
|
+
类型: `boolean`
|
|
109
|
+
|
|
110
|
+
禁用插件。
|
|
111
|
+
|
|
112
|
+
## Transform Context
|
|
113
|
+
|
|
114
|
+
转换函数会接收到如下上下文:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
interface TransformContext {
|
|
118
|
+
root: Root
|
|
119
|
+
input: Input
|
|
120
|
+
filePath?: string
|
|
121
|
+
decl?: Declaration
|
|
122
|
+
rule?: Rule
|
|
123
|
+
atRule?: AtRule
|
|
124
|
+
prop?: string
|
|
125
|
+
selector?: string
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 说明
|
|
130
|
+
|
|
131
|
+
- 正则会跳过引号字符串、`url(...)` 和 `var(...)`,避免误替换。
|
|
132
|
+
- 绝对单位(`in/cm/mm/pt/pc/q`)默认不处理,可自行添加到 `unitMap`。
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
let postcss_plugin_shared = require("postcss-plugin-shared");
|
|
2
|
+
|
|
3
|
+
//#region package.json
|
|
4
|
+
var name = "postcss-units-to-px";
|
|
5
|
+
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/defaults.ts
|
|
8
|
+
const defaultUnitMap = {
|
|
9
|
+
rem: 16,
|
|
10
|
+
em: 16,
|
|
11
|
+
vw: 3.75,
|
|
12
|
+
vh: 6.67,
|
|
13
|
+
vmin: 3.75,
|
|
14
|
+
vmax: 6.67,
|
|
15
|
+
rpx: .5
|
|
16
|
+
};
|
|
17
|
+
const defaultOptions = {
|
|
18
|
+
unitMap: defaultUnitMap,
|
|
19
|
+
unitPrecision: 5,
|
|
20
|
+
selectorBlackList: [],
|
|
21
|
+
propList: ["*"],
|
|
22
|
+
replace: true,
|
|
23
|
+
mediaQuery: false,
|
|
24
|
+
minValue: 0,
|
|
25
|
+
exclude: [/node_modules/i],
|
|
26
|
+
disabled: false
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/shared.ts
|
|
31
|
+
const postcssPlugin = name;
|
|
32
|
+
const getConfig = (0, postcss_plugin_shared.createConfigGetter)(defaultOptions);
|
|
33
|
+
const blacklistedSelector = postcss_plugin_shared.maybeBlacklistedSelector;
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/plugin.ts
|
|
37
|
+
function normalizeUnitMap(unitMap) {
|
|
38
|
+
const normalized = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const [unit, rule] of Object.entries(unitMap)) {
|
|
40
|
+
const key = unit.trim().toLowerCase();
|
|
41
|
+
if (!key) continue;
|
|
42
|
+
normalized.set(key, rule ?? null);
|
|
43
|
+
}
|
|
44
|
+
return normalized;
|
|
45
|
+
}
|
|
46
|
+
function getUnitsForRegex(unitMap) {
|
|
47
|
+
return Array.from(unitMap.keys()).sort((a, b) => b.length - a.length);
|
|
48
|
+
}
|
|
49
|
+
function createReplace(unitMap, unitPrecision, minValue, transform, context) {
|
|
50
|
+
const shouldRound = unitPrecision >= 0 && unitPrecision <= 100;
|
|
51
|
+
return function replace(m, $1) {
|
|
52
|
+
if (!$1) return m;
|
|
53
|
+
const value = Number($1);
|
|
54
|
+
if (Number.isNaN(value)) return m;
|
|
55
|
+
if (value < minValue) return m;
|
|
56
|
+
const unit = m.slice($1.length).toLowerCase();
|
|
57
|
+
const rule = unitMap.get(unit);
|
|
58
|
+
let pxValue;
|
|
59
|
+
if (typeof rule === "function") pxValue = rule(value, context);
|
|
60
|
+
else if (typeof rule === "number") pxValue = value * rule;
|
|
61
|
+
else if (transform) pxValue = transform(value, unit, context);
|
|
62
|
+
else return m;
|
|
63
|
+
if (pxValue === void 0 || Number.isNaN(pxValue)) return m;
|
|
64
|
+
const roundedValue = shouldRound ? (0, postcss_plugin_shared.toFixed)(pxValue, unitPrecision) : pxValue;
|
|
65
|
+
if (roundedValue === 0) return "0";
|
|
66
|
+
return `${roundedValue}px`;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const plugin = (options = {}) => {
|
|
70
|
+
const { unitMap: mergedUnitMap, transform, unitPrecision, minValue, selectorBlackList, propList, replace, mediaQuery, exclude, disabled } = getConfig(options);
|
|
71
|
+
if (disabled) return { postcssPlugin };
|
|
72
|
+
const normalizedUnitMap = normalizeUnitMap(mergedUnitMap);
|
|
73
|
+
const units = getUnitsForRegex(normalizedUnitMap);
|
|
74
|
+
if (units.length === 0) return { postcssPlugin };
|
|
75
|
+
const unitRegex = (0, postcss_plugin_shared.createUnitRegex)({ units });
|
|
76
|
+
const unitTestRegex = new RegExp(unitRegex.source, unitRegex.flags.replace("g", ""));
|
|
77
|
+
const satisfyPropList = (0, postcss_plugin_shared.createPropListMatcher)(propList);
|
|
78
|
+
const excludeFn = (0, postcss_plugin_shared.createExcludeMatcher)(exclude);
|
|
79
|
+
const hasSelectorBlacklist = selectorBlackList.length > 0;
|
|
80
|
+
return {
|
|
81
|
+
postcssPlugin,
|
|
82
|
+
Once(css) {
|
|
83
|
+
const input = css.source.input;
|
|
84
|
+
const filePath = input.file;
|
|
85
|
+
if (excludeFn(filePath)) return;
|
|
86
|
+
const selectorBlacklistCache = hasSelectorBlacklist ? /* @__PURE__ */ new WeakMap() : void 0;
|
|
87
|
+
css.walkDecls((decl) => {
|
|
88
|
+
if (!satisfyPropList(decl.prop) || !unitTestRegex.test(decl.value)) return;
|
|
89
|
+
const rule = decl.parent;
|
|
90
|
+
if (selectorBlacklistCache) {
|
|
91
|
+
const cached = selectorBlacklistCache.get(rule);
|
|
92
|
+
const isBlacklisted = cached ?? Boolean(blacklistedSelector(selectorBlackList, rule.selector));
|
|
93
|
+
if (cached === void 0) selectorBlacklistCache.set(rule, isBlacklisted);
|
|
94
|
+
if (isBlacklisted) return;
|
|
95
|
+
}
|
|
96
|
+
const replacer = createReplace(normalizedUnitMap, unitPrecision, minValue, transform, {
|
|
97
|
+
root: css,
|
|
98
|
+
input,
|
|
99
|
+
filePath,
|
|
100
|
+
decl,
|
|
101
|
+
rule,
|
|
102
|
+
atRule: rule.parent?.type === "atrule" ? rule.parent : void 0,
|
|
103
|
+
prop: decl.prop,
|
|
104
|
+
selector: rule.selector
|
|
105
|
+
});
|
|
106
|
+
const value = decl.value.replace(unitRegex, replacer);
|
|
107
|
+
if (value === decl.value) return;
|
|
108
|
+
if ((rule.nodes?.length ?? 0) > 1 && (0, postcss_plugin_shared.declarationExists)(rule, decl.prop, value)) return;
|
|
109
|
+
if (replace) {
|
|
110
|
+
decl.value = value;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
decl.cloneAfter({ value });
|
|
114
|
+
});
|
|
115
|
+
css.walkAtRules((atRule) => {
|
|
116
|
+
if (!mediaQuery || atRule.name !== "media") return;
|
|
117
|
+
if (!unitTestRegex.test(atRule.params)) return;
|
|
118
|
+
const replacer = createReplace(normalizedUnitMap, unitPrecision, minValue, transform, {
|
|
119
|
+
root: css,
|
|
120
|
+
input,
|
|
121
|
+
filePath,
|
|
122
|
+
atRule
|
|
123
|
+
});
|
|
124
|
+
atRule.params = atRule.params.replace(unitRegex, replacer);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
plugin.postcss = true;
|
|
130
|
+
var plugin_default = plugin;
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/index.ts
|
|
134
|
+
var src_default = plugin_default;
|
|
135
|
+
|
|
136
|
+
//#endregion
|
|
137
|
+
module.exports = src_default;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AtRule, Declaration, Input, PluginCreator, Root, Rule } from "postcss";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
interface TransformContext {
|
|
5
|
+
root: Root;
|
|
6
|
+
input: Input;
|
|
7
|
+
filePath?: string;
|
|
8
|
+
decl?: Declaration;
|
|
9
|
+
rule?: Rule;
|
|
10
|
+
atRule?: AtRule;
|
|
11
|
+
prop?: string;
|
|
12
|
+
selector?: string;
|
|
13
|
+
}
|
|
14
|
+
type UnitTransform = (value: number, context: TransformContext) => number;
|
|
15
|
+
type GlobalTransform = (value: number, unit: string, context: TransformContext) => number;
|
|
16
|
+
type UnitMap = Record<string, number | UnitTransform | null>;
|
|
17
|
+
interface UserDefinedOptions {
|
|
18
|
+
unitMap?: UnitMap;
|
|
19
|
+
transform?: GlobalTransform;
|
|
20
|
+
unitPrecision?: number;
|
|
21
|
+
minValue?: number;
|
|
22
|
+
selectorBlackList?: (string | RegExp)[];
|
|
23
|
+
propList?: (string | RegExp)[];
|
|
24
|
+
replace?: boolean;
|
|
25
|
+
mediaQuery?: boolean;
|
|
26
|
+
exclude?: (string | RegExp)[] | ((filePath: string) => boolean);
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
}
|
|
29
|
+
type PostcssUnitsToPx = PluginCreator<UserDefinedOptions>;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/plugin.d.ts
|
|
32
|
+
declare const plugin: PostcssUnitsToPx;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { GlobalTransform, PostcssUnitsToPx, TransformContext, UnitMap, UnitTransform, UserDefinedOptions, plugin as default };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AtRule, Declaration, Input, PluginCreator, Root, Rule } from "postcss";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
interface TransformContext {
|
|
5
|
+
root: Root;
|
|
6
|
+
input: Input;
|
|
7
|
+
filePath?: string;
|
|
8
|
+
decl?: Declaration;
|
|
9
|
+
rule?: Rule;
|
|
10
|
+
atRule?: AtRule;
|
|
11
|
+
prop?: string;
|
|
12
|
+
selector?: string;
|
|
13
|
+
}
|
|
14
|
+
type UnitTransform = (value: number, context: TransformContext) => number;
|
|
15
|
+
type GlobalTransform = (value: number, unit: string, context: TransformContext) => number;
|
|
16
|
+
type UnitMap = Record<string, number | UnitTransform | null>;
|
|
17
|
+
interface UserDefinedOptions {
|
|
18
|
+
unitMap?: UnitMap;
|
|
19
|
+
transform?: GlobalTransform;
|
|
20
|
+
unitPrecision?: number;
|
|
21
|
+
minValue?: number;
|
|
22
|
+
selectorBlackList?: (string | RegExp)[];
|
|
23
|
+
propList?: (string | RegExp)[];
|
|
24
|
+
replace?: boolean;
|
|
25
|
+
mediaQuery?: boolean;
|
|
26
|
+
exclude?: (string | RegExp)[] | ((filePath: string) => boolean);
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
}
|
|
29
|
+
type PostcssUnitsToPx = PluginCreator<UserDefinedOptions>;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/plugin.d.ts
|
|
32
|
+
declare const plugin: PostcssUnitsToPx;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { GlobalTransform, PostcssUnitsToPx, TransformContext, UnitMap, UnitTransform, UserDefinedOptions, plugin as default };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { createConfigGetter, createExcludeMatcher, createPropListMatcher, createUnitRegex, declarationExists, maybeBlacklistedSelector, toFixed } from "postcss-plugin-shared";
|
|
2
|
+
|
|
3
|
+
//#region package.json
|
|
4
|
+
var name = "postcss-units-to-px";
|
|
5
|
+
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/defaults.ts
|
|
8
|
+
const defaultUnitMap = {
|
|
9
|
+
rem: 16,
|
|
10
|
+
em: 16,
|
|
11
|
+
vw: 3.75,
|
|
12
|
+
vh: 6.67,
|
|
13
|
+
vmin: 3.75,
|
|
14
|
+
vmax: 6.67,
|
|
15
|
+
rpx: .5
|
|
16
|
+
};
|
|
17
|
+
const defaultOptions = {
|
|
18
|
+
unitMap: defaultUnitMap,
|
|
19
|
+
unitPrecision: 5,
|
|
20
|
+
selectorBlackList: [],
|
|
21
|
+
propList: ["*"],
|
|
22
|
+
replace: true,
|
|
23
|
+
mediaQuery: false,
|
|
24
|
+
minValue: 0,
|
|
25
|
+
exclude: [/node_modules/i],
|
|
26
|
+
disabled: false
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/shared.ts
|
|
31
|
+
const postcssPlugin = name;
|
|
32
|
+
const getConfig = createConfigGetter(defaultOptions);
|
|
33
|
+
const blacklistedSelector = maybeBlacklistedSelector;
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/plugin.ts
|
|
37
|
+
function normalizeUnitMap(unitMap) {
|
|
38
|
+
const normalized = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const [unit, rule] of Object.entries(unitMap)) {
|
|
40
|
+
const key = unit.trim().toLowerCase();
|
|
41
|
+
if (!key) continue;
|
|
42
|
+
normalized.set(key, rule ?? null);
|
|
43
|
+
}
|
|
44
|
+
return normalized;
|
|
45
|
+
}
|
|
46
|
+
function getUnitsForRegex(unitMap) {
|
|
47
|
+
return Array.from(unitMap.keys()).sort((a, b) => b.length - a.length);
|
|
48
|
+
}
|
|
49
|
+
function createReplace(unitMap, unitPrecision, minValue, transform, context) {
|
|
50
|
+
const shouldRound = unitPrecision >= 0 && unitPrecision <= 100;
|
|
51
|
+
return function replace(m, $1) {
|
|
52
|
+
if (!$1) return m;
|
|
53
|
+
const value = Number($1);
|
|
54
|
+
if (Number.isNaN(value)) return m;
|
|
55
|
+
if (value < minValue) return m;
|
|
56
|
+
const unit = m.slice($1.length).toLowerCase();
|
|
57
|
+
const rule = unitMap.get(unit);
|
|
58
|
+
let pxValue;
|
|
59
|
+
if (typeof rule === "function") pxValue = rule(value, context);
|
|
60
|
+
else if (typeof rule === "number") pxValue = value * rule;
|
|
61
|
+
else if (transform) pxValue = transform(value, unit, context);
|
|
62
|
+
else return m;
|
|
63
|
+
if (pxValue === void 0 || Number.isNaN(pxValue)) return m;
|
|
64
|
+
const roundedValue = shouldRound ? toFixed(pxValue, unitPrecision) : pxValue;
|
|
65
|
+
if (roundedValue === 0) return "0";
|
|
66
|
+
return `${roundedValue}px`;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const plugin = (options = {}) => {
|
|
70
|
+
const { unitMap: mergedUnitMap, transform, unitPrecision, minValue, selectorBlackList, propList, replace, mediaQuery, exclude, disabled } = getConfig(options);
|
|
71
|
+
if (disabled) return { postcssPlugin };
|
|
72
|
+
const normalizedUnitMap = normalizeUnitMap(mergedUnitMap);
|
|
73
|
+
const units = getUnitsForRegex(normalizedUnitMap);
|
|
74
|
+
if (units.length === 0) return { postcssPlugin };
|
|
75
|
+
const unitRegex = createUnitRegex({ units });
|
|
76
|
+
const unitTestRegex = new RegExp(unitRegex.source, unitRegex.flags.replace("g", ""));
|
|
77
|
+
const satisfyPropList = createPropListMatcher(propList);
|
|
78
|
+
const excludeFn = createExcludeMatcher(exclude);
|
|
79
|
+
const hasSelectorBlacklist = selectorBlackList.length > 0;
|
|
80
|
+
return {
|
|
81
|
+
postcssPlugin,
|
|
82
|
+
Once(css) {
|
|
83
|
+
const input = css.source.input;
|
|
84
|
+
const filePath = input.file;
|
|
85
|
+
if (excludeFn(filePath)) return;
|
|
86
|
+
const selectorBlacklistCache = hasSelectorBlacklist ? /* @__PURE__ */ new WeakMap() : void 0;
|
|
87
|
+
css.walkDecls((decl) => {
|
|
88
|
+
if (!satisfyPropList(decl.prop) || !unitTestRegex.test(decl.value)) return;
|
|
89
|
+
const rule = decl.parent;
|
|
90
|
+
if (selectorBlacklistCache) {
|
|
91
|
+
const cached = selectorBlacklistCache.get(rule);
|
|
92
|
+
const isBlacklisted = cached ?? Boolean(blacklistedSelector(selectorBlackList, rule.selector));
|
|
93
|
+
if (cached === void 0) selectorBlacklistCache.set(rule, isBlacklisted);
|
|
94
|
+
if (isBlacklisted) return;
|
|
95
|
+
}
|
|
96
|
+
const replacer = createReplace(normalizedUnitMap, unitPrecision, minValue, transform, {
|
|
97
|
+
root: css,
|
|
98
|
+
input,
|
|
99
|
+
filePath,
|
|
100
|
+
decl,
|
|
101
|
+
rule,
|
|
102
|
+
atRule: rule.parent?.type === "atrule" ? rule.parent : void 0,
|
|
103
|
+
prop: decl.prop,
|
|
104
|
+
selector: rule.selector
|
|
105
|
+
});
|
|
106
|
+
const value = decl.value.replace(unitRegex, replacer);
|
|
107
|
+
if (value === decl.value) return;
|
|
108
|
+
if ((rule.nodes?.length ?? 0) > 1 && declarationExists(rule, decl.prop, value)) return;
|
|
109
|
+
if (replace) {
|
|
110
|
+
decl.value = value;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
decl.cloneAfter({ value });
|
|
114
|
+
});
|
|
115
|
+
css.walkAtRules((atRule) => {
|
|
116
|
+
if (!mediaQuery || atRule.name !== "media") return;
|
|
117
|
+
if (!unitTestRegex.test(atRule.params)) return;
|
|
118
|
+
const replacer = createReplace(normalizedUnitMap, unitPrecision, minValue, transform, {
|
|
119
|
+
root: css,
|
|
120
|
+
input,
|
|
121
|
+
filePath,
|
|
122
|
+
atRule
|
|
123
|
+
});
|
|
124
|
+
atRule.params = atRule.params.replace(unitRegex, replacer);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
plugin.postcss = true;
|
|
130
|
+
var plugin_default = plugin;
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/index.ts
|
|
134
|
+
var src_default = plugin_default;
|
|
135
|
+
|
|
136
|
+
//#endregion
|
|
137
|
+
export { src_default as default };
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "postcss-units-to-px",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"description": "Convert multiple CSS units to px with PostCSS.",
|
|
6
|
+
"author": "ice breaker <1324318532@qq.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/sonofmagic/postcss-plugins.git",
|
|
11
|
+
"directory": "packages/postcss-units-to-px"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/sonofmagic/postcss-plugins/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"postcss",
|
|
18
|
+
"postcss-plugin",
|
|
19
|
+
"css",
|
|
20
|
+
"units",
|
|
21
|
+
"px",
|
|
22
|
+
"unit-conversion",
|
|
23
|
+
"responsive"
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.mts",
|
|
29
|
+
"import": {
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
31
|
+
"default": "./dist/index.mjs"
|
|
32
|
+
},
|
|
33
|
+
"require": {
|
|
34
|
+
"types": "./dist/index.d.cts",
|
|
35
|
+
"default": "./dist/index.cjs"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"main": "./dist/index.cjs",
|
|
40
|
+
"module": "./dist/index.mjs",
|
|
41
|
+
"types": "./dist/index.d.mts",
|
|
42
|
+
"files": [
|
|
43
|
+
"dist"
|
|
44
|
+
],
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"postcss": "^8"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"postcss-plugin-shared": "^1.0.0"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"dev": "tsdown -w",
|
|
53
|
+
"build": "tsdown",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:dev": "vitest",
|
|
56
|
+
"release": "pnpm publish",
|
|
57
|
+
"lint": "eslint .",
|
|
58
|
+
"lint:fix": "eslint . --fix"
|
|
59
|
+
}
|
|
60
|
+
}
|