@unocss/transformer-directives 0.45.29 → 0.46.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/dist/index.cjs +168 -162
- package/dist/index.d.ts +8 -1
- package/dist/index.mjs +170 -164
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,168 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
const core = require('@unocss/core');
|
|
6
6
|
const cssTree = require('css-tree');
|
|
7
7
|
|
|
8
|
+
const themeFnRE = /theme\((.*?)\)/g;
|
|
9
|
+
function handleThemeFn({ code, uno, options }, node) {
|
|
10
|
+
const { throwOnMissing = true } = options;
|
|
11
|
+
const offset = node.value.loc.start.offset;
|
|
12
|
+
const str = code.original.slice(offset, node.value.loc.end.offset);
|
|
13
|
+
const matches = Array.from(str.matchAll(themeFnRE));
|
|
14
|
+
if (!matches.length)
|
|
15
|
+
return;
|
|
16
|
+
for (const match of matches) {
|
|
17
|
+
const rawArg = match[1].trim();
|
|
18
|
+
if (!rawArg)
|
|
19
|
+
throw new Error("theme() expect exact one argument, but got 0");
|
|
20
|
+
let value = uno.config.theme;
|
|
21
|
+
const keys = rawArg.slice(1, -1).split(".");
|
|
22
|
+
keys.every((key) => {
|
|
23
|
+
if (value[key] != null)
|
|
24
|
+
value = value[key];
|
|
25
|
+
else if (value[+key] != null)
|
|
26
|
+
value = value[+key];
|
|
27
|
+
else
|
|
28
|
+
return false;
|
|
29
|
+
return true;
|
|
30
|
+
});
|
|
31
|
+
if (typeof value === "string") {
|
|
32
|
+
code.overwrite(
|
|
33
|
+
offset + match.index,
|
|
34
|
+
offset + match.index + match[0].length,
|
|
35
|
+
value
|
|
36
|
+
);
|
|
37
|
+
} else if (throwOnMissing) {
|
|
38
|
+
throw new Error(`theme of "${rawArg.slice(1, -1)}" did not found`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const screenRuleRE = /(@screen) (.+) /g;
|
|
44
|
+
function handleScreen({ code, uno }, node) {
|
|
45
|
+
let breakpointName = "";
|
|
46
|
+
let prefix = "";
|
|
47
|
+
if (node.name === "screen" && node.prelude?.type === "Raw")
|
|
48
|
+
breakpointName = node.prelude.value.trim();
|
|
49
|
+
if (!breakpointName)
|
|
50
|
+
return;
|
|
51
|
+
const match = breakpointName.match(/^(?:(lt|at)-)?(\w+)$/);
|
|
52
|
+
if (match) {
|
|
53
|
+
prefix = match[1];
|
|
54
|
+
breakpointName = match[2];
|
|
55
|
+
}
|
|
56
|
+
const resolveBreakpoints = () => {
|
|
57
|
+
let breakpoints;
|
|
58
|
+
if (uno.userConfig && uno.userConfig.theme)
|
|
59
|
+
breakpoints = uno.userConfig.theme.breakpoints;
|
|
60
|
+
if (!breakpoints)
|
|
61
|
+
breakpoints = uno.config.theme.breakpoints;
|
|
62
|
+
return breakpoints;
|
|
63
|
+
};
|
|
64
|
+
const variantEntries = Object.entries(resolveBreakpoints() ?? {}).map(([point, size], idx) => [point, size, idx]);
|
|
65
|
+
const generateMediaQuery = (breakpointName2, prefix2) => {
|
|
66
|
+
const [, size, idx] = variantEntries.find((i) => i[0] === breakpointName2);
|
|
67
|
+
if (prefix2) {
|
|
68
|
+
if (prefix2 === "lt")
|
|
69
|
+
return `@media (max-width: ${calcMaxWidthBySize(size)})`;
|
|
70
|
+
else if (prefix2 === "at")
|
|
71
|
+
return `@media (min-width: ${size})${variantEntries[idx + 1] ? ` and (max-width: ${calcMaxWidthBySize(variantEntries[idx + 1][1])})` : ""}`;
|
|
72
|
+
else
|
|
73
|
+
throw new Error(`breakpoint variant not supported: ${prefix2}`);
|
|
74
|
+
}
|
|
75
|
+
return `@media (min-width: ${size})`;
|
|
76
|
+
};
|
|
77
|
+
if (!variantEntries.find((i) => i[0] === breakpointName))
|
|
78
|
+
throw new Error(`breakpoint ${breakpointName} not found`);
|
|
79
|
+
const offset = node.loc.start.offset;
|
|
80
|
+
const str = code.original.slice(offset, node.loc.end.offset);
|
|
81
|
+
const matches = Array.from(str.matchAll(screenRuleRE));
|
|
82
|
+
if (!matches.length)
|
|
83
|
+
return;
|
|
84
|
+
for (const match2 of matches) {
|
|
85
|
+
code.overwrite(
|
|
86
|
+
offset + match2.index,
|
|
87
|
+
offset + match2.index + match2[0].length,
|
|
88
|
+
`${generateMediaQuery(breakpointName, prefix)} `
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function calcMaxWidthBySize(size) {
|
|
93
|
+
const value = size.match(/^-?[0-9]+\.?[0-9]*/)?.[0] || "";
|
|
94
|
+
const unit = size.slice(value.length);
|
|
95
|
+
const maxWidth = parseFloat(value) - 0.1;
|
|
96
|
+
return Number.isNaN(maxWidth) ? size : `${maxWidth}${unit}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function handleApply(ctx, node) {
|
|
100
|
+
const { code, uno, options, filename, offset } = ctx;
|
|
101
|
+
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
102
|
+
await Promise.all(
|
|
103
|
+
node.block.children.map(async (childNode) => {
|
|
104
|
+
if (childNode.type === "Raw")
|
|
105
|
+
return transformDirectives(code, uno, options, filename, childNode.value, calcOffset(childNode.loc.start.offset));
|
|
106
|
+
await parseApply(ctx, node, childNode);
|
|
107
|
+
}).toArray()
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
async function parseApply({ code, uno, options, offset }, node, childNode) {
|
|
111
|
+
const { varStyle = "--at-" } = options;
|
|
112
|
+
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
113
|
+
let body;
|
|
114
|
+
if (childNode.type === "Atrule" && childNode.name === "apply" && childNode.prelude && childNode.prelude.type === "Raw") {
|
|
115
|
+
body = childNode.prelude.value.trim();
|
|
116
|
+
} else if (varStyle !== false && childNode.type === "Declaration" && childNode.property === `${varStyle}apply` && childNode.value.type === "Raw") {
|
|
117
|
+
body = childNode.value.value.trim();
|
|
118
|
+
if (body.match(/^(['"]).*\1$/))
|
|
119
|
+
body = body.slice(1, -1);
|
|
120
|
+
}
|
|
121
|
+
if (!body)
|
|
122
|
+
return;
|
|
123
|
+
const classNames = core.expandVariantGroup(body).split(/\s+/g).map((className) => className.trim().replace(/\\/, ""));
|
|
124
|
+
const utils = (await Promise.all(
|
|
125
|
+
classNames.map((i) => uno.parseToken(i, "-"))
|
|
126
|
+
)).filter(core.notNull).flat().sort((a, b) => a[0] - b[0]).sort((a, b) => (a[3] ? uno.parentOrders.get(a[3]) ?? 0 : 0) - (b[3] ? uno.parentOrders.get(b[3]) ?? 0 : 0)).reduce((acc, item) => {
|
|
127
|
+
const target = acc.find((i) => i[1] === item[1] && i[3] === item[3]);
|
|
128
|
+
if (target)
|
|
129
|
+
target[2] += item[2];
|
|
130
|
+
else
|
|
131
|
+
acc.push([...item]);
|
|
132
|
+
return acc;
|
|
133
|
+
}, []);
|
|
134
|
+
if (!utils.length)
|
|
135
|
+
return;
|
|
136
|
+
for (const i of utils) {
|
|
137
|
+
const [, _selector, body2, parent] = i;
|
|
138
|
+
const selector = _selector?.replace(core.regexScopePlaceholder, " ") || _selector;
|
|
139
|
+
if (parent || selector && selector !== ".\\-") {
|
|
140
|
+
let newSelector = cssTree.generate(node.prelude);
|
|
141
|
+
if (selector && selector !== ".\\-") {
|
|
142
|
+
const selectorAST = cssTree.parse(selector, {
|
|
143
|
+
context: "selector"
|
|
144
|
+
});
|
|
145
|
+
const prelude = cssTree.clone(node.prelude);
|
|
146
|
+
prelude.children.forEach((child) => {
|
|
147
|
+
const parentSelectorAst = cssTree.clone(selectorAST);
|
|
148
|
+
parentSelectorAst.children.forEach((i2) => {
|
|
149
|
+
if (i2.type === "ClassSelector" && i2.name === "\\-")
|
|
150
|
+
Object.assign(i2, cssTree.clone(child));
|
|
151
|
+
});
|
|
152
|
+
Object.assign(child, parentSelectorAst);
|
|
153
|
+
});
|
|
154
|
+
newSelector = cssTree.generate(prelude);
|
|
155
|
+
}
|
|
156
|
+
let css = `${newSelector}{${body2}}`;
|
|
157
|
+
if (parent)
|
|
158
|
+
css = `${parent}{${css}}`;
|
|
159
|
+
code.appendLeft(calcOffset(node.loc.end.offset), css);
|
|
160
|
+
} else {
|
|
161
|
+
code.appendRight(calcOffset(childNode.loc.end.offset), body2);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
code.remove(
|
|
165
|
+
calcOffset(childNode.loc.start.offset),
|
|
166
|
+
calcOffset(childNode.loc.end.offset)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
8
170
|
function transformerDirectives(options = {}) {
|
|
9
171
|
return {
|
|
10
172
|
name: "css-directive",
|
|
@@ -15,13 +177,8 @@ function transformerDirectives(options = {}) {
|
|
|
15
177
|
}
|
|
16
178
|
};
|
|
17
179
|
}
|
|
18
|
-
const themeFnRE = /theme\((.*?)\)/g;
|
|
19
|
-
const screenRuleRE = /(@screen) (.+) /g;
|
|
20
180
|
async function transformDirectives(code, uno, options, filename, originalCode, offset) {
|
|
21
|
-
const {
|
|
22
|
-
varStyle = "--at-",
|
|
23
|
-
throwOnMissing = true
|
|
24
|
-
} = options;
|
|
181
|
+
const { varStyle = "--at-" } = options;
|
|
25
182
|
const isApply = code.original.includes("@apply") || varStyle !== false && code.original.includes(varStyle);
|
|
26
183
|
const isScreen = code.original.includes("@screen");
|
|
27
184
|
const hasThemeFn = code.original.match(themeFnRE);
|
|
@@ -34,170 +191,19 @@ async function transformDirectives(code, uno, options, filename, originalCode, o
|
|
|
34
191
|
});
|
|
35
192
|
if (ast.type !== "StyleSheet")
|
|
36
193
|
return;
|
|
37
|
-
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
38
|
-
const handleApply = async (node, childNode) => {
|
|
39
|
-
let body;
|
|
40
|
-
if (childNode.type === "Atrule" && childNode.name === "apply" && childNode.prelude && childNode.prelude.type === "Raw") {
|
|
41
|
-
body = childNode.prelude.value.trim();
|
|
42
|
-
} else if (varStyle !== false && childNode.type === "Declaration" && childNode.property === `${varStyle}apply` && childNode.value.type === "Raw") {
|
|
43
|
-
body = childNode.value.value.trim();
|
|
44
|
-
if (body.match(/^(['"]).*\1$/))
|
|
45
|
-
body = body.slice(1, -1);
|
|
46
|
-
}
|
|
47
|
-
if (!body)
|
|
48
|
-
return;
|
|
49
|
-
const classNames = core.expandVariantGroup(body).split(/\s+/g).map((className) => className.trim().replace(/\\/, ""));
|
|
50
|
-
const utils = (await Promise.all(
|
|
51
|
-
classNames.map((i) => uno.parseToken(i, "-"))
|
|
52
|
-
)).filter(core.notNull).flat().sort((a, b) => a[0] - b[0]).sort((a, b) => (a[3] ? uno.parentOrders.get(a[3]) ?? 0 : 0) - (b[3] ? uno.parentOrders.get(b[3]) ?? 0 : 0)).reduce((acc, item) => {
|
|
53
|
-
const target = acc.find((i) => i[1] === item[1] && i[3] === item[3]);
|
|
54
|
-
if (target)
|
|
55
|
-
target[2] += item[2];
|
|
56
|
-
else
|
|
57
|
-
acc.push([...item]);
|
|
58
|
-
return acc;
|
|
59
|
-
}, []);
|
|
60
|
-
if (!utils.length)
|
|
61
|
-
return;
|
|
62
|
-
for (const i of utils) {
|
|
63
|
-
const [, _selector, body2, parent] = i;
|
|
64
|
-
const selector = _selector?.replace(core.regexScopePlaceholder, " ") || _selector;
|
|
65
|
-
if (parent || selector && selector !== ".\\-") {
|
|
66
|
-
let newSelector = cssTree.generate(node.prelude);
|
|
67
|
-
if (selector && selector !== ".\\-") {
|
|
68
|
-
const selectorAST = cssTree.parse(selector, {
|
|
69
|
-
context: "selector"
|
|
70
|
-
});
|
|
71
|
-
const prelude = cssTree.clone(node.prelude);
|
|
72
|
-
prelude.children.forEach((child) => {
|
|
73
|
-
const parentSelectorAst = cssTree.clone(selectorAST);
|
|
74
|
-
parentSelectorAst.children.forEach((i2) => {
|
|
75
|
-
if (i2.type === "ClassSelector" && i2.name === "\\-")
|
|
76
|
-
Object.assign(i2, cssTree.clone(child));
|
|
77
|
-
});
|
|
78
|
-
Object.assign(child, parentSelectorAst);
|
|
79
|
-
});
|
|
80
|
-
newSelector = cssTree.generate(prelude);
|
|
81
|
-
}
|
|
82
|
-
let css = `${newSelector}{${body2}}`;
|
|
83
|
-
if (parent)
|
|
84
|
-
css = `${parent}{${css}}`;
|
|
85
|
-
code.appendLeft(calcOffset(node.loc.end.offset), css);
|
|
86
|
-
} else {
|
|
87
|
-
code.appendRight(calcOffset(childNode.loc.end.offset), body2);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
code.remove(
|
|
91
|
-
calcOffset(childNode.loc.start.offset),
|
|
92
|
-
calcOffset(childNode.loc.end.offset)
|
|
93
|
-
);
|
|
94
|
-
};
|
|
95
|
-
const handleThemeFn = (node) => {
|
|
96
|
-
const value = node.value;
|
|
97
|
-
const offset2 = value.loc.start.offset;
|
|
98
|
-
const str = code.original.slice(offset2, value.loc.end.offset);
|
|
99
|
-
const matches = Array.from(str.matchAll(themeFnRE));
|
|
100
|
-
if (!matches.length)
|
|
101
|
-
return;
|
|
102
|
-
for (const match of matches) {
|
|
103
|
-
const rawArg = match[1].trim();
|
|
104
|
-
if (!rawArg)
|
|
105
|
-
throw new Error("theme() expect exact one argument, but got 0");
|
|
106
|
-
let value2 = uno.config.theme;
|
|
107
|
-
const keys = rawArg.slice(1, -1).split(".");
|
|
108
|
-
keys.every((key) => {
|
|
109
|
-
if (value2[key] != null)
|
|
110
|
-
value2 = value2[key];
|
|
111
|
-
else if (value2[+key] != null)
|
|
112
|
-
value2 = value2[+key];
|
|
113
|
-
else
|
|
114
|
-
return false;
|
|
115
|
-
return true;
|
|
116
|
-
});
|
|
117
|
-
if (typeof value2 === "string") {
|
|
118
|
-
code.overwrite(
|
|
119
|
-
offset2 + match.index,
|
|
120
|
-
offset2 + match.index + match[0].length,
|
|
121
|
-
value2
|
|
122
|
-
);
|
|
123
|
-
} else if (throwOnMissing) {
|
|
124
|
-
throw new Error(`theme of "${rawArg.slice(1, -1)}" did not found`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
const handleScreen = (node) => {
|
|
129
|
-
let breakpointName = "";
|
|
130
|
-
let prefix;
|
|
131
|
-
if (node.name === "screen" && node.prelude?.type === "Raw")
|
|
132
|
-
breakpointName = node.prelude.value.trim();
|
|
133
|
-
if (!breakpointName)
|
|
134
|
-
return;
|
|
135
|
-
const match = breakpointName.match(/^(?:(lt|at)-)?(\w+)$/);
|
|
136
|
-
if (match) {
|
|
137
|
-
prefix = match[1];
|
|
138
|
-
breakpointName = match[2];
|
|
139
|
-
}
|
|
140
|
-
const resolveBreakpoints = () => {
|
|
141
|
-
let breakpoints;
|
|
142
|
-
if (uno.userConfig && uno.userConfig.theme)
|
|
143
|
-
breakpoints = uno.userConfig.theme.breakpoints;
|
|
144
|
-
if (!breakpoints)
|
|
145
|
-
breakpoints = uno.config.theme.breakpoints;
|
|
146
|
-
return breakpoints;
|
|
147
|
-
};
|
|
148
|
-
const variantEntries = Object.entries(resolveBreakpoints() ?? {}).map(([point, size], idx) => [point, size, idx]);
|
|
149
|
-
const generateMediaQuery = (breakpointName2, prefix2) => {
|
|
150
|
-
const [, size, idx] = variantEntries.find((i) => i[0] === breakpointName2);
|
|
151
|
-
if (prefix2) {
|
|
152
|
-
if (prefix2 === "lt")
|
|
153
|
-
return `@media (max-width: ${calcMaxWidthBySize(size)})`;
|
|
154
|
-
else if (prefix2 === "at")
|
|
155
|
-
return `@media (min-width: ${size})${variantEntries[idx + 1] ? ` and (max-width: ${calcMaxWidthBySize(variantEntries[idx + 1][1])})` : ""}`;
|
|
156
|
-
else
|
|
157
|
-
throw new Error(`breakpoint variant not supported: ${prefix2}`);
|
|
158
|
-
}
|
|
159
|
-
return `@media (min-width: ${size})`;
|
|
160
|
-
};
|
|
161
|
-
if (!variantEntries.find((i) => i[0] === breakpointName))
|
|
162
|
-
throw new Error(`breakpoint ${breakpointName} not found`);
|
|
163
|
-
const offset2 = node.loc.start.offset;
|
|
164
|
-
const str = code.original.slice(offset2, node.loc.end.offset);
|
|
165
|
-
const matches = Array.from(str.matchAll(screenRuleRE));
|
|
166
|
-
if (!matches.length)
|
|
167
|
-
return;
|
|
168
|
-
for (const match2 of matches) {
|
|
169
|
-
code.overwrite(
|
|
170
|
-
offset2 + match2.index,
|
|
171
|
-
offset2 + match2.index + match2[0].length,
|
|
172
|
-
`${generateMediaQuery(breakpointName, prefix)} `
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
194
|
const stack = [];
|
|
177
195
|
const processNode = async (node, _item, _list) => {
|
|
196
|
+
const ctx = { options, uno, code, filename, offset };
|
|
178
197
|
if (isScreen && node.type === "Atrule")
|
|
179
|
-
handleScreen(node);
|
|
198
|
+
handleScreen(ctx, node);
|
|
180
199
|
if (hasThemeFn && node.type === "Declaration")
|
|
181
|
-
handleThemeFn(node);
|
|
182
|
-
if (isApply && node.type === "Rule")
|
|
183
|
-
await
|
|
184
|
-
node.block.children.map(async (childNode, _childItem) => {
|
|
185
|
-
if (childNode.type === "Raw")
|
|
186
|
-
return transformDirectives(code, uno, options, filename, childNode.value, calcOffset(childNode.loc.start.offset));
|
|
187
|
-
await handleApply(node, childNode);
|
|
188
|
-
}).toArray()
|
|
189
|
-
);
|
|
190
|
-
}
|
|
200
|
+
handleThemeFn(ctx, node);
|
|
201
|
+
if (isApply && node.type === "Rule")
|
|
202
|
+
await handleApply(ctx, node);
|
|
191
203
|
};
|
|
192
204
|
cssTree.walk(ast, (...args) => stack.push(processNode(...args)));
|
|
193
205
|
await Promise.all(stack);
|
|
194
206
|
}
|
|
195
|
-
function calcMaxWidthBySize(size) {
|
|
196
|
-
const value = size.match(/^-?[0-9]+\.?[0-9]*/)?.[0] || "";
|
|
197
|
-
const unit = size.slice(value.length);
|
|
198
|
-
const maxWidth = parseFloat(value) - 0.1;
|
|
199
|
-
return Number.isNaN(maxWidth) ? size : `${maxWidth}${unit}`;
|
|
200
|
-
}
|
|
201
207
|
|
|
202
208
|
exports["default"] = transformerDirectives;
|
|
203
209
|
exports.transformDirectives = transformDirectives;
|
package/dist/index.d.ts
CHANGED
|
@@ -18,7 +18,14 @@ interface TransformerDirectivesOptions {
|
|
|
18
18
|
*/
|
|
19
19
|
throwOnMissing?: boolean;
|
|
20
20
|
}
|
|
21
|
+
interface TransformerDirectivesContext {
|
|
22
|
+
code: MagicString;
|
|
23
|
+
uno: UnoGenerator;
|
|
24
|
+
options: TransformerDirectivesOptions;
|
|
25
|
+
offset?: number;
|
|
26
|
+
filename?: string;
|
|
27
|
+
}
|
|
21
28
|
declare function transformerDirectives(options?: TransformerDirectivesOptions): SourceCodeTransformer;
|
|
22
29
|
declare function transformDirectives(code: MagicString, uno: UnoGenerator, options: TransformerDirectivesOptions, filename?: string, originalCode?: string, offset?: number): Promise<void>;
|
|
23
30
|
|
|
24
|
-
export { TransformerDirectivesOptions, transformerDirectives as default, transformDirectives };
|
|
31
|
+
export { TransformerDirectivesContext, TransformerDirectivesOptions, transformerDirectives as default, transformDirectives };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,167 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { expandVariantGroup, notNull, regexScopePlaceholder, cssIdRE } from '@unocss/core';
|
|
2
|
+
import { generate, parse, clone, walk } from 'css-tree';
|
|
3
|
+
|
|
4
|
+
const themeFnRE = /theme\((.*?)\)/g;
|
|
5
|
+
function handleThemeFn({ code, uno, options }, node) {
|
|
6
|
+
const { throwOnMissing = true } = options;
|
|
7
|
+
const offset = node.value.loc.start.offset;
|
|
8
|
+
const str = code.original.slice(offset, node.value.loc.end.offset);
|
|
9
|
+
const matches = Array.from(str.matchAll(themeFnRE));
|
|
10
|
+
if (!matches.length)
|
|
11
|
+
return;
|
|
12
|
+
for (const match of matches) {
|
|
13
|
+
const rawArg = match[1].trim();
|
|
14
|
+
if (!rawArg)
|
|
15
|
+
throw new Error("theme() expect exact one argument, but got 0");
|
|
16
|
+
let value = uno.config.theme;
|
|
17
|
+
const keys = rawArg.slice(1, -1).split(".");
|
|
18
|
+
keys.every((key) => {
|
|
19
|
+
if (value[key] != null)
|
|
20
|
+
value = value[key];
|
|
21
|
+
else if (value[+key] != null)
|
|
22
|
+
value = value[+key];
|
|
23
|
+
else
|
|
24
|
+
return false;
|
|
25
|
+
return true;
|
|
26
|
+
});
|
|
27
|
+
if (typeof value === "string") {
|
|
28
|
+
code.overwrite(
|
|
29
|
+
offset + match.index,
|
|
30
|
+
offset + match.index + match[0].length,
|
|
31
|
+
value
|
|
32
|
+
);
|
|
33
|
+
} else if (throwOnMissing) {
|
|
34
|
+
throw new Error(`theme of "${rawArg.slice(1, -1)}" did not found`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const screenRuleRE = /(@screen) (.+) /g;
|
|
40
|
+
function handleScreen({ code, uno }, node) {
|
|
41
|
+
let breakpointName = "";
|
|
42
|
+
let prefix = "";
|
|
43
|
+
if (node.name === "screen" && node.prelude?.type === "Raw")
|
|
44
|
+
breakpointName = node.prelude.value.trim();
|
|
45
|
+
if (!breakpointName)
|
|
46
|
+
return;
|
|
47
|
+
const match = breakpointName.match(/^(?:(lt|at)-)?(\w+)$/);
|
|
48
|
+
if (match) {
|
|
49
|
+
prefix = match[1];
|
|
50
|
+
breakpointName = match[2];
|
|
51
|
+
}
|
|
52
|
+
const resolveBreakpoints = () => {
|
|
53
|
+
let breakpoints;
|
|
54
|
+
if (uno.userConfig && uno.userConfig.theme)
|
|
55
|
+
breakpoints = uno.userConfig.theme.breakpoints;
|
|
56
|
+
if (!breakpoints)
|
|
57
|
+
breakpoints = uno.config.theme.breakpoints;
|
|
58
|
+
return breakpoints;
|
|
59
|
+
};
|
|
60
|
+
const variantEntries = Object.entries(resolveBreakpoints() ?? {}).map(([point, size], idx) => [point, size, idx]);
|
|
61
|
+
const generateMediaQuery = (breakpointName2, prefix2) => {
|
|
62
|
+
const [, size, idx] = variantEntries.find((i) => i[0] === breakpointName2);
|
|
63
|
+
if (prefix2) {
|
|
64
|
+
if (prefix2 === "lt")
|
|
65
|
+
return `@media (max-width: ${calcMaxWidthBySize(size)})`;
|
|
66
|
+
else if (prefix2 === "at")
|
|
67
|
+
return `@media (min-width: ${size})${variantEntries[idx + 1] ? ` and (max-width: ${calcMaxWidthBySize(variantEntries[idx + 1][1])})` : ""}`;
|
|
68
|
+
else
|
|
69
|
+
throw new Error(`breakpoint variant not supported: ${prefix2}`);
|
|
70
|
+
}
|
|
71
|
+
return `@media (min-width: ${size})`;
|
|
72
|
+
};
|
|
73
|
+
if (!variantEntries.find((i) => i[0] === breakpointName))
|
|
74
|
+
throw new Error(`breakpoint ${breakpointName} not found`);
|
|
75
|
+
const offset = node.loc.start.offset;
|
|
76
|
+
const str = code.original.slice(offset, node.loc.end.offset);
|
|
77
|
+
const matches = Array.from(str.matchAll(screenRuleRE));
|
|
78
|
+
if (!matches.length)
|
|
79
|
+
return;
|
|
80
|
+
for (const match2 of matches) {
|
|
81
|
+
code.overwrite(
|
|
82
|
+
offset + match2.index,
|
|
83
|
+
offset + match2.index + match2[0].length,
|
|
84
|
+
`${generateMediaQuery(breakpointName, prefix)} `
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function calcMaxWidthBySize(size) {
|
|
89
|
+
const value = size.match(/^-?[0-9]+\.?[0-9]*/)?.[0] || "";
|
|
90
|
+
const unit = size.slice(value.length);
|
|
91
|
+
const maxWidth = parseFloat(value) - 0.1;
|
|
92
|
+
return Number.isNaN(maxWidth) ? size : `${maxWidth}${unit}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function handleApply(ctx, node) {
|
|
96
|
+
const { code, uno, options, filename, offset } = ctx;
|
|
97
|
+
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
98
|
+
await Promise.all(
|
|
99
|
+
node.block.children.map(async (childNode) => {
|
|
100
|
+
if (childNode.type === "Raw")
|
|
101
|
+
return transformDirectives(code, uno, options, filename, childNode.value, calcOffset(childNode.loc.start.offset));
|
|
102
|
+
await parseApply(ctx, node, childNode);
|
|
103
|
+
}).toArray()
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
async function parseApply({ code, uno, options, offset }, node, childNode) {
|
|
107
|
+
const { varStyle = "--at-" } = options;
|
|
108
|
+
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
109
|
+
let body;
|
|
110
|
+
if (childNode.type === "Atrule" && childNode.name === "apply" && childNode.prelude && childNode.prelude.type === "Raw") {
|
|
111
|
+
body = childNode.prelude.value.trim();
|
|
112
|
+
} else if (varStyle !== false && childNode.type === "Declaration" && childNode.property === `${varStyle}apply` && childNode.value.type === "Raw") {
|
|
113
|
+
body = childNode.value.value.trim();
|
|
114
|
+
if (body.match(/^(['"]).*\1$/))
|
|
115
|
+
body = body.slice(1, -1);
|
|
116
|
+
}
|
|
117
|
+
if (!body)
|
|
118
|
+
return;
|
|
119
|
+
const classNames = expandVariantGroup(body).split(/\s+/g).map((className) => className.trim().replace(/\\/, ""));
|
|
120
|
+
const utils = (await Promise.all(
|
|
121
|
+
classNames.map((i) => uno.parseToken(i, "-"))
|
|
122
|
+
)).filter(notNull).flat().sort((a, b) => a[0] - b[0]).sort((a, b) => (a[3] ? uno.parentOrders.get(a[3]) ?? 0 : 0) - (b[3] ? uno.parentOrders.get(b[3]) ?? 0 : 0)).reduce((acc, item) => {
|
|
123
|
+
const target = acc.find((i) => i[1] === item[1] && i[3] === item[3]);
|
|
124
|
+
if (target)
|
|
125
|
+
target[2] += item[2];
|
|
126
|
+
else
|
|
127
|
+
acc.push([...item]);
|
|
128
|
+
return acc;
|
|
129
|
+
}, []);
|
|
130
|
+
if (!utils.length)
|
|
131
|
+
return;
|
|
132
|
+
for (const i of utils) {
|
|
133
|
+
const [, _selector, body2, parent] = i;
|
|
134
|
+
const selector = _selector?.replace(regexScopePlaceholder, " ") || _selector;
|
|
135
|
+
if (parent || selector && selector !== ".\\-") {
|
|
136
|
+
let newSelector = generate(node.prelude);
|
|
137
|
+
if (selector && selector !== ".\\-") {
|
|
138
|
+
const selectorAST = parse(selector, {
|
|
139
|
+
context: "selector"
|
|
140
|
+
});
|
|
141
|
+
const prelude = clone(node.prelude);
|
|
142
|
+
prelude.children.forEach((child) => {
|
|
143
|
+
const parentSelectorAst = clone(selectorAST);
|
|
144
|
+
parentSelectorAst.children.forEach((i2) => {
|
|
145
|
+
if (i2.type === "ClassSelector" && i2.name === "\\-")
|
|
146
|
+
Object.assign(i2, clone(child));
|
|
147
|
+
});
|
|
148
|
+
Object.assign(child, parentSelectorAst);
|
|
149
|
+
});
|
|
150
|
+
newSelector = generate(prelude);
|
|
151
|
+
}
|
|
152
|
+
let css = `${newSelector}{${body2}}`;
|
|
153
|
+
if (parent)
|
|
154
|
+
css = `${parent}{${css}}`;
|
|
155
|
+
code.appendLeft(calcOffset(node.loc.end.offset), css);
|
|
156
|
+
} else {
|
|
157
|
+
code.appendRight(calcOffset(childNode.loc.end.offset), body2);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
code.remove(
|
|
161
|
+
calcOffset(childNode.loc.start.offset),
|
|
162
|
+
calcOffset(childNode.loc.end.offset)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
3
165
|
|
|
4
166
|
function transformerDirectives(options = {}) {
|
|
5
167
|
return {
|
|
@@ -11,13 +173,8 @@ function transformerDirectives(options = {}) {
|
|
|
11
173
|
}
|
|
12
174
|
};
|
|
13
175
|
}
|
|
14
|
-
const themeFnRE = /theme\((.*?)\)/g;
|
|
15
|
-
const screenRuleRE = /(@screen) (.+) /g;
|
|
16
176
|
async function transformDirectives(code, uno, options, filename, originalCode, offset) {
|
|
17
|
-
const {
|
|
18
|
-
varStyle = "--at-",
|
|
19
|
-
throwOnMissing = true
|
|
20
|
-
} = options;
|
|
177
|
+
const { varStyle = "--at-" } = options;
|
|
21
178
|
const isApply = code.original.includes("@apply") || varStyle !== false && code.original.includes(varStyle);
|
|
22
179
|
const isScreen = code.original.includes("@screen");
|
|
23
180
|
const hasThemeFn = code.original.match(themeFnRE);
|
|
@@ -30,169 +187,18 @@ async function transformDirectives(code, uno, options, filename, originalCode, o
|
|
|
30
187
|
});
|
|
31
188
|
if (ast.type !== "StyleSheet")
|
|
32
189
|
return;
|
|
33
|
-
const calcOffset = (pos) => offset ? pos + offset : pos;
|
|
34
|
-
const handleApply = async (node, childNode) => {
|
|
35
|
-
let body;
|
|
36
|
-
if (childNode.type === "Atrule" && childNode.name === "apply" && childNode.prelude && childNode.prelude.type === "Raw") {
|
|
37
|
-
body = childNode.prelude.value.trim();
|
|
38
|
-
} else if (varStyle !== false && childNode.type === "Declaration" && childNode.property === `${varStyle}apply` && childNode.value.type === "Raw") {
|
|
39
|
-
body = childNode.value.value.trim();
|
|
40
|
-
if (body.match(/^(['"]).*\1$/))
|
|
41
|
-
body = body.slice(1, -1);
|
|
42
|
-
}
|
|
43
|
-
if (!body)
|
|
44
|
-
return;
|
|
45
|
-
const classNames = expandVariantGroup(body).split(/\s+/g).map((className) => className.trim().replace(/\\/, ""));
|
|
46
|
-
const utils = (await Promise.all(
|
|
47
|
-
classNames.map((i) => uno.parseToken(i, "-"))
|
|
48
|
-
)).filter(notNull).flat().sort((a, b) => a[0] - b[0]).sort((a, b) => (a[3] ? uno.parentOrders.get(a[3]) ?? 0 : 0) - (b[3] ? uno.parentOrders.get(b[3]) ?? 0 : 0)).reduce((acc, item) => {
|
|
49
|
-
const target = acc.find((i) => i[1] === item[1] && i[3] === item[3]);
|
|
50
|
-
if (target)
|
|
51
|
-
target[2] += item[2];
|
|
52
|
-
else
|
|
53
|
-
acc.push([...item]);
|
|
54
|
-
return acc;
|
|
55
|
-
}, []);
|
|
56
|
-
if (!utils.length)
|
|
57
|
-
return;
|
|
58
|
-
for (const i of utils) {
|
|
59
|
-
const [, _selector, body2, parent] = i;
|
|
60
|
-
const selector = _selector?.replace(regexScopePlaceholder, " ") || _selector;
|
|
61
|
-
if (parent || selector && selector !== ".\\-") {
|
|
62
|
-
let newSelector = generate(node.prelude);
|
|
63
|
-
if (selector && selector !== ".\\-") {
|
|
64
|
-
const selectorAST = parse(selector, {
|
|
65
|
-
context: "selector"
|
|
66
|
-
});
|
|
67
|
-
const prelude = clone(node.prelude);
|
|
68
|
-
prelude.children.forEach((child) => {
|
|
69
|
-
const parentSelectorAst = clone(selectorAST);
|
|
70
|
-
parentSelectorAst.children.forEach((i2) => {
|
|
71
|
-
if (i2.type === "ClassSelector" && i2.name === "\\-")
|
|
72
|
-
Object.assign(i2, clone(child));
|
|
73
|
-
});
|
|
74
|
-
Object.assign(child, parentSelectorAst);
|
|
75
|
-
});
|
|
76
|
-
newSelector = generate(prelude);
|
|
77
|
-
}
|
|
78
|
-
let css = `${newSelector}{${body2}}`;
|
|
79
|
-
if (parent)
|
|
80
|
-
css = `${parent}{${css}}`;
|
|
81
|
-
code.appendLeft(calcOffset(node.loc.end.offset), css);
|
|
82
|
-
} else {
|
|
83
|
-
code.appendRight(calcOffset(childNode.loc.end.offset), body2);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
code.remove(
|
|
87
|
-
calcOffset(childNode.loc.start.offset),
|
|
88
|
-
calcOffset(childNode.loc.end.offset)
|
|
89
|
-
);
|
|
90
|
-
};
|
|
91
|
-
const handleThemeFn = (node) => {
|
|
92
|
-
const value = node.value;
|
|
93
|
-
const offset2 = value.loc.start.offset;
|
|
94
|
-
const str = code.original.slice(offset2, value.loc.end.offset);
|
|
95
|
-
const matches = Array.from(str.matchAll(themeFnRE));
|
|
96
|
-
if (!matches.length)
|
|
97
|
-
return;
|
|
98
|
-
for (const match of matches) {
|
|
99
|
-
const rawArg = match[1].trim();
|
|
100
|
-
if (!rawArg)
|
|
101
|
-
throw new Error("theme() expect exact one argument, but got 0");
|
|
102
|
-
let value2 = uno.config.theme;
|
|
103
|
-
const keys = rawArg.slice(1, -1).split(".");
|
|
104
|
-
keys.every((key) => {
|
|
105
|
-
if (value2[key] != null)
|
|
106
|
-
value2 = value2[key];
|
|
107
|
-
else if (value2[+key] != null)
|
|
108
|
-
value2 = value2[+key];
|
|
109
|
-
else
|
|
110
|
-
return false;
|
|
111
|
-
return true;
|
|
112
|
-
});
|
|
113
|
-
if (typeof value2 === "string") {
|
|
114
|
-
code.overwrite(
|
|
115
|
-
offset2 + match.index,
|
|
116
|
-
offset2 + match.index + match[0].length,
|
|
117
|
-
value2
|
|
118
|
-
);
|
|
119
|
-
} else if (throwOnMissing) {
|
|
120
|
-
throw new Error(`theme of "${rawArg.slice(1, -1)}" did not found`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
const handleScreen = (node) => {
|
|
125
|
-
let breakpointName = "";
|
|
126
|
-
let prefix;
|
|
127
|
-
if (node.name === "screen" && node.prelude?.type === "Raw")
|
|
128
|
-
breakpointName = node.prelude.value.trim();
|
|
129
|
-
if (!breakpointName)
|
|
130
|
-
return;
|
|
131
|
-
const match = breakpointName.match(/^(?:(lt|at)-)?(\w+)$/);
|
|
132
|
-
if (match) {
|
|
133
|
-
prefix = match[1];
|
|
134
|
-
breakpointName = match[2];
|
|
135
|
-
}
|
|
136
|
-
const resolveBreakpoints = () => {
|
|
137
|
-
let breakpoints;
|
|
138
|
-
if (uno.userConfig && uno.userConfig.theme)
|
|
139
|
-
breakpoints = uno.userConfig.theme.breakpoints;
|
|
140
|
-
if (!breakpoints)
|
|
141
|
-
breakpoints = uno.config.theme.breakpoints;
|
|
142
|
-
return breakpoints;
|
|
143
|
-
};
|
|
144
|
-
const variantEntries = Object.entries(resolveBreakpoints() ?? {}).map(([point, size], idx) => [point, size, idx]);
|
|
145
|
-
const generateMediaQuery = (breakpointName2, prefix2) => {
|
|
146
|
-
const [, size, idx] = variantEntries.find((i) => i[0] === breakpointName2);
|
|
147
|
-
if (prefix2) {
|
|
148
|
-
if (prefix2 === "lt")
|
|
149
|
-
return `@media (max-width: ${calcMaxWidthBySize(size)})`;
|
|
150
|
-
else if (prefix2 === "at")
|
|
151
|
-
return `@media (min-width: ${size})${variantEntries[idx + 1] ? ` and (max-width: ${calcMaxWidthBySize(variantEntries[idx + 1][1])})` : ""}`;
|
|
152
|
-
else
|
|
153
|
-
throw new Error(`breakpoint variant not supported: ${prefix2}`);
|
|
154
|
-
}
|
|
155
|
-
return `@media (min-width: ${size})`;
|
|
156
|
-
};
|
|
157
|
-
if (!variantEntries.find((i) => i[0] === breakpointName))
|
|
158
|
-
throw new Error(`breakpoint ${breakpointName} not found`);
|
|
159
|
-
const offset2 = node.loc.start.offset;
|
|
160
|
-
const str = code.original.slice(offset2, node.loc.end.offset);
|
|
161
|
-
const matches = Array.from(str.matchAll(screenRuleRE));
|
|
162
|
-
if (!matches.length)
|
|
163
|
-
return;
|
|
164
|
-
for (const match2 of matches) {
|
|
165
|
-
code.overwrite(
|
|
166
|
-
offset2 + match2.index,
|
|
167
|
-
offset2 + match2.index + match2[0].length,
|
|
168
|
-
`${generateMediaQuery(breakpointName, prefix)} `
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
190
|
const stack = [];
|
|
173
191
|
const processNode = async (node, _item, _list) => {
|
|
192
|
+
const ctx = { options, uno, code, filename, offset };
|
|
174
193
|
if (isScreen && node.type === "Atrule")
|
|
175
|
-
handleScreen(node);
|
|
194
|
+
handleScreen(ctx, node);
|
|
176
195
|
if (hasThemeFn && node.type === "Declaration")
|
|
177
|
-
handleThemeFn(node);
|
|
178
|
-
if (isApply && node.type === "Rule")
|
|
179
|
-
await
|
|
180
|
-
node.block.children.map(async (childNode, _childItem) => {
|
|
181
|
-
if (childNode.type === "Raw")
|
|
182
|
-
return transformDirectives(code, uno, options, filename, childNode.value, calcOffset(childNode.loc.start.offset));
|
|
183
|
-
await handleApply(node, childNode);
|
|
184
|
-
}).toArray()
|
|
185
|
-
);
|
|
186
|
-
}
|
|
196
|
+
handleThemeFn(ctx, node);
|
|
197
|
+
if (isApply && node.type === "Rule")
|
|
198
|
+
await handleApply(ctx, node);
|
|
187
199
|
};
|
|
188
200
|
walk(ast, (...args) => stack.push(processNode(...args)));
|
|
189
201
|
await Promise.all(stack);
|
|
190
202
|
}
|
|
191
|
-
function calcMaxWidthBySize(size) {
|
|
192
|
-
const value = size.match(/^-?[0-9]+\.?[0-9]*/)?.[0] || "";
|
|
193
|
-
const unit = size.slice(value.length);
|
|
194
|
-
const maxWidth = parseFloat(value) - 0.1;
|
|
195
|
-
return Number.isNaN(maxWidth) ? size : `${maxWidth}${unit}`;
|
|
196
|
-
}
|
|
197
203
|
|
|
198
204
|
export { transformerDirectives as default, transformDirectives };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unocss/transformer-directives",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "UnoCSS transformer for `@apply` directive",
|
|
5
5
|
"author": "hannoeru <me@hanlee.co>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@unocss/core": "0.
|
|
35
|
+
"@unocss/core": "0.46.0",
|
|
36
36
|
"css-tree": "^2.2.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"magic-string": "^0.26.
|
|
39
|
+
"magic-string": "^0.26.7"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "unbuild",
|