@unocss/transformer-directives 0.45.29 → 0.45.30

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 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 Promise.all(
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 { cssIdRE, expandVariantGroup, notNull, regexScopePlaceholder } from '@unocss/core';
2
- import { parse, walk, generate, clone } from 'css-tree';
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 Promise.all(
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.45.29",
3
+ "version": "0.45.30",
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.45.29",
35
+ "@unocss/core": "0.45.30",
36
36
  "css-tree": "^2.2.1"
37
37
  },
38
38
  "devDependencies": {
39
- "magic-string": "^0.26.6"
39
+ "magic-string": "^0.26.7"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "unbuild",