@master/css-compiler 2.0.0-beta.99 → 2.0.0-rc.70
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/README.md +34 -133
- package/dist/browser.d.ts +22 -0
- package/dist/browser.mjs +47 -0
- package/dist/core.d.ts +48 -0
- package/dist/core.mjs +2019 -0
- package/dist/css-transform.d.ts +6 -0
- package/dist/css-transform.mjs +18 -0
- package/dist/index.d.ts +46 -3
- package/dist/index.mjs +394 -10
- package/dist/lexer/imports.d.ts +13 -0
- package/dist/lexer/imports.mjs +44 -0
- package/dist/lexer/source.d.ts +3 -0
- package/dist/lexer/source.mjs +9 -0
- package/dist/lexer/standalone-master.d.ts +21 -0
- package/dist/lexer/standalone-master.mjs +233 -0
- package/dist/lower-css-directives.d.ts +16 -0
- package/dist/lower-css-directives.mjs +969 -0
- package/dist/master-css-manifest.d.ts +14 -0
- package/dist/master-css-manifest.mjs +760 -0
- package/dist/native-declaration.d.ts +2 -0
- package/dist/native-declaration.mjs +25 -0
- package/dist/utils/selectors.d.ts +3 -0
- package/dist/utils/selectors.mjs +55 -0
- package/dist/utils/unquote.d.ts +1 -0
- package/dist/utils/unquote.mjs +10 -0
- package/dist/utils/wrap-at-rules.d.ts +1 -0
- package/dist/utils/wrap-at-rules.mjs +10 -0
- package/package.json +1 -1
- package/dist/compiler.d.ts +0 -19
- package/dist/extract.d.ts +0 -3
- package/dist/index.cjs +0 -10
- package/dist/options/index.d.ts +0 -17
package/dist/core.mjs
ADDED
|
@@ -0,0 +1,2019 @@
|
|
|
1
|
+
import { createCSSDirectiveVariantReference, CSSDirectiveError, createCSSDirectiveSourceReference } from '@master/css-schema/css-directives';
|
|
2
|
+
import { getCSSTransform, encodeCSS, decodeCSS } from './css-transform.mjs';
|
|
3
|
+
export { setCSSTransform } from './css-transform.mjs';
|
|
4
|
+
import { removeSourceRanges, createSourceLocationResolver, collectCSSDirectiveRanges, replaceSourceRanges, findCSSClosingQuote, findCSSStatementEnd, findCSSBlockEnd, collectMasterCSSClassListTokenRanges, skipCSSWhitespace } from '@master/css-lexer';
|
|
5
|
+
import { collectStandaloneCSSDirectiveExtractionPolicy, removeStandaloneCSSDirectives, createCSSDirectiveExtractionPolicy } from './lexer/standalone-master.mjs';
|
|
6
|
+
export { findStandaloneCSSDirectiveStatements, findStandaloneMasterDirectiveStatements, mergeCSSDirectiveExtractionPolicy, removeStandaloneMasterDirectives } from './lexer/standalone-master.mjs';
|
|
7
|
+
import { combineSelectorLists } from './utils/selectors.mjs';
|
|
8
|
+
|
|
9
|
+
const MASTER_CUSTOM_AT_RULES = {
|
|
10
|
+
settings: {
|
|
11
|
+
prelude: '*',
|
|
12
|
+
body: 'style-block'
|
|
13
|
+
},
|
|
14
|
+
theme: {
|
|
15
|
+
prelude: '*',
|
|
16
|
+
body: 'style-block'
|
|
17
|
+
},
|
|
18
|
+
defaults: {
|
|
19
|
+
prelude: null,
|
|
20
|
+
body: 'style-block'
|
|
21
|
+
},
|
|
22
|
+
components: {
|
|
23
|
+
prelude: null,
|
|
24
|
+
body: 'style-block'
|
|
25
|
+
},
|
|
26
|
+
utilities: {
|
|
27
|
+
prelude: null,
|
|
28
|
+
body: 'style-block'
|
|
29
|
+
},
|
|
30
|
+
'custom-variant': {
|
|
31
|
+
prelude: '*',
|
|
32
|
+
body: 'style-block'
|
|
33
|
+
},
|
|
34
|
+
variant: {
|
|
35
|
+
prelude: '*',
|
|
36
|
+
body: 'style-block'
|
|
37
|
+
},
|
|
38
|
+
dark: {
|
|
39
|
+
prelude: null,
|
|
40
|
+
body: 'style-block'
|
|
41
|
+
},
|
|
42
|
+
light: {
|
|
43
|
+
prelude: null,
|
|
44
|
+
body: 'style-block'
|
|
45
|
+
},
|
|
46
|
+
slot: {
|
|
47
|
+
prelude: null,
|
|
48
|
+
body: null
|
|
49
|
+
},
|
|
50
|
+
compose: {
|
|
51
|
+
prelude: '*',
|
|
52
|
+
body: null
|
|
53
|
+
},
|
|
54
|
+
reference: {
|
|
55
|
+
prelude: '<string>',
|
|
56
|
+
body: null
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const MANAGED_DEFINITION_DIRECTIVE_LAYERS = {
|
|
60
|
+
defaults: 'defaults',
|
|
61
|
+
components: 'components',
|
|
62
|
+
utilities: 'utilities'
|
|
63
|
+
};
|
|
64
|
+
const MASTER_VARIANT_SHORTHAND_TOKENS = {
|
|
65
|
+
dark: '@dark',
|
|
66
|
+
light: '@light'
|
|
67
|
+
};
|
|
68
|
+
function createSourceReference(parsed, range) {
|
|
69
|
+
return createCSSDirectiveSourceReference(parsed.filename, {
|
|
70
|
+
start: range.start,
|
|
71
|
+
end: range.end
|
|
72
|
+
}, parsed.source);
|
|
73
|
+
}
|
|
74
|
+
function decodeCSSQuotedString(source) {
|
|
75
|
+
return source.replace(/\\([\s\S])/g, '$1');
|
|
76
|
+
}
|
|
77
|
+
function findCSSReferenceStatements(source, filename = 'master.css') {
|
|
78
|
+
const statements = [];
|
|
79
|
+
for (const range of collectCSSDirectiveRanges(source)){
|
|
80
|
+
if (range.name !== 'reference') continue;
|
|
81
|
+
if (range.depth !== 0) {
|
|
82
|
+
throw new Error('@reference must be top-level');
|
|
83
|
+
}
|
|
84
|
+
if (range.blockRange) {
|
|
85
|
+
throw new Error('@reference requires a statement');
|
|
86
|
+
}
|
|
87
|
+
if (!range.semicolonRange) {
|
|
88
|
+
throw new Error('@reference requires a semicolon');
|
|
89
|
+
}
|
|
90
|
+
if (range.quotedStringRanges.length !== 1) {
|
|
91
|
+
throw new Error('@reference requires one quoted CSS resource');
|
|
92
|
+
}
|
|
93
|
+
const prelude = source.slice(range.preludeRange.start, range.preludeRange.end).trim();
|
|
94
|
+
const quoted = source.slice(range.quotedStringRanges[0].start, range.quotedStringRanges[0].end);
|
|
95
|
+
if (prelude !== quoted) {
|
|
96
|
+
throw new Error('@reference only accepts one quoted CSS resource');
|
|
97
|
+
}
|
|
98
|
+
statements.push({
|
|
99
|
+
start: range.start,
|
|
100
|
+
end: range.end,
|
|
101
|
+
statement: source.slice(range.start, range.end),
|
|
102
|
+
source: decodeCSSQuotedString(source.slice(range.quotedStringRanges[0].contentRange.start, range.quotedStringRanges[0].contentRange.end)),
|
|
103
|
+
file: filename
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return statements;
|
|
107
|
+
}
|
|
108
|
+
function removeCSSReferenceStatements(source, filename = 'master.css') {
|
|
109
|
+
return removeSourceRanges(source, findCSSReferenceStatements(source, filename));
|
|
110
|
+
}
|
|
111
|
+
function trimSourceRangeEnd(source, start, end) {
|
|
112
|
+
while(end > start && /\s/.test(source[end - 1] || ''))end--;
|
|
113
|
+
return end;
|
|
114
|
+
}
|
|
115
|
+
function createSelectorSourceReference(parsed, rule) {
|
|
116
|
+
const source = parsed.source;
|
|
117
|
+
const loc = rule.value?.loc;
|
|
118
|
+
const resolveLocation = parsed.sourceLocationResolver;
|
|
119
|
+
if (!source || !loc || !resolveLocation) return;
|
|
120
|
+
const start = resolveLocation({
|
|
121
|
+
line: loc.line,
|
|
122
|
+
column: loc.column
|
|
123
|
+
});
|
|
124
|
+
if (start === -1) return;
|
|
125
|
+
const statementEnd = findCSSStatementEnd(source, start);
|
|
126
|
+
if (statementEnd.reason !== 'block') return;
|
|
127
|
+
return createSourceReference(parsed, {
|
|
128
|
+
start,
|
|
129
|
+
end: trimSourceRangeEnd(source, start, statementEnd.end)
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function parseOnOff(value) {
|
|
133
|
+
if (value === 'on') return true;
|
|
134
|
+
if (value === 'off') return false;
|
|
135
|
+
}
|
|
136
|
+
function parseDefaultMode(value) {
|
|
137
|
+
if (value === 'false') throw new Error('default-mode must be a mode name or none');
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
function parseNumber(value) {
|
|
141
|
+
const numberValue = Number(value);
|
|
142
|
+
if (!Number.isNaN(numberValue) && String(numberValue) === value) return numberValue;
|
|
143
|
+
}
|
|
144
|
+
function parseList(value) {
|
|
145
|
+
return value.split(',').flatMap((part)=>part.trim().split(/\s+/)).filter(Boolean);
|
|
146
|
+
}
|
|
147
|
+
function parseVariableValue(value) {
|
|
148
|
+
const trimmed = value.trim();
|
|
149
|
+
const numberValue = parseNumber(trimmed);
|
|
150
|
+
return numberValue === undefined ? trimmed : numberValue;
|
|
151
|
+
}
|
|
152
|
+
function addThemeMode(manifestInput, mode) {
|
|
153
|
+
manifestInput.modes ??= [];
|
|
154
|
+
if (!manifestInput.modes.includes(mode)) manifestInput.modes.push(mode);
|
|
155
|
+
}
|
|
156
|
+
function normalizeThemeTokenName(property) {
|
|
157
|
+
if (!property.startsWith('--')) {
|
|
158
|
+
throw new Error(`@theme token declarations must be CSS custom properties: ${property}`);
|
|
159
|
+
}
|
|
160
|
+
const name = property.slice(2);
|
|
161
|
+
if (!name) {
|
|
162
|
+
throw new Error('@theme token name cannot be empty');
|
|
163
|
+
}
|
|
164
|
+
return name;
|
|
165
|
+
}
|
|
166
|
+
function defineThemeVariable(manifestInput, property, rawValue, mode, inline, isStatic) {
|
|
167
|
+
const name = normalizeThemeTokenName(property);
|
|
168
|
+
const value = parseVariableValue(rawValue);
|
|
169
|
+
if (mode) {
|
|
170
|
+
addThemeMode(manifestInput, mode);
|
|
171
|
+
}
|
|
172
|
+
manifestInput.variables ??= [];
|
|
173
|
+
const definition = {
|
|
174
|
+
name,
|
|
175
|
+
value,
|
|
176
|
+
...mode ? {
|
|
177
|
+
mode
|
|
178
|
+
} : {},
|
|
179
|
+
...inline ? {
|
|
180
|
+
inline: true
|
|
181
|
+
} : {},
|
|
182
|
+
...isStatic ? {
|
|
183
|
+
static: true
|
|
184
|
+
} : {}
|
|
185
|
+
};
|
|
186
|
+
const foundIndex = manifestInput.variables.findIndex((existing)=>existing.name === definition.name && existing.mode === definition.mode);
|
|
187
|
+
if (foundIndex !== -1) {
|
|
188
|
+
manifestInput.variables.splice(foundIndex, 1);
|
|
189
|
+
}
|
|
190
|
+
manifestInput.variables.push(definition);
|
|
191
|
+
}
|
|
192
|
+
function parseMasterOption(manifestInput, property, value) {
|
|
193
|
+
switch(property){
|
|
194
|
+
case 'root-size':
|
|
195
|
+
{
|
|
196
|
+
const rootSize = parseNumber(value);
|
|
197
|
+
if (rootSize === undefined) throw new Error('root-size must be a number');
|
|
198
|
+
manifestInput.rootSize = rootSize;
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
case 'base-unit':
|
|
202
|
+
{
|
|
203
|
+
const baseUnit = parseNumber(value);
|
|
204
|
+
if (baseUnit === undefined) throw new Error('base-unit must be a number');
|
|
205
|
+
manifestInput.baseUnit = baseUnit;
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
case 'default-mode':
|
|
209
|
+
manifestInput.defaultMode = parseDefaultMode(value);
|
|
210
|
+
return true;
|
|
211
|
+
case 'mode-trigger':
|
|
212
|
+
if (value !== 'class' && value !== 'media' && value !== 'host') {
|
|
213
|
+
throw new Error('mode-trigger must be class, media, or host');
|
|
214
|
+
}
|
|
215
|
+
manifestInput.modeTrigger = value;
|
|
216
|
+
return true;
|
|
217
|
+
case 'important':
|
|
218
|
+
{
|
|
219
|
+
const important = parseOnOff(value);
|
|
220
|
+
if (important === undefined) throw new Error('important must be on or off');
|
|
221
|
+
manifestInput.important = important;
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
case 'modes':
|
|
225
|
+
for (const mode of parseList(value)){
|
|
226
|
+
addThemeMode(manifestInput, mode);
|
|
227
|
+
}
|
|
228
|
+
return true;
|
|
229
|
+
case 'scope':
|
|
230
|
+
manifestInput.scope = value;
|
|
231
|
+
return true;
|
|
232
|
+
default:
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function formatDeclaration(declaration) {
|
|
237
|
+
const result = getCSSTransform()({
|
|
238
|
+
filename: 'master-css-declaration.css',
|
|
239
|
+
code: encodeCSS('.x{}'),
|
|
240
|
+
visitor: {
|
|
241
|
+
Rule: {
|
|
242
|
+
style (rule) {
|
|
243
|
+
rule.value.declarations.declarations = [
|
|
244
|
+
declaration
|
|
245
|
+
];
|
|
246
|
+
return rule;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
const output = decodeCSS(result.code);
|
|
252
|
+
const bodyStart = output.indexOf('{');
|
|
253
|
+
const bodyEnd = output.lastIndexOf('}');
|
|
254
|
+
const body = output.slice(bodyStart + 1, bodyEnd).trim();
|
|
255
|
+
return body.replace(/;$/, '').trim();
|
|
256
|
+
}
|
|
257
|
+
function formatSelectors(selectors) {
|
|
258
|
+
const result = getCSSTransform()({
|
|
259
|
+
filename: 'master-css-selector.css',
|
|
260
|
+
code: encodeCSS('.x{color:red}'),
|
|
261
|
+
minify: true,
|
|
262
|
+
visitor: {
|
|
263
|
+
Rule: {
|
|
264
|
+
style (rule) {
|
|
265
|
+
rule.value.selectors = selectors;
|
|
266
|
+
return rule;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
const output = decodeCSS(result.code);
|
|
272
|
+
return output.slice(0, output.indexOf('{')).trim();
|
|
273
|
+
}
|
|
274
|
+
function collectSelectorClassNames(value, classNames = new Set()) {
|
|
275
|
+
if (!value || typeof value !== 'object') return classNames;
|
|
276
|
+
if (Array.isArray(value)) {
|
|
277
|
+
for (const item of value){
|
|
278
|
+
collectSelectorClassNames(item, classNames);
|
|
279
|
+
}
|
|
280
|
+
return classNames;
|
|
281
|
+
}
|
|
282
|
+
const node = value;
|
|
283
|
+
if (node.type === 'class' && typeof node.name === 'string') {
|
|
284
|
+
classNames.add(node.name);
|
|
285
|
+
}
|
|
286
|
+
for (const key of Object.keys(node)){
|
|
287
|
+
collectSelectorClassNames(node[key], classNames);
|
|
288
|
+
}
|
|
289
|
+
return classNames;
|
|
290
|
+
}
|
|
291
|
+
function getSelectorClassNames(selector) {
|
|
292
|
+
return [
|
|
293
|
+
...collectSelectorClassNames(selector)
|
|
294
|
+
];
|
|
295
|
+
}
|
|
296
|
+
function recordNativeClassNames(parsed, classNames) {
|
|
297
|
+
for (const className of classNames){
|
|
298
|
+
if (!parsed.nativeClassNames.includes(className)) {
|
|
299
|
+
parsed.nativeClassNames.push(className);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function getNativeStyleSelectorFilters(rule, parsed, classFilter) {
|
|
304
|
+
const selectors = rule.value.selectors;
|
|
305
|
+
if (!selectors?.length) return;
|
|
306
|
+
const selectorEntries = selectors.map((selector)=>({
|
|
307
|
+
selector,
|
|
308
|
+
classNames: getSelectorClassNames(selector)
|
|
309
|
+
}));
|
|
310
|
+
recordNativeClassNames(parsed, selectorEntries.flatMap((entry)=>entry.classNames));
|
|
311
|
+
if (!classFilter) return;
|
|
312
|
+
const selectorFilters = selectorEntries.map(({ classNames })=>!classNames.length || classNames.some((className)=>classFilter.has(className)));
|
|
313
|
+
if (selectorFilters.every(Boolean)) return;
|
|
314
|
+
return selectorFilters;
|
|
315
|
+
}
|
|
316
|
+
function removeEmptyRuleBlock(rule) {
|
|
317
|
+
return rule.value?.rules?.length ? undefined : [];
|
|
318
|
+
}
|
|
319
|
+
function pruneEmptyRuleBlocks(code, filename) {
|
|
320
|
+
return getCSSTransform()({
|
|
321
|
+
filename,
|
|
322
|
+
code,
|
|
323
|
+
visitor: {
|
|
324
|
+
Rule: {
|
|
325
|
+
media: removeEmptyRuleBlock,
|
|
326
|
+
supports: removeEmptyRuleBlock,
|
|
327
|
+
container: removeEmptyRuleBlock,
|
|
328
|
+
'starting-style': removeEmptyRuleBlock,
|
|
329
|
+
'layer-block': removeEmptyRuleBlock
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}).code;
|
|
333
|
+
}
|
|
334
|
+
function filterNativeCSS(code, filename, parsed, classFilter) {
|
|
335
|
+
const selectorFilters = [];
|
|
336
|
+
return getCSSTransform()({
|
|
337
|
+
filename,
|
|
338
|
+
code,
|
|
339
|
+
visitor: {
|
|
340
|
+
Rule: {
|
|
341
|
+
style (rule) {
|
|
342
|
+
const filters = getNativeStyleSelectorFilters(rule, parsed, classFilter);
|
|
343
|
+
if (!filters) return;
|
|
344
|
+
if (!filters.some(Boolean)) return [];
|
|
345
|
+
selectorFilters.push(...filters);
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
Selector () {
|
|
349
|
+
if (!selectorFilters.length) return;
|
|
350
|
+
if (!selectorFilters.shift()) return [];
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}).code;
|
|
354
|
+
}
|
|
355
|
+
function formatDeclarationValue(declaration) {
|
|
356
|
+
if (declaration.property === 'unparsed') {
|
|
357
|
+
return formatTokenOrValues(declaration.value.value);
|
|
358
|
+
}
|
|
359
|
+
const formatted = formatDeclarationValueOverride(declaration);
|
|
360
|
+
if (formatted !== undefined) return formatted;
|
|
361
|
+
let declarationText;
|
|
362
|
+
try {
|
|
363
|
+
declarationText = formatDeclaration(declaration);
|
|
364
|
+
} catch (error) {
|
|
365
|
+
const fallback = formatDeclarationValueFallback(declaration);
|
|
366
|
+
if (fallback !== undefined) return fallback;
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
const colonIndex = declarationText.indexOf(':');
|
|
370
|
+
return declarationText.slice(colonIndex + 1).trim();
|
|
371
|
+
}
|
|
372
|
+
function formatNumber(value) {
|
|
373
|
+
return String(value).replace(/^(-?)0\./, '$1.');
|
|
374
|
+
}
|
|
375
|
+
function formatDeclarationValueOverride(declaration) {
|
|
376
|
+
if (declaration.property !== 'aspect-ratio') return;
|
|
377
|
+
const value = declaration.value;
|
|
378
|
+
if (!Array.isArray(value.ratio)) return;
|
|
379
|
+
const ratio = `${formatNumber(value.ratio[0])}/${formatNumber(value.ratio[1])}`;
|
|
380
|
+
return value.auto ? `auto ${ratio}` : ratio;
|
|
381
|
+
}
|
|
382
|
+
function formatDimensionNumber(value) {
|
|
383
|
+
if (Number.isInteger(value) && Math.abs(value) >= 1_000_000_000) {
|
|
384
|
+
return value.toExponential().replace('e+', 'e');
|
|
385
|
+
}
|
|
386
|
+
return formatNumber(value);
|
|
387
|
+
}
|
|
388
|
+
function formatDimensionValue(value) {
|
|
389
|
+
if (value?.type !== 'dimension') return;
|
|
390
|
+
return `${formatDimensionNumber(value.value.value)}${value.value.unit}`;
|
|
391
|
+
}
|
|
392
|
+
function formatBorderRadiusCorner(value) {
|
|
393
|
+
if (!Array.isArray(value) || value.length !== 2) return;
|
|
394
|
+
const horizontal = formatDimensionValue(value[0]);
|
|
395
|
+
const vertical = formatDimensionValue(value[1]);
|
|
396
|
+
if (!horizontal || !vertical) return;
|
|
397
|
+
return horizontal === vertical ? horizontal : `${horizontal} ${vertical}`;
|
|
398
|
+
}
|
|
399
|
+
function formatDeclarationValueFallback(declaration) {
|
|
400
|
+
if (declaration.property === 'custom') {
|
|
401
|
+
const value = declaration.value;
|
|
402
|
+
if (Array.isArray(value.value)) return formatTokenOrValues(value.value);
|
|
403
|
+
}
|
|
404
|
+
if (declaration.property !== 'border-radius') return;
|
|
405
|
+
const value = declaration.value;
|
|
406
|
+
const corners = [
|
|
407
|
+
value.topLeft,
|
|
408
|
+
value.topRight,
|
|
409
|
+
value.bottomRight,
|
|
410
|
+
value.bottomLeft
|
|
411
|
+
].map(formatBorderRadiusCorner);
|
|
412
|
+
const firstCorner = corners[0];
|
|
413
|
+
if (!firstCorner || corners.some((corner)=>corner !== firstCorner)) return;
|
|
414
|
+
return firstCorner;
|
|
415
|
+
}
|
|
416
|
+
function formatPropertyId(propertyId) {
|
|
417
|
+
return propertyId.property || '';
|
|
418
|
+
}
|
|
419
|
+
function formatVariableName(name) {
|
|
420
|
+
return name.ident;
|
|
421
|
+
}
|
|
422
|
+
function formatVariable(value) {
|
|
423
|
+
const name = formatVariableName(value.name);
|
|
424
|
+
const fallback = value.fallback && formatTokenOrValues(value.fallback);
|
|
425
|
+
return fallback ? `var(${name}, ${fallback})` : `var(${name})`;
|
|
426
|
+
}
|
|
427
|
+
function formatEnvironmentVariable(value) {
|
|
428
|
+
const name = value.name.ident || value.name.value || '';
|
|
429
|
+
const fallback = value.fallback && formatTokenOrValues(value.fallback);
|
|
430
|
+
return fallback ? `env(${name}, ${fallback})` : `env(${name})`;
|
|
431
|
+
}
|
|
432
|
+
function formatToken(token) {
|
|
433
|
+
switch(token.type){
|
|
434
|
+
case 'ident':
|
|
435
|
+
return token.value;
|
|
436
|
+
case 'at-keyword':
|
|
437
|
+
return '@' + token.value;
|
|
438
|
+
case 'hash':
|
|
439
|
+
case 'id-hash':
|
|
440
|
+
return '#' + token.value;
|
|
441
|
+
case 'string':
|
|
442
|
+
return JSON.stringify(token.value);
|
|
443
|
+
case 'unquoted-url':
|
|
444
|
+
return `url(${token.value})`;
|
|
445
|
+
case 'number':
|
|
446
|
+
return String(token.value);
|
|
447
|
+
case 'percentage':
|
|
448
|
+
return `${token.value * 100}%`;
|
|
449
|
+
case 'dimension':
|
|
450
|
+
return `${token.value}${token.unit}`;
|
|
451
|
+
case 'white-space':
|
|
452
|
+
return token.value;
|
|
453
|
+
case 'comment':
|
|
454
|
+
return `/*${token.value}*/`;
|
|
455
|
+
case 'colon':
|
|
456
|
+
return ':';
|
|
457
|
+
case 'semicolon':
|
|
458
|
+
return ';';
|
|
459
|
+
case 'comma':
|
|
460
|
+
return ',';
|
|
461
|
+
case 'delim':
|
|
462
|
+
return token.value;
|
|
463
|
+
case 'function':
|
|
464
|
+
return `${token.value}(`;
|
|
465
|
+
case 'parenthesis-block':
|
|
466
|
+
return '(';
|
|
467
|
+
case 'square-bracket-block':
|
|
468
|
+
return '[';
|
|
469
|
+
case 'curly-bracket-block':
|
|
470
|
+
return '{';
|
|
471
|
+
case 'close-parenthesis':
|
|
472
|
+
return ')';
|
|
473
|
+
case 'close-square-bracket':
|
|
474
|
+
return ']';
|
|
475
|
+
case 'close-curly-bracket':
|
|
476
|
+
return '}';
|
|
477
|
+
default:
|
|
478
|
+
return '';
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function formatTokenOrValue(value) {
|
|
482
|
+
if (value.type === 'token') return formatToken(value.value);
|
|
483
|
+
if (value.type === 'dashed-ident') return value.value;
|
|
484
|
+
if (value.type === 'length') return `${formatNumber(value.value.value)}${value.value.unit}`;
|
|
485
|
+
if (value.type === 'angle') return `${formatNumber(value.value.value)}${value.value.type}`;
|
|
486
|
+
if (value.type === 'time') return value.value.type === 'seconds' ? `${formatNumber(value.value.value)}s` : `${formatNumber(value.value.value)}ms`;
|
|
487
|
+
if (value.type === 'resolution') return `${formatNumber(value.value.value)}${value.value.type}`;
|
|
488
|
+
if (value.type === 'var') return formatVariable(value.value);
|
|
489
|
+
if (value.type === 'env') return formatEnvironmentVariable(value.value);
|
|
490
|
+
if (value.type === 'function') return `${value.value.name}(${formatTokenOrValues(value.value.arguments)})`;
|
|
491
|
+
if (value.type === 'url') return `url(${JSON.stringify(value.value.url)})`;
|
|
492
|
+
if (value.type === 'animation-name') {
|
|
493
|
+
if (value.value.type === 'none') return 'none';
|
|
494
|
+
if (value.value.type === 'string') return JSON.stringify(value.value.value);
|
|
495
|
+
return value.value.value;
|
|
496
|
+
}
|
|
497
|
+
const declaration = {
|
|
498
|
+
property: 'custom',
|
|
499
|
+
value: {
|
|
500
|
+
name: '--x',
|
|
501
|
+
value: [
|
|
502
|
+
value
|
|
503
|
+
]
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
return formatDeclarationValue(declaration);
|
|
507
|
+
}
|
|
508
|
+
function formatTokenOrValues(values) {
|
|
509
|
+
return values.map(formatTokenOrValue).join('').trim();
|
|
510
|
+
}
|
|
511
|
+
function formatPrelude(prelude) {
|
|
512
|
+
if (!prelude) return '';
|
|
513
|
+
if (typeof prelude === 'string') return prelude.trim();
|
|
514
|
+
if (Array.isArray(prelude)) return formatTokenOrValues(prelude);
|
|
515
|
+
if (prelude.type === 'token-list') return formatTokenOrValues(prelude.value);
|
|
516
|
+
if (Array.isArray(prelude.value)) return formatTokenOrValues(prelude.value);
|
|
517
|
+
if (typeof prelude.value === 'string') return prelude.value.trim();
|
|
518
|
+
return '';
|
|
519
|
+
}
|
|
520
|
+
const COMPARISON_OPERATORS = {
|
|
521
|
+
'equal': '=',
|
|
522
|
+
'greater-than': '>',
|
|
523
|
+
'greater-than-equal': '>=',
|
|
524
|
+
'less-than': '<',
|
|
525
|
+
'less-than-equal': '<='
|
|
526
|
+
};
|
|
527
|
+
function formatFeatureValue(value) {
|
|
528
|
+
if (value === undefined || value === null) return '';
|
|
529
|
+
if (typeof value === 'string' || typeof value === 'number') return String(value);
|
|
530
|
+
switch(value.type){
|
|
531
|
+
case 'ident':
|
|
532
|
+
return value.value;
|
|
533
|
+
case 'number':
|
|
534
|
+
return String(value.value);
|
|
535
|
+
case 'length':
|
|
536
|
+
{
|
|
537
|
+
let resolvedValue = value.value;
|
|
538
|
+
while(resolvedValue && typeof resolvedValue === 'object' && 'value' in resolvedValue && !('unit' in resolvedValue)){
|
|
539
|
+
resolvedValue = resolvedValue.value;
|
|
540
|
+
}
|
|
541
|
+
if (typeof resolvedValue === 'number') return formatNumber(resolvedValue);
|
|
542
|
+
return `${formatNumber(resolvedValue.value)}${resolvedValue.unit || ''}`;
|
|
543
|
+
}
|
|
544
|
+
case 'ratio':
|
|
545
|
+
return `${value.value.numerator}/${value.value.denominator}`;
|
|
546
|
+
default:
|
|
547
|
+
return formatPrelude(value) || String(value.value ?? '');
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function formatCondition(condition) {
|
|
551
|
+
if (!condition) return '';
|
|
552
|
+
switch(condition.type){
|
|
553
|
+
case 'feature':
|
|
554
|
+
{
|
|
555
|
+
const feature = condition.value;
|
|
556
|
+
switch(feature.type){
|
|
557
|
+
case 'plain':
|
|
558
|
+
return `(${feature.name}:${formatFeatureValue(feature.value)})`;
|
|
559
|
+
case 'range':
|
|
560
|
+
return `(${feature.name}${COMPARISON_OPERATORS[feature.operator] || feature.operator}${formatFeatureValue(feature.value)})`;
|
|
561
|
+
case 'boolean':
|
|
562
|
+
return `(${feature.name})`;
|
|
563
|
+
case 'interval':
|
|
564
|
+
return `(${formatFeatureValue(feature.start)}${COMPARISON_OPERATORS[feature.startOperator] || feature.startOperator}${feature.name}${COMPARISON_OPERATORS[feature.endOperator] || feature.endOperator}${formatFeatureValue(feature.end)})`;
|
|
565
|
+
default:
|
|
566
|
+
return '';
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
case 'operation':
|
|
570
|
+
return condition.conditions.map(formatCondition).filter(Boolean).join(` ${condition.operator} `);
|
|
571
|
+
case 'not':
|
|
572
|
+
return `not ${formatCondition(condition.value)}`;
|
|
573
|
+
default:
|
|
574
|
+
return '';
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function formatMediaQuery(query) {
|
|
578
|
+
return query.mediaQueries.map((mediaQuery)=>{
|
|
579
|
+
const parts = [];
|
|
580
|
+
if (mediaQuery.qualifier) parts.push(mediaQuery.qualifier);
|
|
581
|
+
if (mediaQuery.mediaType && mediaQuery.mediaType !== 'all') parts.push(mediaQuery.mediaType);
|
|
582
|
+
const condition = formatCondition(mediaQuery.condition);
|
|
583
|
+
if (condition) {
|
|
584
|
+
if (parts.length) {
|
|
585
|
+
parts.push('and', condition);
|
|
586
|
+
} else {
|
|
587
|
+
parts.push(condition);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return parts.join(' ') || 'all';
|
|
591
|
+
}).join(', ');
|
|
592
|
+
}
|
|
593
|
+
function formatSupportsCondition(condition) {
|
|
594
|
+
switch(condition.type){
|
|
595
|
+
case 'declaration':
|
|
596
|
+
return `(${formatPropertyId(condition.propertyId)}:${formatFeatureValue(condition.value)})`;
|
|
597
|
+
case 'selector':
|
|
598
|
+
return `selector(${condition.value})`;
|
|
599
|
+
case 'not':
|
|
600
|
+
return `not ${formatSupportsCondition(condition.value)}`;
|
|
601
|
+
case 'and':
|
|
602
|
+
case 'or':
|
|
603
|
+
return condition.value.map(formatSupportsCondition).join(` ${condition.type} `);
|
|
604
|
+
default:
|
|
605
|
+
return '';
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
function formatNestedAtRule(rule) {
|
|
609
|
+
switch(rule.type){
|
|
610
|
+
case 'media':
|
|
611
|
+
return `@media ${formatMediaQuery(rule.value.query)}`;
|
|
612
|
+
case 'supports':
|
|
613
|
+
return `@supports ${formatSupportsCondition(rule.value.condition)}`;
|
|
614
|
+
case 'container':
|
|
615
|
+
{
|
|
616
|
+
const condition = formatCondition(rule.value.condition);
|
|
617
|
+
return `@container ${[
|
|
618
|
+
rule.value.name,
|
|
619
|
+
condition
|
|
620
|
+
].filter(Boolean).join(' ')}`;
|
|
621
|
+
}
|
|
622
|
+
case 'starting-style':
|
|
623
|
+
return '@starting-style';
|
|
624
|
+
case 'layer-block':
|
|
625
|
+
return `@layer ${(rule.value.name || []).join('.')}`;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function getNestedAtRuleChildren(rule) {
|
|
629
|
+
if (rule.type === 'media' || rule.type === 'supports' || rule.type === 'container' || rule.type === 'starting-style' || rule.type === 'layer-block') {
|
|
630
|
+
return rule.value.rules;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function getParsedStyleDefinitions(parsed) {
|
|
634
|
+
parsed.styleDefinitions ??= [];
|
|
635
|
+
return parsed.styleDefinitions;
|
|
636
|
+
}
|
|
637
|
+
function nextStyleOrder(parsed) {
|
|
638
|
+
parsed.styleOrder = (parsed.styleOrder || 0) + 1;
|
|
639
|
+
return parsed.styleOrder;
|
|
640
|
+
}
|
|
641
|
+
function getDeclarationName(declaration) {
|
|
642
|
+
if (declaration.property === 'custom') return declaration.value.name;
|
|
643
|
+
if (declaration.property === 'unparsed') return formatPropertyId(declaration.value.propertyId);
|
|
644
|
+
return declaration.property;
|
|
645
|
+
}
|
|
646
|
+
function collectDeclarations(block) {
|
|
647
|
+
const declarations = {};
|
|
648
|
+
for (const declaration of block.declarations || []){
|
|
649
|
+
declarations[getDeclarationName(declaration)] = formatDeclarationValue(declaration);
|
|
650
|
+
}
|
|
651
|
+
for (const declaration of block.importantDeclarations || []){
|
|
652
|
+
declarations[getDeclarationName(declaration)] = `${formatDeclarationValue(declaration)} !important`;
|
|
653
|
+
}
|
|
654
|
+
return declarations;
|
|
655
|
+
}
|
|
656
|
+
const WEBKIT_PAIRED_DECLARATIONS = {
|
|
657
|
+
'-webkit-backdrop-filter': 'backdrop-filter',
|
|
658
|
+
'-webkit-box-decoration-break': 'box-decoration-break',
|
|
659
|
+
'-webkit-mask-image': 'mask-image',
|
|
660
|
+
'-webkit-text-decoration': 'text-decoration',
|
|
661
|
+
'-webkit-user-select': 'user-select'
|
|
662
|
+
};
|
|
663
|
+
const WEBKIT_ONLY_DECLARATIONS = {
|
|
664
|
+
'-webkit-box-orient': 'box-orient'
|
|
665
|
+
};
|
|
666
|
+
function escapeRegExp(value) {
|
|
667
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
668
|
+
}
|
|
669
|
+
function rawBlockContainsDeclaration(rawBlock, property) {
|
|
670
|
+
return new RegExp(`(?:^|[;{])\\s*${escapeRegExp(property)}\\s*:`).test(rawBlock);
|
|
671
|
+
}
|
|
672
|
+
function restoreRawWebkitPairedDeclarations(rule, parsed, declarations) {
|
|
673
|
+
const source = parsed.source;
|
|
674
|
+
const loc = rule.value?.loc;
|
|
675
|
+
const resolveLocation = parsed.sourceLocationResolver;
|
|
676
|
+
if (!source || !loc || !resolveLocation) return declarations;
|
|
677
|
+
const start = resolveLocation({
|
|
678
|
+
line: loc.line,
|
|
679
|
+
column: loc.column
|
|
680
|
+
});
|
|
681
|
+
if (start === -1) return declarations;
|
|
682
|
+
const statementEnd = findCSSStatementEnd(source, start);
|
|
683
|
+
if (statementEnd.reason !== 'block' || !statementEnd.delimiterRange) return declarations;
|
|
684
|
+
const blockEnd = findCSSBlockEnd(source, statementEnd.delimiterRange.start);
|
|
685
|
+
if (blockEnd === -1) return declarations;
|
|
686
|
+
const rawBlock = source.slice(statementEnd.delimiterRange.start + 1, blockEnd);
|
|
687
|
+
const restoredBefore = new Map();
|
|
688
|
+
const skippedProperties = new Set();
|
|
689
|
+
for(const prefixedProperty in WEBKIT_ONLY_DECLARATIONS){
|
|
690
|
+
if (declarations[prefixedProperty] !== undefined) continue;
|
|
691
|
+
const property = WEBKIT_ONLY_DECLARATIONS[prefixedProperty];
|
|
692
|
+
const value = declarations[property];
|
|
693
|
+
if (value === undefined) continue;
|
|
694
|
+
if (!rawBlockContainsDeclaration(rawBlock, prefixedProperty)) continue;
|
|
695
|
+
if (rawBlockContainsDeclaration(rawBlock, property)) continue;
|
|
696
|
+
restoredBefore.set(property, [
|
|
697
|
+
[
|
|
698
|
+
prefixedProperty,
|
|
699
|
+
value
|
|
700
|
+
]
|
|
701
|
+
]);
|
|
702
|
+
skippedProperties.add(property);
|
|
703
|
+
}
|
|
704
|
+
for(const prefixedProperty in WEBKIT_PAIRED_DECLARATIONS){
|
|
705
|
+
if (declarations[prefixedProperty] !== undefined) continue;
|
|
706
|
+
const property = WEBKIT_PAIRED_DECLARATIONS[prefixedProperty];
|
|
707
|
+
const value = declarations[property];
|
|
708
|
+
if (value === undefined) continue;
|
|
709
|
+
if (!rawBlockContainsDeclaration(rawBlock, prefixedProperty)) continue;
|
|
710
|
+
const restored = restoredBefore.get(property) || [];
|
|
711
|
+
restored.push([
|
|
712
|
+
prefixedProperty,
|
|
713
|
+
value
|
|
714
|
+
]);
|
|
715
|
+
restoredBefore.set(property, restored);
|
|
716
|
+
}
|
|
717
|
+
if (!restoredBefore.size) return declarations;
|
|
718
|
+
const restoredDeclarations = {};
|
|
719
|
+
for(const property in declarations){
|
|
720
|
+
for (const [prefixedProperty, value] of restoredBefore.get(property) || []){
|
|
721
|
+
restoredDeclarations[prefixedProperty] = value;
|
|
722
|
+
}
|
|
723
|
+
if (skippedProperties.has(property)) continue;
|
|
724
|
+
restoredDeclarations[property] = declarations[property];
|
|
725
|
+
}
|
|
726
|
+
return restoredDeclarations;
|
|
727
|
+
}
|
|
728
|
+
function parseSettingsDeclarations(block, manifestInput) {
|
|
729
|
+
for (const declaration of block.declarations || []){
|
|
730
|
+
const property = getDeclarationName(declaration);
|
|
731
|
+
const value = formatDeclarationValue(declaration);
|
|
732
|
+
if (!parseMasterOption(manifestInput, property, value)) {
|
|
733
|
+
throw new Error(`Unsupported @settings option: ${property}`);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
for (const declaration of block.importantDeclarations || []){
|
|
737
|
+
const property = getDeclarationName(declaration);
|
|
738
|
+
throw new Error(`@settings does not accept !important declarations: ${property}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function parseThemeDeclarations(block, manifestInput, mode, inline, isStatic) {
|
|
742
|
+
for (const declaration of block.declarations || []){
|
|
743
|
+
const property = getDeclarationName(declaration);
|
|
744
|
+
const value = formatDeclarationValue(declaration);
|
|
745
|
+
defineThemeVariable(manifestInput, property, value, mode, inline, isStatic);
|
|
746
|
+
}
|
|
747
|
+
for (const declaration of block.importantDeclarations || []){
|
|
748
|
+
const property = normalizeThemeTokenName(getDeclarationName(declaration));
|
|
749
|
+
throw new Error(`@theme token declarations cannot be !important: ${property}`);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function parseCustomVariantPrelude(prelude) {
|
|
753
|
+
const trimmed = prelude.trim();
|
|
754
|
+
const tokenMatch = /^(:{1,2}[-_a-zA-Z][-_a-zA-Z0-9]*|@[-_a-zA-Z][-_a-zA-Z0-9]*)$/.exec(trimmed);
|
|
755
|
+
if (!tokenMatch) return;
|
|
756
|
+
return {
|
|
757
|
+
token: tokenMatch[1]
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
function defineVariant(manifestInput, variant) {
|
|
761
|
+
manifestInput.variants ??= [];
|
|
762
|
+
const foundIndex = manifestInput.variants.findIndex((existing)=>existing.token === variant.token);
|
|
763
|
+
if (foundIndex !== -1) manifestInput.variants.splice(foundIndex, 1);
|
|
764
|
+
manifestInput.variants.push(variant);
|
|
765
|
+
}
|
|
766
|
+
function isSlotRule(rule) {
|
|
767
|
+
return (rule.type === 'unknown' || rule.type === 'custom') && rule.value?.name === 'slot';
|
|
768
|
+
}
|
|
769
|
+
function assertNotSlotRule(rule) {
|
|
770
|
+
if (isSlotRule(rule)) {
|
|
771
|
+
throw new Error('@slot can only be used inside @custom-variant');
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function assertNoVariantTemplateDeclarations(rule, token) {
|
|
775
|
+
const declarations = collectDeclarations(rule.value.declarations);
|
|
776
|
+
if (Object.keys(declarations).length) {
|
|
777
|
+
throw new Error(`@custom-variant ${token} does not accept declarations`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
function createVariantTemplateBranch(path) {
|
|
781
|
+
const selector = path.selectors.reduce((current, selectorTemplate)=>selectorTemplate.replace(/&/g, current), '&');
|
|
782
|
+
return {
|
|
783
|
+
...selector !== '&' ? {
|
|
784
|
+
selector
|
|
785
|
+
} : {},
|
|
786
|
+
...path.atRules.length ? {
|
|
787
|
+
atRules: [
|
|
788
|
+
...path.atRules
|
|
789
|
+
]
|
|
790
|
+
} : {},
|
|
791
|
+
...path.layer ? {
|
|
792
|
+
layer: path.layer
|
|
793
|
+
} : {}
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
function collectVariantTemplateBranches(rules, token, path = {
|
|
797
|
+
selectors: [],
|
|
798
|
+
atRules: []
|
|
799
|
+
}) {
|
|
800
|
+
const branches = [];
|
|
801
|
+
for (const child of rules){
|
|
802
|
+
if (isSlotRule(child)) {
|
|
803
|
+
branches.push(createVariantTemplateBranch(path));
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
if (isCustomVariantDefinition(child)) {
|
|
807
|
+
throw new Error('@custom-variant cannot be nested inside @custom-variant');
|
|
808
|
+
}
|
|
809
|
+
if (parseMasterVariantBlock(child)) {
|
|
810
|
+
throw new Error('@variant cannot be used inside @custom-variant');
|
|
811
|
+
}
|
|
812
|
+
if (child.type === 'keyframes') {
|
|
813
|
+
throw new Error('@keyframes cannot be used inside @custom-variant');
|
|
814
|
+
}
|
|
815
|
+
if (child.type === 'style') {
|
|
816
|
+
const selectors = child.value.selectors.map((selector)=>formatSelectors([
|
|
817
|
+
selector
|
|
818
|
+
]));
|
|
819
|
+
for (const selector of selectors){
|
|
820
|
+
if (!selector.includes('&')) {
|
|
821
|
+
throw new Error(`@custom-variant ${token} selector value must include "&"`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
assertNoVariantTemplateDeclarations(child, token);
|
|
825
|
+
for (const selector of selectors){
|
|
826
|
+
branches.push(...collectVariantTemplateBranches(child.value.rules, token, {
|
|
827
|
+
...path,
|
|
828
|
+
selectors: [
|
|
829
|
+
...path.selectors,
|
|
830
|
+
selector
|
|
831
|
+
]
|
|
832
|
+
}));
|
|
833
|
+
}
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (child.type === 'layer-block') {
|
|
837
|
+
const layer = formatNestedAtRule(child)?.replace(/^@layer\s+/, '').trim();
|
|
838
|
+
if (layer !== 'base' && layer !== 'defaults' && layer !== 'components' && layer !== 'utilities') {
|
|
839
|
+
throw new Error(`@custom-variant ${token} only accepts Master CSS layers`);
|
|
840
|
+
}
|
|
841
|
+
if (path.layer && path.layer !== layer) {
|
|
842
|
+
throw new Error(`@custom-variant ${token} cannot assign multiple layers`);
|
|
843
|
+
}
|
|
844
|
+
branches.push(...collectVariantTemplateBranches(child.value.rules, token, {
|
|
845
|
+
...path,
|
|
846
|
+
layer
|
|
847
|
+
}));
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
const nestedAtRuleChildren = getNestedAtRuleChildren(child);
|
|
851
|
+
if (nestedAtRuleChildren) {
|
|
852
|
+
const atRule = formatNestedAtRule(child);
|
|
853
|
+
if (!atRule) {
|
|
854
|
+
throw new Error(`Unsupported nested at-rule in @custom-variant ${token}`);
|
|
855
|
+
}
|
|
856
|
+
branches.push(...collectVariantTemplateBranches(nestedAtRuleChildren, token, {
|
|
857
|
+
...path,
|
|
858
|
+
atRules: [
|
|
859
|
+
...path.atRules,
|
|
860
|
+
atRule
|
|
861
|
+
]
|
|
862
|
+
}));
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
if (child.type === 'nested-declarations') {
|
|
866
|
+
const declarations = collectDeclarations(child.value.declarations);
|
|
867
|
+
if (Object.keys(declarations).length) {
|
|
868
|
+
throw new Error(`@custom-variant ${token} does not accept declarations`);
|
|
869
|
+
}
|
|
870
|
+
continue;
|
|
871
|
+
}
|
|
872
|
+
throw new Error(`Unsupported rule inside @custom-variant ${token}`);
|
|
873
|
+
}
|
|
874
|
+
return branches;
|
|
875
|
+
}
|
|
876
|
+
function parseVariantDefinition(rule, parsed) {
|
|
877
|
+
const prelude = parseCustomVariantPrelude(formatPrelude(rule.value.prelude));
|
|
878
|
+
if (!prelude) {
|
|
879
|
+
throw new Error('@custom-variant requires a full variant token');
|
|
880
|
+
}
|
|
881
|
+
const { token } = prelude;
|
|
882
|
+
const bodyRules = rule.value.body?.value;
|
|
883
|
+
if (bodyRules?.length) {
|
|
884
|
+
const branches = collectVariantTemplateBranches(bodyRules, token);
|
|
885
|
+
if (!branches.length) {
|
|
886
|
+
throw new Error(`@custom-variant ${token} requires @slot`);
|
|
887
|
+
}
|
|
888
|
+
defineVariant(parsed.manifestInput, {
|
|
889
|
+
token,
|
|
890
|
+
branches
|
|
891
|
+
});
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
throw new Error(`@custom-variant ${token} requires a block body`);
|
|
895
|
+
}
|
|
896
|
+
function isCustomVariantDefinition(rule) {
|
|
897
|
+
return (rule.type === 'unknown' || rule.type === 'custom') && rule.value?.name === 'custom-variant';
|
|
898
|
+
}
|
|
899
|
+
function getManagedDefinitionDirectiveLayer(rule) {
|
|
900
|
+
if (rule.type !== 'custom' || !rule.value) return;
|
|
901
|
+
return MANAGED_DEFINITION_DIRECTIVE_LAYERS[rule.value.name];
|
|
902
|
+
}
|
|
903
|
+
function parseManagedStyleDefinitionSelector(selectors) {
|
|
904
|
+
const names = selectors.map((selector)=>selector[0]?.type === 'class' ? selector[0].name : undefined);
|
|
905
|
+
const name = names[0];
|
|
906
|
+
if (!name || names.some((eachName)=>eachName !== name)) return;
|
|
907
|
+
const selectorTexts = selectors.map((selector)=>{
|
|
908
|
+
const selectorText = formatSelectors([
|
|
909
|
+
selector
|
|
910
|
+
]);
|
|
911
|
+
const classPattern = new RegExp(`^\\.${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?=$|[^a-zA-Z0-9_-])`);
|
|
912
|
+
return selectorText.replace(classPattern, '&');
|
|
913
|
+
});
|
|
914
|
+
return {
|
|
915
|
+
name,
|
|
916
|
+
selectors: selectorTexts,
|
|
917
|
+
selector: selectorTexts.join(',')
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
function parseManagedDefinitionNameSelector(selectors) {
|
|
921
|
+
if (selectors.length !== 1) return;
|
|
922
|
+
const selector = selectors[0];
|
|
923
|
+
if (selector.length !== 1) return;
|
|
924
|
+
const node = selector[0];
|
|
925
|
+
if (node?.type !== 'type' || node.namespace) return;
|
|
926
|
+
return {
|
|
927
|
+
name: node.name,
|
|
928
|
+
selectors: [
|
|
929
|
+
'&'
|
|
930
|
+
],
|
|
931
|
+
selector: '&'
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
function takeComposeRange(parsed) {
|
|
935
|
+
const ranges = parsed.composeRanges || [];
|
|
936
|
+
const index = parsed.composeRangeIndex || 0;
|
|
937
|
+
parsed.composeRangeIndex = index + 1;
|
|
938
|
+
return ranges[index];
|
|
939
|
+
}
|
|
940
|
+
function trimSourceRange(source, range) {
|
|
941
|
+
let start = range.start;
|
|
942
|
+
let end = range.end;
|
|
943
|
+
while(start < end && /\s/.test(source[start] || ''))start++;
|
|
944
|
+
while(end > start && /\s/.test(source[end - 1] || ''))end--;
|
|
945
|
+
return {
|
|
946
|
+
start,
|
|
947
|
+
end
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
function createComposeQuotedSyntaxError(parsed, range) {
|
|
951
|
+
return new CSSDirectiveError('compose-quoted-syntax', '@compose only accepts unquoted class lists', createSourceReference(parsed, range));
|
|
952
|
+
}
|
|
953
|
+
function createComposeGroupSyntaxError(parsed, range) {
|
|
954
|
+
return new CSSDirectiveError('compose-group-syntax', '@compose does not accept group syntax', createSourceReference(parsed, range));
|
|
955
|
+
}
|
|
956
|
+
function validateComposeRange(parsed, range) {
|
|
957
|
+
const source = parsed.source || '';
|
|
958
|
+
if (range.blockRange) {
|
|
959
|
+
throw createComposeGroupSyntaxError(parsed, range.blockRange);
|
|
960
|
+
}
|
|
961
|
+
if (range.quotedStringRanges.length) {
|
|
962
|
+
throw createComposeQuotedSyntaxError(parsed, range.quotedStringRanges[0]);
|
|
963
|
+
}
|
|
964
|
+
const contentRange = trimSourceRange(source, range.preludeRange);
|
|
965
|
+
const classList = source.slice(contentRange.start, contentRange.end);
|
|
966
|
+
for (const token of collectMasterCSSClassListTokenRanges(classList)){
|
|
967
|
+
if (token.token.startsWith('{')) {
|
|
968
|
+
throw createComposeGroupSyntaxError(parsed, {
|
|
969
|
+
start: contentRange.start + token.start,
|
|
970
|
+
end: contentRange.start + token.end
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
function validateComposeRanges(parsed) {
|
|
976
|
+
for (const range of parsed.composeRanges || []){
|
|
977
|
+
validateComposeRange(parsed, range);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
function parseComposeRule(rule, parsed) {
|
|
981
|
+
if (rule.type === 'custom' && rule.value.name === 'compose') {
|
|
982
|
+
const range = takeComposeRange(parsed);
|
|
983
|
+
const contentRange = range && parsed.source ? trimSourceRange(parsed.source, range.preludeRange) : undefined;
|
|
984
|
+
const classList = contentRange && parsed.source ? parsed.source.slice(contentRange.start, contentRange.end) : formatPrelude(rule.value.prelude);
|
|
985
|
+
const classTokens = collectMasterCSSClassListTokenRanges(classList);
|
|
986
|
+
return {
|
|
987
|
+
classNames: classTokens.map((token)=>({
|
|
988
|
+
className: token.token,
|
|
989
|
+
...contentRange ? {
|
|
990
|
+
source: createSourceReference(parsed, {
|
|
991
|
+
start: contentRange.start + token.start,
|
|
992
|
+
end: contentRange.start + token.end
|
|
993
|
+
})
|
|
994
|
+
} : {}
|
|
995
|
+
})),
|
|
996
|
+
...range ? {
|
|
997
|
+
directiveSource: createSourceReference(parsed, range)
|
|
998
|
+
} : {}
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
function createComposePlacementError(parsed) {
|
|
1003
|
+
const range = takeComposeRange(parsed);
|
|
1004
|
+
return new CSSDirectiveError('compose-placement', '@compose requires a style rule', range ? createSourceReference(parsed, range) : undefined);
|
|
1005
|
+
}
|
|
1006
|
+
function getMasterVariantShorthandToken(name) {
|
|
1007
|
+
return name && MASTER_VARIANT_SHORTHAND_TOKENS[name];
|
|
1008
|
+
}
|
|
1009
|
+
function parseMasterVariantBlock(rule) {
|
|
1010
|
+
if (rule.type !== 'custom' && rule.type !== 'unknown') return;
|
|
1011
|
+
const shorthandToken = getMasterVariantShorthandToken(rule.value?.name);
|
|
1012
|
+
if (!shorthandToken && rule.value?.name !== 'variant') return;
|
|
1013
|
+
const prelude = formatPrelude(rule.value.prelude);
|
|
1014
|
+
if (shorthandToken && prelude) {
|
|
1015
|
+
throw new Error(`@${rule.value.name} does not accept a prelude`);
|
|
1016
|
+
}
|
|
1017
|
+
const token = shorthandToken || prelude;
|
|
1018
|
+
if (!token) {
|
|
1019
|
+
throw new Error('@variant requires a Master CSS variant');
|
|
1020
|
+
}
|
|
1021
|
+
if (!/^(?::{1,2}[^\s:]\S*|@\S+)$/.test(token)) {
|
|
1022
|
+
throw new Error('@variant requires a full variant token');
|
|
1023
|
+
}
|
|
1024
|
+
const rules = rule.value.body?.value;
|
|
1025
|
+
if (!Array.isArray(rules)) {
|
|
1026
|
+
throw new Error('@variant requires a style block');
|
|
1027
|
+
}
|
|
1028
|
+
return {
|
|
1029
|
+
token,
|
|
1030
|
+
rules: rules
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
function isNestedStyleRule(rule) {
|
|
1034
|
+
return rule.type === 'style' || rule.type === 'media' || rule.type === 'supports' || rule.type === 'container' || rule.type === 'starting-style' || rule.type === 'layer-block' || Boolean(parseMasterVariantBlock(rule));
|
|
1035
|
+
}
|
|
1036
|
+
function collectDirectiveStyleRule(rule, parsed) {
|
|
1037
|
+
return collectDirectiveStyleRuleBody(rule.value.declarations, rule.value.rules, parsed, (declarations)=>restoreRawWebkitPairedDeclarations(rule, parsed, declarations));
|
|
1038
|
+
}
|
|
1039
|
+
function collectDirectiveStyleRuleBody(block, rules, parsed, transformDeclarations = (declarations)=>declarations) {
|
|
1040
|
+
const items = [];
|
|
1041
|
+
const declarations = transformDeclarations(collectDeclarations(block));
|
|
1042
|
+
if (Object.keys(declarations).length) {
|
|
1043
|
+
items.push({
|
|
1044
|
+
type: 'declarations',
|
|
1045
|
+
declarations
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
for (const child of rules){
|
|
1049
|
+
assertNotSlotRule(child);
|
|
1050
|
+
if (child.type === 'nested-declarations') {
|
|
1051
|
+
const nestedDeclarations = collectDeclarations(child.value.declarations);
|
|
1052
|
+
if (Object.keys(nestedDeclarations).length) {
|
|
1053
|
+
items.push({
|
|
1054
|
+
type: 'declarations',
|
|
1055
|
+
declarations: nestedDeclarations
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
if (isNestedStyleRule(child)) {
|
|
1061
|
+
items.push({
|
|
1062
|
+
type: 'nested',
|
|
1063
|
+
rule: child
|
|
1064
|
+
});
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
const compose = parseComposeRule(child, parsed);
|
|
1068
|
+
if (compose) {
|
|
1069
|
+
items.push({
|
|
1070
|
+
type: 'compose',
|
|
1071
|
+
classNames: compose.classNames,
|
|
1072
|
+
...compose.directiveSource ? {
|
|
1073
|
+
directiveSource: compose.directiveSource
|
|
1074
|
+
} : {}
|
|
1075
|
+
});
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
throw new Error('Style definitions only accept declarations, @compose, nested selectors, and nested at-rules');
|
|
1079
|
+
}
|
|
1080
|
+
return items;
|
|
1081
|
+
}
|
|
1082
|
+
function combineStyleSelectorLists(parentSelectors, childSelectorAST) {
|
|
1083
|
+
const childSelectors = childSelectorAST.map((selector)=>formatSelectors([
|
|
1084
|
+
selector
|
|
1085
|
+
]));
|
|
1086
|
+
return combineSelectorLists(parentSelectors, childSelectors);
|
|
1087
|
+
}
|
|
1088
|
+
const EMPTY_DECLARATION_BLOCK = {
|
|
1089
|
+
declarations: [],
|
|
1090
|
+
importantDeclarations: []
|
|
1091
|
+
};
|
|
1092
|
+
function parseStyleDefinitionBody(parsed, selectorDefinition, items, atRules = [], layer, name = selectorDefinition.name) {
|
|
1093
|
+
const definitions = getParsedStyleDefinitions(parsed);
|
|
1094
|
+
for (const item of items){
|
|
1095
|
+
if (item.type === 'compose') {
|
|
1096
|
+
definitions.push(...item.classNames.map(({ className, source })=>({
|
|
1097
|
+
type: 'compose',
|
|
1098
|
+
order: nextStyleOrder(parsed),
|
|
1099
|
+
className,
|
|
1100
|
+
selector: selectorDefinition.selector,
|
|
1101
|
+
...source ? {
|
|
1102
|
+
source
|
|
1103
|
+
} : {},
|
|
1104
|
+
...item.directiveSource ? {
|
|
1105
|
+
directiveSource: item.directiveSource
|
|
1106
|
+
} : {},
|
|
1107
|
+
...selectorDefinition.source ? {
|
|
1108
|
+
selectorSource: selectorDefinition.source
|
|
1109
|
+
} : {},
|
|
1110
|
+
...name ? {
|
|
1111
|
+
name
|
|
1112
|
+
} : {},
|
|
1113
|
+
...atRules.length ? {
|
|
1114
|
+
atRules: [
|
|
1115
|
+
...atRules
|
|
1116
|
+
]
|
|
1117
|
+
} : {},
|
|
1118
|
+
...layer ? {
|
|
1119
|
+
layer
|
|
1120
|
+
} : {}
|
|
1121
|
+
})));
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
if (item.type === 'declarations') {
|
|
1125
|
+
if (!Object.keys(item.declarations).length) continue;
|
|
1126
|
+
definitions.push({
|
|
1127
|
+
type: 'native',
|
|
1128
|
+
order: nextStyleOrder(parsed),
|
|
1129
|
+
selector: selectorDefinition.selector,
|
|
1130
|
+
...selectorDefinition.source ? {
|
|
1131
|
+
selectorSource: selectorDefinition.source
|
|
1132
|
+
} : {},
|
|
1133
|
+
...name ? {
|
|
1134
|
+
name
|
|
1135
|
+
} : {},
|
|
1136
|
+
declarations: item.declarations,
|
|
1137
|
+
...atRules.length ? {
|
|
1138
|
+
atRules: [
|
|
1139
|
+
...atRules
|
|
1140
|
+
]
|
|
1141
|
+
} : {},
|
|
1142
|
+
...layer ? {
|
|
1143
|
+
layer
|
|
1144
|
+
} : {}
|
|
1145
|
+
});
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
if (name) {
|
|
1149
|
+
parseNestedManagedStyleChildRule(item.rule, parsed, selectorDefinition, atRules, layer);
|
|
1150
|
+
} else {
|
|
1151
|
+
parseNestedNativeStyleChildRule(item.rule, parsed, selectorDefinition, atRules);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
if (name && !parsed.classNames.includes(name)) {
|
|
1155
|
+
parsed.classNames.push(name);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
function parseStyleRuleBody(rules, parsed, selectorDefinition, atRules = [], layer, name = selectorDefinition.name) {
|
|
1159
|
+
parseStyleDefinitionBody(parsed, selectorDefinition, collectDirectiveStyleRuleBody(EMPTY_DECLARATION_BLOCK, rules, parsed), atRules, layer, name);
|
|
1160
|
+
}
|
|
1161
|
+
function parseNestedManagedStyleChildRule(child, parsed, parentSelectorDefinition, atRules, layer) {
|
|
1162
|
+
assertNotSlotRule(child);
|
|
1163
|
+
if (child.type === 'layer-block') {
|
|
1164
|
+
throw new Error('Nested @layer blocks are not allowed inside managed style definitions');
|
|
1165
|
+
}
|
|
1166
|
+
const masterVariantBlock = parseMasterVariantBlock(child);
|
|
1167
|
+
if (masterVariantBlock) {
|
|
1168
|
+
parseStyleRuleBody(masterVariantBlock.rules, parsed, parentSelectorDefinition, [
|
|
1169
|
+
...atRules,
|
|
1170
|
+
createCSSDirectiveVariantReference(masterVariantBlock.token)
|
|
1171
|
+
], layer);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const nestedAtRuleChildren = getNestedAtRuleChildren(child);
|
|
1175
|
+
if (nestedAtRuleChildren) {
|
|
1176
|
+
const atRule = formatNestedAtRule(child);
|
|
1177
|
+
if (!atRule) {
|
|
1178
|
+
throw new Error('Unsupported nested at-rule in @master');
|
|
1179
|
+
}
|
|
1180
|
+
parseStyleRuleBody(nestedAtRuleChildren, parsed, parentSelectorDefinition, [
|
|
1181
|
+
...atRules,
|
|
1182
|
+
atRule
|
|
1183
|
+
], layer);
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
if (child.type === 'style') {
|
|
1187
|
+
parseManagedStyleRule(child, parsed, atRules, layer, parentSelectorDefinition);
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
throw new Error('Style definitions only accept declarations, @compose, nested selectors, and nested at-rules');
|
|
1191
|
+
}
|
|
1192
|
+
function parseManagedStyleRule(rule, parsed, atRules = [], layer, parentSelectorDefinition) {
|
|
1193
|
+
const selectorDefinition = parentSelectorDefinition ? (()=>{
|
|
1194
|
+
const selectors = combineStyleSelectorLists(parentSelectorDefinition.selectors, rule.value.selectors);
|
|
1195
|
+
return {
|
|
1196
|
+
name: parentSelectorDefinition.name,
|
|
1197
|
+
selectors,
|
|
1198
|
+
selector: selectors.join(','),
|
|
1199
|
+
source: createSelectorSourceReference(parsed, rule) || parentSelectorDefinition.source
|
|
1200
|
+
};
|
|
1201
|
+
})() : parseManagedStyleDefinitionSelector(rule.value.selectors);
|
|
1202
|
+
if (!selectorDefinition) {
|
|
1203
|
+
throw new Error('Managed style definition selector must start with a single class selector');
|
|
1204
|
+
}
|
|
1205
|
+
selectorDefinition.source ||= createSelectorSourceReference(parsed, rule);
|
|
1206
|
+
parseStyleDefinitionBody(parsed, selectorDefinition, collectDirectiveStyleRule(rule, parsed), atRules, layer);
|
|
1207
|
+
}
|
|
1208
|
+
function parseNativeSelectorDefinition(selectors, parentSelectorDefinition) {
|
|
1209
|
+
const selectorTexts = parentSelectorDefinition ? combineStyleSelectorLists(parentSelectorDefinition.selectors, selectors) : selectors.map((selector)=>formatSelectors([
|
|
1210
|
+
selector
|
|
1211
|
+
]));
|
|
1212
|
+
return {
|
|
1213
|
+
selectors: selectorTexts,
|
|
1214
|
+
selector: selectorTexts.join(',')
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
function parseNativeRuleBody(rules, parsed, atRules = [], parentSelectorDefinition) {
|
|
1218
|
+
for (const child of rules){
|
|
1219
|
+
assertNotSlotRule(child);
|
|
1220
|
+
const compose = parseComposeRule(child, parsed);
|
|
1221
|
+
if (compose && parentSelectorDefinition) {
|
|
1222
|
+
parseStyleDefinitionBody(parsed, parentSelectorDefinition, [
|
|
1223
|
+
{
|
|
1224
|
+
type: 'compose',
|
|
1225
|
+
classNames: compose.classNames,
|
|
1226
|
+
...compose.directiveSource ? {
|
|
1227
|
+
directiveSource: compose.directiveSource
|
|
1228
|
+
} : {}
|
|
1229
|
+
}
|
|
1230
|
+
], atRules, undefined, undefined);
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
if (child.type === 'nested-declarations' && parentSelectorDefinition) {
|
|
1234
|
+
const declarations = collectDeclarations(child.value.declarations);
|
|
1235
|
+
if (Object.keys(declarations).length) {
|
|
1236
|
+
parseStyleDefinitionBody(parsed, parentSelectorDefinition, [
|
|
1237
|
+
{
|
|
1238
|
+
type: 'declarations',
|
|
1239
|
+
declarations
|
|
1240
|
+
}
|
|
1241
|
+
], atRules, undefined, undefined);
|
|
1242
|
+
}
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
const masterVariantBlock = parseMasterVariantBlock(child);
|
|
1246
|
+
if (masterVariantBlock) {
|
|
1247
|
+
parseNativeRuleBody(masterVariantBlock.rules, parsed, [
|
|
1248
|
+
...atRules,
|
|
1249
|
+
createCSSDirectiveVariantReference(masterVariantBlock.token)
|
|
1250
|
+
], parentSelectorDefinition);
|
|
1251
|
+
continue;
|
|
1252
|
+
}
|
|
1253
|
+
const nestedAtRuleChildren = getNestedAtRuleChildren(child);
|
|
1254
|
+
if (nestedAtRuleChildren) {
|
|
1255
|
+
const atRule = formatNestedAtRule(child);
|
|
1256
|
+
if (!atRule) {
|
|
1257
|
+
throw new Error('Unsupported nested at-rule in native CSS');
|
|
1258
|
+
}
|
|
1259
|
+
parseNativeRuleBody(nestedAtRuleChildren, parsed, [
|
|
1260
|
+
...atRules,
|
|
1261
|
+
atRule
|
|
1262
|
+
], parentSelectorDefinition);
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
if (child.type === 'style') {
|
|
1266
|
+
parseNativeStyleRule(child, parsed, atRules, parentSelectorDefinition);
|
|
1267
|
+
continue;
|
|
1268
|
+
}
|
|
1269
|
+
if (child.type === 'keyframes') {
|
|
1270
|
+
throw new Error('@keyframes is not allowed inside @variant. Move managed animation definitions to top-level @theme.');
|
|
1271
|
+
}
|
|
1272
|
+
if (compose) {
|
|
1273
|
+
throw new CSSDirectiveError('compose-placement', '@compose requires a style rule', compose.directiveSource);
|
|
1274
|
+
}
|
|
1275
|
+
if (child.type === 'nested-declarations') {
|
|
1276
|
+
throw new Error('Native @variant blocks only accept style rules, declarations, @compose, and nested at-rules');
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
function parseNestedNativeStyleChildRule(child, parsed, parentSelectorDefinition, atRules) {
|
|
1281
|
+
assertNotSlotRule(child);
|
|
1282
|
+
const masterVariantBlock = parseMasterVariantBlock(child);
|
|
1283
|
+
if (masterVariantBlock) {
|
|
1284
|
+
parseNativeRuleBody(masterVariantBlock.rules, parsed, [
|
|
1285
|
+
...atRules,
|
|
1286
|
+
createCSSDirectiveVariantReference(masterVariantBlock.token)
|
|
1287
|
+
], parentSelectorDefinition);
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
const nestedAtRuleChildren = getNestedAtRuleChildren(child);
|
|
1291
|
+
if (nestedAtRuleChildren) {
|
|
1292
|
+
const atRule = formatNestedAtRule(child);
|
|
1293
|
+
if (!atRule) {
|
|
1294
|
+
throw new Error('Unsupported nested at-rule in native CSS');
|
|
1295
|
+
}
|
|
1296
|
+
parseNativeRuleBody(nestedAtRuleChildren, parsed, [
|
|
1297
|
+
...atRules,
|
|
1298
|
+
atRule
|
|
1299
|
+
], parentSelectorDefinition);
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
if (child.type === 'style') {
|
|
1303
|
+
parseNativeStyleRule(child, parsed, atRules, parentSelectorDefinition);
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
throw new Error('Native CSS rules only accept declarations, @compose, nested selectors, and nested at-rules');
|
|
1307
|
+
}
|
|
1308
|
+
function parseNativeStyleRule(rule, parsed, atRules = [], parentSelectorDefinition) {
|
|
1309
|
+
const selectorDefinition = parseNativeSelectorDefinition(rule.value.selectors, parentSelectorDefinition);
|
|
1310
|
+
selectorDefinition.source = createSelectorSourceReference(parsed, rule) || parentSelectorDefinition?.source;
|
|
1311
|
+
parseStyleDefinitionBody(parsed, selectorDefinition, collectDirectiveStyleRule(rule, parsed), atRules, undefined, undefined);
|
|
1312
|
+
}
|
|
1313
|
+
function formatKeyframeSelector(selector) {
|
|
1314
|
+
switch(selector.type){
|
|
1315
|
+
case 'from':
|
|
1316
|
+
case 'to':
|
|
1317
|
+
return selector.type;
|
|
1318
|
+
case 'percentage':
|
|
1319
|
+
return `${selector.value * 100}%`;
|
|
1320
|
+
case 'timeline-range-percentage':
|
|
1321
|
+
return `${selector.value.name} ${selector.value.percentage * 100}%`;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
function parseKeyframes(rule, manifestInput, isStatic) {
|
|
1325
|
+
const name = rule.value.name.value;
|
|
1326
|
+
if (!name) {
|
|
1327
|
+
throw new Error('@keyframes requires a name');
|
|
1328
|
+
}
|
|
1329
|
+
const keyframes = {};
|
|
1330
|
+
for (const keyframe of rule.value.keyframes){
|
|
1331
|
+
const declarations = collectDeclarations(keyframe.declarations);
|
|
1332
|
+
for (const selector of keyframe.selectors.map(formatKeyframeSelector)){
|
|
1333
|
+
keyframes[selector] = declarations;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
manifestInput.animations ??= {};
|
|
1337
|
+
manifestInput.animations[name] = keyframes;
|
|
1338
|
+
if (isStatic) {
|
|
1339
|
+
manifestInput.animationOptions ??= {};
|
|
1340
|
+
manifestInput.animationOptions[name] = {
|
|
1341
|
+
static: true
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
function getCustomRulePrelude(rule) {
|
|
1346
|
+
return rule.prelude ?? rule.value?.prelude;
|
|
1347
|
+
}
|
|
1348
|
+
function getCustomRuleBody(rule) {
|
|
1349
|
+
return rule.body ?? rule.value?.body;
|
|
1350
|
+
}
|
|
1351
|
+
function getSettingsSection(rule) {
|
|
1352
|
+
const prelude = formatPrelude(getCustomRulePrelude(rule));
|
|
1353
|
+
if (!prelude) return 'root';
|
|
1354
|
+
throw new Error(`Unsupported @settings section: ${prelude}`);
|
|
1355
|
+
}
|
|
1356
|
+
function getThemePrelude(rule) {
|
|
1357
|
+
const prelude = formatPrelude(getCustomRulePrelude(rule));
|
|
1358
|
+
if (!prelude) return {};
|
|
1359
|
+
const parts = prelude.split(/\s+/).filter(Boolean);
|
|
1360
|
+
let mode;
|
|
1361
|
+
let inline = false;
|
|
1362
|
+
let isStatic = false;
|
|
1363
|
+
for (const part of parts){
|
|
1364
|
+
if (part === 'inline' || part === 'static') {
|
|
1365
|
+
if (part === 'inline') {
|
|
1366
|
+
if (inline) throw new Error('@theme inline modifier cannot be repeated');
|
|
1367
|
+
inline = true;
|
|
1368
|
+
} else {
|
|
1369
|
+
if (isStatic) throw new Error('@theme static modifier cannot be repeated');
|
|
1370
|
+
isStatic = true;
|
|
1371
|
+
}
|
|
1372
|
+
continue;
|
|
1373
|
+
}
|
|
1374
|
+
if (mode) {
|
|
1375
|
+
throw new Error('@theme mode must be a single token');
|
|
1376
|
+
}
|
|
1377
|
+
mode = part;
|
|
1378
|
+
}
|
|
1379
|
+
if (inline && isStatic) {
|
|
1380
|
+
throw new Error('@theme inline and static cannot be combined');
|
|
1381
|
+
}
|
|
1382
|
+
if (inline && mode) {
|
|
1383
|
+
throw new Error('@theme inline cannot be mode-specific');
|
|
1384
|
+
}
|
|
1385
|
+
return {
|
|
1386
|
+
...mode ? {
|
|
1387
|
+
mode
|
|
1388
|
+
} : {},
|
|
1389
|
+
...inline ? {
|
|
1390
|
+
inline: true
|
|
1391
|
+
} : {},
|
|
1392
|
+
...isStatic ? {
|
|
1393
|
+
static: true
|
|
1394
|
+
} : {}
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
function parseSettingsStyleRule(rule) {
|
|
1398
|
+
if (parseManagedStyleDefinitionSelector(rule.value.selectors)) {
|
|
1399
|
+
throw new Error('@settings does not accept class definitions');
|
|
1400
|
+
}
|
|
1401
|
+
throw new Error(`Unsupported @settings selector: ${formatSelectors(rule.value.selectors)}`);
|
|
1402
|
+
}
|
|
1403
|
+
function parseSettingsChildRule(child, parsed, section) {
|
|
1404
|
+
if (child.type === 'layer-block') {
|
|
1405
|
+
throw new Error('@settings does not accept @layer');
|
|
1406
|
+
}
|
|
1407
|
+
const masterVariantBlock = parseMasterVariantBlock(child);
|
|
1408
|
+
if (masterVariantBlock) {
|
|
1409
|
+
throw new Error('@settings does not accept @variant');
|
|
1410
|
+
}
|
|
1411
|
+
const nestedAtRuleChildren = getNestedAtRuleChildren(child);
|
|
1412
|
+
if (nestedAtRuleChildren) {
|
|
1413
|
+
throw new Error('@settings does not accept nested at-rules');
|
|
1414
|
+
}
|
|
1415
|
+
if (child.type === 'nested-declarations') {
|
|
1416
|
+
parseSettingsDeclarations(child.value.declarations, parsed.manifestInput);
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
if (child.type === 'keyframes') {
|
|
1420
|
+
throw new Error('@settings does not accept @keyframes. Move managed animation definitions to top-level @theme.');
|
|
1421
|
+
}
|
|
1422
|
+
if (child.type === 'style') {
|
|
1423
|
+
parseSettingsStyleRule(child);
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
throw new Error(`Unsupported rule in @settings${section === 'root' ? '' : ' ' + section}`);
|
|
1427
|
+
}
|
|
1428
|
+
function parseSettingsRule(rule, parsed) {
|
|
1429
|
+
const section = getSettingsSection(rule);
|
|
1430
|
+
const body = getCustomRuleBody(rule);
|
|
1431
|
+
if (!Array.isArray(body?.value)) {
|
|
1432
|
+
throw new Error('@settings requires a style block');
|
|
1433
|
+
}
|
|
1434
|
+
for (const child of body.value){
|
|
1435
|
+
parseSettingsChildRule(child, parsed, section);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
function parseThemeRule(rule, parsed) {
|
|
1439
|
+
const { mode, inline, static: isStatic } = getThemePrelude(rule);
|
|
1440
|
+
const body = getCustomRuleBody(rule);
|
|
1441
|
+
if (!Array.isArray(body?.value)) {
|
|
1442
|
+
throw new Error('@theme requires a style block');
|
|
1443
|
+
}
|
|
1444
|
+
if (mode) addThemeMode(parsed.manifestInput, mode);
|
|
1445
|
+
for (const child of body.value){
|
|
1446
|
+
if (child.type === 'nested-declarations') {
|
|
1447
|
+
parseThemeDeclarations(child.value.declarations, parsed.manifestInput, mode, inline, isStatic);
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
if (child.type === 'keyframes') {
|
|
1451
|
+
if (mode || inline) {
|
|
1452
|
+
throw new Error('@theme keyframes cannot be mode-specific or inline');
|
|
1453
|
+
}
|
|
1454
|
+
parseKeyframes(child, parsed.manifestInput, isStatic);
|
|
1455
|
+
continue;
|
|
1456
|
+
}
|
|
1457
|
+
throw new Error('@theme only accepts theme token declarations and @keyframes definitions');
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
const MANAGED_DYNAMIC_SOURCE_KINDS = new Set([
|
|
1461
|
+
'number',
|
|
1462
|
+
'color',
|
|
1463
|
+
'image'
|
|
1464
|
+
]);
|
|
1465
|
+
const MANAGED_DYNAMIC_RAW_ANY_SOURCE = '*';
|
|
1466
|
+
function parseManagedPatternSegments(pattern) {
|
|
1467
|
+
const segments = [
|
|
1468
|
+
...pattern.matchAll(/<([^<>]*)>/g)
|
|
1469
|
+
];
|
|
1470
|
+
if (segments.length !== 1) {
|
|
1471
|
+
throw new Error('Managed pattern must contain exactly one <...> segment');
|
|
1472
|
+
}
|
|
1473
|
+
const [segment] = segments;
|
|
1474
|
+
const segmentStart = segment.index;
|
|
1475
|
+
const segmentEnd = segmentStart + segment[0].length;
|
|
1476
|
+
if (pattern.slice(0, segmentStart).includes('<') || pattern.slice(segmentEnd).includes('>')) {
|
|
1477
|
+
throw new Error('Managed pattern must contain exactly one <...> segment');
|
|
1478
|
+
}
|
|
1479
|
+
return {
|
|
1480
|
+
segment,
|
|
1481
|
+
segmentStart,
|
|
1482
|
+
segmentEnd,
|
|
1483
|
+
prefix: pattern.slice(0, segmentStart),
|
|
1484
|
+
suffix: pattern.slice(segmentEnd),
|
|
1485
|
+
rawValues: segment[1].trim()
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
function parseManagedEnumPatternName(source) {
|
|
1489
|
+
const pattern = source.trim();
|
|
1490
|
+
if (!pattern) {
|
|
1491
|
+
throw new Error('Managed enum pattern requires a name');
|
|
1492
|
+
}
|
|
1493
|
+
const { prefix, suffix, rawValues } = parseManagedPatternSegments(pattern);
|
|
1494
|
+
if (!prefix || suffix) {
|
|
1495
|
+
throw new Error('Managed enum pattern must use a prefix before <...> and no suffix');
|
|
1496
|
+
}
|
|
1497
|
+
if (!rawValues) {
|
|
1498
|
+
throw new Error('Managed enum pattern cannot be empty');
|
|
1499
|
+
}
|
|
1500
|
+
if (rawValues.includes(',')) {
|
|
1501
|
+
throw new Error('Managed enum pattern values must use "|" separators like text-<left|right>');
|
|
1502
|
+
}
|
|
1503
|
+
const values = rawValues.split('|').map((value)=>value.trim());
|
|
1504
|
+
if (values.length < 2 || values.some((value)=>!value)) {
|
|
1505
|
+
throw new Error('Managed enum pattern requires at least two values separated by "|"');
|
|
1506
|
+
}
|
|
1507
|
+
for (const value of values){
|
|
1508
|
+
if (!/^-?[_a-zA-Z0-9][-_a-zA-Z0-9]*$/.test(value)) {
|
|
1509
|
+
throw new Error(`Invalid managed enum value: ${value}`);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return {
|
|
1513
|
+
kind: 'pattern',
|
|
1514
|
+
name: `${prefix}<${values.join('|')}>`,
|
|
1515
|
+
pattern: {
|
|
1516
|
+
prefix,
|
|
1517
|
+
values
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
function parseManagedDynamicPatternName(source) {
|
|
1522
|
+
const pattern = source.trim();
|
|
1523
|
+
if (!pattern) {
|
|
1524
|
+
throw new Error('Managed dynamic utility requires a name');
|
|
1525
|
+
}
|
|
1526
|
+
const { prefix, suffix, rawValues } = parseManagedPatternSegments(pattern);
|
|
1527
|
+
if (!prefix.endsWith(':') || prefix === ':' || suffix) {
|
|
1528
|
+
throw new Error('Managed dynamic utilities must use key:<...> syntax');
|
|
1529
|
+
}
|
|
1530
|
+
const key = prefix.slice(0, -1);
|
|
1531
|
+
if (!/^-?[_a-zA-Z][-_a-zA-Z0-9]*$/.test(key)) {
|
|
1532
|
+
throw new Error(`Invalid managed dynamic utility key: ${key}`);
|
|
1533
|
+
}
|
|
1534
|
+
if (!rawValues) {
|
|
1535
|
+
throw new Error('Managed dynamic utility source list cannot be empty');
|
|
1536
|
+
}
|
|
1537
|
+
if (rawValues.includes(',')) {
|
|
1538
|
+
throw new Error('Managed dynamic utility source lists must use "|" separators like font:<~font-size|number>');
|
|
1539
|
+
}
|
|
1540
|
+
const values = rawValues.split('|').map((value)=>value.trim());
|
|
1541
|
+
if (values.some((value)=>!value)) {
|
|
1542
|
+
throw new Error('Managed dynamic utility source list cannot contain empty entries');
|
|
1543
|
+
}
|
|
1544
|
+
const variableAliasRefs = [];
|
|
1545
|
+
const canonicalValues = [];
|
|
1546
|
+
const literalValues = [];
|
|
1547
|
+
let kind;
|
|
1548
|
+
let arbitrary = false;
|
|
1549
|
+
const addCanonicalValue = (value, unique = false)=>{
|
|
1550
|
+
if (!unique || !canonicalValues.includes(value)) canonicalValues.push(value);
|
|
1551
|
+
};
|
|
1552
|
+
const addVariableAliasRef = (value)=>{
|
|
1553
|
+
if (!variableAliasRefs.includes(value)) variableAliasRefs.push(value);
|
|
1554
|
+
addCanonicalValue(value, true);
|
|
1555
|
+
};
|
|
1556
|
+
for (const value of values){
|
|
1557
|
+
if (value[0] === '~' || value[0] === '=') {
|
|
1558
|
+
const namespace = value.slice(1);
|
|
1559
|
+
if (!/^[_a-zA-Z][-_a-zA-Z0-9]*$/.test(namespace)) {
|
|
1560
|
+
throw new Error(`Invalid managed dynamic utility namespace: ${value}`);
|
|
1561
|
+
}
|
|
1562
|
+
addVariableAliasRef(value);
|
|
1563
|
+
continue;
|
|
1564
|
+
}
|
|
1565
|
+
if (MANAGED_DYNAMIC_SOURCE_KINDS.has(value)) {
|
|
1566
|
+
if (kind && kind !== value) {
|
|
1567
|
+
throw new Error('Managed dynamic utilities only support one raw value kind per entry');
|
|
1568
|
+
}
|
|
1569
|
+
kind = value;
|
|
1570
|
+
if (kind === 'color') addVariableAliasRef('~color');
|
|
1571
|
+
addCanonicalValue(value);
|
|
1572
|
+
continue;
|
|
1573
|
+
}
|
|
1574
|
+
if (value === MANAGED_DYNAMIC_RAW_ANY_SOURCE) {
|
|
1575
|
+
arbitrary = true;
|
|
1576
|
+
addCanonicalValue(value);
|
|
1577
|
+
continue;
|
|
1578
|
+
}
|
|
1579
|
+
if (/^-?[_a-zA-Z0-9][-_a-zA-Z0-9]*$/.test(value)) {
|
|
1580
|
+
if (!literalValues.includes(value)) literalValues.push(value);
|
|
1581
|
+
addCanonicalValue(value);
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1584
|
+
throw new Error(`Unsupported managed dynamic utility source: ${value}`);
|
|
1585
|
+
}
|
|
1586
|
+
if (arbitrary && literalValues.length) {
|
|
1587
|
+
throw new Error('Managed dynamic utility wildcard cannot be combined with enum values');
|
|
1588
|
+
}
|
|
1589
|
+
if (literalValues.length) {
|
|
1590
|
+
if (variableAliasRefs.length) {
|
|
1591
|
+
throw new Error('Managed dynamic utility enum values cannot be combined with namespaces');
|
|
1592
|
+
}
|
|
1593
|
+
if (!kind && literalValues.length < 2) {
|
|
1594
|
+
throw new Error('Managed dynamic utility enum source requires at least two values separated by "|"');
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
kind: 'dynamic',
|
|
1599
|
+
name: `${key}:<${canonicalValues.join('|')}>`,
|
|
1600
|
+
dynamic: {
|
|
1601
|
+
key,
|
|
1602
|
+
...variableAliasRefs.length ? {
|
|
1603
|
+
variableAliasRefs
|
|
1604
|
+
} : {},
|
|
1605
|
+
...kind ? {
|
|
1606
|
+
kind
|
|
1607
|
+
} : {},
|
|
1608
|
+
...literalValues.length ? {
|
|
1609
|
+
values: literalValues
|
|
1610
|
+
} : {},
|
|
1611
|
+
...arbitrary ? {
|
|
1612
|
+
arbitrary
|
|
1613
|
+
} : {}
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
function parseManagedPatternName(source) {
|
|
1618
|
+
const pattern = source.trim();
|
|
1619
|
+
const { prefix } = parseManagedPatternSegments(pattern);
|
|
1620
|
+
return prefix.endsWith(':') ? parseManagedDynamicPatternName(pattern) : parseManagedEnumPatternName(pattern);
|
|
1621
|
+
}
|
|
1622
|
+
function parseManagedPatternNameIfNeeded(source) {
|
|
1623
|
+
return source.includes('<') || source.includes('>') ? parseManagedPatternName(source) : undefined;
|
|
1624
|
+
}
|
|
1625
|
+
function ensureUtilityDefinitionRules(definition) {
|
|
1626
|
+
if (!definition.declarations) return;
|
|
1627
|
+
const declarations = definition.declarations;
|
|
1628
|
+
delete definition.declarations;
|
|
1629
|
+
const atRules = definition.atRules;
|
|
1630
|
+
delete definition.atRules;
|
|
1631
|
+
definition.rules ??= [];
|
|
1632
|
+
definition.rules.push({
|
|
1633
|
+
declarations,
|
|
1634
|
+
...atRules?.length ? {
|
|
1635
|
+
atRules: [
|
|
1636
|
+
...atRules
|
|
1637
|
+
]
|
|
1638
|
+
} : {}
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
function pushUtilityDefinitionRule(definition, declarations, selector = '&', atRules = []) {
|
|
1642
|
+
if (!Object.keys(declarations).length) return;
|
|
1643
|
+
const rule = {
|
|
1644
|
+
declarations,
|
|
1645
|
+
...selector !== '&' ? {
|
|
1646
|
+
selector
|
|
1647
|
+
} : {},
|
|
1648
|
+
...atRules.length ? {
|
|
1649
|
+
atRules: [
|
|
1650
|
+
...atRules
|
|
1651
|
+
]
|
|
1652
|
+
} : {}
|
|
1653
|
+
};
|
|
1654
|
+
if (!definition.declarations && !definition.rules?.length && !rule.selector && !rule.atRules) {
|
|
1655
|
+
definition.declarations = declarations;
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
ensureUtilityDefinitionRules(definition);
|
|
1659
|
+
definition.rules ??= [];
|
|
1660
|
+
definition.rules.push(rule);
|
|
1661
|
+
}
|
|
1662
|
+
function parseManagedPatternStyleDefinitionBody(parsed, definition, items, directiveName, selectors = [
|
|
1663
|
+
'&'
|
|
1664
|
+
], atRules = []) {
|
|
1665
|
+
for (const item of items){
|
|
1666
|
+
if (item.type === 'declarations') {
|
|
1667
|
+
for (const selector of selectors){
|
|
1668
|
+
pushUtilityDefinitionRule(definition, item.declarations, selector, atRules);
|
|
1669
|
+
}
|
|
1670
|
+
continue;
|
|
1671
|
+
}
|
|
1672
|
+
if (item.type === 'compose') {
|
|
1673
|
+
throw new CSSDirectiveError('compose-placement', `@compose is not supported inside managed pattern definitions`, item.directiveSource);
|
|
1674
|
+
}
|
|
1675
|
+
parseManagedPatternChildRule(item.rule, parsed, definition, directiveName, selectors, atRules);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
function parseManagedPatternChildRule(child, parsed, definition, directiveName, selectors = [
|
|
1679
|
+
'&'
|
|
1680
|
+
], atRules = []) {
|
|
1681
|
+
if (child.type === 'layer-block') {
|
|
1682
|
+
throw new Error(`Nested @layer blocks are not allowed inside @${directiveName}`);
|
|
1683
|
+
}
|
|
1684
|
+
assertNotSlotRule(child);
|
|
1685
|
+
const masterVariantBlock = parseMasterVariantBlock(child);
|
|
1686
|
+
if (masterVariantBlock) {
|
|
1687
|
+
parseManagedPatternStyleDefinitionBody(parsed, definition, collectDirectiveStyleRuleBody(EMPTY_DECLARATION_BLOCK, masterVariantBlock.rules, parsed), directiveName, selectors, [
|
|
1688
|
+
...atRules,
|
|
1689
|
+
createCSSDirectiveVariantReference(masterVariantBlock.token)
|
|
1690
|
+
]);
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
const nestedAtRuleChildren = getNestedAtRuleChildren(child);
|
|
1694
|
+
if (nestedAtRuleChildren) {
|
|
1695
|
+
const atRule = formatNestedAtRule(child);
|
|
1696
|
+
if (!atRule) {
|
|
1697
|
+
throw new Error(`Unsupported nested at-rule inside @${directiveName}`);
|
|
1698
|
+
}
|
|
1699
|
+
parseManagedPatternStyleDefinitionBody(parsed, definition, collectDirectiveStyleRuleBody(EMPTY_DECLARATION_BLOCK, nestedAtRuleChildren, parsed), directiveName, selectors, [
|
|
1700
|
+
...atRules,
|
|
1701
|
+
atRule
|
|
1702
|
+
]);
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
if (child.type === 'style') {
|
|
1706
|
+
parseManagedPatternStyleDefinitionBody(parsed, definition, collectDirectiveStyleRule(child, parsed), directiveName, combineStyleSelectorLists(selectors, child.value.selectors), atRules);
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
const compose = parseComposeRule(child, parsed);
|
|
1710
|
+
if (compose) {
|
|
1711
|
+
throw new CSSDirectiveError('compose-placement', `@compose is not supported inside managed pattern definitions`, compose.directiveSource);
|
|
1712
|
+
}
|
|
1713
|
+
if (child.type === 'keyframes') {
|
|
1714
|
+
throw new Error(`@keyframes is not allowed inside @${directiveName}. Move managed animation definitions to top-level @theme.`);
|
|
1715
|
+
}
|
|
1716
|
+
throw new Error(`Managed pattern definitions only accept declarations, nested selectors, and nested at-rules`);
|
|
1717
|
+
}
|
|
1718
|
+
function parseManagedPatternDefinitionRule(child, parsed, parsedPattern, atRules, layer, directiveName) {
|
|
1719
|
+
const definition = {
|
|
1720
|
+
name: parsedPattern.name,
|
|
1721
|
+
type: parsedPattern.kind === 'pattern' ? 'pattern' : 'dynamic',
|
|
1722
|
+
layer,
|
|
1723
|
+
...parsedPattern.kind === 'pattern' ? {
|
|
1724
|
+
pattern: parsedPattern.pattern
|
|
1725
|
+
} : {
|
|
1726
|
+
dynamic: parsedPattern.dynamic
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
parseManagedPatternStyleDefinitionBody(parsed, definition, collectDirectiveStyleRule(child, parsed), directiveName, [
|
|
1730
|
+
'&'
|
|
1731
|
+
], atRules);
|
|
1732
|
+
parsed.manifestInput.utilities ??= [];
|
|
1733
|
+
parsed.manifestInput.utilities.push(definition);
|
|
1734
|
+
}
|
|
1735
|
+
function containsNativeStyleDirective(rule) {
|
|
1736
|
+
if ((rule.type === 'unknown' || rule.type === 'custom') && (rule.value?.name === 'compose' || rule.value?.name === 'variant' || rule.value?.name === 'slot' || getMasterVariantShorthandToken(rule.value?.name))) {
|
|
1737
|
+
return true;
|
|
1738
|
+
}
|
|
1739
|
+
if (rule.type === 'style') {
|
|
1740
|
+
return (rule.value.rules || []).some(containsNativeStyleDirective);
|
|
1741
|
+
}
|
|
1742
|
+
const nestedAtRuleChildren = getNestedAtRuleChildren(rule);
|
|
1743
|
+
if (nestedAtRuleChildren) {
|
|
1744
|
+
return nestedAtRuleChildren.some(containsNativeStyleDirective);
|
|
1745
|
+
}
|
|
1746
|
+
return false;
|
|
1747
|
+
}
|
|
1748
|
+
function createManagedDefinitionNameError(rule) {
|
|
1749
|
+
return new Error(`Managed definition names must be bare identifiers: ${formatSelectors(rule.value.selectors)}`);
|
|
1750
|
+
}
|
|
1751
|
+
function parseManagedDefinitionDirectiveChildRule(child, parsed, atRules, layer, directiveName) {
|
|
1752
|
+
if (child.type === 'layer-block') {
|
|
1753
|
+
throw new Error(`Nested @layer blocks are not allowed inside @${directiveName}`);
|
|
1754
|
+
}
|
|
1755
|
+
const masterVariantBlock = parseMasterVariantBlock(child);
|
|
1756
|
+
if (masterVariantBlock) {
|
|
1757
|
+
for (const nestedChild of masterVariantBlock.rules){
|
|
1758
|
+
parseManagedDefinitionDirectiveChildRule(nestedChild, parsed, [
|
|
1759
|
+
...atRules,
|
|
1760
|
+
createCSSDirectiveVariantReference(masterVariantBlock.token)
|
|
1761
|
+
], layer, directiveName);
|
|
1762
|
+
}
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
const nestedAtRuleChildren = getNestedAtRuleChildren(child);
|
|
1766
|
+
if (nestedAtRuleChildren) {
|
|
1767
|
+
const atRule = formatNestedAtRule(child);
|
|
1768
|
+
if (!atRule) {
|
|
1769
|
+
throw new Error(`Unsupported nested at-rule inside @${directiveName}`);
|
|
1770
|
+
}
|
|
1771
|
+
for (const nestedChild of nestedAtRuleChildren){
|
|
1772
|
+
parseManagedDefinitionDirectiveChildRule(nestedChild, parsed, [
|
|
1773
|
+
...atRules,
|
|
1774
|
+
atRule
|
|
1775
|
+
], layer, directiveName);
|
|
1776
|
+
}
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
if (child.type === 'nested-declarations') {
|
|
1780
|
+
throw new Error(`@${directiveName} only accepts bare managed names and nested at-rules`);
|
|
1781
|
+
}
|
|
1782
|
+
if (child.type === 'keyframes') {
|
|
1783
|
+
throw new Error('@keyframes is not allowed inside managed definition directives. Move managed animation definitions to top-level @theme.');
|
|
1784
|
+
}
|
|
1785
|
+
if (child.type === 'style') {
|
|
1786
|
+
const selectorSource = createSelectorSourceReference(parsed, child);
|
|
1787
|
+
const selectorText = selectorSource && parsed.source ? parsed.source.slice(selectorSource.range.start, selectorSource.range.end).trim() : '';
|
|
1788
|
+
const parsedPattern = parseManagedPatternNameIfNeeded(selectorText);
|
|
1789
|
+
const selectorDefinition = parseManagedDefinitionNameSelector(child.value.selectors);
|
|
1790
|
+
if (!selectorDefinition) {
|
|
1791
|
+
throw createManagedDefinitionNameError(child);
|
|
1792
|
+
}
|
|
1793
|
+
if (parsedPattern) {
|
|
1794
|
+
parseManagedPatternDefinitionRule(child, parsed, parsedPattern, atRules, layer, directiveName);
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
selectorDefinition.source = selectorSource;
|
|
1798
|
+
parseStyleDefinitionBody(parsed, selectorDefinition, collectDirectiveStyleRule(child, parsed), atRules, layer);
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
if ((child.type === 'unknown' || child.type === 'custom') && child.value?.name === 'compose') {
|
|
1802
|
+
throw createComposePlacementError(parsed);
|
|
1803
|
+
}
|
|
1804
|
+
if (isCustomVariantDefinition(child)) {
|
|
1805
|
+
throw new Error('@custom-variant must be top-level');
|
|
1806
|
+
}
|
|
1807
|
+
throw new Error(`Unsupported rule inside @${directiveName}`);
|
|
1808
|
+
}
|
|
1809
|
+
function parseManagedDefinitionDirectiveRule(rule, parsed) {
|
|
1810
|
+
const layer = getManagedDefinitionDirectiveLayer(rule);
|
|
1811
|
+
if (!layer) return;
|
|
1812
|
+
const body = getCustomRuleBody(rule);
|
|
1813
|
+
if (!Array.isArray(body?.value)) {
|
|
1814
|
+
throw new Error(`@${rule.value.name} requires a style block`);
|
|
1815
|
+
}
|
|
1816
|
+
for (const child of body.value){
|
|
1817
|
+
parseManagedDefinitionDirectiveChildRule(child, parsed, [], layer, rule.value.name);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
function isManagedDefinitionDirectiveRange(range) {
|
|
1821
|
+
return range.depth === 0 && (range.name === 'defaults' || range.name === 'components' || range.name === 'utilities') && range.blockContentRange;
|
|
1822
|
+
}
|
|
1823
|
+
function skipCSSWhitespaceAndComments(source, index, end) {
|
|
1824
|
+
while(index < end){
|
|
1825
|
+
index = skipCSSWhitespace(source, index);
|
|
1826
|
+
if (source[index] === '/' && source[index + 1] === '*') {
|
|
1827
|
+
const close = source.indexOf('*/', index + 2);
|
|
1828
|
+
index = close === -1 ? end : close + 2;
|
|
1829
|
+
continue;
|
|
1830
|
+
}
|
|
1831
|
+
break;
|
|
1832
|
+
}
|
|
1833
|
+
return index;
|
|
1834
|
+
}
|
|
1835
|
+
function createManagedPatternNameMask(length) {
|
|
1836
|
+
return 'm' + '_'.repeat(Math.max(0, length - 1));
|
|
1837
|
+
}
|
|
1838
|
+
function collectManagedPatternEntryNameMasks(source, start, end, replacements) {
|
|
1839
|
+
let index = start;
|
|
1840
|
+
while(index < end){
|
|
1841
|
+
index = skipCSSWhitespaceAndComments(source, index, end);
|
|
1842
|
+
if (index >= end) break;
|
|
1843
|
+
const char = source[index];
|
|
1844
|
+
if (char === '"' || char === '\'') {
|
|
1845
|
+
index = findCSSClosingQuote(source, index, char, end) + 1;
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
const statementEnd = findCSSStatementEnd(source, index);
|
|
1849
|
+
if (char === '@') {
|
|
1850
|
+
if (statementEnd.reason === 'block' && statementEnd.delimiterRange) {
|
|
1851
|
+
const blockEnd = findCSSBlockEnd(source, statementEnd.delimiterRange.start);
|
|
1852
|
+
collectManagedPatternEntryNameMasks(source, statementEnd.delimiterRange.start + 1, blockEnd === -1 ? end : Math.min(blockEnd, end), replacements);
|
|
1853
|
+
index = blockEnd === -1 ? end : blockEnd + 1;
|
|
1854
|
+
continue;
|
|
1855
|
+
}
|
|
1856
|
+
index = Math.max(index + 1, statementEnd.end);
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
if (statementEnd.reason === 'block' && statementEnd.delimiterRange) {
|
|
1860
|
+
const entryNameRange = trimSourceRange(source, {
|
|
1861
|
+
start: index,
|
|
1862
|
+
end: statementEnd.delimiterRange.start
|
|
1863
|
+
});
|
|
1864
|
+
const entryName = source.slice(entryNameRange.start, entryNameRange.end);
|
|
1865
|
+
if (parseManagedPatternNameIfNeeded(entryName)) {
|
|
1866
|
+
replacements.push({
|
|
1867
|
+
...entryNameRange,
|
|
1868
|
+
replacement: createManagedPatternNameMask(entryNameRange.end - entryNameRange.start)
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
const blockEnd = findCSSBlockEnd(source, statementEnd.delimiterRange.start);
|
|
1872
|
+
index = blockEnd === -1 ? end : blockEnd + 1;
|
|
1873
|
+
continue;
|
|
1874
|
+
}
|
|
1875
|
+
index = Math.max(index + 1, statementEnd.end);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
function maskManagedPatternEntryNames(source) {
|
|
1879
|
+
const replacements = [];
|
|
1880
|
+
for (const range of collectCSSDirectiveRanges(source)){
|
|
1881
|
+
if (!isManagedDefinitionDirectiveRange(range)) continue;
|
|
1882
|
+
if (!range.blockContentRange) continue;
|
|
1883
|
+
collectManagedPatternEntryNameMasks(source, range.blockContentRange.start, range.blockContentRange.end, replacements);
|
|
1884
|
+
}
|
|
1885
|
+
return replaceSourceRanges(source, replacements);
|
|
1886
|
+
}
|
|
1887
|
+
function compileCSS(source, options = {}) {
|
|
1888
|
+
const filename = options.from || 'master.css';
|
|
1889
|
+
const references = findCSSReferenceStatements(source, filename);
|
|
1890
|
+
const sourceWithoutReferences = removeSourceRanges(source, references);
|
|
1891
|
+
const parsed = {
|
|
1892
|
+
manifestInput: {},
|
|
1893
|
+
extractionPolicy: createCSSDirectiveExtractionPolicy(),
|
|
1894
|
+
classNames: [],
|
|
1895
|
+
nativeClassNames: [],
|
|
1896
|
+
warnings: [],
|
|
1897
|
+
source,
|
|
1898
|
+
filename,
|
|
1899
|
+
composeRanges: collectCSSDirectiveRanges(source).filter((range)=>range.name === 'compose'),
|
|
1900
|
+
composeRangeIndex: 0,
|
|
1901
|
+
sourceLocationResolver: createSourceLocationResolver(source)
|
|
1902
|
+
};
|
|
1903
|
+
const classFilter = options.classes === undefined ? undefined : new Set(options.classes);
|
|
1904
|
+
parsed.extractionPolicy = collectStandaloneCSSDirectiveExtractionPolicy(sourceWithoutReferences, filename);
|
|
1905
|
+
validateComposeRanges(parsed);
|
|
1906
|
+
const preprocessedSource = maskManagedPatternEntryNames(removeStandaloneCSSDirectives(sourceWithoutReferences, filename));
|
|
1907
|
+
let ruleDepth = 0;
|
|
1908
|
+
const transformed = getCSSTransform()({
|
|
1909
|
+
filename,
|
|
1910
|
+
code: encodeCSS(preprocessedSource),
|
|
1911
|
+
customAtRules: MASTER_CUSTOM_AT_RULES,
|
|
1912
|
+
visitor: {
|
|
1913
|
+
Rule (rule) {
|
|
1914
|
+
if (ruleDepth === 0) {
|
|
1915
|
+
if (isCustomVariantDefinition(rule)) {
|
|
1916
|
+
parseVariantDefinition(rule, parsed);
|
|
1917
|
+
return [];
|
|
1918
|
+
}
|
|
1919
|
+
const masterVariantBlock = parseMasterVariantBlock(rule);
|
|
1920
|
+
if (masterVariantBlock) {
|
|
1921
|
+
parseNativeRuleBody(masterVariantBlock.rules, parsed, [
|
|
1922
|
+
createCSSDirectiveVariantReference(masterVariantBlock.token)
|
|
1923
|
+
]);
|
|
1924
|
+
return [];
|
|
1925
|
+
}
|
|
1926
|
+
const nestedAtRuleChildren = getNestedAtRuleChildren(rule);
|
|
1927
|
+
if (nestedAtRuleChildren && containsNativeStyleDirective(rule)) {
|
|
1928
|
+
const atRule = formatNestedAtRule(rule);
|
|
1929
|
+
if (!atRule) {
|
|
1930
|
+
throw new Error('Unsupported nested at-rule in native CSS');
|
|
1931
|
+
}
|
|
1932
|
+
parseNativeRuleBody(nestedAtRuleChildren, parsed, [
|
|
1933
|
+
atRule
|
|
1934
|
+
]);
|
|
1935
|
+
return [];
|
|
1936
|
+
}
|
|
1937
|
+
if (rule.type === 'style' && containsNativeStyleDirective(rule)) {
|
|
1938
|
+
parseNativeStyleRule(rule, parsed);
|
|
1939
|
+
return [];
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
if (rule.type === 'custom') {
|
|
1943
|
+
switch(rule.value.name){
|
|
1944
|
+
case 'settings':
|
|
1945
|
+
if (ruleDepth !== 0) {
|
|
1946
|
+
throw new Error('@settings must be top-level');
|
|
1947
|
+
}
|
|
1948
|
+
parseSettingsRule(rule, parsed);
|
|
1949
|
+
return [];
|
|
1950
|
+
case 'theme':
|
|
1951
|
+
if (ruleDepth !== 0) {
|
|
1952
|
+
throw new Error('@theme must be top-level');
|
|
1953
|
+
}
|
|
1954
|
+
parseThemeRule(rule, parsed);
|
|
1955
|
+
return [];
|
|
1956
|
+
case 'defaults':
|
|
1957
|
+
case 'components':
|
|
1958
|
+
case 'utilities':
|
|
1959
|
+
if (ruleDepth !== 0) {
|
|
1960
|
+
throw new Error(`@${rule.value.name} must be top-level`);
|
|
1961
|
+
}
|
|
1962
|
+
parseManagedDefinitionDirectiveRule(rule, parsed);
|
|
1963
|
+
return [];
|
|
1964
|
+
case 'compose':
|
|
1965
|
+
throw createComposePlacementError(parsed);
|
|
1966
|
+
case 'custom-variant':
|
|
1967
|
+
throw new Error('@custom-variant must be top-level');
|
|
1968
|
+
case 'variant':
|
|
1969
|
+
throw new Error('@variant requires a style rule or nested style rules');
|
|
1970
|
+
case 'reference':
|
|
1971
|
+
throw new Error('@reference must be top-level');
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
ruleDepth++;
|
|
1975
|
+
},
|
|
1976
|
+
RuleExit () {
|
|
1977
|
+
ruleDepth--;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
});
|
|
1981
|
+
let remainingCSS = '';
|
|
1982
|
+
if (options.preserveNativeCSS !== false) {
|
|
1983
|
+
const filteredCode = filterNativeCSS(transformed.code, filename, parsed, classFilter);
|
|
1984
|
+
const remainingCode = classFilter ? pruneEmptyRuleBlocks(filteredCode, filename) : filteredCode;
|
|
1985
|
+
remainingCSS = decodeCSS(remainingCode).trim();
|
|
1986
|
+
}
|
|
1987
|
+
return {
|
|
1988
|
+
manifestInput: parsed.manifestInput,
|
|
1989
|
+
extractionPolicy: parsed.extractionPolicy,
|
|
1990
|
+
classNames: parsed.classNames,
|
|
1991
|
+
nativeClassNames: parsed.nativeClassNames,
|
|
1992
|
+
warnings: parsed.warnings,
|
|
1993
|
+
nativeCSS: remainingCSS,
|
|
1994
|
+
css: remainingCSS,
|
|
1995
|
+
generatedCSS: '',
|
|
1996
|
+
dependencies: [],
|
|
1997
|
+
...references.length ? {
|
|
1998
|
+
references
|
|
1999
|
+
} : {},
|
|
2000
|
+
...parsed.styleDefinitions ? {
|
|
2001
|
+
styleDefinitions: parsed.styleDefinitions
|
|
2002
|
+
} : {}
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
function parseDirectives(source, options = {}) {
|
|
2006
|
+
const { manifestInput, extractionPolicy, classNames, nativeClassNames, warnings, styleDefinitions } = compileCSS(source, options);
|
|
2007
|
+
return {
|
|
2008
|
+
manifestInput,
|
|
2009
|
+
extractionPolicy,
|
|
2010
|
+
classNames,
|
|
2011
|
+
nativeClassNames,
|
|
2012
|
+
warnings,
|
|
2013
|
+
...styleDefinitions ? {
|
|
2014
|
+
styleDefinitions
|
|
2015
|
+
} : {}
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
export { collectStandaloneCSSDirectiveExtractionPolicy, compileCSS, createCSSDirectiveExtractionPolicy, findCSSReferenceStatements, maskManagedPatternEntryNames, parseDirectives, removeCSSReferenceStatements, removeStandaloneCSSDirectives };
|