@jesscss/css-parser 1.0.8-alpha.6 → 2.0.0-alpha.2
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/lib/advancedActionsParser.d.ts +60 -0
- package/lib/advancedActionsParser.js +203 -0
- package/lib/advancedActionsParser.js.map +1 -0
- package/lib/cssActionsParser.d.ts +155 -0
- package/lib/cssActionsParser.js +376 -0
- package/lib/cssActionsParser.js.map +1 -0
- package/lib/cssErrorMessageProvider.d.ts +14 -0
- package/lib/cssErrorMessageProvider.js +40 -0
- package/lib/cssErrorMessageProvider.js.map +1 -0
- package/lib/cssParser.d.ts +36 -103
- package/lib/cssParser.js +75 -58
- package/lib/cssParser.js.map +1 -0
- package/lib/cssTokens.d.ts +539 -5
- package/lib/cssTokens.js +488 -232
- package/lib/cssTokens.js.map +1 -0
- package/lib/index.d.ts +8 -16
- package/lib/index.js +9 -41
- package/lib/index.js.map +1 -0
- package/lib/productions.d.ts +273 -0
- package/lib/productions.js +3499 -0
- package/lib/productions.js.map +1 -0
- package/lib/test/ast-serialize.test.d.ts +1 -0
- package/lib/test/ast-serialize.test.js +157 -0
- package/lib/test/ast-serialize.test.js.map +1 -0
- package/lib/test/container.test.d.ts +1 -0
- package/lib/test/container.test.js +369 -0
- package/lib/test/container.test.js.map +1 -0
- package/lib/test/css-files.test.d.ts +1 -0
- package/lib/test/css-files.test.js +21 -0
- package/lib/test/css-files.test.js.map +1 -0
- package/lib/test/less-output.test.d.ts +1 -0
- package/lib/test/less-output.test.js +52 -0
- package/lib/test/less-output.test.js.map +1 -0
- package/lib/util/cst.d.ts +7 -2
- package/lib/util/cst.js +5 -9
- package/lib/util/cst.js.map +1 -0
- package/lib/util/index.d.ts +19 -13
- package/lib/util/index.js +98 -87
- package/lib/util/index.js.map +1 -0
- package/package.json +43 -20
- package/lib/productions/atRules.d.ts +0 -2
- package/lib/productions/atRules.js +0 -196
- package/lib/productions/blocks.d.ts +0 -2
- package/lib/productions/blocks.js +0 -181
- package/lib/productions/declarations.d.ts +0 -14
- package/lib/productions/declarations.js +0 -59
- package/lib/productions/root.d.ts +0 -2
- package/lib/productions/root.js +0 -49
- package/lib/productions/selectors.d.ts +0 -2
- package/lib/productions/selectors.js +0 -231
- package/lib/productions/values.d.ts +0 -2
- package/lib/productions/values.js +0 -114
|
@@ -0,0 +1,3499 @@
|
|
|
1
|
+
import { EMPTY_ALT, tokenMatcher } from 'chevrotain';
|
|
2
|
+
import { Node, Any, Keyword, Block, Ruleset, Declaration, SelectorList, CompoundSelector, ComplexSelector, Combinator, BasicSelector, Ampersand, List, Sequence, Call, Url, Paren, Operation, Quoted, PseudoSelector, AttributeSelector, AtRule, QueryCondition, CustomDeclaration, RawRules } from '@jesscss/core';
|
|
3
|
+
export function stylesheet(T) {
|
|
4
|
+
const $ = this;
|
|
5
|
+
// stylesheet
|
|
6
|
+
// : CHARSET? main EOF
|
|
7
|
+
// ;
|
|
8
|
+
return (options = {}) => {
|
|
9
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
10
|
+
let context;
|
|
11
|
+
if (!RECORDING_PHASE) {
|
|
12
|
+
/** Auto-creates tree context */
|
|
13
|
+
context = this.context;
|
|
14
|
+
}
|
|
15
|
+
let charset;
|
|
16
|
+
$.OPTION(() => {
|
|
17
|
+
charset = $.CONSUME(T.Charset);
|
|
18
|
+
});
|
|
19
|
+
const ctx = { isRoot: true };
|
|
20
|
+
let root = $.SUBRULE($.main);
|
|
21
|
+
if (!RECORDING_PHASE) {
|
|
22
|
+
let rules = root.value;
|
|
23
|
+
if (charset) {
|
|
24
|
+
let loc = $.getLocationInfo(charset);
|
|
25
|
+
let rootLoc = root.location;
|
|
26
|
+
rules.unshift(new Any(charset.image, { role: 'charset' }, loc, context));
|
|
27
|
+
rootLoc[0] = loc[0];
|
|
28
|
+
rootLoc[1] = loc[1];
|
|
29
|
+
rootLoc[2] = loc[2];
|
|
30
|
+
}
|
|
31
|
+
return root;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function main(T, alt) {
|
|
36
|
+
let $ = this;
|
|
37
|
+
alt ??= (ctx = {}) => [
|
|
38
|
+
{ ALT: () => $.SUBRULE($.qualifiedRule) },
|
|
39
|
+
{ ALT: () => $.SUBRULE($.atRule) }
|
|
40
|
+
];
|
|
41
|
+
return (ctx = {}) => {
|
|
42
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
43
|
+
const isRoot = !!ctx.isRoot;
|
|
44
|
+
let context;
|
|
45
|
+
if (!RECORDING_PHASE) {
|
|
46
|
+
context = this.context;
|
|
47
|
+
}
|
|
48
|
+
let rules;
|
|
49
|
+
if (!RECORDING_PHASE) {
|
|
50
|
+
rules = [];
|
|
51
|
+
}
|
|
52
|
+
let requiredSemi = false;
|
|
53
|
+
let lastRule;
|
|
54
|
+
/**
|
|
55
|
+
* In this production rule, semi-colons are not required
|
|
56
|
+
* but this is repurposed by declarationList and by Less / Sass,
|
|
57
|
+
* so that's why this gate is here.
|
|
58
|
+
*/
|
|
59
|
+
$.MANY({
|
|
60
|
+
GATE: () => !requiredSemi || (requiredSemi && ($.LA(1).tokenType === T.Semi
|
|
61
|
+
|| $.LA(0).tokenType === T.Semi)),
|
|
62
|
+
DEF: () => {
|
|
63
|
+
const localAlt = typeof alt === 'function' ? alt(ctx) : alt;
|
|
64
|
+
let value = $.OR(localAlt);
|
|
65
|
+
if (!RECORDING_PHASE) {
|
|
66
|
+
if (!(value instanceof Node)) {
|
|
67
|
+
/** This is a semi-colon token */
|
|
68
|
+
if (lastRule) {
|
|
69
|
+
lastRule.options.semi = true;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
rules.push(new Any(';', { role: 'semi' }, $.getLocationInfo($.LA(1)), context));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
requiredSemi = !!value.requiredSemi;
|
|
77
|
+
rules.push(value);
|
|
78
|
+
lastRule = value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
if (!RECORDING_PHASE) {
|
|
84
|
+
let returnNode = $.getRulesWithComments(rules, $.getLocationInfo($.LA(1)));
|
|
85
|
+
// Attaches remaining whitespace at the end of rules
|
|
86
|
+
const wrapped = $.wrap(returnNode, true);
|
|
87
|
+
return wrapped;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function qualifiedRule(T, selectorAlt) {
|
|
92
|
+
const $ = this;
|
|
93
|
+
selectorAlt ??= (ctx = {}) => [
|
|
94
|
+
{
|
|
95
|
+
GATE: () => !ctx.inner,
|
|
96
|
+
ALT: () => $.SUBRULE($.selectorList, { ARGS: [ctx] })
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
GATE: () => !!ctx.inner,
|
|
100
|
+
ALT: () => $.SUBRULE($.forgivingSelectorList, { ARGS: [ctx] })
|
|
101
|
+
}
|
|
102
|
+
];
|
|
103
|
+
// qualifiedRule
|
|
104
|
+
// : selectorList WS* LCURLY declarationList RCURLY
|
|
105
|
+
// ;
|
|
106
|
+
return (ctx = {}) => {
|
|
107
|
+
$.startRule();
|
|
108
|
+
let selector = $.OR(selectorAlt(ctx));
|
|
109
|
+
$.CONSUME(T.LCurly);
|
|
110
|
+
let rules = $.SUBRULE($.declarationList);
|
|
111
|
+
$.CONSUME(T.RCurly);
|
|
112
|
+
if (!$.RECORDING_PHASE) {
|
|
113
|
+
let location = $.endRule();
|
|
114
|
+
const ruleset = new Ruleset({
|
|
115
|
+
selector,
|
|
116
|
+
rules
|
|
117
|
+
}, undefined, location, this.context);
|
|
118
|
+
return ruleset;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** * SELECTORS ***/
|
|
123
|
+
/** @see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors */
|
|
124
|
+
/**
|
|
125
|
+
A selector with a single component, such as a single id selector
|
|
126
|
+
or type selector, that's not used in combination with or contains
|
|
127
|
+
any other selector component or combinator
|
|
128
|
+
.e.g `a` | `#selected` | `.foo`
|
|
129
|
+
|
|
130
|
+
@todo Define known pseudos
|
|
131
|
+
|
|
132
|
+
NOTE: A COLOR_IDENT_START token is a valid ID
|
|
133
|
+
*/
|
|
134
|
+
// simpleSelector
|
|
135
|
+
// : classSelector
|
|
136
|
+
// | ID
|
|
137
|
+
// | COLOR_IDENT_START
|
|
138
|
+
// | identifier
|
|
139
|
+
// | AMPERSAND
|
|
140
|
+
// | STAR
|
|
141
|
+
// | pseudoSelector
|
|
142
|
+
// | attributeSelector
|
|
143
|
+
// ;
|
|
144
|
+
export function simpleSelector(T, selectorAlt) {
|
|
145
|
+
const $ = this;
|
|
146
|
+
selectorAlt ??= (ctx = {}) => [
|
|
147
|
+
{
|
|
148
|
+
/**
|
|
149
|
+
* It used to be the case that, in CSS Nesting, the first selector
|
|
150
|
+
* could not be an identifier. However, it looks like that's no
|
|
151
|
+
* longer the case.
|
|
152
|
+
*
|
|
153
|
+
* @see: https://github.com/w3c/csswg-drafts/issues/9317
|
|
154
|
+
*/
|
|
155
|
+
ALT: () => $.CONSUME(T.Ident)
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
/** In CSS Nesting, outer selector can't contain an ampersand */
|
|
159
|
+
GATE: () => !!ctx.inner,
|
|
160
|
+
ALT: () => $.CONSUME(T.Ampersand)
|
|
161
|
+
},
|
|
162
|
+
{ ALT: () => $.SUBRULE($.classSelector, { ARGS: [ctx] }) },
|
|
163
|
+
{ ALT: () => $.SUBRULE($.idSelector, { ARGS: [ctx] }) },
|
|
164
|
+
{ ALT: () => $.CONSUME(T.Star) },
|
|
165
|
+
{ ALT: () => $.SUBRULE($.pseudoSelector, { ARGS: [ctx] }) },
|
|
166
|
+
{ ALT: () => $.SUBRULE($.attributeSelector, { ARGS: [ctx] }) },
|
|
167
|
+
/** Supports keyframes selectors */
|
|
168
|
+
{ ALT: () => $.CONSUME(T.DimensionInt) },
|
|
169
|
+
{ ALT: () => $.CONSUME(T.DimensionNum) }
|
|
170
|
+
];
|
|
171
|
+
return (ctx = {}) => {
|
|
172
|
+
let selector = $.OR(selectorAlt(ctx));
|
|
173
|
+
if (!$.RECORDING_PHASE) {
|
|
174
|
+
if ($.isToken(selector)) {
|
|
175
|
+
if (selector.tokenType.name === 'Ampersand') {
|
|
176
|
+
return new Ampersand(undefined, undefined, $.getLocationInfo(selector), this.context);
|
|
177
|
+
}
|
|
178
|
+
return new BasicSelector(selector.image, undefined, $.getLocationInfo(selector), this.context);
|
|
179
|
+
}
|
|
180
|
+
return selector;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// classSelector
|
|
185
|
+
// : DOT identifier
|
|
186
|
+
// ;
|
|
187
|
+
export function classSelector(T) {
|
|
188
|
+
const $ = this;
|
|
189
|
+
return () => {
|
|
190
|
+
let selector = $.CONSUME(T.DotName);
|
|
191
|
+
if (!$.RECORDING_PHASE) {
|
|
192
|
+
return new BasicSelector(selector.image, undefined, $.getLocationInfo(selector), this.context);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
export function idSelector(T, selectorAlt) {
|
|
197
|
+
const $ = this;
|
|
198
|
+
selectorAlt ??= (ctx = {}) => [
|
|
199
|
+
{ ALT: () => $.CONSUME(T.HashName) },
|
|
200
|
+
{ ALT: () => $.CONSUME(T.ColorIdentStart) }
|
|
201
|
+
];
|
|
202
|
+
/** #id, #FF0000 are both valid ids */
|
|
203
|
+
return (ctx = {}) => {
|
|
204
|
+
let selector = $.OR(selectorAlt(ctx));
|
|
205
|
+
if (!$.RECORDING_PHASE) {
|
|
206
|
+
return new BasicSelector(selector.image, undefined, $.getLocationInfo(selector), this.context);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
export function pseudoSelector(T, selectorAlt) {
|
|
211
|
+
const $ = this;
|
|
212
|
+
const createPseudo = (name, arg) => {
|
|
213
|
+
if (!$.RECORDING_PHASE) {
|
|
214
|
+
let location = $.endRule();
|
|
215
|
+
return new PseudoSelector({
|
|
216
|
+
name,
|
|
217
|
+
arg
|
|
218
|
+
}, undefined, location, this.context);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
selectorAlt ??= (ctx = {}) => [
|
|
222
|
+
{
|
|
223
|
+
ALT: () => {
|
|
224
|
+
let name = $.CONSUME(T.NthPseudoClass);
|
|
225
|
+
let val = $.SUBRULE($.nthValue, { ARGS: [ctx] });
|
|
226
|
+
$.CONSUME(T.RParen);
|
|
227
|
+
return createPseudo(name.image.slice(0, -1), val);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
ALT: () => {
|
|
232
|
+
let name = $.CONSUME(T.SelectorPseudoClass);
|
|
233
|
+
let val = $.SUBRULE($.forgivingSelectorList, { ARGS: [ctx] });
|
|
234
|
+
$.CONSUME2(T.RParen);
|
|
235
|
+
return createPseudo(name.image.slice(0, -1), val);
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
ALT: () => {
|
|
240
|
+
let name = $.CONSUME(T.Colon).image;
|
|
241
|
+
$.OPTION({
|
|
242
|
+
GATE: $.noSep,
|
|
243
|
+
DEF: () => {
|
|
244
|
+
name += $.CONSUME2(T.Colon).image;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
/**
|
|
248
|
+
* We use OR often to assert that no whitespace is allowed.
|
|
249
|
+
* There's no other way currently to do a positive-assertion Gate
|
|
250
|
+
* in Chevrotain.
|
|
251
|
+
*/
|
|
252
|
+
let values = $.OR4([
|
|
253
|
+
{
|
|
254
|
+
/** ::unknown(values) */
|
|
255
|
+
GATE: $.noSep,
|
|
256
|
+
ALT: () => {
|
|
257
|
+
name += $.CONSUME(T.GenericFunctionStart).image;
|
|
258
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
259
|
+
let values;
|
|
260
|
+
if (!RECORDING_PHASE) {
|
|
261
|
+
values = [];
|
|
262
|
+
name = name.slice(0, -1);
|
|
263
|
+
}
|
|
264
|
+
let valuesLocation;
|
|
265
|
+
$.startRule();
|
|
266
|
+
$.MANY(() => {
|
|
267
|
+
let val = $.SUBRULE($.anyInnerValue);
|
|
268
|
+
if (!RECORDING_PHASE) {
|
|
269
|
+
values.push(val);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
if (!RECORDING_PHASE) {
|
|
273
|
+
valuesLocation = $.endRule();
|
|
274
|
+
}
|
|
275
|
+
$.CONSUME3(T.RParen);
|
|
276
|
+
if (!RECORDING_PHASE && values.length) {
|
|
277
|
+
return new Sequence(values, undefined, valuesLocation, this.context);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
/** ::unknown */
|
|
283
|
+
GATE: $.noSep,
|
|
284
|
+
ALT: () => {
|
|
285
|
+
name += $.CONSUME(T.Ident).image;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
]);
|
|
289
|
+
return createPseudo(name, values);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
];
|
|
293
|
+
// pseudoSelector
|
|
294
|
+
// : NTH_PSEUDO_CLASS '(' WS* nthValue WS* ')'
|
|
295
|
+
// | FUNCTIONAL_PSEUDO_CLASS '(' WS* forgivingSelectorList WS* ')'
|
|
296
|
+
// | COLON COLON? identifier ('(' anyInnerValue* ')')?
|
|
297
|
+
// ;
|
|
298
|
+
return (ctx = {}) => {
|
|
299
|
+
$.startRule();
|
|
300
|
+
return $.OR(selectorAlt(ctx));
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
export function nthValue(T, valueAlt) {
|
|
304
|
+
const $ = this;
|
|
305
|
+
valueAlt ??= (ctx = {}) => {
|
|
306
|
+
return [
|
|
307
|
+
{ ALT: () => $.CONSUME(T.NthOdd) },
|
|
308
|
+
{ ALT: () => $.CONSUME(T.NthEven) },
|
|
309
|
+
{ ALT: () => $.CONSUME(T.Integer) },
|
|
310
|
+
{
|
|
311
|
+
ALT: () => {
|
|
312
|
+
$.OR2([
|
|
313
|
+
{ ALT: () => $.CONSUME(T.NthSignedDimension) },
|
|
314
|
+
{ ALT: () => $.CONSUME(T.NthUnsignedDimension) },
|
|
315
|
+
{ ALT: () => $.CONSUME(T.NthSignedPlus) },
|
|
316
|
+
{ ALT: () => $.CONSUME(T.NthIdent) }
|
|
317
|
+
]);
|
|
318
|
+
$.OPTION(() => {
|
|
319
|
+
$.OR3([
|
|
320
|
+
{ ALT: () => $.CONSUME(T.SignedInt) },
|
|
321
|
+
{
|
|
322
|
+
ALT: () => {
|
|
323
|
+
$.CONSUME(T.Minus);
|
|
324
|
+
$.CONSUME(T.UnsignedInt);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
]);
|
|
328
|
+
});
|
|
329
|
+
$.OPTION2(() => {
|
|
330
|
+
$.CONSUME(T.Of);
|
|
331
|
+
$.SUBRULE($.complexSelector, { ARGS: [ctx] });
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
];
|
|
336
|
+
};
|
|
337
|
+
/**
|
|
338
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child
|
|
339
|
+
*/
|
|
340
|
+
return (ctx = {}) => {
|
|
341
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
342
|
+
$.startRule();
|
|
343
|
+
let startTokenOffset;
|
|
344
|
+
if (!RECORDING_PHASE) {
|
|
345
|
+
startTokenOffset = this.LA(1).startOffset;
|
|
346
|
+
}
|
|
347
|
+
$.OR(valueAlt(ctx));
|
|
348
|
+
if (!RECORDING_PHASE) {
|
|
349
|
+
/** Coelesce all token values into one value */
|
|
350
|
+
let endTokenOffset = $.LA(-1).startOffset;
|
|
351
|
+
let location = $.endRule();
|
|
352
|
+
let origTokens = this.originalInput;
|
|
353
|
+
let origLength = origTokens.length;
|
|
354
|
+
let tokenValues = '';
|
|
355
|
+
for (let i = 0; i < origLength; i++) {
|
|
356
|
+
let token = origTokens[i];
|
|
357
|
+
if (token.startOffset >= startTokenOffset) {
|
|
358
|
+
tokenValues += token.image;
|
|
359
|
+
}
|
|
360
|
+
if (token.startOffset > endTokenOffset) {
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return $.wrap(new Any(tokenValues, { role: 'any' }, location, this.context), 'both');
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
// attributeSelector
|
|
369
|
+
// : LSQUARE WS* identifier (STAR | TILDE | CARET | DOLLAR | PIPE)? EQ WS* (identifier | STRING) WS* (ATTRIBUTE_FLAG WS*)? RSQUARE
|
|
370
|
+
// ;
|
|
371
|
+
export function attributeSelector(T, valueAlt) {
|
|
372
|
+
const $ = this;
|
|
373
|
+
valueAlt ??= (ctx = {}) => [
|
|
374
|
+
{
|
|
375
|
+
ALT: () => {
|
|
376
|
+
let token = $.CONSUME2(T.Ident);
|
|
377
|
+
if (!$.RECORDING_PHASE) {
|
|
378
|
+
return new Any(token.image, { role: 'ident' }, $.getLocationInfo(token), this.context);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
{ ALT: () => $.SUBRULE($.string) }
|
|
383
|
+
];
|
|
384
|
+
return (ctx = {}) => {
|
|
385
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
386
|
+
$.startRule();
|
|
387
|
+
$.CONSUME(T.LSquare);
|
|
388
|
+
let key = $.CONSUME(T.Ident);
|
|
389
|
+
let op;
|
|
390
|
+
let value;
|
|
391
|
+
let mod;
|
|
392
|
+
$.OPTION(() => {
|
|
393
|
+
op = $.OR([
|
|
394
|
+
{ ALT: () => $.CONSUME(T.Eq) },
|
|
395
|
+
{ ALT: () => $.CONSUME(T.AttrMatch) }
|
|
396
|
+
]);
|
|
397
|
+
value = $.OR2(valueAlt(ctx));
|
|
398
|
+
});
|
|
399
|
+
$.OPTION2(() => mod = $.CONSUME(T.AttrFlag));
|
|
400
|
+
$.CONSUME(T.RSquare);
|
|
401
|
+
if (!RECORDING_PHASE) {
|
|
402
|
+
let location = $.endRule();
|
|
403
|
+
return new AttributeSelector({
|
|
404
|
+
name: key.image,
|
|
405
|
+
op: op?.image,
|
|
406
|
+
value,
|
|
407
|
+
mod: mod?.image
|
|
408
|
+
}, undefined, location, this.context);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
export function compoundSelector(T) {
|
|
413
|
+
const $ = this;
|
|
414
|
+
/**
|
|
415
|
+
A sequence of simple selectors that are not separated by
|
|
416
|
+
a combinator.
|
|
417
|
+
.e.g. `a#selected`
|
|
418
|
+
*/
|
|
419
|
+
// compoundSelector
|
|
420
|
+
// : simpleSelector+
|
|
421
|
+
// ;
|
|
422
|
+
return (ctx = {}) => {
|
|
423
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
424
|
+
let selectors;
|
|
425
|
+
if (!RECORDING_PHASE) {
|
|
426
|
+
selectors = [];
|
|
427
|
+
}
|
|
428
|
+
let sel = $.SUBRULE($.simpleSelector, { ARGS: [ctx] });
|
|
429
|
+
if (!RECORDING_PHASE) {
|
|
430
|
+
selectors.push(sel);
|
|
431
|
+
}
|
|
432
|
+
$.MANY({
|
|
433
|
+
/** Make sure we don't ignore space combinators */
|
|
434
|
+
GATE: () => !$.hasWS(),
|
|
435
|
+
DEF: () => {
|
|
436
|
+
sel = $.SUBRULE2($.simpleSelector, { ARGS: [ctx] });
|
|
437
|
+
if (!RECORDING_PHASE) {
|
|
438
|
+
/** Make sure we don't add implicit whitespace */
|
|
439
|
+
sel.pre = 0;
|
|
440
|
+
selectors.push(sel);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
if (!RECORDING_PHASE) {
|
|
445
|
+
if (selectors.length === 1) {
|
|
446
|
+
return selectors[0];
|
|
447
|
+
}
|
|
448
|
+
return new CompoundSelector(selectors, undefined, $.getLocationFromNodes(selectors), this.context);
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* @param manyGate - Exposed for Less to exclude the keyword 'all' from the selector list
|
|
454
|
+
*/
|
|
455
|
+
export function complexSelector(T, manyGate) {
|
|
456
|
+
const $ = this;
|
|
457
|
+
manyGate ??= (ctx) => () => $.hasWS() || tokenMatcher($.LA(1), T.Combinator);
|
|
458
|
+
/**
|
|
459
|
+
A sequence of one or more simple and/or compound selectors
|
|
460
|
+
that are separated by combinators.
|
|
461
|
+
.e.g. a#selected > .icon
|
|
462
|
+
*/
|
|
463
|
+
// complexSelector
|
|
464
|
+
// : compoundSelector (WS* (combinator WS*)? compoundSelector)*
|
|
465
|
+
// ;
|
|
466
|
+
return (ctx = {}) => {
|
|
467
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
468
|
+
let GATE = manyGate(ctx);
|
|
469
|
+
$.startRule();
|
|
470
|
+
let selectors = [$.SUBRULE($.compoundSelector, { ARGS: [ctx] })];
|
|
471
|
+
/**
|
|
472
|
+
* Only space combinators and specified combinators will enter the MANY
|
|
473
|
+
*/
|
|
474
|
+
$.MANY({
|
|
475
|
+
GATE,
|
|
476
|
+
DEF: () => {
|
|
477
|
+
let co;
|
|
478
|
+
let combinator;
|
|
479
|
+
$.OPTION(() => {
|
|
480
|
+
co = $.CONSUME(T.Combinator);
|
|
481
|
+
});
|
|
482
|
+
if (!RECORDING_PHASE) {
|
|
483
|
+
if (co) {
|
|
484
|
+
combinator = $.wrap(new Combinator(co.image, undefined, $.getLocationInfo(co), this.context), 'both');
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
/** Whitespace combinators are special */
|
|
488
|
+
let startOffset = this.LA(1).startOffset;
|
|
489
|
+
/**
|
|
490
|
+
* Technically, a whitespace combinator may not actually _include_
|
|
491
|
+
* a literal space (it can be a newline, for example), but we'll just use a
|
|
492
|
+
* space for now.
|
|
493
|
+
*/
|
|
494
|
+
combinator = new Combinator(' ', undefined, undefined, this.context);
|
|
495
|
+
let pre = $.getPrePost(startOffset);
|
|
496
|
+
if (pre === 1) {
|
|
497
|
+
pre = 0;
|
|
498
|
+
}
|
|
499
|
+
else if (pre) {
|
|
500
|
+
let last = pre[pre.length - 1];
|
|
501
|
+
if (typeof last === 'string' && last.endsWith(' ')) {
|
|
502
|
+
/** remove the last character if a space */
|
|
503
|
+
pre[pre.length - 1] = last.slice(0, -1);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
combinator.pre = pre;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
let compound = $.SUBRULE2($.compoundSelector, { ARGS: [ctx] });
|
|
510
|
+
if (!RECORDING_PHASE) {
|
|
511
|
+
selectors.push(combinator, compound);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
if (!RECORDING_PHASE) {
|
|
516
|
+
let location = $.endRule();
|
|
517
|
+
if (selectors.length === 1) {
|
|
518
|
+
return selectors[0];
|
|
519
|
+
}
|
|
520
|
+
return new ComplexSelector(selectors, undefined, location, this.context);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
A selector representing an element relative to one or more
|
|
526
|
+
anchor elements preceded by a combinator.
|
|
527
|
+
e.g. + div#topic > #reference
|
|
528
|
+
*/
|
|
529
|
+
// relativeSelector
|
|
530
|
+
// : (combinator WS*)? complexSelector
|
|
531
|
+
// ;
|
|
532
|
+
export function relativeSelector(T) {
|
|
533
|
+
const $ = this;
|
|
534
|
+
return (ctx = {}) => {
|
|
535
|
+
return $.OR([
|
|
536
|
+
{
|
|
537
|
+
ALT: () => {
|
|
538
|
+
let co = $.CONSUME(T.Combinator);
|
|
539
|
+
let complex = $.SUBRULE($.complexSelector, { ARGS: [ctx] });
|
|
540
|
+
if (!$.RECORDING_PHASE) {
|
|
541
|
+
let combinator = new Combinator(co.image, undefined, $.getLocationInfo(co), this.context);
|
|
542
|
+
if (complex instanceof ComplexSelector) {
|
|
543
|
+
complex.value.unshift(combinator);
|
|
544
|
+
let location = complex.location;
|
|
545
|
+
location[0] = co.startOffset;
|
|
546
|
+
location[1] = co.startLine;
|
|
547
|
+
location[2] = co.startColumn;
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
complex = new ComplexSelector([combinator, complex], undefined, $.getLocationFromNodes([combinator, complex]), this.context);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return complex;
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
ALT: () => $.SUBRULE2($.complexSelector, { ARGS: [ctx] })
|
|
558
|
+
}
|
|
559
|
+
]);
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
export function forgivingSelectorList(T) {
|
|
563
|
+
const $ = this;
|
|
564
|
+
/**
|
|
565
|
+
https://www.w3.org/TR/css-nesting-1/
|
|
566
|
+
|
|
567
|
+
NOTE: implementers should throw a parsing
|
|
568
|
+
error if the selectorlist starts with an identifier
|
|
569
|
+
*/
|
|
570
|
+
// forgivingSelectorList
|
|
571
|
+
// : relativeSelector (WS* COMMA WS* relativeSelector)*
|
|
572
|
+
// ;
|
|
573
|
+
return (ctx = {}) => {
|
|
574
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
575
|
+
$.startRule();
|
|
576
|
+
let sequences;
|
|
577
|
+
let i = 0;
|
|
578
|
+
if (!RECORDING_PHASE) {
|
|
579
|
+
sequences = [];
|
|
580
|
+
}
|
|
581
|
+
$.AT_LEAST_ONE_SEP({
|
|
582
|
+
SEP: T.Comma,
|
|
583
|
+
DEF: () => {
|
|
584
|
+
let selector = $.SUBRULE($.relativeSelector, { ARGS: [ctx] });
|
|
585
|
+
if (!RECORDING_PHASE) {
|
|
586
|
+
i++;
|
|
587
|
+
if (i === 1 && ctx.qualifiedRule) {
|
|
588
|
+
// Only attach post; leave pre for the parent Rules to lift comments
|
|
589
|
+
sequences.push($.wrap(selector, true));
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
sequences.push($.wrap(selector, i === 1 ? true : 'both'));
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
if (!RECORDING_PHASE) {
|
|
598
|
+
let location = $.endRule();
|
|
599
|
+
if (sequences.length === 1) {
|
|
600
|
+
return sequences[0];
|
|
601
|
+
}
|
|
602
|
+
return new SelectorList(sequences, undefined, location, this.context);
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
export function selectorList(T) {
|
|
607
|
+
const $ = this;
|
|
608
|
+
// selectorList
|
|
609
|
+
// : complexSelector (WS* COMMA WS* complexSelector)*
|
|
610
|
+
// ;
|
|
611
|
+
return (ctx = {}) => {
|
|
612
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
613
|
+
$.startRule();
|
|
614
|
+
let i = 0;
|
|
615
|
+
let sequences;
|
|
616
|
+
if (!RECORDING_PHASE) {
|
|
617
|
+
sequences = [];
|
|
618
|
+
}
|
|
619
|
+
$.AT_LEAST_ONE_SEP({
|
|
620
|
+
SEP: T.Comma,
|
|
621
|
+
DEF: () => {
|
|
622
|
+
let sel = $.SUBRULE2($.complexSelector, { ARGS: [ctx] });
|
|
623
|
+
if (!RECORDING_PHASE) {
|
|
624
|
+
i++;
|
|
625
|
+
// Do not consume leading pre for the first selector of a qualified rule,
|
|
626
|
+
// so that pre-rule comments remain available to be lifted to Rules.
|
|
627
|
+
if (i === 1 && ctx.qualifiedRule) {
|
|
628
|
+
// Only attach post; leave pre for the parent Rules to lift comments
|
|
629
|
+
sequences.push($.wrap(sel, true));
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
sequences.push($.wrap(sel, i === 1 ? true : 'both'));
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
if (!RECORDING_PHASE) {
|
|
638
|
+
let location = $.endRule();
|
|
639
|
+
if (sequences.length === 1) {
|
|
640
|
+
return sequences[0];
|
|
641
|
+
}
|
|
642
|
+
return new SelectorList(sequences, undefined, location, this.context);
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
export function declarationList(T, alt) {
|
|
647
|
+
const $ = this;
|
|
648
|
+
/** * Declarations ***/
|
|
649
|
+
// https://www.w3.org/TR/css-syntax-3/#declaration-list-diagram
|
|
650
|
+
// declarationList
|
|
651
|
+
// : WS* (
|
|
652
|
+
// declaration? (WS* SEMI declarationList)*
|
|
653
|
+
// | innerAtRule declarationList
|
|
654
|
+
// | innerQualifiedRule declarationList
|
|
655
|
+
// )
|
|
656
|
+
// ;
|
|
657
|
+
/**
|
|
658
|
+
* Originally this was structured much like the CSS spec,
|
|
659
|
+
* like this:
|
|
660
|
+
* $.OPTION(() => $.SUBRULE($.declaration))
|
|
661
|
+
* $.OPTION2(() => {
|
|
662
|
+
* $.CONSUME(T.Semi)
|
|
663
|
+
* $.SUBRULE3($.declarationList)
|
|
664
|
+
* })
|
|
665
|
+
* ...but chevrotain-allstar doesn't deal well with
|
|
666
|
+
* recursivity, as it predicts the ENTIRE path for
|
|
667
|
+
* each alt
|
|
668
|
+
*/
|
|
669
|
+
alt ??= (ctx = {}) => [
|
|
670
|
+
{ ALT: () => $.SUBRULE($.declaration, { ARGS: [ctx] }) },
|
|
671
|
+
{ ALT: () => $.SUBRULE($.innerAtRule, { ARGS: [{ ...ctx, inner: true }] }) },
|
|
672
|
+
{ ALT: () => $.SUBRULE2($.qualifiedRule, { ARGS: [{ ...ctx, inner: true }] }) },
|
|
673
|
+
{ ALT: () => $.CONSUME2(T.Semi) }
|
|
674
|
+
];
|
|
675
|
+
return main.call(this, T, alt);
|
|
676
|
+
}
|
|
677
|
+
export function declaration(T, alt) {
|
|
678
|
+
const $ = this;
|
|
679
|
+
alt ??= (ctx = {}) => [
|
|
680
|
+
{
|
|
681
|
+
ALT: () => {
|
|
682
|
+
let name;
|
|
683
|
+
$.OR2([
|
|
684
|
+
{
|
|
685
|
+
ALT: () => name = $.CONSUME(T.Ident)
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
GATE: () => $.legacyMode,
|
|
689
|
+
ALT: () => name = $.CONSUME(T.LegacyPropIdent)
|
|
690
|
+
}
|
|
691
|
+
]);
|
|
692
|
+
let assign = $.CONSUME(T.Assign);
|
|
693
|
+
let value = $.SUBRULE($.valueList, { ARGS: [ctx] });
|
|
694
|
+
let important;
|
|
695
|
+
$.OPTION(() => {
|
|
696
|
+
important = $.CONSUME(T.Important);
|
|
697
|
+
});
|
|
698
|
+
if (!$.RECORDING_PHASE) {
|
|
699
|
+
let nameNode = $.wrap(new Any(name.image, { role: 'property' }, $.getLocationInfo(name), this.context), true);
|
|
700
|
+
return [nameNode, assign, value, important];
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
ALT: () => {
|
|
706
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
707
|
+
let name = $.CONSUME(T.CustomProperty);
|
|
708
|
+
let assign = $.CONSUME2(T.Assign);
|
|
709
|
+
let nodes;
|
|
710
|
+
if (!RECORDING_PHASE) {
|
|
711
|
+
nodes = [];
|
|
712
|
+
}
|
|
713
|
+
$.startRule();
|
|
714
|
+
$.MANY(() => {
|
|
715
|
+
let val = $.SUBRULE($.customValue, { ARGS: [{ ...ctx, inCustomPropertyValue: true }] });
|
|
716
|
+
if (!RECORDING_PHASE) {
|
|
717
|
+
nodes.push(val);
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
if (!RECORDING_PHASE) {
|
|
721
|
+
let location = $.endRule();
|
|
722
|
+
let nameNode = $.wrap(new Any(name.image, { role: 'property' }, $.getLocationInfo(name), this.context), true);
|
|
723
|
+
let value = new Sequence(nodes, undefined, location, this.context);
|
|
724
|
+
return [nameNode, assign, value];
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
];
|
|
729
|
+
// declaration
|
|
730
|
+
// : identifier WS* COLON WS* valueList (WS* IMPORTANT)?
|
|
731
|
+
// | CUSTOM_IDENT WS* COLON CUSTOM_VALUE*
|
|
732
|
+
// ;
|
|
733
|
+
return (ctx = {}) => {
|
|
734
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
735
|
+
$.startRule();
|
|
736
|
+
let name;
|
|
737
|
+
let assign;
|
|
738
|
+
let value;
|
|
739
|
+
let important;
|
|
740
|
+
let val = $.OR(alt(ctx));
|
|
741
|
+
if (!RECORDING_PHASE) {
|
|
742
|
+
([name, assign, value, important] = val);
|
|
743
|
+
}
|
|
744
|
+
if (!RECORDING_PHASE) {
|
|
745
|
+
let location = $.endRule();
|
|
746
|
+
const isCustom = name.valueOf().startsWith('--');
|
|
747
|
+
const wrapCtx = isCustom ? { ...ctx, inCustomPropertyValue: true } : ctx;
|
|
748
|
+
return new (isCustom ? CustomDeclaration : Declaration)({
|
|
749
|
+
name: name,
|
|
750
|
+
value: $.wrap(value, 'both', wrapCtx),
|
|
751
|
+
important: important ? $.wrap(new Any(important.image, { role: 'flag' }, $.getLocationInfo(important), this.context), 'both') : undefined
|
|
752
|
+
}, { assign: assign.image }, location, this.context);
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* @todo - This could be implemented with a multi-mode lexer?
|
|
758
|
+
* Multi-modes was the right way to do it with Antlr, but
|
|
759
|
+
* Chevrotain does not support recursive tokens very well.
|
|
760
|
+
*/
|
|
761
|
+
export function customValue(T, alt) {
|
|
762
|
+
const $ = this;
|
|
763
|
+
/** Should be almost anything, but custom blocks need matching closers */
|
|
764
|
+
// Order matters: prefer nested blocks first, then strings, then raw tokens.
|
|
765
|
+
// Avoid knownFunctions here to remove ambiguity with custom blocks.
|
|
766
|
+
alt ??= (ctx = {}) => [
|
|
767
|
+
{
|
|
768
|
+
ALT: () => {
|
|
769
|
+
return $.SUBRULE($.customBlock, { ARGS: [ctx] });
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
ALT: () => {
|
|
774
|
+
return $.SUBRULE($.string, { ARGS: [ctx] });
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
ALT: () => {
|
|
779
|
+
const token = $.OR3([
|
|
780
|
+
{ ALT: () => $.CONSUME(T.Value) },
|
|
781
|
+
{ ALT: () => $.CONSUME(T.CustomProperty) },
|
|
782
|
+
{ ALT: () => $.CONSUME(T.Colon) },
|
|
783
|
+
{ ALT: () => $.CONSUME(T.AtName) },
|
|
784
|
+
{ ALT: () => $.CONSUME(T.Comma) },
|
|
785
|
+
{ ALT: () => $.CONSUME(T.Important) },
|
|
786
|
+
{ ALT: () => $.CONSUME(T.Unknown) }
|
|
787
|
+
]);
|
|
788
|
+
if (!$.RECORDING_PHASE) {
|
|
789
|
+
return $.wrap($.processValueToken(token, ctx), undefined, ctx);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
];
|
|
794
|
+
return (ctx = {}) => {
|
|
795
|
+
return $.OR(alt(ctx));
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
export function innerCustomValue(T, alt) {
|
|
799
|
+
const $ = this;
|
|
800
|
+
alt ??= (ctx = {}) => [
|
|
801
|
+
{
|
|
802
|
+
ALT: () => {
|
|
803
|
+
/** Can also have semi-colons */
|
|
804
|
+
let semi = $.CONSUME(T.Semi);
|
|
805
|
+
if (!$.RECORDING_PHASE) {
|
|
806
|
+
return $.wrap(new Any(semi.image, { role: 'semi' }, $.getLocationInfo(semi), this.context));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
},
|
|
810
|
+
{ ALT: () => $.SUBRULE($.customValue, { ARGS: [ctx] }) }
|
|
811
|
+
];
|
|
812
|
+
return (ctx = {}) => $.OR(alt(ctx));
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Extra tokens in a custom property or general enclosed. Should include any
|
|
816
|
+
* and every token possible (except semis), including unknown tokens.
|
|
817
|
+
*
|
|
818
|
+
* @todo - In tests, is there a way to test that every token is captured?
|
|
819
|
+
*/
|
|
820
|
+
export function extraTokens(T, alt) {
|
|
821
|
+
const $ = this;
|
|
822
|
+
alt ??= (ctx = {}) => [
|
|
823
|
+
{ ALT: () => $.SUBRULE($.functionCallLike, { ARGS: [ctx] }) },
|
|
824
|
+
{ ALT: () => $.CONSUME(T.Value) },
|
|
825
|
+
{ ALT: () => $.CONSUME(T.CustomProperty) },
|
|
826
|
+
{ ALT: () => $.CONSUME(T.Colon) },
|
|
827
|
+
{ ALT: () => $.CONSUME(T.AtName) },
|
|
828
|
+
{ ALT: () => $.CONSUME(T.Comma) },
|
|
829
|
+
{ ALT: () => $.CONSUME(T.Important) },
|
|
830
|
+
{ ALT: () => $.CONSUME(T.Unknown) }
|
|
831
|
+
];
|
|
832
|
+
return (ctx = {}) => {
|
|
833
|
+
let node = $.OR(alt(ctx));
|
|
834
|
+
if (!$.RECORDING_PHASE) {
|
|
835
|
+
if (!(node instanceof Node)) {
|
|
836
|
+
node = $.wrap($.processValueToken(node));
|
|
837
|
+
}
|
|
838
|
+
return node;
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
export function customBlock(T, alt) {
|
|
843
|
+
const $ = this;
|
|
844
|
+
alt ??= (ctx = {}) => [
|
|
845
|
+
{
|
|
846
|
+
ALT: () => {
|
|
847
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
848
|
+
let start;
|
|
849
|
+
let end;
|
|
850
|
+
let nodes;
|
|
851
|
+
if (!RECORDING_PHASE) {
|
|
852
|
+
nodes = [];
|
|
853
|
+
}
|
|
854
|
+
$.OR2([
|
|
855
|
+
/**
|
|
856
|
+
* All tokens that have a left parentheses.
|
|
857
|
+
* These need to match a right parentheses.
|
|
858
|
+
*/
|
|
859
|
+
{ ALT: () => start = $.CONSUME(T.LParen) },
|
|
860
|
+
{ ALT: () => start = $.CONSUME(T.FunctionStart) },
|
|
861
|
+
{ ALT: () => start = $.CONSUME(T.FunctionalPseudoClass) }
|
|
862
|
+
]);
|
|
863
|
+
$.MANY(() => {
|
|
864
|
+
let val = $.SUBRULE($.innerCustomValue, { ARGS: [ctx] });
|
|
865
|
+
if (!$.RECORDING_PHASE) {
|
|
866
|
+
nodes.push(val);
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
end = $.CONSUME(T.RParen);
|
|
870
|
+
return [start, nodes, end];
|
|
871
|
+
}
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
ALT: () => {
|
|
875
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
876
|
+
let nodes;
|
|
877
|
+
if (!RECORDING_PHASE) {
|
|
878
|
+
nodes = [];
|
|
879
|
+
}
|
|
880
|
+
let start = $.CONSUME(T.LSquare);
|
|
881
|
+
$.MANY2(() => {
|
|
882
|
+
let val = $.SUBRULE2($.innerCustomValue, { ARGS: [ctx] });
|
|
883
|
+
if (!RECORDING_PHASE) {
|
|
884
|
+
nodes.push(val);
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
let end = $.CONSUME(T.RSquare);
|
|
888
|
+
return [start, nodes, end];
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
ALT: () => {
|
|
893
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
894
|
+
let nodes;
|
|
895
|
+
if (!RECORDING_PHASE) {
|
|
896
|
+
nodes = [];
|
|
897
|
+
}
|
|
898
|
+
let start = $.CONSUME(T.LCurly);
|
|
899
|
+
$.MANY3(() => {
|
|
900
|
+
let val = $.SUBRULE3($.innerCustomValue, { ARGS: [ctx] });
|
|
901
|
+
if (!RECORDING_PHASE) {
|
|
902
|
+
nodes.push(val);
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
let end = $.CONSUME(T.RCurly);
|
|
906
|
+
return [start, nodes, end];
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
];
|
|
910
|
+
return (ctx = {}) => {
|
|
911
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
912
|
+
$.startRule();
|
|
913
|
+
let start;
|
|
914
|
+
let end;
|
|
915
|
+
let nodes;
|
|
916
|
+
let val = $.OR(alt(ctx));
|
|
917
|
+
if (!RECORDING_PHASE) {
|
|
918
|
+
([start, nodes, end] = val);
|
|
919
|
+
}
|
|
920
|
+
if (!RECORDING_PHASE) {
|
|
921
|
+
let location = $.endRule();
|
|
922
|
+
let type;
|
|
923
|
+
switch (start.image) {
|
|
924
|
+
case '[':
|
|
925
|
+
type = 'square';
|
|
926
|
+
break;
|
|
927
|
+
case '{':
|
|
928
|
+
type = 'curly';
|
|
929
|
+
break;
|
|
930
|
+
}
|
|
931
|
+
if (type) {
|
|
932
|
+
// Preserve inner sequence post so trailing semicolons become part of block content
|
|
933
|
+
const seqLoc = nodes.length ? $.getLocationFromNodes(nodes) : undefined;
|
|
934
|
+
let seq = new Sequence(nodes, undefined, seqLoc, this.context);
|
|
935
|
+
return $.wrap(new Block($.wrap(seq, true, ctx), { type }, location, this.context), undefined, ctx);
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
let startNode = $.wrap(new Any(start.image, { role: 'any' }, $.getLocationInfo(start), this.context), undefined, ctx);
|
|
939
|
+
let endNode = $.wrap(new Any(end.image, { role: 'any' }, $.getLocationInfo(end), this.context), undefined, ctx);
|
|
940
|
+
nodes = [startNode, ...nodes, endNode];
|
|
941
|
+
return new Sequence(nodes, undefined, location, this.context);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
export function valueList(T) {
|
|
947
|
+
const $ = this;
|
|
948
|
+
/** Values separated by commas */
|
|
949
|
+
// valueList
|
|
950
|
+
// : value+ (, value+)*
|
|
951
|
+
// ;
|
|
952
|
+
return (ctx = {}) => {
|
|
953
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
954
|
+
$.startRule();
|
|
955
|
+
let nodes;
|
|
956
|
+
if (!RECORDING_PHASE) {
|
|
957
|
+
nodes = [];
|
|
958
|
+
}
|
|
959
|
+
$.AT_LEAST_ONE_SEP({
|
|
960
|
+
SEP: T.Comma,
|
|
961
|
+
DEF: () => {
|
|
962
|
+
let seq = $.SUBRULE($.valueSequence, { ARGS: [ctx] });
|
|
963
|
+
if (!RECORDING_PHASE) {
|
|
964
|
+
nodes.push(seq);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
});
|
|
968
|
+
if (!RECORDING_PHASE) {
|
|
969
|
+
let location = $.endRule();
|
|
970
|
+
if (nodes.length === 1) {
|
|
971
|
+
return nodes[0];
|
|
972
|
+
}
|
|
973
|
+
return new List(nodes, undefined, location, this.context);
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
export function valueSequence(T) {
|
|
978
|
+
const $ = this;
|
|
979
|
+
/** Often space-separated */
|
|
980
|
+
return (ctx = {}) => {
|
|
981
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
982
|
+
$.startRule();
|
|
983
|
+
let nodes;
|
|
984
|
+
if (!RECORDING_PHASE) {
|
|
985
|
+
nodes = [];
|
|
986
|
+
}
|
|
987
|
+
$.AT_LEAST_ONE(() => {
|
|
988
|
+
let value = $.SUBRULE($.value, { ARGS: [ctx] });
|
|
989
|
+
if (!RECORDING_PHASE) {
|
|
990
|
+
nodes.push($.wrap(value));
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
if (!RECORDING_PHASE) {
|
|
994
|
+
let location = $.endRule();
|
|
995
|
+
if (nodes.length === 1) {
|
|
996
|
+
return $.wrap(nodes[0], true);
|
|
997
|
+
}
|
|
998
|
+
return $.wrap(new Sequence(nodes, undefined, location, this.context), true);
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
export function squareValue(T) {
|
|
1003
|
+
const $ = this;
|
|
1004
|
+
return (ctx = {}) => {
|
|
1005
|
+
$.startRule();
|
|
1006
|
+
$.CONSUME(T.LSquare);
|
|
1007
|
+
let ident = $.CONSUME(T.Ident);
|
|
1008
|
+
$.CONSUME(T.RSquare);
|
|
1009
|
+
if (!$.RECORDING_PHASE) {
|
|
1010
|
+
let location = $.endRule();
|
|
1011
|
+
let identNode = new Any(ident.image, { role: 'ident' }, $.getLocationInfo(ident), this.context);
|
|
1012
|
+
return new Block(identNode, { type: 'square' }, location, this.context);
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
// value
|
|
1017
|
+
// : WS
|
|
1018
|
+
// | identifier
|
|
1019
|
+
// | integer
|
|
1020
|
+
// | number
|
|
1021
|
+
// | dimension
|
|
1022
|
+
// | COLOR_IDENT_START
|
|
1023
|
+
// | COLOR_INT_START
|
|
1024
|
+
// | STRING
|
|
1025
|
+
// | function
|
|
1026
|
+
// | '[' identifier ']'
|
|
1027
|
+
// | unknownValue
|
|
1028
|
+
// ;
|
|
1029
|
+
export function value(T, valueAlt) {
|
|
1030
|
+
const $ = this;
|
|
1031
|
+
valueAlt ??= (ctx = {}) => [
|
|
1032
|
+
/** Function should appear before Ident */
|
|
1033
|
+
{ ALT: () => $.SUBRULE($.functionCall, { ARGS: [ctx] }) },
|
|
1034
|
+
{ ALT: () => $.CONSUME(T.Ident) },
|
|
1035
|
+
{ ALT: () => $.CONSUME(T.Dimension) },
|
|
1036
|
+
{ ALT: () => $.CONSUME(T.Number) },
|
|
1037
|
+
{ ALT: () => $.CONSUME(T.Color) },
|
|
1038
|
+
{ ALT: () => $.CONSUME(T.UnicodeRange) },
|
|
1039
|
+
{ ALT: () => $.SUBRULE($.string, { ARGS: [ctx] }) },
|
|
1040
|
+
{ ALT: () => $.SUBRULE($.squareValue, { ARGS: [ctx] }) },
|
|
1041
|
+
{
|
|
1042
|
+
/** e.g. progid:DXImageTransform.Microsoft.Blur(pixelradius=2) */
|
|
1043
|
+
GATE: () => $.legacyMode,
|
|
1044
|
+
ALT: () => $.CONSUME(T.LegacyMSFilter)
|
|
1045
|
+
}
|
|
1046
|
+
];
|
|
1047
|
+
return (ctx = {}) => {
|
|
1048
|
+
$.startRule();
|
|
1049
|
+
let node = $.OR(valueAlt(ctx));
|
|
1050
|
+
/**
|
|
1051
|
+
* Allows slash separators. Note that, structurally, the meaning
|
|
1052
|
+
* of slash separators in CSS is inconsistent and ambiguous. It
|
|
1053
|
+
* could separate a sequence of tokens from another sequence,
|
|
1054
|
+
* or it could separate ONE token from another, with other tokens
|
|
1055
|
+
* not included in the "slash list", OR it can represent division
|
|
1056
|
+
* in a math expression. CSS is just, unfortunately, not a very
|
|
1057
|
+
* syntactically-consistent language, and each property's value
|
|
1058
|
+
* essentially has a defined "micro-syntax".
|
|
1059
|
+
*/
|
|
1060
|
+
let additionalValue;
|
|
1061
|
+
$.OPTION(() => {
|
|
1062
|
+
$.CONSUME(T.Slash);
|
|
1063
|
+
additionalValue = $.SUBRULE($.value, { ARGS: [ctx] });
|
|
1064
|
+
});
|
|
1065
|
+
if (!$.RECORDING_PHASE) {
|
|
1066
|
+
let location = $.endRule();
|
|
1067
|
+
if (!(node instanceof Node)) {
|
|
1068
|
+
node = $.processValueToken(node);
|
|
1069
|
+
}
|
|
1070
|
+
if (additionalValue) {
|
|
1071
|
+
return $.wrap(new List([$.wrap(node, true), additionalValue], { sep: '/' }, location, this.context));
|
|
1072
|
+
}
|
|
1073
|
+
return $.wrap(node);
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
export function string(T, stringAlt) {
|
|
1078
|
+
const $ = this;
|
|
1079
|
+
stringAlt ??= (ctx = {}) => [
|
|
1080
|
+
{
|
|
1081
|
+
ALT: () => {
|
|
1082
|
+
$.startRule();
|
|
1083
|
+
let quote = $.CONSUME(T.SingleQuoteStart);
|
|
1084
|
+
let contents;
|
|
1085
|
+
$.OPTION(() => contents = $.CONSUME(T.SingleQuoteStringContents));
|
|
1086
|
+
$.CONSUME(T.SingleQuoteEnd);
|
|
1087
|
+
if (!$.RECORDING_PHASE) {
|
|
1088
|
+
let location = $.endRule();
|
|
1089
|
+
const escaped = quote.image.startsWith('~');
|
|
1090
|
+
const quoteChar = quote.image.replace(/^~/, '');
|
|
1091
|
+
let value = contents?.image ?? '';
|
|
1092
|
+
if (escaped) {
|
|
1093
|
+
value = value.replace(/\\(?:\r\n?|\n|\f)/g, '\n');
|
|
1094
|
+
}
|
|
1095
|
+
return new Quoted(new Any(value, { role: 'any' }), { quote: quoteChar, escaped }, location, this.context);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
},
|
|
1099
|
+
{
|
|
1100
|
+
ALT: () => {
|
|
1101
|
+
$.startRule();
|
|
1102
|
+
let quote = $.CONSUME(T.DoubleQuoteStart);
|
|
1103
|
+
let contents;
|
|
1104
|
+
$.OPTION2(() => contents = $.CONSUME(T.DoubleQuoteStringContents));
|
|
1105
|
+
$.CONSUME(T.DoubleQuoteEnd);
|
|
1106
|
+
if (!$.RECORDING_PHASE) {
|
|
1107
|
+
let location = $.endRule();
|
|
1108
|
+
const escaped = quote.image.startsWith('~');
|
|
1109
|
+
const quoteChar = quote.image.replace(/^~/, '');
|
|
1110
|
+
let value = contents?.image ?? '';
|
|
1111
|
+
if (escaped) {
|
|
1112
|
+
value = value.replace(/\\(?:\r\n?|\n|\f)/g, '\n');
|
|
1113
|
+
}
|
|
1114
|
+
return new Quoted(new Any(value, { role: 'any' }), { quote: quoteChar, escaped }, location, this.context);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
];
|
|
1119
|
+
return (ctx = {}) => $.OR(stringAlt(ctx));
|
|
1120
|
+
}
|
|
1121
|
+
/** Implementers can decide to throw errors or warnings or not */
|
|
1122
|
+
// unknownValue
|
|
1123
|
+
// : COLON
|
|
1124
|
+
// | EQ
|
|
1125
|
+
// | DOT
|
|
1126
|
+
// | ID
|
|
1127
|
+
// | UNKNOWN
|
|
1128
|
+
// | AT_RULE
|
|
1129
|
+
// ;
|
|
1130
|
+
// $.RULE('unknownValue', () => {
|
|
1131
|
+
// $.OR([
|
|
1132
|
+
// { ALT: () => $.CONSUME(T.Colon) },
|
|
1133
|
+
// { ALT: () => $.CONSUME(T.Eq) },
|
|
1134
|
+
// { ALT: () => $.CONSUME(T.DotName) },
|
|
1135
|
+
// { ALT: () => $.CONSUME(T.HashName) },
|
|
1136
|
+
// { ALT: () => $.CONSUME(T.Unknown) },
|
|
1137
|
+
// { ALT: () => $.CONSUME(T.AtName) }
|
|
1138
|
+
// ])
|
|
1139
|
+
// })
|
|
1140
|
+
/** Abstracted for easy over-ride */
|
|
1141
|
+
// $.RULE('expression', () => {
|
|
1142
|
+
// $.SUBRULE($.mathSum)
|
|
1143
|
+
// })
|
|
1144
|
+
export function mathSum(T) {
|
|
1145
|
+
const $ = this;
|
|
1146
|
+
let opAlt = [
|
|
1147
|
+
{ ALT: () => $.CONSUME(T.Plus) },
|
|
1148
|
+
{ ALT: () => $.CONSUME(T.Minus) }
|
|
1149
|
+
];
|
|
1150
|
+
// mathSum
|
|
1151
|
+
// : mathProduct (WS* ('+' | '-') WS* mathProduct)*
|
|
1152
|
+
// ;
|
|
1153
|
+
return (ctx = {}) => {
|
|
1154
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1155
|
+
$.startRule();
|
|
1156
|
+
let left = $.SUBRULE($.mathProduct, { ARGS: [ctx] });
|
|
1157
|
+
$.MANY(() => {
|
|
1158
|
+
let op = $.OR(opAlt);
|
|
1159
|
+
let right = $.SUBRULE2($.mathProduct, { ARGS: [ctx] });
|
|
1160
|
+
if (!RECORDING_PHASE) {
|
|
1161
|
+
left = new Operation([left, op.image, right], { inCalc: true }, undefined, this.context);
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
if (!RECORDING_PHASE) {
|
|
1165
|
+
left._location = $.endRule();
|
|
1166
|
+
return left;
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
// mathProduct
|
|
1171
|
+
// : mathValue (WS* ('*' | '/') WS* mathValue)*
|
|
1172
|
+
// ;
|
|
1173
|
+
export function mathProduct(T) {
|
|
1174
|
+
const $ = this;
|
|
1175
|
+
let opAlt = [
|
|
1176
|
+
{ ALT: () => $.CONSUME(T.Star) },
|
|
1177
|
+
{ ALT: () => $.CONSUME(T.Divide) }
|
|
1178
|
+
];
|
|
1179
|
+
return (ctx = {}) => {
|
|
1180
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1181
|
+
$.startRule();
|
|
1182
|
+
let left = $.SUBRULE($.mathValue, { ARGS: [ctx] });
|
|
1183
|
+
$.MANY(() => {
|
|
1184
|
+
let op = $.OR(opAlt);
|
|
1185
|
+
let right = $.SUBRULE2($.mathValue, { ARGS: [ctx] });
|
|
1186
|
+
if (!RECORDING_PHASE) {
|
|
1187
|
+
left = new Operation([left, op.image, right], { inCalc: true }, undefined, this.context);
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
if (!RECORDING_PHASE) {
|
|
1191
|
+
left._location = $.endRule();
|
|
1192
|
+
return left;
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
// mathValue
|
|
1197
|
+
// : number
|
|
1198
|
+
// | dimension
|
|
1199
|
+
// | percentage
|
|
1200
|
+
// | mathConstant
|
|
1201
|
+
// | '(' WS* mathSum WS* ')'
|
|
1202
|
+
// ;
|
|
1203
|
+
export function mathValue(T, alt) {
|
|
1204
|
+
const $ = this;
|
|
1205
|
+
alt ??= (ctx = {}) => [
|
|
1206
|
+
{ ALT: () => $.CONSUME(T.Number) },
|
|
1207
|
+
{ ALT: () => $.CONSUME(T.Dimension) },
|
|
1208
|
+
// Allow identifiers like channel names in color space calcs (e.g., calc(l - 0.1))
|
|
1209
|
+
{ ALT: () => $.CONSUME(T.Ident) },
|
|
1210
|
+
{ ALT: () => $.CONSUME(T.MathConstant) },
|
|
1211
|
+
{ ALT: () => $.SUBRULE($.knownFunctions, { ARGS: [ctx] }) },
|
|
1212
|
+
{ ALT: () => $.SUBRULE($.mathParen, { ARGS: [ctx] }) }
|
|
1213
|
+
];
|
|
1214
|
+
return (ctx = {}) => {
|
|
1215
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1216
|
+
let node = $.OR(alt(ctx));
|
|
1217
|
+
if (!RECORDING_PHASE) {
|
|
1218
|
+
if (!(node instanceof Node)) {
|
|
1219
|
+
node = $.processValueToken(node);
|
|
1220
|
+
}
|
|
1221
|
+
return $.wrap(node, 'both');
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
export function mathParen(T) {
|
|
1226
|
+
const $ = this;
|
|
1227
|
+
return (ctx = {}) => {
|
|
1228
|
+
$.startRule();
|
|
1229
|
+
$.CONSUME(T.LParen);
|
|
1230
|
+
let node = $.SUBRULE($.mathSum, { ARGS: [ctx] });
|
|
1231
|
+
$.CONSUME(T.RParen);
|
|
1232
|
+
if (!$.RECORDING_PHASE) {
|
|
1233
|
+
let location = $.endRule();
|
|
1234
|
+
return new Paren(node, undefined, location, this.context);
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
// function
|
|
1239
|
+
// : URL_FUNCTION
|
|
1240
|
+
// | VAR_FUNCTION '(' WS* CUSTOM_IDENT (WS* COMMA WS* valueList)? ')'
|
|
1241
|
+
// | CALC_FUNCTION '(' WS* mathSum WS* ')'
|
|
1242
|
+
// | identifier '(' valueList ')'
|
|
1243
|
+
// ;
|
|
1244
|
+
// These have special parsing rules
|
|
1245
|
+
export function knownFunctions(T, alt) {
|
|
1246
|
+
const $ = this;
|
|
1247
|
+
alt ??= (ctx = {}) => [
|
|
1248
|
+
{ ALT: () => $.SUBRULE($.urlFunction, { ARGS: [ctx] }) },
|
|
1249
|
+
{ ALT: () => $.SUBRULE($.varFunction, { ARGS: [ctx] }) },
|
|
1250
|
+
{ ALT: () => $.SUBRULE($.calcFunction, { ARGS: [ctx] }) }
|
|
1251
|
+
];
|
|
1252
|
+
return (ctx = {}) => $.OR(alt(ctx));
|
|
1253
|
+
}
|
|
1254
|
+
export function ifFunctionArgs(T) {
|
|
1255
|
+
const $ = this;
|
|
1256
|
+
return (ctx = {}) => {
|
|
1257
|
+
const RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1258
|
+
$.startRule();
|
|
1259
|
+
let branches;
|
|
1260
|
+
if (!RECORDING_PHASE) {
|
|
1261
|
+
branches = [];
|
|
1262
|
+
}
|
|
1263
|
+
$.AT_LEAST_ONE_SEP({
|
|
1264
|
+
SEP: T.Semi,
|
|
1265
|
+
DEF: () => {
|
|
1266
|
+
const condition = $.SUBRULE($.valueSequence, { ARGS: [{ ...ctx, inner: true }] });
|
|
1267
|
+
$.CONSUME(T.Assign);
|
|
1268
|
+
const value = $.SUBRULE($.valueList, { ARGS: [{ ...ctx, inner: true }] });
|
|
1269
|
+
if (!RECORDING_PHASE) {
|
|
1270
|
+
const sep = $.wrap(new Any(':', { role: 'operator' }, undefined, this.context), true);
|
|
1271
|
+
const loc = $.getLocationFromNodes([condition, value]);
|
|
1272
|
+
branches.push(new Sequence([$.wrap(condition, true), sep, $.wrap(value, true)], undefined, loc, this.context));
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
$.OPTION(() => $.CONSUME2(T.Semi));
|
|
1277
|
+
if (!RECORDING_PHASE) {
|
|
1278
|
+
const location = $.endRule();
|
|
1279
|
+
if (branches.length === 1) {
|
|
1280
|
+
return branches[0];
|
|
1281
|
+
}
|
|
1282
|
+
return new List(branches, { sep: ';' }, location, this.context);
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
export function ifFunction(T) {
|
|
1287
|
+
const $ = this;
|
|
1288
|
+
return (ctx = {}) => {
|
|
1289
|
+
$.startRule();
|
|
1290
|
+
const start = $.CONSUME(T.FunctionStart);
|
|
1291
|
+
const args = $.SUBRULE($.ifFunctionArgs, { ARGS: [{ ...ctx, inner: true }] });
|
|
1292
|
+
$.CONSUME(T.RParen);
|
|
1293
|
+
if (!$.RECORDING_PHASE) {
|
|
1294
|
+
const location = $.endRule();
|
|
1295
|
+
return new Call({
|
|
1296
|
+
name: start.image.slice(0, -1),
|
|
1297
|
+
args: new List([args])
|
|
1298
|
+
}, undefined, location, this.context);
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
export function varFunction(T) {
|
|
1303
|
+
const $ = this;
|
|
1304
|
+
return (ctx = {}) => {
|
|
1305
|
+
$.startRule();
|
|
1306
|
+
$.CONSUME(T.Var);
|
|
1307
|
+
let prop = $.CONSUME(T.CustomProperty);
|
|
1308
|
+
let args;
|
|
1309
|
+
$.OPTION(() => {
|
|
1310
|
+
$.CONSUME(T.Comma);
|
|
1311
|
+
args = $.SUBRULE($.valueList, { ARGS: [ctx] });
|
|
1312
|
+
});
|
|
1313
|
+
$.CONSUME(T.RParen);
|
|
1314
|
+
if (!$.RECORDING_PHASE) {
|
|
1315
|
+
let location = $.endRule();
|
|
1316
|
+
let propNode = $.wrap(new Any(prop.image, { role: 'customprop' }, $.getLocationInfo(prop), this.context), 'both');
|
|
1317
|
+
if (!args) {
|
|
1318
|
+
args = new List([propNode], undefined, $.getLocationInfo(prop), this.context);
|
|
1319
|
+
}
|
|
1320
|
+
else {
|
|
1321
|
+
let { startOffset, startLine, startColumn } = prop;
|
|
1322
|
+
args.value.unshift(propNode);
|
|
1323
|
+
args.location[0] = startOffset;
|
|
1324
|
+
args.location[1] = startLine;
|
|
1325
|
+
args.location[2] = startColumn;
|
|
1326
|
+
}
|
|
1327
|
+
return new Call({
|
|
1328
|
+
name: 'var',
|
|
1329
|
+
args
|
|
1330
|
+
}, undefined, location, this.context);
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
export function calcFunction(T) {
|
|
1335
|
+
const $ = this;
|
|
1336
|
+
return (ctx = {}) => {
|
|
1337
|
+
$.startRule();
|
|
1338
|
+
$.CONSUME(T.Calc);
|
|
1339
|
+
let args = $.SUBRULE($.mathSum, { ARGS: [ctx] });
|
|
1340
|
+
$.CONSUME2(T.RParen);
|
|
1341
|
+
if (!$.RECORDING_PHASE) {
|
|
1342
|
+
let location = $.endRule();
|
|
1343
|
+
return new Call({
|
|
1344
|
+
name: 'calc',
|
|
1345
|
+
args: new List([args])
|
|
1346
|
+
}, undefined, location, this.context);
|
|
1347
|
+
}
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
export function urlFunction(T, alt) {
|
|
1351
|
+
const $ = this;
|
|
1352
|
+
alt ??= (ctx = {}) => [
|
|
1353
|
+
{ ALT: () => $.SUBRULE($.string, { ARGS: [ctx] }) },
|
|
1354
|
+
{ ALT: () => $.CONSUME(T.NonQuotedUrl) }
|
|
1355
|
+
];
|
|
1356
|
+
return (ctx = {}) => {
|
|
1357
|
+
$.startRule();
|
|
1358
|
+
$.CONSUME(T.UrlStart);
|
|
1359
|
+
let node = $.OR(alt(ctx));
|
|
1360
|
+
$.CONSUME(T.UrlEnd);
|
|
1361
|
+
if (!$.RECORDING_PHASE) {
|
|
1362
|
+
let location = $.endRule();
|
|
1363
|
+
if (!(node instanceof Node)) {
|
|
1364
|
+
/** Whitespace should be included in the NonQuotedUrl */
|
|
1365
|
+
node = new Any(node.image, { role: 'urlvalue' }, $.getLocationInfo(node), this.context);
|
|
1366
|
+
}
|
|
1367
|
+
return new Url(node, undefined, location, this.context);
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
All At-rules according to spec are supposed to end with either
|
|
1373
|
+
a semi-colon or a curly-braced block. Some pre-processors
|
|
1374
|
+
(like PostCSS) allow the semi-colon to be optional, so we also
|
|
1375
|
+
allow it and insert it if it's missing.
|
|
1376
|
+
*/
|
|
1377
|
+
// atRule
|
|
1378
|
+
// : importAtRule
|
|
1379
|
+
// | mediaAtRule
|
|
1380
|
+
// | unknownAtRule
|
|
1381
|
+
// | pageAtRule
|
|
1382
|
+
// | fontFaceAtRule
|
|
1383
|
+
// | supportsAtRule
|
|
1384
|
+
// ;
|
|
1385
|
+
export function atRule(T, alt) {
|
|
1386
|
+
const $ = this;
|
|
1387
|
+
let ruleAlt = alt ?? ((ctx) => ([
|
|
1388
|
+
{ ALT: () => $.SUBRULE($.containerAtRule, { ARGS: [ctx] }) },
|
|
1389
|
+
{ ALT: () => $.SUBRULE($.scopeAtRule, { ARGS: [ctx] }) },
|
|
1390
|
+
{ ALT: () => $.SUBRULE($.documentAtRule, { ARGS: [ctx] }) },
|
|
1391
|
+
{ ALT: () => $.SUBRULE($.layerAtRule, { ARGS: [ctx] }) },
|
|
1392
|
+
{ ALT: () => $.SUBRULE($.keyframesAtRule, { ARGS: [ctx] }) },
|
|
1393
|
+
{ ALT: () => $.SUBRULE($.importAtRule, { ARGS: [ctx] }) },
|
|
1394
|
+
{ ALT: () => $.SUBRULE($.mediaAtRule, { ARGS: [ctx] }) },
|
|
1395
|
+
{ ALT: () => $.SUBRULE($.pageAtRule, { ARGS: [ctx] }) },
|
|
1396
|
+
{ ALT: () => $.SUBRULE($.fontFaceAtRule, { ARGS: [ctx] }) },
|
|
1397
|
+
{ ALT: () => $.SUBRULE($.supportsAtRule, { ARGS: [ctx] }) },
|
|
1398
|
+
{ ALT: () => $.SUBRULE($.nestedAtRule, { ARGS: [ctx] }) },
|
|
1399
|
+
{ ALT: () => $.SUBRULE($.nonNestedAtRule, { ARGS: [ctx] }) },
|
|
1400
|
+
{ ALT: () => $.SUBRULE($.unknownAtRule, { ARGS: [ctx] }) }
|
|
1401
|
+
]));
|
|
1402
|
+
return (ctx) => $.OR(ruleAlt(ctx));
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
Inner rules are mostly the same except they have a declarationList
|
|
1406
|
+
instead of a main block within {}
|
|
1407
|
+
*/
|
|
1408
|
+
// innerAtRule
|
|
1409
|
+
// : innerMediaAtRule
|
|
1410
|
+
// | unknownAtRule
|
|
1411
|
+
// ;
|
|
1412
|
+
export function innerAtRule(T, alt) {
|
|
1413
|
+
const $ = this;
|
|
1414
|
+
alt ??= (ctx = {}) => [
|
|
1415
|
+
{ ALT: () => $.SUBRULE($.containerAtRule, { ARGS: [ctx] }) },
|
|
1416
|
+
{ ALT: () => $.SUBRULE($.scopeAtRule, { ARGS: [ctx] }) },
|
|
1417
|
+
{ ALT: () => $.SUBRULE($.documentAtRule, { ARGS: [ctx] }) },
|
|
1418
|
+
{ ALT: () => $.SUBRULE($.layerAtRule, { ARGS: [ctx] }) },
|
|
1419
|
+
{ ALT: () => $.SUBRULE($.keyframesAtRule, { ARGS: [ctx] }) },
|
|
1420
|
+
{ ALT: () => $.SUBRULE($.mediaAtRule, { ARGS: [ctx] }) },
|
|
1421
|
+
{ ALT: () => $.SUBRULE($.supportsAtRule, { ARGS: [ctx] }) },
|
|
1422
|
+
{ ALT: () => $.SUBRULE($.nestedAtRule, { ARGS: [ctx] }) },
|
|
1423
|
+
{ ALT: () => $.SUBRULE($.unknownAtRule, { ARGS: [ctx] }) }
|
|
1424
|
+
];
|
|
1425
|
+
return (ctx = {}) => $.OR(alt({ ...ctx, inner: true }));
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* @see https://www.w3.org/TR/css-nesting-1/#conditionals
|
|
1429
|
+
*/
|
|
1430
|
+
export function atRuleBody(T) {
|
|
1431
|
+
const $ = this;
|
|
1432
|
+
return (ctx = {}) => $.OR([
|
|
1433
|
+
{
|
|
1434
|
+
GATE: () => !ctx.inner,
|
|
1435
|
+
ALT: () => $.SUBRULE($.main, { ARGS: [ctx] })
|
|
1436
|
+
},
|
|
1437
|
+
{
|
|
1438
|
+
GATE: () => !!ctx.inner,
|
|
1439
|
+
ALT: () => $.SUBRULE($.declarationList, { ARGS: [ctx] })
|
|
1440
|
+
}
|
|
1441
|
+
]);
|
|
1442
|
+
}
|
|
1443
|
+
function resolvePreludeRule($, preludeRule) {
|
|
1444
|
+
if (typeof preludeRule === 'string') {
|
|
1445
|
+
const rec = $;
|
|
1446
|
+
return rec[preludeRule];
|
|
1447
|
+
}
|
|
1448
|
+
return preludeRule;
|
|
1449
|
+
}
|
|
1450
|
+
export function mediaAtRule(T, preludeRule) {
|
|
1451
|
+
const $ = this;
|
|
1452
|
+
return (ctx = {}) => {
|
|
1453
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1454
|
+
$.startRule();
|
|
1455
|
+
let name = $.CONSUME(T.AtMedia);
|
|
1456
|
+
let rules;
|
|
1457
|
+
const resolvedPreludeRule = resolvePreludeRule($, preludeRule);
|
|
1458
|
+
const prelude = typeof resolvedPreludeRule === 'function'
|
|
1459
|
+
? $.SUBRULE(resolvedPreludeRule, { ARGS: [ctx] })
|
|
1460
|
+
: $.SUBRULE($.mediaQueryList, { ARGS: [ctx] });
|
|
1461
|
+
$.CONSUME(T.LCurly);
|
|
1462
|
+
rules = $.SUBRULE($.atRuleBody, { ARGS: [ctx] });
|
|
1463
|
+
$.CONSUME(T.RCurly);
|
|
1464
|
+
if (!RECORDING_PHASE) {
|
|
1465
|
+
let location = $.endRule();
|
|
1466
|
+
return new AtRule({
|
|
1467
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
1468
|
+
prelude: $.wrap(prelude, true),
|
|
1469
|
+
rules
|
|
1470
|
+
}, { nestable: true }, location, this.context);
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
export function mediaQueryList(T) {
|
|
1475
|
+
const $ = this;
|
|
1476
|
+
return (ctx = {}) => {
|
|
1477
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1478
|
+
$.startRule();
|
|
1479
|
+
let queries = RECORDING_PHASE ? undefined : [];
|
|
1480
|
+
$.AT_LEAST_ONE_SEP({
|
|
1481
|
+
SEP: T.Comma,
|
|
1482
|
+
DEF: () => {
|
|
1483
|
+
let query = $.SUBRULE($.mediaQuery, { ARGS: [ctx] });
|
|
1484
|
+
if (!RECORDING_PHASE) {
|
|
1485
|
+
queries.push(query);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1489
|
+
if (!RECORDING_PHASE) {
|
|
1490
|
+
if (queries.length === 1) {
|
|
1491
|
+
$.endRule();
|
|
1492
|
+
return queries[0];
|
|
1493
|
+
}
|
|
1494
|
+
return new List(queries, undefined, $.endRule(), this.context);
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* @see https://w3c.github.io/csswg-drafts/mediaqueries/#mq-syntax
|
|
1500
|
+
* Note, some of the spec had to be re-written for less ambiguity.
|
|
1501
|
+
* However, this is a spec-compliant implementation.
|
|
1502
|
+
*/
|
|
1503
|
+
// mediaQuery
|
|
1504
|
+
// : mediaCondition
|
|
1505
|
+
// | ((NOT | ONLY) WS*)? mediaType (WS* AND WS* mediaConditionWithoutOr)?
|
|
1506
|
+
// ;
|
|
1507
|
+
export function mediaQuery(T, alt) {
|
|
1508
|
+
const $ = this;
|
|
1509
|
+
alt ??= (ctx = {}) => [
|
|
1510
|
+
{ ALT: () => $.SUBRULE($.mediaCondition, { ARGS: [ctx] }) },
|
|
1511
|
+
{
|
|
1512
|
+
ALT: () => {
|
|
1513
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1514
|
+
$.startRule();
|
|
1515
|
+
let token;
|
|
1516
|
+
let node;
|
|
1517
|
+
let nodes;
|
|
1518
|
+
if (!RECORDING_PHASE) {
|
|
1519
|
+
nodes = [];
|
|
1520
|
+
}
|
|
1521
|
+
$.OPTION(() => {
|
|
1522
|
+
$.OR2([
|
|
1523
|
+
{ ALT: () => token = $.CONSUME(T.Not) },
|
|
1524
|
+
{ ALT: () => token = $.CONSUME(T.Only) }
|
|
1525
|
+
]);
|
|
1526
|
+
});
|
|
1527
|
+
if (token && !RECORDING_PHASE) {
|
|
1528
|
+
nodes.push($.wrap(new Keyword(token.image, undefined, $.getLocationInfo(token), this.context), 'both'));
|
|
1529
|
+
token = undefined;
|
|
1530
|
+
}
|
|
1531
|
+
let type = $.SUBRULE($.mediaType, { ARGS: [ctx] });
|
|
1532
|
+
if (!RECORDING_PHASE) {
|
|
1533
|
+
nodes.push(type);
|
|
1534
|
+
}
|
|
1535
|
+
$.OPTION2(() => {
|
|
1536
|
+
token = $.CONSUME(T.And);
|
|
1537
|
+
node = $.SUBRULE($.mediaConditionWithoutOr, { ARGS: [ctx] });
|
|
1538
|
+
});
|
|
1539
|
+
if (!RECORDING_PHASE) {
|
|
1540
|
+
if (token) {
|
|
1541
|
+
nodes.push($.wrap(new Keyword(token.image, undefined, $.getLocationInfo(token), this.context), 'both'));
|
|
1542
|
+
}
|
|
1543
|
+
if (node) {
|
|
1544
|
+
nodes.push(node);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
if (!RECORDING_PHASE) {
|
|
1548
|
+
let location = $.endRule();
|
|
1549
|
+
return new QueryCondition(nodes, undefined, location, this.context);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
];
|
|
1554
|
+
return (ctx = {}) => $.OR(alt(ctx));
|
|
1555
|
+
}
|
|
1556
|
+
/** Doesn't include only, not, and, or, layer */
|
|
1557
|
+
// mediaType
|
|
1558
|
+
// : IDENT
|
|
1559
|
+
// | SCREEN
|
|
1560
|
+
// | PRINT
|
|
1561
|
+
// | ALL
|
|
1562
|
+
// ;
|
|
1563
|
+
export function mediaType(T, alt) {
|
|
1564
|
+
const $ = this;
|
|
1565
|
+
alt ??= (ctx = {}) => [
|
|
1566
|
+
{ ALT: () => $.CONSUME(T.PlainIdent) },
|
|
1567
|
+
{ ALT: () => $.CONSUME(T.Screen) },
|
|
1568
|
+
{ ALT: () => $.CONSUME(T.Print) },
|
|
1569
|
+
{ ALT: () => $.CONSUME(T.All) }
|
|
1570
|
+
];
|
|
1571
|
+
return (ctx = {}) => {
|
|
1572
|
+
let token = $.OR(alt(ctx));
|
|
1573
|
+
if (!$.RECORDING_PHASE) {
|
|
1574
|
+
return $.wrap(new Keyword(token.image, undefined, $.getLocationInfo(token), this.context), 'both');
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
// mediaCondition
|
|
1579
|
+
// : mediaNot | mediaInParens ( WS* (mediaAnd* | mediaOr* ))
|
|
1580
|
+
// ;
|
|
1581
|
+
export function mediaCondition(T, alt) {
|
|
1582
|
+
const $ = this;
|
|
1583
|
+
alt ??= (ctx = {}) => [
|
|
1584
|
+
{ ALT: () => $.SUBRULE($.mediaNot, { ARGS: [ctx] }) },
|
|
1585
|
+
{
|
|
1586
|
+
ALT: () => {
|
|
1587
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1588
|
+
$.startRule();
|
|
1589
|
+
let nodes;
|
|
1590
|
+
if (!RECORDING_PHASE) {
|
|
1591
|
+
nodes = [];
|
|
1592
|
+
}
|
|
1593
|
+
let node = $.SUBRULE($.mediaInParens, { ARGS: [ctx] });
|
|
1594
|
+
if (!RECORDING_PHASE) {
|
|
1595
|
+
nodes.push(node);
|
|
1596
|
+
}
|
|
1597
|
+
$.MANY(() => {
|
|
1598
|
+
let rule = $.OR2([
|
|
1599
|
+
{ ALT: () => $.SUBRULE($.mediaAnd, { ARGS: [ctx] }) },
|
|
1600
|
+
{ ALT: () => $.SUBRULE($.mediaOr, { ARGS: [ctx] }) }
|
|
1601
|
+
]);
|
|
1602
|
+
if (!RECORDING_PHASE) {
|
|
1603
|
+
nodes.push(...rule);
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1606
|
+
if (!RECORDING_PHASE) {
|
|
1607
|
+
// Only wrap in QueryCondition if there are multiple nodes (AND/OR operators)
|
|
1608
|
+
// Otherwise, return the single node directly (like Sequence does)
|
|
1609
|
+
if (nodes.length === 1) {
|
|
1610
|
+
$.endRule();
|
|
1611
|
+
return nodes[0];
|
|
1612
|
+
}
|
|
1613
|
+
return new QueryCondition(nodes, undefined, $.endRule(), this.context);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
];
|
|
1618
|
+
return (ctx = {}) => $.OR(alt(ctx));
|
|
1619
|
+
}
|
|
1620
|
+
// mediaConditionWithoutOr
|
|
1621
|
+
// : mediaNot | mediaInParens (WS* mediaAnd)*
|
|
1622
|
+
// ;
|
|
1623
|
+
export function mediaConditionWithoutOr(T, alt) {
|
|
1624
|
+
const $ = this;
|
|
1625
|
+
alt ??= (ctx = {}) => [
|
|
1626
|
+
{ ALT: () => $.SUBRULE($.mediaNot, { ARGS: [ctx] }) },
|
|
1627
|
+
{
|
|
1628
|
+
ALT: () => {
|
|
1629
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1630
|
+
$.startRule();
|
|
1631
|
+
let nodes;
|
|
1632
|
+
if (!RECORDING_PHASE) {
|
|
1633
|
+
nodes = [];
|
|
1634
|
+
}
|
|
1635
|
+
let node = $.SUBRULE($.mediaInParens, { ARGS: [ctx] });
|
|
1636
|
+
if (!RECORDING_PHASE) {
|
|
1637
|
+
nodes.push(node);
|
|
1638
|
+
}
|
|
1639
|
+
$.MANY(() => {
|
|
1640
|
+
let rule = $.SUBRULE($.mediaAnd, { ARGS: [ctx] });
|
|
1641
|
+
if (!RECORDING_PHASE) {
|
|
1642
|
+
nodes.push(...rule);
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1645
|
+
if (!RECORDING_PHASE) {
|
|
1646
|
+
// Only wrap in QueryCondition if there are multiple nodes (AND operators)
|
|
1647
|
+
// Otherwise, return the single node directly (like Sequence does)
|
|
1648
|
+
if (nodes.length === 1) {
|
|
1649
|
+
$.endRule();
|
|
1650
|
+
return nodes[0];
|
|
1651
|
+
}
|
|
1652
|
+
return new QueryCondition(nodes, undefined, $.endRule(), this.context);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
];
|
|
1657
|
+
return (ctx = {}) => $.OR(alt(ctx));
|
|
1658
|
+
}
|
|
1659
|
+
// mediaNot
|
|
1660
|
+
// : NOT WS* mediaInParens
|
|
1661
|
+
// ;
|
|
1662
|
+
export function mediaNot(T) {
|
|
1663
|
+
const $ = this;
|
|
1664
|
+
return (ctx = {}) => {
|
|
1665
|
+
$.startRule();
|
|
1666
|
+
let token = $.CONSUME(T.Not);
|
|
1667
|
+
let node = $.SUBRULE($.mediaInParens, { ARGS: [ctx] });
|
|
1668
|
+
if (!$.RECORDING_PHASE) {
|
|
1669
|
+
return new QueryCondition([
|
|
1670
|
+
$.wrap(new Keyword(token.image, undefined, $.getLocationInfo(token), this.context), 'both'),
|
|
1671
|
+
node
|
|
1672
|
+
], undefined, $.endRule(), this.context);
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
// mediaAnd
|
|
1677
|
+
// : AND WS* mediaInParens
|
|
1678
|
+
// ;
|
|
1679
|
+
export function mediaAnd(T) {
|
|
1680
|
+
const $ = this;
|
|
1681
|
+
/** Returns an array */
|
|
1682
|
+
return (ctx = {}) => {
|
|
1683
|
+
let token = $.CONSUME(T.And);
|
|
1684
|
+
let node = $.SUBRULE($.mediaInParens, { ARGS: [ctx] });
|
|
1685
|
+
if (!$.RECORDING_PHASE) {
|
|
1686
|
+
return [
|
|
1687
|
+
$.wrap(new Keyword(token.image, undefined, $.getLocationInfo(token), this.context), 'both'),
|
|
1688
|
+
node
|
|
1689
|
+
];
|
|
1690
|
+
}
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
// mediaOr
|
|
1694
|
+
// : OR WS* mediaInParens
|
|
1695
|
+
// ;
|
|
1696
|
+
export function mediaOr(T) {
|
|
1697
|
+
const $ = this;
|
|
1698
|
+
/** Returns an array */
|
|
1699
|
+
return (ctx = {}) => {
|
|
1700
|
+
let token = $.CONSUME(T.Or);
|
|
1701
|
+
let node = $.SUBRULE($.mediaInParens, { ARGS: [ctx] });
|
|
1702
|
+
if (!$.RECORDING_PHASE) {
|
|
1703
|
+
return [
|
|
1704
|
+
$.wrap(new Keyword(token.image, undefined, $.getLocationInfo(token), this.context), 'both'),
|
|
1705
|
+
node
|
|
1706
|
+
];
|
|
1707
|
+
}
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
// mediaInParens
|
|
1711
|
+
// : '(' WS* (mediaCondition | mediaFeature) WS* ')'
|
|
1712
|
+
// | generalEnclosed
|
|
1713
|
+
// ;
|
|
1714
|
+
export function mediaInParens(T, alt) {
|
|
1715
|
+
const $ = this;
|
|
1716
|
+
alt ??= (ctx = {}) => [
|
|
1717
|
+
{ ALT: () => $.SUBRULE($.mediaCondition, { ARGS: [ctx] }) },
|
|
1718
|
+
{ ALT: () => $.SUBRULE($.mediaFeature, { ARGS: [ctx] }) }
|
|
1719
|
+
];
|
|
1720
|
+
return (ctx = {}) => {
|
|
1721
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1722
|
+
$.startRule();
|
|
1723
|
+
$.CONSUME(T.LParen);
|
|
1724
|
+
/*
|
|
1725
|
+
* CSS also allows for parentheses to contain
|
|
1726
|
+
* almost anything, including a wild sequence
|
|
1727
|
+
* of tokens (e.g. `@media (!!&) {}`), as it would
|
|
1728
|
+
* be up to the user agent to decide what the content
|
|
1729
|
+
* of the parentheses means. (CSS defines this as
|
|
1730
|
+
* "generalEnclosed" in the spec.)
|
|
1731
|
+
*
|
|
1732
|
+
* But that would mean that detecting errors in
|
|
1733
|
+
* parsing would not be possible. So we only parse
|
|
1734
|
+
* "known" media queries.
|
|
1735
|
+
*/
|
|
1736
|
+
let node = $.OR2(alt(ctx));
|
|
1737
|
+
$.CONSUME(T.RParen);
|
|
1738
|
+
if (!RECORDING_PHASE) {
|
|
1739
|
+
let location = $.endRule();
|
|
1740
|
+
return $.wrap(new Paren($.wrap(node, 'both'), undefined, location, this.context));
|
|
1741
|
+
}
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
An identifier is a legal value, so it can be
|
|
1746
|
+
ambiguous which side of the expression we're on
|
|
1747
|
+
while parsing. The browser figures this out
|
|
1748
|
+
post-parsing.
|
|
1749
|
+
*/
|
|
1750
|
+
// mediaFeature
|
|
1751
|
+
// : identifier (WS* (
|
|
1752
|
+
// COLON WS* mfValue
|
|
1753
|
+
// | mediaRange
|
|
1754
|
+
// | mfComparison WS* mfNonIdentifierValue
|
|
1755
|
+
// ))?
|
|
1756
|
+
// | mfNonIdentifierValue WS* (
|
|
1757
|
+
// mfComparison WS* identifier
|
|
1758
|
+
// | mediaRange
|
|
1759
|
+
// )
|
|
1760
|
+
// ;
|
|
1761
|
+
export function mediaFeature(T, alt) {
|
|
1762
|
+
const $ = this;
|
|
1763
|
+
alt ??= (ctx = {}) => [
|
|
1764
|
+
{
|
|
1765
|
+
ALT: () => {
|
|
1766
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1767
|
+
$.startRule();
|
|
1768
|
+
let rule;
|
|
1769
|
+
let ident = $.CONSUME(T.Ident);
|
|
1770
|
+
$.OPTION(() => {
|
|
1771
|
+
rule = $.OR2([
|
|
1772
|
+
{
|
|
1773
|
+
ALT: () => {
|
|
1774
|
+
$.CONSUME(T.Colon);
|
|
1775
|
+
let value = $.SUBRULE($.mfValue, { ARGS: [ctx] });
|
|
1776
|
+
if (!RECORDING_PHASE) {
|
|
1777
|
+
let location = $.endRule();
|
|
1778
|
+
return $.wrap(new Declaration({
|
|
1779
|
+
name: $.wrap(new Any(ident.image, { role: 'property' }), true),
|
|
1780
|
+
value: $.wrap(value)
|
|
1781
|
+
}, undefined, location, this.context), 'both');
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
},
|
|
1785
|
+
{
|
|
1786
|
+
ALT: () => {
|
|
1787
|
+
let seq = $.SUBRULE($.mediaRange, { ARGS: [ctx] });
|
|
1788
|
+
if (!RECORDING_PHASE) {
|
|
1789
|
+
let [startOffset, startLine, startColumn] = $.endRule();
|
|
1790
|
+
seq.value.unshift($.wrap(new Any(ident.image, { role: 'ident' }, $.getLocationInfo(ident), this.context)));
|
|
1791
|
+
seq.location[0] = startOffset;
|
|
1792
|
+
seq.location[1] = startLine;
|
|
1793
|
+
seq.location[2] = startColumn;
|
|
1794
|
+
return new QueryCondition(seq.value, undefined, seq.location, this.context);
|
|
1795
|
+
}
|
|
1796
|
+
return seq;
|
|
1797
|
+
}
|
|
1798
|
+
},
|
|
1799
|
+
{
|
|
1800
|
+
ALT: () => {
|
|
1801
|
+
let op = $.SUBRULE($.mfComparison, { ARGS: [ctx] });
|
|
1802
|
+
let value = $.SUBRULE($.mfNonIdentifierValue, { ARGS: [ctx] });
|
|
1803
|
+
if (!RECORDING_PHASE) {
|
|
1804
|
+
let location = $.endRule();
|
|
1805
|
+
return new QueryCondition([
|
|
1806
|
+
$.wrap(new Any(ident.image, { role: 'ident' }, $.getLocationInfo(ident), this.context)),
|
|
1807
|
+
$.wrap(new Any(op.image, { role: 'operator' }, $.getLocationInfo(op), this.context), 'both'),
|
|
1808
|
+
value
|
|
1809
|
+
], undefined, location, this.context);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
]);
|
|
1814
|
+
});
|
|
1815
|
+
if (!RECORDING_PHASE && !rule) {
|
|
1816
|
+
let location = $.endRule();
|
|
1817
|
+
let anyNode = new Keyword(ident.image, undefined, location, this.context);
|
|
1818
|
+
return $.wrap(new QueryCondition([anyNode], undefined, location, this.context), 'both');
|
|
1819
|
+
}
|
|
1820
|
+
return rule;
|
|
1821
|
+
}
|
|
1822
|
+
},
|
|
1823
|
+
{
|
|
1824
|
+
ALT: () => {
|
|
1825
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1826
|
+
$.startRule();
|
|
1827
|
+
let rule1 = $.SUBRULE2($.mfNonIdentifierValue, { ARGS: [{ ...ctx }] });
|
|
1828
|
+
return $.OR3([
|
|
1829
|
+
{
|
|
1830
|
+
ALT: () => {
|
|
1831
|
+
let op = $.SUBRULE2($.mfComparison, { ARGS: [{ ...ctx }] });
|
|
1832
|
+
let value = $.CONSUME2(T.Ident);
|
|
1833
|
+
if (!RECORDING_PHASE) {
|
|
1834
|
+
let location = $.endRule();
|
|
1835
|
+
return new QueryCondition([
|
|
1836
|
+
rule1,
|
|
1837
|
+
$.wrap(new Any(op.image, { role: 'operator' }, $.getLocationInfo(op), this.context)),
|
|
1838
|
+
$.wrap(new Any(value.image, { role: 'ident' }, $.getLocationInfo(value), this.context), 'both')
|
|
1839
|
+
], undefined, location, this.context);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
ALT: () => {
|
|
1845
|
+
let seq = $.SUBRULE2($.mediaRange, { ARGS: [{ ...ctx }] });
|
|
1846
|
+
if (!RECORDING_PHASE) {
|
|
1847
|
+
let [startOffset, startLine, startColumn] = $.endRule();
|
|
1848
|
+
seq.value.unshift(rule1);
|
|
1849
|
+
seq.location[0] = startOffset;
|
|
1850
|
+
seq.location[1] = startLine;
|
|
1851
|
+
seq.location[2] = startColumn;
|
|
1852
|
+
return new QueryCondition(seq.value, undefined, seq.location, this.context);
|
|
1853
|
+
}
|
|
1854
|
+
return seq;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
]);
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
];
|
|
1861
|
+
return (ctx = {}) => $.OR(alt(ctx));
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* @note Both comparison operators have to match.
|
|
1865
|
+
*/
|
|
1866
|
+
// mediaRange
|
|
1867
|
+
// : mfLt WS* identifier (WS* mfLt WS* mfValue)?
|
|
1868
|
+
// | mfGt WS* identifier (WS* mfGt WS* mfValue)?
|
|
1869
|
+
// ;
|
|
1870
|
+
export function mediaRange(T, alt) {
|
|
1871
|
+
const $ = this;
|
|
1872
|
+
alt ??= (ctx = {}) => [
|
|
1873
|
+
{
|
|
1874
|
+
ALT: () => {
|
|
1875
|
+
let op1 = $.CONSUME(T.MfLt);
|
|
1876
|
+
let val1 = $.CONSUME(T.Ident);
|
|
1877
|
+
let op2;
|
|
1878
|
+
let val2;
|
|
1879
|
+
$.OPTION(() => {
|
|
1880
|
+
op2 = $.CONSUME2(T.MfLt);
|
|
1881
|
+
val2 = $.SUBRULE($.mfValue, { ARGS: [ctx] });
|
|
1882
|
+
});
|
|
1883
|
+
return [op1, val1, op2, val2];
|
|
1884
|
+
}
|
|
1885
|
+
},
|
|
1886
|
+
{
|
|
1887
|
+
ALT: () => {
|
|
1888
|
+
let op1 = $.CONSUME(T.MfGt);
|
|
1889
|
+
let val1 = $.CONSUME2(T.Ident);
|
|
1890
|
+
let op2;
|
|
1891
|
+
let val2;
|
|
1892
|
+
$.OPTION2(() => {
|
|
1893
|
+
op2 = $.CONSUME2(T.MfGt);
|
|
1894
|
+
val2 = $.SUBRULE2($.mfValue, { ARGS: [ctx] });
|
|
1895
|
+
});
|
|
1896
|
+
return [op1, val1, op2, val2];
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
];
|
|
1900
|
+
return (ctx = {}) => {
|
|
1901
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
1902
|
+
$.startRule();
|
|
1903
|
+
let op1;
|
|
1904
|
+
let val1;
|
|
1905
|
+
let op2;
|
|
1906
|
+
let val2;
|
|
1907
|
+
let val = $.OR(alt(ctx));
|
|
1908
|
+
if (!RECORDING_PHASE) {
|
|
1909
|
+
([op1, val1, op2, val2] = val);
|
|
1910
|
+
}
|
|
1911
|
+
if (!$.RECORDING_PHASE) {
|
|
1912
|
+
let location = $.endRule();
|
|
1913
|
+
let nodes = [
|
|
1914
|
+
$.wrap(new Any(op1.image, { role: 'operator' }, $.getLocationInfo(op1), this.context)),
|
|
1915
|
+
$.wrap(new Any(val1.image, { role: 'ident' }, $.getLocationInfo(val1), this.context), 'both')
|
|
1916
|
+
];
|
|
1917
|
+
if (op2) {
|
|
1918
|
+
nodes.push($.wrap(new Any(op2.image, { role: 'operator' }, $.getLocationInfo(op2), this.context)));
|
|
1919
|
+
nodes.push($.wrap(val2, 'both'));
|
|
1920
|
+
}
|
|
1921
|
+
return new Sequence(nodes, undefined, location, this.context);
|
|
1922
|
+
}
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
// mfNonIdentifierValue
|
|
1926
|
+
// : number (WS* '/' WS* number)?
|
|
1927
|
+
// | dimension
|
|
1928
|
+
// ;
|
|
1929
|
+
export function mfNonIdentifierValue(T, alt) {
|
|
1930
|
+
const $ = this;
|
|
1931
|
+
alt ??= (ctx = {}) => [
|
|
1932
|
+
{
|
|
1933
|
+
ALT: () => {
|
|
1934
|
+
$.startRule();
|
|
1935
|
+
let num1 = $.CONSUME(T.Number);
|
|
1936
|
+
let num2;
|
|
1937
|
+
$.OPTION(() => {
|
|
1938
|
+
$.CONSUME(T.Slash);
|
|
1939
|
+
num2 = $.CONSUME2(T.Number);
|
|
1940
|
+
});
|
|
1941
|
+
if (!$.RECORDING_PHASE) {
|
|
1942
|
+
let location = $.endRule();
|
|
1943
|
+
let num1Node = $.wrap($.processValueToken(num1), 'both');
|
|
1944
|
+
if (!num2) {
|
|
1945
|
+
return num1Node;
|
|
1946
|
+
}
|
|
1947
|
+
let num2Node = $.wrap($.processValueToken(num2), 'both');
|
|
1948
|
+
return new List([num1Node, num2Node], { sep: '/' }, location, this.context);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
},
|
|
1952
|
+
{
|
|
1953
|
+
ALT: () => {
|
|
1954
|
+
let dim = $.CONSUME(T.Dimension);
|
|
1955
|
+
if (!$.RECORDING_PHASE) {
|
|
1956
|
+
return $.wrap($.processValueToken(dim), 'both');
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
];
|
|
1961
|
+
return (ctx = {}) => $.OR(alt(ctx));
|
|
1962
|
+
}
|
|
1963
|
+
// mfValue
|
|
1964
|
+
// : mfNonIdentifierValue | identifier
|
|
1965
|
+
// ;
|
|
1966
|
+
export function mfValue(T, alt) {
|
|
1967
|
+
const $ = this;
|
|
1968
|
+
alt ??= (ctx = {}) => [
|
|
1969
|
+
{ ALT: () => $.SUBRULE($.mfNonIdentifierValue, { ARGS: [ctx] }) },
|
|
1970
|
+
{
|
|
1971
|
+
ALT: () => {
|
|
1972
|
+
let token = $.CONSUME(T.Ident);
|
|
1973
|
+
if (!$.RECORDING_PHASE) {
|
|
1974
|
+
return $.wrap(new Any(token.image, { role: 'ident' }, $.getLocationInfo(token), this.context), 'both');
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
];
|
|
1979
|
+
return (ctx = {}) => $.OR(alt(ctx));
|
|
1980
|
+
}
|
|
1981
|
+
// mfComparison
|
|
1982
|
+
// : mfLt | mfGt | mfEq
|
|
1983
|
+
// ;
|
|
1984
|
+
export function mfComparison(T) {
|
|
1985
|
+
const $ = this;
|
|
1986
|
+
let comparisonAlt = [
|
|
1987
|
+
{ ALT: () => $.CONSUME(T.MfLt) },
|
|
1988
|
+
{ ALT: () => $.CONSUME(T.MfGt) },
|
|
1989
|
+
{ ALT: () => $.CONSUME(T.Eq) }
|
|
1990
|
+
];
|
|
1991
|
+
return () => $.OR(comparisonAlt);
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* @see https://www.w3.org/TR/css-page-3/
|
|
1995
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/@page
|
|
1996
|
+
*/
|
|
1997
|
+
export function pageAtRule(T) {
|
|
1998
|
+
const $ = this;
|
|
1999
|
+
return (ctx = {}) => {
|
|
2000
|
+
$.startRule();
|
|
2001
|
+
let name = $.CONSUME(T.AtPage);
|
|
2002
|
+
let selector = [];
|
|
2003
|
+
$.MANY_SEP({
|
|
2004
|
+
SEP: T.Comma,
|
|
2005
|
+
DEF: () => selector.push($.SUBRULE($.pageSelector, { ARGS: [ctx] }))
|
|
2006
|
+
});
|
|
2007
|
+
$.CONSUME(T.LCurly);
|
|
2008
|
+
let rules = $.SUBRULE($.declarationList, { ARGS: [ctx] });
|
|
2009
|
+
$.CONSUME(T.RCurly);
|
|
2010
|
+
if (!$.RECORDING_PHASE) {
|
|
2011
|
+
let location = $.endRule();
|
|
2012
|
+
return new AtRule({
|
|
2013
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
2014
|
+
prelude: selector.length ? $.wrap(new List(selector, undefined, $.getLocationFromNodes(selector), this.context), true) : undefined,
|
|
2015
|
+
rules
|
|
2016
|
+
}, undefined, location, this.context);
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
export function pageSelector(T) {
|
|
2021
|
+
const $ = this;
|
|
2022
|
+
return (ctx = {}) => {
|
|
2023
|
+
$.startRule();
|
|
2024
|
+
let token = '';
|
|
2025
|
+
$.OPTION(() => token += $.CONSUME(T.Ident).image);
|
|
2026
|
+
$.MANY({
|
|
2027
|
+
GATE: () => $.LA(1).tokenType === T.Colon && $.noSep(1),
|
|
2028
|
+
DEF: () => {
|
|
2029
|
+
token += $.CONSUME(T.Colon).image;
|
|
2030
|
+
token += $.CONSUME(T.PagePseudoClassKeywords).image;
|
|
2031
|
+
}
|
|
2032
|
+
});
|
|
2033
|
+
if (!$.RECORDING_PHASE) {
|
|
2034
|
+
let location = $.endRule();
|
|
2035
|
+
return $.wrap(new BasicSelector(token, undefined, location, this.context));
|
|
2036
|
+
}
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
// fontFaceAtRule
|
|
2040
|
+
// : FONT_FACE_RULE WS* LCURLY declarationList RCURLY
|
|
2041
|
+
// ;
|
|
2042
|
+
export function fontFaceAtRule(T) {
|
|
2043
|
+
const $ = this;
|
|
2044
|
+
return (ctx = {}) => {
|
|
2045
|
+
$.startRule();
|
|
2046
|
+
let name = $.CONSUME(T.AtFontFace);
|
|
2047
|
+
$.CONSUME(T.LCurly);
|
|
2048
|
+
let rules = $.SUBRULE($.declarationList, { ARGS: [ctx] });
|
|
2049
|
+
$.CONSUME(T.RCurly);
|
|
2050
|
+
if (!$.RECORDING_PHASE) {
|
|
2051
|
+
let location = $.endRule();
|
|
2052
|
+
return new AtRule({
|
|
2053
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
2054
|
+
rules
|
|
2055
|
+
}, undefined, location, this.context);
|
|
2056
|
+
}
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
// keyframesAtRule
|
|
2060
|
+
// : (AT_KEYFRAMES | vendorKeyframes) WS* IDENT WS* '{' keyframeBlock* '}'
|
|
2061
|
+
// ;
|
|
2062
|
+
export function keyframesAtRule(T) {
|
|
2063
|
+
const $ = this;
|
|
2064
|
+
return (ctx = {}) => {
|
|
2065
|
+
const RECORDING_PHASE = $.RECORDING_PHASE;
|
|
2066
|
+
$.startRule();
|
|
2067
|
+
let atTok = $.CONSUME(T.AtKeyframes);
|
|
2068
|
+
// prelude: a single animation name
|
|
2069
|
+
let preludeNode = $.SUBRULE($.keyframesName, { ARGS: [ctx] });
|
|
2070
|
+
$.CONSUME(T.LCurly);
|
|
2071
|
+
const rules = $.SUBRULE($.declarationList, { ARGS: [ctx] });
|
|
2072
|
+
$.CONSUME(T.RCurly);
|
|
2073
|
+
if (!$.RECORDING_PHASE) {
|
|
2074
|
+
return new AtRule({
|
|
2075
|
+
name: $.wrap(new Any(atTok.image, { role: 'atkeyword' }, $.getLocationInfo(atTok), this.context), true),
|
|
2076
|
+
prelude: preludeNode ? $.wrap(preludeNode, 'both') : undefined,
|
|
2077
|
+
// Include isolated comments inside the keyframes body
|
|
2078
|
+
rules
|
|
2079
|
+
}, undefined, $.endRule(), this.context);
|
|
2080
|
+
}
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
/**
|
|
2084
|
+
* Keyframes name prelude
|
|
2085
|
+
* CSS: Ident | String
|
|
2086
|
+
*/
|
|
2087
|
+
export function keyframesName(T) {
|
|
2088
|
+
const $ = this;
|
|
2089
|
+
return (ctx = {}) => {
|
|
2090
|
+
const RECORDING_PHASE = $.RECORDING_PHASE;
|
|
2091
|
+
let node;
|
|
2092
|
+
$.OR({
|
|
2093
|
+
DEF: [
|
|
2094
|
+
{ ALT: () => {
|
|
2095
|
+
const tok = $.CONSUME(T.Ident);
|
|
2096
|
+
if (!RECORDING_PHASE) {
|
|
2097
|
+
node = $.wrap($.processValueToken(tok));
|
|
2098
|
+
}
|
|
2099
|
+
} },
|
|
2100
|
+
{ ALT: () => node = $.SUBRULE($.string, { ARGS: [ctx] }) }
|
|
2101
|
+
]
|
|
2102
|
+
});
|
|
2103
|
+
return node;
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
// containerAtRule: @container <container-name>? <container-query-list> { main }
|
|
2107
|
+
/**
|
|
2108
|
+
* Parses @container at-rule with optional container name and container query list.
|
|
2109
|
+
*
|
|
2110
|
+
* WHAT I'M TRYING TO DO:
|
|
2111
|
+
* Disambiguate between:
|
|
2112
|
+
* 1. `@container sidebar (width > 400px)` - `sidebar` is a container name
|
|
2113
|
+
* 2. `@container size(min-width: 60ch)` - `size` is NOT a container name, it's a function call (FunctionStart token)
|
|
2114
|
+
* 3. `@container (width > 400px)` - no container name, query starts directly
|
|
2115
|
+
*
|
|
2116
|
+
* Strategy:
|
|
2117
|
+
* - If next token is FunctionStart (like `size(` or `style(`), it's a query function, NOT a container name
|
|
2118
|
+
* - If next token is Ident (not a query keyword), it COULD be a container name
|
|
2119
|
+
* - The containerQueryList production will handle parsing the actual query (whether it's a function or condition)
|
|
2120
|
+
*
|
|
2121
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@container
|
|
2122
|
+
*/
|
|
2123
|
+
export function containerAtRule(T, preludeRule) {
|
|
2124
|
+
const $ = this;
|
|
2125
|
+
return (ctx = {}) => {
|
|
2126
|
+
$.startRule();
|
|
2127
|
+
const name = $.CONSUME(T.AtContainer);
|
|
2128
|
+
let prelude;
|
|
2129
|
+
let containerName;
|
|
2130
|
+
let queryList;
|
|
2131
|
+
if (preludeRule) {
|
|
2132
|
+
const resolvedPreludeRule = resolvePreludeRule($, preludeRule);
|
|
2133
|
+
if (typeof resolvedPreludeRule === 'function') {
|
|
2134
|
+
prelude = $.SUBRULE(resolvedPreludeRule, { ARGS: [ctx] });
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
else {
|
|
2138
|
+
$.OR([
|
|
2139
|
+
{
|
|
2140
|
+
GATE: () => {
|
|
2141
|
+
const next = $.LA(1);
|
|
2142
|
+
// If it's a FunctionStart (like `size(` or `style(`), it's a query function, not a container name
|
|
2143
|
+
if (tokenMatcher(next, T.FunctionStart)) {
|
|
2144
|
+
return false;
|
|
2145
|
+
}
|
|
2146
|
+
// If it's an Ident (not a query keyword), it could be a container name
|
|
2147
|
+
return (next.tokenType === T.Ident || next.tokenType === T.PlainIdent)
|
|
2148
|
+
&& next.image.toLowerCase() !== 'not'
|
|
2149
|
+
&& next.image.toLowerCase() !== 'only'
|
|
2150
|
+
&& next.image.toLowerCase() !== 'and'
|
|
2151
|
+
&& next.image.toLowerCase() !== 'or';
|
|
2152
|
+
},
|
|
2153
|
+
ALT: () => {
|
|
2154
|
+
containerName = $.SUBRULE($.containerName, { ARGS: [ctx] });
|
|
2155
|
+
queryList = $.SUBRULE($.containerQueryList, { ARGS: [ctx] });
|
|
2156
|
+
}
|
|
2157
|
+
},
|
|
2158
|
+
{
|
|
2159
|
+
ALT: () => {
|
|
2160
|
+
queryList = $.SUBRULE2($.containerQueryList, { ARGS: [ctx] });
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
]);
|
|
2164
|
+
queryList = queryList;
|
|
2165
|
+
}
|
|
2166
|
+
$.CONSUME(T.LCurly);
|
|
2167
|
+
const rules = $.SUBRULE($.atRuleBody, { ARGS: [ctx] });
|
|
2168
|
+
$.CONSUME(T.RCurly);
|
|
2169
|
+
if (!$.RECORDING_PHASE) {
|
|
2170
|
+
let preludeNodes = [];
|
|
2171
|
+
if (!prelude && containerName) {
|
|
2172
|
+
preludeNodes.push($.wrap(containerName, true));
|
|
2173
|
+
}
|
|
2174
|
+
if (!prelude) {
|
|
2175
|
+
preludeNodes.push($.wrap(queryList, containerName ? true : 'both'));
|
|
2176
|
+
prelude = preludeNodes.length
|
|
2177
|
+
? $.wrap(new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), this.context), 'both')
|
|
2178
|
+
: undefined;
|
|
2179
|
+
}
|
|
2180
|
+
return new AtRule({
|
|
2181
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
2182
|
+
prelude,
|
|
2183
|
+
rules
|
|
2184
|
+
}, { nestable: true }, $.endRule(), this.context);
|
|
2185
|
+
}
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Container name: an optional identifier
|
|
2190
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@container#container-name
|
|
2191
|
+
*/
|
|
2192
|
+
export function containerName(T) {
|
|
2193
|
+
const $ = this;
|
|
2194
|
+
return (ctx = {}) => {
|
|
2195
|
+
let token = $.CONSUME(T.Ident);
|
|
2196
|
+
if (!$.RECORDING_PHASE) {
|
|
2197
|
+
return $.wrap(new Any(token.image, { role: 'ident' }, $.getLocationInfo(token), this.context), 'both');
|
|
2198
|
+
}
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
/**
|
|
2202
|
+
* Container query list: comma-separated list of container queries
|
|
2203
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@container#container-query
|
|
2204
|
+
*/
|
|
2205
|
+
export function containerQueryList(T) {
|
|
2206
|
+
const $ = this;
|
|
2207
|
+
return (ctx = {}) => {
|
|
2208
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
2209
|
+
$.startRule();
|
|
2210
|
+
let queries = RECORDING_PHASE ? undefined : [];
|
|
2211
|
+
$.AT_LEAST_ONE_SEP({
|
|
2212
|
+
SEP: T.Comma,
|
|
2213
|
+
DEF: () => {
|
|
2214
|
+
let query = $.SUBRULE($.containerQuery, { ARGS: [ctx] });
|
|
2215
|
+
if (!RECORDING_PHASE) {
|
|
2216
|
+
queries.push(query);
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
});
|
|
2220
|
+
if (!RECORDING_PHASE) {
|
|
2221
|
+
if (queries.length === 1) {
|
|
2222
|
+
$.endRule();
|
|
2223
|
+
return queries[0];
|
|
2224
|
+
}
|
|
2225
|
+
return new List(queries, undefined, $.endRule(), this.context);
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* Container query: a container condition or container query type function
|
|
2231
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@container#container-query
|
|
2232
|
+
*
|
|
2233
|
+
* Container queries can be:
|
|
2234
|
+
* - Regular conditions: (width > 400px)
|
|
2235
|
+
* - Container query type functions: size(min-width: 60ch), style(--responsive: true), scroll-state(stuck: top)
|
|
2236
|
+
*/
|
|
2237
|
+
export function containerQuery(T) {
|
|
2238
|
+
const $ = this;
|
|
2239
|
+
return (ctx = {}) => {
|
|
2240
|
+
return $.OR([
|
|
2241
|
+
{
|
|
2242
|
+
// Container query type function: any FunctionStart token
|
|
2243
|
+
// This allows for size(...), style(...), scroll-state(...), and any Less-evaluated functions
|
|
2244
|
+
GATE: () => tokenMatcher($.LA(1), T.FunctionStart),
|
|
2245
|
+
ALT: () => {
|
|
2246
|
+
$.startRule();
|
|
2247
|
+
let nodes;
|
|
2248
|
+
if (!$.RECORDING_PHASE) {
|
|
2249
|
+
nodes = [];
|
|
2250
|
+
}
|
|
2251
|
+
// Parse first function call
|
|
2252
|
+
const funcStart = $.CONSUME(T.FunctionStart);
|
|
2253
|
+
const funcName = funcStart.image.slice(0, -1);
|
|
2254
|
+
let args = !$.RECORDING_PHASE ? [] : undefined;
|
|
2255
|
+
$.AT_LEAST_ONE_SEP({
|
|
2256
|
+
SEP: T.Comma,
|
|
2257
|
+
DEF: () => {
|
|
2258
|
+
// Arguments can be QueryConditions, declarations, or just a name (Any)
|
|
2259
|
+
$.OR2([
|
|
2260
|
+
{
|
|
2261
|
+
// QueryCondition: starts with LParen or Not
|
|
2262
|
+
GATE: () => {
|
|
2263
|
+
const next = $.LA(1);
|
|
2264
|
+
return next.tokenType === T.LParen || next.tokenType === T.Not;
|
|
2265
|
+
},
|
|
2266
|
+
ALT: () => {
|
|
2267
|
+
const arg = $.SUBRULE2($.containerCondition, { ARGS: [ctx] });
|
|
2268
|
+
if (!$.RECORDING_PHASE) {
|
|
2269
|
+
args.push($.wrap(arg));
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
},
|
|
2273
|
+
{
|
|
2274
|
+
// Declaration: starts with Ident or CustomProperty followed by Assign (colon)
|
|
2275
|
+
GATE: () => {
|
|
2276
|
+
const next = $.LA(1);
|
|
2277
|
+
const after = $.LA(2);
|
|
2278
|
+
const isIdent = next.tokenType === T.Ident || next.tokenType === T.PlainIdent || next.tokenType === T.CustomProperty;
|
|
2279
|
+
return isIdent && after && tokenMatcher(after, T.Assign);
|
|
2280
|
+
},
|
|
2281
|
+
ALT: () => {
|
|
2282
|
+
const arg = $.SUBRULE($.declaration, { ARGS: [ctx] });
|
|
2283
|
+
if (!$.RECORDING_PHASE) {
|
|
2284
|
+
args.push($.wrap(arg));
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
},
|
|
2288
|
+
{
|
|
2289
|
+
// Just a name (Any): Ident, PlainIdent, or CustomProperty without Assign
|
|
2290
|
+
GATE: () => {
|
|
2291
|
+
const next = $.LA(1);
|
|
2292
|
+
const after = $.LA(2);
|
|
2293
|
+
const isIdent = next.tokenType === T.Ident || next.tokenType === T.PlainIdent || next.tokenType === T.CustomProperty;
|
|
2294
|
+
return isIdent && (!after || !tokenMatcher(after, T.Assign));
|
|
2295
|
+
},
|
|
2296
|
+
ALT: () => {
|
|
2297
|
+
let nameToken;
|
|
2298
|
+
$.OR3([
|
|
2299
|
+
{ ALT: () => nameToken = $.CONSUME(T.Ident) },
|
|
2300
|
+
{ ALT: () => nameToken = $.CONSUME(T.PlainIdent) },
|
|
2301
|
+
{ ALT: () => nameToken = $.CONSUME(T.CustomProperty) }
|
|
2302
|
+
]);
|
|
2303
|
+
if (!$.RECORDING_PHASE && nameToken) {
|
|
2304
|
+
const nameNode = $.wrap(new Any(nameToken.image, { role: 'name' }, $.getLocationInfo(nameToken), this.context), true);
|
|
2305
|
+
args.push(nameNode);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
]);
|
|
2310
|
+
}
|
|
2311
|
+
});
|
|
2312
|
+
$.CONSUME(T.RParen);
|
|
2313
|
+
if (!$.RECORDING_PHASE) {
|
|
2314
|
+
const call = new Call({
|
|
2315
|
+
name: funcName,
|
|
2316
|
+
args: args.length > 0 ? new List(args) : undefined
|
|
2317
|
+
}, undefined, $.getLocationFromNodes([funcStart]), this.context);
|
|
2318
|
+
nodes.push(call);
|
|
2319
|
+
}
|
|
2320
|
+
// Check for and/or after the function call (similar to mediaCondition)
|
|
2321
|
+
$.MANY(() => {
|
|
2322
|
+
let rule = $.OR4([
|
|
2323
|
+
{ ALT: () => $.SUBRULE($.containerAnd, { ARGS: [ctx] }) },
|
|
2324
|
+
{ ALT: () => $.SUBRULE($.containerOr, { ARGS: [ctx] }) }
|
|
2325
|
+
]);
|
|
2326
|
+
if (!$.RECORDING_PHASE) {
|
|
2327
|
+
nodes.push(...rule);
|
|
2328
|
+
}
|
|
2329
|
+
});
|
|
2330
|
+
if (!$.RECORDING_PHASE) {
|
|
2331
|
+
const location = $.endRule();
|
|
2332
|
+
// Always wrap function calls in QueryCondition (even if alone)
|
|
2333
|
+
return new QueryCondition(nodes, undefined, location, this.context);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
},
|
|
2337
|
+
{
|
|
2338
|
+
// Regular container condition
|
|
2339
|
+
ALT: () => $.SUBRULE($.containerCondition, { ARGS: [ctx] })
|
|
2340
|
+
}
|
|
2341
|
+
]);
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* Container condition: similar to media condition but without mediaType variant
|
|
2346
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@container#container-query
|
|
2347
|
+
*
|
|
2348
|
+
* Container conditions can also have `not` followed by a container query type function,
|
|
2349
|
+
* and `and`/`or` can be followed by `not`, which media queries don't support.
|
|
2350
|
+
*/
|
|
2351
|
+
export function containerCondition(T, alt) {
|
|
2352
|
+
const $ = this;
|
|
2353
|
+
return (ctx = {}) => {
|
|
2354
|
+
return $.OR([
|
|
2355
|
+
{
|
|
2356
|
+
// Handle `not` followed by a container query type function (e.g., `not scroll-state(...)`)
|
|
2357
|
+
GATE: () => {
|
|
2358
|
+
const next = $.LA(1);
|
|
2359
|
+
if (next.tokenType === T.Not) {
|
|
2360
|
+
const afterNot = $.LA(2);
|
|
2361
|
+
return afterNot && tokenMatcher(afterNot, T.FunctionStart);
|
|
2362
|
+
}
|
|
2363
|
+
return false;
|
|
2364
|
+
},
|
|
2365
|
+
ALT: () => {
|
|
2366
|
+
$.startRule();
|
|
2367
|
+
const notToken = $.CONSUME(T.Not);
|
|
2368
|
+
// Parse the function call as a container query
|
|
2369
|
+
const funcQuery = $.SUBRULE($.containerQuery, { ARGS: [ctx] });
|
|
2370
|
+
if (!$.RECORDING_PHASE) {
|
|
2371
|
+
return new QueryCondition([
|
|
2372
|
+
$.wrap(new Keyword(notToken.image, undefined, $.getLocationInfo(notToken), this.context), 'both'),
|
|
2373
|
+
funcQuery
|
|
2374
|
+
], undefined, $.endRule(), this.context);
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
},
|
|
2378
|
+
{
|
|
2379
|
+
// Custom container condition that handles `and not` and `or not`
|
|
2380
|
+
// Always use container path for LParen (containerInParens handles the same as mediaInParens,
|
|
2381
|
+
// but containerAnd/containerOr can handle container-specific cases)
|
|
2382
|
+
GATE: () => {
|
|
2383
|
+
const next = $.LA(1);
|
|
2384
|
+
return next.tokenType === T.LParen;
|
|
2385
|
+
},
|
|
2386
|
+
ALT: () => {
|
|
2387
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
2388
|
+
$.startRule();
|
|
2389
|
+
let nodes;
|
|
2390
|
+
if (!RECORDING_PHASE) {
|
|
2391
|
+
nodes = [];
|
|
2392
|
+
}
|
|
2393
|
+
let node = $.SUBRULE($.containerInParens, { ARGS: [ctx] });
|
|
2394
|
+
if (!RECORDING_PHASE) {
|
|
2395
|
+
nodes.push(node);
|
|
2396
|
+
}
|
|
2397
|
+
$.MANY(() => {
|
|
2398
|
+
let rule = $.OR2([
|
|
2399
|
+
{ ALT: () => $.SUBRULE($.containerAnd, { ARGS: [ctx] }) },
|
|
2400
|
+
{ ALT: () => $.SUBRULE($.containerOr, { ARGS: [ctx] }) }
|
|
2401
|
+
]);
|
|
2402
|
+
if (!RECORDING_PHASE) {
|
|
2403
|
+
nodes.push(...rule);
|
|
2404
|
+
}
|
|
2405
|
+
});
|
|
2406
|
+
if (!RECORDING_PHASE) {
|
|
2407
|
+
if (nodes.length === 1) {
|
|
2408
|
+
$.endRule();
|
|
2409
|
+
return nodes[0];
|
|
2410
|
+
}
|
|
2411
|
+
return new QueryCondition(nodes, undefined, $.endRule(), this.context);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
},
|
|
2415
|
+
{
|
|
2416
|
+
// For cases not starting with LParen (like `not` at start), reuse media condition logic
|
|
2417
|
+
GATE: () => {
|
|
2418
|
+
const next = $.LA(1);
|
|
2419
|
+
// Only use mediaCondition if it doesn't start with LParen (LParen case handled above)
|
|
2420
|
+
return next.tokenType !== T.LParen;
|
|
2421
|
+
},
|
|
2422
|
+
ALT: () => $.SUBRULE3($.mediaCondition, { ARGS: [ctx] })
|
|
2423
|
+
}
|
|
2424
|
+
]);
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Container and: similar to mediaAnd but can handle `and not` and function calls
|
|
2429
|
+
*/
|
|
2430
|
+
export function containerAnd(T) {
|
|
2431
|
+
const $ = this;
|
|
2432
|
+
return (ctx = {}) => {
|
|
2433
|
+
let token = $.CONSUME(T.And);
|
|
2434
|
+
// Handle `and not` or `and` followed by containerInParens or function call
|
|
2435
|
+
let node;
|
|
2436
|
+
$.OR3([
|
|
2437
|
+
{
|
|
2438
|
+
GATE: () => $.LA(1).tokenType === T.Not,
|
|
2439
|
+
ALT: () => {
|
|
2440
|
+
const notToken = $.CONSUME(T.Not);
|
|
2441
|
+
node = $.SUBRULE($.containerInParens, { ARGS: [ctx] });
|
|
2442
|
+
if (!$.RECORDING_PHASE) {
|
|
2443
|
+
const notNode = $.wrap(new Keyword(notToken.image, undefined, $.getLocationInfo(notToken), this.context), 'both');
|
|
2444
|
+
node = new QueryCondition([notNode, node], undefined, $.getLocationFromNodes([notNode, node]), this.context);
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
GATE: () => tokenMatcher($.LA(1), T.FunctionStart),
|
|
2450
|
+
ALT: () => {
|
|
2451
|
+
// Parse function call (reuse containerQuery logic)
|
|
2452
|
+
const funcStart = $.CONSUME(T.FunctionStart);
|
|
2453
|
+
const funcName = funcStart.image.slice(0, -1);
|
|
2454
|
+
let args = !$.RECORDING_PHASE ? [] : undefined;
|
|
2455
|
+
$.AT_LEAST_ONE_SEP({
|
|
2456
|
+
SEP: T.Comma,
|
|
2457
|
+
DEF: () => {
|
|
2458
|
+
$.OR2([
|
|
2459
|
+
{
|
|
2460
|
+
GATE: () => {
|
|
2461
|
+
const next = $.LA(1);
|
|
2462
|
+
return next.tokenType === T.LParen || next.tokenType === T.Not;
|
|
2463
|
+
},
|
|
2464
|
+
ALT: () => {
|
|
2465
|
+
const arg = $.SUBRULE2($.containerCondition, { ARGS: [ctx] });
|
|
2466
|
+
if (!$.RECORDING_PHASE) {
|
|
2467
|
+
args.push($.wrap(arg));
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
},
|
|
2471
|
+
{
|
|
2472
|
+
GATE: () => {
|
|
2473
|
+
const next = $.LA(1);
|
|
2474
|
+
const after = $.LA(2);
|
|
2475
|
+
const isIdent = next.tokenType === T.Ident || next.tokenType === T.PlainIdent || next.tokenType === T.CustomProperty;
|
|
2476
|
+
return isIdent && after && tokenMatcher(after, T.Assign);
|
|
2477
|
+
},
|
|
2478
|
+
ALT: () => {
|
|
2479
|
+
const arg = $.SUBRULE($.declaration, { ARGS: [ctx] });
|
|
2480
|
+
if (!$.RECORDING_PHASE) {
|
|
2481
|
+
args.push($.wrap(arg));
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
},
|
|
2485
|
+
{
|
|
2486
|
+
GATE: () => {
|
|
2487
|
+
const next = $.LA(1);
|
|
2488
|
+
const after = $.LA(2);
|
|
2489
|
+
const isIdent = next.tokenType === T.Ident || next.tokenType === T.PlainIdent || next.tokenType === T.CustomProperty;
|
|
2490
|
+
return isIdent && (!after || !tokenMatcher(after, T.Assign));
|
|
2491
|
+
},
|
|
2492
|
+
ALT: () => {
|
|
2493
|
+
let nameToken;
|
|
2494
|
+
$.OR7([
|
|
2495
|
+
{ ALT: () => nameToken = $.CONSUME(T.Ident) },
|
|
2496
|
+
{ ALT: () => nameToken = $.CONSUME(T.PlainIdent) },
|
|
2497
|
+
{ ALT: () => nameToken = $.CONSUME(T.CustomProperty) }
|
|
2498
|
+
]);
|
|
2499
|
+
if (!$.RECORDING_PHASE && nameToken) {
|
|
2500
|
+
const nameNode = $.wrap(new Any(nameToken.image, { role: 'name' }, $.getLocationInfo(nameToken), this.context), true);
|
|
2501
|
+
args.push(nameNode);
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
]);
|
|
2506
|
+
}
|
|
2507
|
+
});
|
|
2508
|
+
$.CONSUME(T.RParen);
|
|
2509
|
+
if (!$.RECORDING_PHASE) {
|
|
2510
|
+
node = new Call({
|
|
2511
|
+
name: funcName,
|
|
2512
|
+
args: args.length > 0 ? new List(args) : undefined
|
|
2513
|
+
}, undefined, $.getLocationFromNodes([funcStart]), this.context);
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
},
|
|
2517
|
+
{
|
|
2518
|
+
ALT: () => {
|
|
2519
|
+
node = $.SUBRULE2($.containerInParens, { ARGS: [ctx] });
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
]);
|
|
2523
|
+
if (!$.RECORDING_PHASE && node) {
|
|
2524
|
+
return [
|
|
2525
|
+
$.wrap(new Keyword(token.image, undefined, $.getLocationInfo(token), this.context), 'both'),
|
|
2526
|
+
node
|
|
2527
|
+
];
|
|
2528
|
+
}
|
|
2529
|
+
};
|
|
2530
|
+
}
|
|
2531
|
+
/**
|
|
2532
|
+
* Container or: similar to mediaOr but can handle `or not` and function calls
|
|
2533
|
+
*/
|
|
2534
|
+
export function containerOr(T) {
|
|
2535
|
+
const $ = this;
|
|
2536
|
+
return (ctx = {}) => {
|
|
2537
|
+
let token = $.CONSUME(T.Or);
|
|
2538
|
+
// Handle `or not` or `or` followed by containerInParens or function call
|
|
2539
|
+
let node;
|
|
2540
|
+
$.OR3([
|
|
2541
|
+
{
|
|
2542
|
+
GATE: () => $.LA(1).tokenType === T.Not,
|
|
2543
|
+
ALT: () => {
|
|
2544
|
+
const notToken = $.CONSUME(T.Not);
|
|
2545
|
+
node = $.SUBRULE($.containerInParens, { ARGS: [ctx] });
|
|
2546
|
+
if (!$.RECORDING_PHASE) {
|
|
2547
|
+
const notNode = $.wrap(new Keyword(notToken.image, undefined, $.getLocationInfo(notToken), this.context), 'both');
|
|
2548
|
+
node = new QueryCondition([notNode, node], undefined, $.getLocationFromNodes([notNode, node]), this.context);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
},
|
|
2552
|
+
{
|
|
2553
|
+
GATE: () => tokenMatcher($.LA(1), T.FunctionStart),
|
|
2554
|
+
ALT: () => {
|
|
2555
|
+
// Parse function call (reuse containerQuery logic)
|
|
2556
|
+
const funcStart = $.CONSUME(T.FunctionStart);
|
|
2557
|
+
const funcName = funcStart.image.slice(0, -1);
|
|
2558
|
+
let args = !$.RECORDING_PHASE ? [] : undefined;
|
|
2559
|
+
$.AT_LEAST_ONE_SEP({
|
|
2560
|
+
SEP: T.Comma,
|
|
2561
|
+
DEF: () => {
|
|
2562
|
+
$.OR2([
|
|
2563
|
+
{
|
|
2564
|
+
GATE: () => {
|
|
2565
|
+
const next = $.LA(1);
|
|
2566
|
+
return next.tokenType === T.LParen || next.tokenType === T.Not;
|
|
2567
|
+
},
|
|
2568
|
+
ALT: () => {
|
|
2569
|
+
const arg = $.SUBRULE2($.containerCondition, { ARGS: [ctx] });
|
|
2570
|
+
if (!$.RECORDING_PHASE) {
|
|
2571
|
+
args.push($.wrap(arg));
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
},
|
|
2575
|
+
{
|
|
2576
|
+
GATE: () => {
|
|
2577
|
+
const next = $.LA(1);
|
|
2578
|
+
const after = $.LA(2);
|
|
2579
|
+
const isIdent = next.tokenType === T.Ident || next.tokenType === T.PlainIdent || next.tokenType === T.CustomProperty;
|
|
2580
|
+
return isIdent && after && tokenMatcher(after, T.Assign);
|
|
2581
|
+
},
|
|
2582
|
+
ALT: () => {
|
|
2583
|
+
const arg = $.SUBRULE($.declaration, { ARGS: [ctx] });
|
|
2584
|
+
if (!$.RECORDING_PHASE) {
|
|
2585
|
+
args.push($.wrap(arg));
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
},
|
|
2589
|
+
{
|
|
2590
|
+
GATE: () => {
|
|
2591
|
+
const next = $.LA(1);
|
|
2592
|
+
const after = $.LA(2);
|
|
2593
|
+
const isIdent = next.tokenType === T.Ident || next.tokenType === T.PlainIdent || next.tokenType === T.CustomProperty;
|
|
2594
|
+
return isIdent && (!after || !tokenMatcher(after, T.Assign));
|
|
2595
|
+
},
|
|
2596
|
+
ALT: () => {
|
|
2597
|
+
let nameToken;
|
|
2598
|
+
$.OR9([
|
|
2599
|
+
{ ALT: () => nameToken = $.CONSUME(T.Ident) },
|
|
2600
|
+
{ ALT: () => nameToken = $.CONSUME(T.PlainIdent) },
|
|
2601
|
+
{ ALT: () => nameToken = $.CONSUME(T.CustomProperty) }
|
|
2602
|
+
]);
|
|
2603
|
+
if (!$.RECORDING_PHASE && nameToken) {
|
|
2604
|
+
const nameNode = $.wrap(new Any(nameToken.image, { role: 'name' }, $.getLocationInfo(nameToken), this.context), true);
|
|
2605
|
+
args.push(nameNode);
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
]);
|
|
2610
|
+
}
|
|
2611
|
+
});
|
|
2612
|
+
$.CONSUME(T.RParen);
|
|
2613
|
+
if (!$.RECORDING_PHASE) {
|
|
2614
|
+
node = new Call({
|
|
2615
|
+
name: funcName,
|
|
2616
|
+
args: args.length > 0 ? new List(args) : undefined
|
|
2617
|
+
}, undefined, $.getLocationFromNodes([funcStart]), this.context);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
},
|
|
2621
|
+
{
|
|
2622
|
+
ALT: () => {
|
|
2623
|
+
node = $.SUBRULE2($.containerInParens, { ARGS: [ctx] });
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
]);
|
|
2627
|
+
if (!$.RECORDING_PHASE && node) {
|
|
2628
|
+
return [
|
|
2629
|
+
$.wrap(new Keyword(token.image, undefined, $.getLocationInfo(token), this.context), 'both'),
|
|
2630
|
+
node
|
|
2631
|
+
];
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
/**
|
|
2636
|
+
* Container in parens: similar to media in parens
|
|
2637
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@container#container-query
|
|
2638
|
+
*/
|
|
2639
|
+
export function containerInParens(T, alt) {
|
|
2640
|
+
const $ = this;
|
|
2641
|
+
// Reuse media in parens logic since container queries use the same syntax
|
|
2642
|
+
return (ctx = {}) => {
|
|
2643
|
+
return $.SUBRULE($.mediaInParens, { ARGS: [ctx] });
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
/**
|
|
2647
|
+
* Container feature: similar to media feature
|
|
2648
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@container#container-query
|
|
2649
|
+
*/
|
|
2650
|
+
export function containerFeature(T, alt) {
|
|
2651
|
+
const $ = this;
|
|
2652
|
+
// Reuse media feature logic since container queries use the same syntax
|
|
2653
|
+
return (ctx = {}) => {
|
|
2654
|
+
return $.SUBRULE($.mediaFeature, { ARGS: [ctx] });
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
// scopeAtRule: @scope <prelude>? { main }
|
|
2658
|
+
export function scopeAtRule(T, preludeRule) {
|
|
2659
|
+
const $ = this;
|
|
2660
|
+
return (ctx = {}) => {
|
|
2661
|
+
$.startRule();
|
|
2662
|
+
const name = $.CONSUME(T.AtScope);
|
|
2663
|
+
let prelude;
|
|
2664
|
+
if (preludeRule) {
|
|
2665
|
+
const resolvedPreludeRule = resolvePreludeRule($, preludeRule);
|
|
2666
|
+
if (typeof resolvedPreludeRule === 'function') {
|
|
2667
|
+
prelude = $.SUBRULE(resolvedPreludeRule, { ARGS: [ctx] });
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
else {
|
|
2671
|
+
const preludeNodes = [];
|
|
2672
|
+
$.MANY(() => preludeNodes.push($.wrap($.SUBRULE($.anyOuterValue, { ARGS: [ctx] }))));
|
|
2673
|
+
if (!$.RECORDING_PHASE) {
|
|
2674
|
+
prelude = preludeNodes.length
|
|
2675
|
+
? $.wrap(new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), this.context), 'both')
|
|
2676
|
+
: undefined;
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
$.CONSUME(T.LCurly);
|
|
2680
|
+
const rules = $.SUBRULE($.atRuleBody, { ARGS: [ctx] });
|
|
2681
|
+
$.CONSUME(T.RCurly);
|
|
2682
|
+
if (!$.RECORDING_PHASE) {
|
|
2683
|
+
return new AtRule({
|
|
2684
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
2685
|
+
prelude,
|
|
2686
|
+
rules
|
|
2687
|
+
}, { nestable: true }, $.endRule(), this.context);
|
|
2688
|
+
}
|
|
2689
|
+
};
|
|
2690
|
+
}
|
|
2691
|
+
// documentAtRule (non-standard): @document <prelude>? { main }
|
|
2692
|
+
export function documentAtRule(T) {
|
|
2693
|
+
const $ = this;
|
|
2694
|
+
return (ctx = {}) => {
|
|
2695
|
+
$.startRule();
|
|
2696
|
+
const name = $.CONSUME(T.AtDocument);
|
|
2697
|
+
const preludeNodes = [];
|
|
2698
|
+
$.MANY(() => preludeNodes.push($.wrap($.SUBRULE($.anyOuterValue, { ARGS: [ctx] }))));
|
|
2699
|
+
$.CONSUME(T.LCurly);
|
|
2700
|
+
const rules = $.SUBRULE($.atRuleBody, { ARGS: [ctx] });
|
|
2701
|
+
$.CONSUME(T.RCurly);
|
|
2702
|
+
if (!$.RECORDING_PHASE) {
|
|
2703
|
+
return new AtRule({
|
|
2704
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
2705
|
+
prelude: preludeNodes.length ? $.wrap(new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), this.context), 'both') : undefined,
|
|
2706
|
+
rules
|
|
2707
|
+
}, undefined, $.endRule(), this.context);
|
|
2708
|
+
}
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2711
|
+
/**
|
|
2712
|
+
* `@layer` at rule
|
|
2713
|
+
*
|
|
2714
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
|
|
2715
|
+
*
|
|
2716
|
+
* `@layer` =
|
|
2717
|
+
* `@layer` <layer-name>? { <rule-list> } |
|
|
2718
|
+
* `@layer` <layer-name># ;
|
|
2719
|
+
*
|
|
2720
|
+
* `<layer-name>` =
|
|
2721
|
+
* <ident> [ '.' <ident> ]*
|
|
2722
|
+
*/
|
|
2723
|
+
export function layerAtRule(T) {
|
|
2724
|
+
const $ = this;
|
|
2725
|
+
return (ctx = {}) => {
|
|
2726
|
+
const RECORDING_PHASE = $.RECORDING_PHASE;
|
|
2727
|
+
$.startRule();
|
|
2728
|
+
const atTok = $.CONSUME(T.AtLayer);
|
|
2729
|
+
// Optional single layer-name before a block, or first of a comma list in statement form
|
|
2730
|
+
const preludeNodes = RECORDING_PHASE ? [] : [];
|
|
2731
|
+
return $.OR([
|
|
2732
|
+
{
|
|
2733
|
+
ALT: () => {
|
|
2734
|
+
$.OPTION(() => {
|
|
2735
|
+
const nameNode = $.SUBRULE($.layerName, { ARGS: [ctx] });
|
|
2736
|
+
if (!RECORDING_PHASE) {
|
|
2737
|
+
preludeNodes.push($.wrap(nameNode));
|
|
2738
|
+
}
|
|
2739
|
+
});
|
|
2740
|
+
$.CONSUME(T.LCurly);
|
|
2741
|
+
const rules = $.SUBRULE($.atRuleBody, { ARGS: [ctx] });
|
|
2742
|
+
$.CONSUME(T.RCurly);
|
|
2743
|
+
if (!RECORDING_PHASE) {
|
|
2744
|
+
return new AtRule({
|
|
2745
|
+
name: $.wrap(new Any(atTok.image, { role: 'atkeyword' }, $.getLocationInfo(atTok), this.context), true),
|
|
2746
|
+
prelude: preludeNodes.length ? $.wrap(new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), this.context), 'both') : undefined,
|
|
2747
|
+
rules
|
|
2748
|
+
}, { nestable: true }, $.endRule(), this.context);
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
},
|
|
2752
|
+
{
|
|
2753
|
+
ALT: () => {
|
|
2754
|
+
$.MANY_SEP({
|
|
2755
|
+
SEP: T.Comma,
|
|
2756
|
+
DEF: () => {
|
|
2757
|
+
let nameNode = $.SUBRULE2($.layerName, { ARGS: [ctx] });
|
|
2758
|
+
if (!RECORDING_PHASE) {
|
|
2759
|
+
preludeNodes.push($.wrap(nameNode));
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
});
|
|
2763
|
+
$.CONSUME(T.Semi);
|
|
2764
|
+
if (!RECORDING_PHASE) {
|
|
2765
|
+
return new AtRule({
|
|
2766
|
+
name: $.wrap(new Any(atTok.image, { role: 'atkeyword' }, $.getLocationInfo(atTok), this.context), true),
|
|
2767
|
+
prelude: preludeNodes.length ? $.wrap(new List(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), this.context), 'both') : undefined
|
|
2768
|
+
}, undefined, $.endRule(), this.context);
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
]);
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2775
|
+
/**
|
|
2776
|
+
* <layer-name> = <ident> ('.' <ident>)*
|
|
2777
|
+
*/
|
|
2778
|
+
export function layerName(T) {
|
|
2779
|
+
const $ = this;
|
|
2780
|
+
return (ctx = {}) => {
|
|
2781
|
+
const RECORDING_PHASE = $.RECORDING_PHASE;
|
|
2782
|
+
$.startRule();
|
|
2783
|
+
const nodes = RECORDING_PHASE ? [] : [];
|
|
2784
|
+
const first = $.CONSUME(T.Ident);
|
|
2785
|
+
if (!RECORDING_PHASE) {
|
|
2786
|
+
nodes.push($.wrap($.processValueToken(first)));
|
|
2787
|
+
}
|
|
2788
|
+
$.MANY({
|
|
2789
|
+
GATE: $.noSep,
|
|
2790
|
+
DEF: () => {
|
|
2791
|
+
const seg = $.CONSUME(T.DotName);
|
|
2792
|
+
if (!RECORDING_PHASE) {
|
|
2793
|
+
nodes.push($.wrap($.processValueToken(seg)));
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
});
|
|
2797
|
+
if (!RECORDING_PHASE) {
|
|
2798
|
+
const loc = $.endRule();
|
|
2799
|
+
return new Sequence(nodes, undefined, loc, this.context);
|
|
2800
|
+
}
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
/**
|
|
2804
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/@supports
|
|
2805
|
+
*/
|
|
2806
|
+
// supportsAtRule
|
|
2807
|
+
// : SUPPORTS_RULE WS* supportsCondition WS* LCURLY main RCURLY
|
|
2808
|
+
// ;
|
|
2809
|
+
export function supportsAtRule(T, preludeRule) {
|
|
2810
|
+
const $ = this;
|
|
2811
|
+
return (ctx = {}) => {
|
|
2812
|
+
$.startRule();
|
|
2813
|
+
let name = $.CONSUME(T.AtSupports);
|
|
2814
|
+
const resolvedPreludeRule = resolvePreludeRule($, preludeRule);
|
|
2815
|
+
const prelude = typeof resolvedPreludeRule === 'function'
|
|
2816
|
+
? $.SUBRULE(resolvedPreludeRule, { ARGS: [ctx] })
|
|
2817
|
+
: $.SUBRULE($.supportsCondition, { ARGS: [ctx] });
|
|
2818
|
+
$.CONSUME(T.LCurly);
|
|
2819
|
+
let rules = $.SUBRULE($.atRuleBody, { ARGS: [ctx] });
|
|
2820
|
+
$.CONSUME(T.RCurly);
|
|
2821
|
+
if (!$.RECORDING_PHASE) {
|
|
2822
|
+
let location = $.endRule();
|
|
2823
|
+
return new AtRule({
|
|
2824
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
2825
|
+
prelude: $.wrap(prelude, 'both'),
|
|
2826
|
+
rules
|
|
2827
|
+
}, { nestable: true }, location, this.context);
|
|
2828
|
+
}
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
/** spec-compliant but simplified */
|
|
2832
|
+
// supportsCondition
|
|
2833
|
+
// : NOT supportsInParens
|
|
2834
|
+
// | supportsInParens (WS* AND supportsInParens)*
|
|
2835
|
+
// | supportsInParens (WS* OR supportsInParens)*
|
|
2836
|
+
// ;
|
|
2837
|
+
export function supportsCondition(T) {
|
|
2838
|
+
const $ = this;
|
|
2839
|
+
let conditionAlt = (ctx = {}) => [
|
|
2840
|
+
{
|
|
2841
|
+
GATE: () => $.LA(1).tokenType === T.Not,
|
|
2842
|
+
ALT: () => {
|
|
2843
|
+
$.startRule();
|
|
2844
|
+
let keyword = $.CONSUME(T.Not);
|
|
2845
|
+
let value = $.SUBRULE($.supportsInParens, { ARGS: [ctx] });
|
|
2846
|
+
if (!$.RECORDING_PHASE) {
|
|
2847
|
+
let location = $.endRule();
|
|
2848
|
+
return new QueryCondition([
|
|
2849
|
+
$.wrap(new Keyword(keyword.image, undefined, $.getLocationInfo(keyword), this.context)),
|
|
2850
|
+
value
|
|
2851
|
+
], undefined, location, this.context);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
},
|
|
2855
|
+
{
|
|
2856
|
+
GATE: () => $.LA(1).tokenType !== T.Not,
|
|
2857
|
+
ALT: () => {
|
|
2858
|
+
let start = $.startRule();
|
|
2859
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
2860
|
+
let [startOffset, startLine, startColumn] = start ?? [];
|
|
2861
|
+
let left = $.SUBRULE2($.supportsInParens, { ARGS: [ctx] });
|
|
2862
|
+
/**
|
|
2863
|
+
* Can be followed by many ands or many ors
|
|
2864
|
+
*/
|
|
2865
|
+
$.OR2([
|
|
2866
|
+
{
|
|
2867
|
+
ALT: () => {
|
|
2868
|
+
$.AT_LEAST_ONE(() => {
|
|
2869
|
+
let keyword = $.CONSUME(T.And);
|
|
2870
|
+
let right = $.SUBRULE3($.supportsInParens, { ARGS: [ctx] });
|
|
2871
|
+
if (!RECORDING_PHASE) {
|
|
2872
|
+
let [, , , endOffset, endLine, endColumn] = right.location;
|
|
2873
|
+
left = new QueryCondition([
|
|
2874
|
+
left,
|
|
2875
|
+
$.wrap(new Keyword(keyword.image, undefined, $.getLocationInfo(keyword), this.context)),
|
|
2876
|
+
right
|
|
2877
|
+
], undefined, [startOffset, startLine, startColumn, endOffset, endLine, endColumn], this.context);
|
|
2878
|
+
}
|
|
2879
|
+
});
|
|
2880
|
+
}
|
|
2881
|
+
},
|
|
2882
|
+
{
|
|
2883
|
+
ALT: () => {
|
|
2884
|
+
$.AT_LEAST_ONE2(() => {
|
|
2885
|
+
let keyword = $.CONSUME(T.Or);
|
|
2886
|
+
let right = $.SUBRULE5($.supportsInParens, { ARGS: [ctx] });
|
|
2887
|
+
if (!RECORDING_PHASE) {
|
|
2888
|
+
let [, , , endOffset, endLine, endColumn] = right.location;
|
|
2889
|
+
left = new QueryCondition([
|
|
2890
|
+
left,
|
|
2891
|
+
$.wrap(new Keyword(keyword.image, undefined, $.getLocationInfo(keyword), this.context)),
|
|
2892
|
+
right
|
|
2893
|
+
], undefined, [startOffset, startLine, startColumn, endOffset, endLine, endColumn], this.context);
|
|
2894
|
+
}
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2897
|
+
},
|
|
2898
|
+
{
|
|
2899
|
+
ALT: EMPTY_ALT()
|
|
2900
|
+
}
|
|
2901
|
+
]);
|
|
2902
|
+
if (!RECORDING_PHASE) {
|
|
2903
|
+
$.endRule();
|
|
2904
|
+
}
|
|
2905
|
+
return left;
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
];
|
|
2909
|
+
return (ctx = {}) => $.OR(conditionAlt(ctx));
|
|
2910
|
+
}
|
|
2911
|
+
// supportsInParens
|
|
2912
|
+
// : '(' WS* supportsCondition WS* ')'
|
|
2913
|
+
// | '(' WS* declaration WS* ')'
|
|
2914
|
+
// | generalEnclosed
|
|
2915
|
+
// ;
|
|
2916
|
+
export function supportsInParens(T) {
|
|
2917
|
+
const $ = this;
|
|
2918
|
+
let conditionAlt = (ctx = {}) => [
|
|
2919
|
+
{
|
|
2920
|
+
ALT: () => {
|
|
2921
|
+
$.startRule();
|
|
2922
|
+
/** Function-like call */
|
|
2923
|
+
let name = $.CONSUME(T.Ident);
|
|
2924
|
+
let args;
|
|
2925
|
+
$.OR2([
|
|
2926
|
+
{
|
|
2927
|
+
GATE: $.noSep,
|
|
2928
|
+
ALT: () => {
|
|
2929
|
+
$.CONSUME(T.LParen);
|
|
2930
|
+
args = $.SUBRULE($.valueList, { ARGS: [ctx] });
|
|
2931
|
+
$.CONSUME(T.RParen);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
]);
|
|
2935
|
+
if (!$.RECORDING_PHASE) {
|
|
2936
|
+
let location = $.endRule();
|
|
2937
|
+
return new Call({
|
|
2938
|
+
name: name.image,
|
|
2939
|
+
args
|
|
2940
|
+
}, undefined, location, this.context);
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
},
|
|
2944
|
+
{
|
|
2945
|
+
ALT: () => {
|
|
2946
|
+
$.startRule();
|
|
2947
|
+
let values = [];
|
|
2948
|
+
$.CONSUME2(T.LParen);
|
|
2949
|
+
/**
|
|
2950
|
+
* Intentionally omits "generalEnclosed" from spec.
|
|
2951
|
+
* See the note on media queries.
|
|
2952
|
+
*/
|
|
2953
|
+
let value = $.OR3([
|
|
2954
|
+
{ ALT: () => $.SUBRULE($.supportsCondition, { ARGS: [ctx] }) },
|
|
2955
|
+
{ ALT: () => $.SUBRULE($.declaration, { ARGS: [ctx] }) }
|
|
2956
|
+
]);
|
|
2957
|
+
$.CONSUME2(T.RParen);
|
|
2958
|
+
if (!$.RECORDING_PHASE) {
|
|
2959
|
+
let location = $.endRule();
|
|
2960
|
+
if (!(value instanceof Node)) {
|
|
2961
|
+
value = new Sequence(values, undefined, $.getLocationFromNodes(values), this.context);
|
|
2962
|
+
}
|
|
2963
|
+
return $.wrap(new Paren($.wrap(value, 'both'), undefined, location, this.context));
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
];
|
|
2968
|
+
return (ctx = {}) => $.OR(conditionAlt(ctx));
|
|
2969
|
+
}
|
|
2970
|
+
/** Used within anyOuterValue */
|
|
2971
|
+
export function functionCallLike(T) {
|
|
2972
|
+
const $ = this;
|
|
2973
|
+
return (ctx = {}) => {
|
|
2974
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
2975
|
+
$.startRule();
|
|
2976
|
+
const name = $.CONSUME(T.FunctionStart);
|
|
2977
|
+
let args = !RECORDING_PHASE ? [] : undefined;
|
|
2978
|
+
let seq;
|
|
2979
|
+
$.MANY({
|
|
2980
|
+
GATE: () => {
|
|
2981
|
+
let tt = $.LA(1).tokenType;
|
|
2982
|
+
return tt !== T.RParen && tt !== T.UrlEnd;
|
|
2983
|
+
},
|
|
2984
|
+
DEF: () => {
|
|
2985
|
+
const node = $.SUBRULE($.anyOuterValue, { ARGS: [ctx] });
|
|
2986
|
+
if (!RECORDING_PHASE) {
|
|
2987
|
+
args.push($.wrap(node));
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
});
|
|
2991
|
+
if (!RECORDING_PHASE) {
|
|
2992
|
+
let location = args.length ? $.getLocationFromNodes(args) : undefined;
|
|
2993
|
+
if (args.length) {
|
|
2994
|
+
seq = new Sequence(args, undefined, location, this.context);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
$.OR([
|
|
2998
|
+
{ ALT: () => $.CONSUME(T.RParen) },
|
|
2999
|
+
{ ALT: () => $.CONSUME(T.UrlEnd) }
|
|
3000
|
+
]);
|
|
3001
|
+
if (!RECORDING_PHASE) {
|
|
3002
|
+
const location = $.endRule();
|
|
3003
|
+
return $.wrap(new Call({ name: name.image.slice(0, -1), args: new List(seq ? [seq] : []) }, undefined, location, this.context));
|
|
3004
|
+
}
|
|
3005
|
+
};
|
|
3006
|
+
}
|
|
3007
|
+
export function functionCall(T, alt) {
|
|
3008
|
+
const $ = this;
|
|
3009
|
+
const modernColorFunctions = new Set(['rgb', 'rgba', 'hsl', 'hsla']);
|
|
3010
|
+
const isModernColorCall = (name, args) => {
|
|
3011
|
+
if (!modernColorFunctions.has(name.toLowerCase())) {
|
|
3012
|
+
return false;
|
|
3013
|
+
}
|
|
3014
|
+
if (!args || args.value.length !== 1) {
|
|
3015
|
+
return false;
|
|
3016
|
+
}
|
|
3017
|
+
const firstArg = args.value[0];
|
|
3018
|
+
return Boolean(firstArg instanceof Sequence && firstArg.value.length >= 2);
|
|
3019
|
+
};
|
|
3020
|
+
alt ??= (ctx = {}) => [
|
|
3021
|
+
{
|
|
3022
|
+
GATE: () => tokenMatcher($.LA(1), T.FunctionStart) && $.LA(1).image.slice(0, -1).toLowerCase() === 'if',
|
|
3023
|
+
ALT: () => $.SUBRULE($.ifFunction, { ARGS: [ctx] })
|
|
3024
|
+
},
|
|
3025
|
+
{
|
|
3026
|
+
// Disambiguate known functions by their dedicated tokens
|
|
3027
|
+
GATE: () => {
|
|
3028
|
+
let tokenType = $.LA(1).tokenType;
|
|
3029
|
+
return tokenType === T.UrlStart
|
|
3030
|
+
|| tokenType === T.Var
|
|
3031
|
+
|| tokenType === T.Calc;
|
|
3032
|
+
},
|
|
3033
|
+
ALT: () => $.SUBRULE($.knownFunctions, { ARGS: [ctx] })
|
|
3034
|
+
},
|
|
3035
|
+
{
|
|
3036
|
+
GATE: () => {
|
|
3037
|
+
let tokenType = $.LA(1).tokenType;
|
|
3038
|
+
return tokenType !== T.UrlStart
|
|
3039
|
+
&& tokenType !== T.Var
|
|
3040
|
+
&& tokenType !== T.Calc;
|
|
3041
|
+
},
|
|
3042
|
+
ALT: () => {
|
|
3043
|
+
$.startRule();
|
|
3044
|
+
let name = $.CONSUME(T.FunctionStart);
|
|
3045
|
+
let args;
|
|
3046
|
+
$.OPTION(() => args = $.SUBRULE($.functionCallArgs, { ARGS: [ctx] }));
|
|
3047
|
+
$.CONSUME(T.RParen);
|
|
3048
|
+
if (!$.RECORDING_PHASE) {
|
|
3049
|
+
let location = $.endRule();
|
|
3050
|
+
const functionName = name.image.slice(0, -1);
|
|
3051
|
+
const modernSyntax = isModernColorCall(functionName, args);
|
|
3052
|
+
return new Call({
|
|
3053
|
+
name: functionName,
|
|
3054
|
+
args
|
|
3055
|
+
}, modernSyntax ? { modernSyntax: true } : undefined, location, this.context);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
];
|
|
3060
|
+
return (ctx = {}) => $.OR(alt(ctx));
|
|
3061
|
+
}
|
|
3062
|
+
/**
|
|
3063
|
+
* Originally, function arguments always had commas,
|
|
3064
|
+
* but it looks like that might be expanded in the
|
|
3065
|
+
* future in CSS to allow for semi-colon separators.
|
|
3066
|
+
* with the same rationale of why this was introduced
|
|
3067
|
+
* by Less (that values can already have commas).
|
|
3068
|
+
*
|
|
3069
|
+
* @see https://drafts.csswg.org/css-values-4/#interpolate
|
|
3070
|
+
*
|
|
3071
|
+
* @todo - if a function is introduced where semi-colons
|
|
3072
|
+
* are separators AND only 1 argument is required, then
|
|
3073
|
+
* that will have to be specially handled.
|
|
3074
|
+
*/
|
|
3075
|
+
export function functionCallArgs(T) {
|
|
3076
|
+
const $ = this;
|
|
3077
|
+
return (ctx = {}) => {
|
|
3078
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
3079
|
+
$.startRule();
|
|
3080
|
+
let node = $.SUBRULE($.valueSequence, { ARGS: [ctx] });
|
|
3081
|
+
let commaNodes;
|
|
3082
|
+
let semiNodes;
|
|
3083
|
+
if (!RECORDING_PHASE) {
|
|
3084
|
+
commaNodes = [$.wrap(node, true)];
|
|
3085
|
+
semiNodes = [];
|
|
3086
|
+
}
|
|
3087
|
+
let isSemiList = false;
|
|
3088
|
+
$.MANY(() => {
|
|
3089
|
+
$.OR([
|
|
3090
|
+
{
|
|
3091
|
+
GATE: () => !isSemiList,
|
|
3092
|
+
ALT: () => {
|
|
3093
|
+
$.CONSUME(T.Comma);
|
|
3094
|
+
node = $.SUBRULE2($.valueSequence, { ARGS: [ctx] });
|
|
3095
|
+
if (!RECORDING_PHASE) {
|
|
3096
|
+
commaNodes.push($.wrap(node, true));
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
},
|
|
3100
|
+
{
|
|
3101
|
+
ALT: () => {
|
|
3102
|
+
isSemiList = true;
|
|
3103
|
+
$.CONSUME(T.Semi);
|
|
3104
|
+
if (!RECORDING_PHASE) {
|
|
3105
|
+
/** Aggregate the previous set of comma-nodes */
|
|
3106
|
+
if (commaNodes.length > 1) {
|
|
3107
|
+
let commaList = new List(commaNodes, undefined, $.getLocationFromNodes(commaNodes), this.context);
|
|
3108
|
+
semiNodes.push(commaList);
|
|
3109
|
+
}
|
|
3110
|
+
else {
|
|
3111
|
+
semiNodes.push(commaNodes[0]);
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
node = $.SUBRULE3($.valueList, { ARGS: [ctx] });
|
|
3115
|
+
if (!RECORDING_PHASE) {
|
|
3116
|
+
semiNodes.push($.wrap(node, true));
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
]);
|
|
3121
|
+
});
|
|
3122
|
+
if (!RECORDING_PHASE) {
|
|
3123
|
+
$.endRule();
|
|
3124
|
+
const nodes = isSemiList ? semiNodes : commaNodes;
|
|
3125
|
+
return new List(nodes, isSemiList ? { sep: ';' } : undefined);
|
|
3126
|
+
}
|
|
3127
|
+
};
|
|
3128
|
+
}
|
|
3129
|
+
// https://www.w3.org/TR/css-cascade-4/#at-import
|
|
3130
|
+
// importAtRule
|
|
3131
|
+
// : IMPORT_RULE WS* (URL_FUNCTION | STRING) (WS* SUPPORTS_FUNCTION WS* (supportsCondition | declaration))? (WS* mediaQuery)? SEMI
|
|
3132
|
+
// ;
|
|
3133
|
+
export function importAtRule(T) {
|
|
3134
|
+
const $ = this;
|
|
3135
|
+
return (ctx = {}) => {
|
|
3136
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
3137
|
+
$.startRule();
|
|
3138
|
+
let name = $.CONSUME(T.AtImport);
|
|
3139
|
+
let preludeNodes;
|
|
3140
|
+
if (!RECORDING_PHASE) {
|
|
3141
|
+
preludeNodes = [];
|
|
3142
|
+
}
|
|
3143
|
+
let node = $.SUBRULE($.importPrelude, { ARGS: [ctx] });
|
|
3144
|
+
if (!RECORDING_PHASE) {
|
|
3145
|
+
preludeNodes.push($.wrap(node));
|
|
3146
|
+
}
|
|
3147
|
+
let extraNodes;
|
|
3148
|
+
$.OPTION(() => {
|
|
3149
|
+
extraNodes = $.SUBRULE($.importPostlude, { ARGS: [ctx] });
|
|
3150
|
+
});
|
|
3151
|
+
if (!RECORDING_PHASE && extraNodes && extraNodes.length) {
|
|
3152
|
+
for (const n of extraNodes) {
|
|
3153
|
+
preludeNodes.push(n);
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
$.CONSUME(T.Semi);
|
|
3157
|
+
if (!RECORDING_PHASE) {
|
|
3158
|
+
let location = $.endRule();
|
|
3159
|
+
return new AtRule({
|
|
3160
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
3161
|
+
prelude: new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), this.context)
|
|
3162
|
+
}, undefined, location, this.context);
|
|
3163
|
+
}
|
|
3164
|
+
};
|
|
3165
|
+
}
|
|
3166
|
+
/** import prelude: url(...) or "string" */
|
|
3167
|
+
export function importPrelude(T) {
|
|
3168
|
+
const $ = this;
|
|
3169
|
+
return (ctx = {}) => {
|
|
3170
|
+
return $.OR([
|
|
3171
|
+
{ ALT: () => $.SUBRULE($.urlFunction, { ARGS: [ctx] }) },
|
|
3172
|
+
{ ALT: () => $.SUBRULE($.string, { ARGS: [ctx] }) }
|
|
3173
|
+
]);
|
|
3174
|
+
};
|
|
3175
|
+
}
|
|
3176
|
+
/** import postlude: optional layer(), supports(), media. Returns Node[] */
|
|
3177
|
+
export function importPostlude(T) {
|
|
3178
|
+
const $ = this;
|
|
3179
|
+
return (ctx = {}) => {
|
|
3180
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
3181
|
+
let nodes;
|
|
3182
|
+
if (!RECORDING_PHASE) {
|
|
3183
|
+
nodes = [];
|
|
3184
|
+
}
|
|
3185
|
+
/** layer(responsive) */
|
|
3186
|
+
$.OPTION(() => {
|
|
3187
|
+
let start = $.CONSUME(T.Layer);
|
|
3188
|
+
let value = $.SUBRULE($.layerName);
|
|
3189
|
+
let end = $.CONSUME(T.RParen);
|
|
3190
|
+
if (!RECORDING_PHASE) {
|
|
3191
|
+
let { startOffset, startLine, startColumn } = start;
|
|
3192
|
+
let { endOffset, endLine, endColumn } = end;
|
|
3193
|
+
let location = [startOffset, startLine, startColumn, endOffset, endLine, endColumn];
|
|
3194
|
+
nodes.push($.wrap(new Call({
|
|
3195
|
+
name: 'layer',
|
|
3196
|
+
args: new List([value])
|
|
3197
|
+
}, undefined, location, this.context)));
|
|
3198
|
+
}
|
|
3199
|
+
});
|
|
3200
|
+
/** supports(display: grid) */
|
|
3201
|
+
$.OPTION2(() => {
|
|
3202
|
+
let start = $.CONSUME(T.Supports);
|
|
3203
|
+
let value = $.OR4([
|
|
3204
|
+
{ ALT: () => $.SUBRULE($.supportsCondition, { ARGS: [ctx] }) },
|
|
3205
|
+
{ ALT: () => $.SUBRULE($.declaration, { ARGS: [ctx] }) }
|
|
3206
|
+
]);
|
|
3207
|
+
let end = $.CONSUME2(T.RParen);
|
|
3208
|
+
if (!RECORDING_PHASE) {
|
|
3209
|
+
let { startOffset, startLine, startColumn } = start;
|
|
3210
|
+
let { endOffset, endLine, endColumn } = end;
|
|
3211
|
+
let location = [startOffset, startLine, startColumn, endOffset, endLine, endColumn];
|
|
3212
|
+
nodes.push($.wrap(new Call({
|
|
3213
|
+
name: 'supports',
|
|
3214
|
+
args: new List([$.wrap(value, 'both')])
|
|
3215
|
+
}, undefined, location, this.context)));
|
|
3216
|
+
}
|
|
3217
|
+
});
|
|
3218
|
+
/** media query list */
|
|
3219
|
+
$.OPTION3(() => {
|
|
3220
|
+
let mediaNode = $.SUBRULE($.mediaQueryList, { ARGS: [ctx] });
|
|
3221
|
+
if (!RECORDING_PHASE) {
|
|
3222
|
+
nodes.push(mediaNode);
|
|
3223
|
+
}
|
|
3224
|
+
});
|
|
3225
|
+
return nodes;
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3228
|
+
/**
|
|
3229
|
+
* @todo - add more structure for known nested at-rules.
|
|
3230
|
+
*/
|
|
3231
|
+
export function nestedAtRule(T) {
|
|
3232
|
+
const $ = this;
|
|
3233
|
+
return (ctx = {}) => {
|
|
3234
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
3235
|
+
$.startRule();
|
|
3236
|
+
let name = $.CONSUME(T.AtNested);
|
|
3237
|
+
let preludeNodes;
|
|
3238
|
+
let rules;
|
|
3239
|
+
if (!RECORDING_PHASE) {
|
|
3240
|
+
preludeNodes = [];
|
|
3241
|
+
}
|
|
3242
|
+
$.MANY(() => {
|
|
3243
|
+
let value = $.SUBRULE($.anyOuterValue, { ARGS: [ctx] });
|
|
3244
|
+
if (!RECORDING_PHASE) {
|
|
3245
|
+
preludeNodes.push($.wrap(value));
|
|
3246
|
+
}
|
|
3247
|
+
});
|
|
3248
|
+
$.CONSUME(T.LCurly);
|
|
3249
|
+
// All known nested at-rules use declaration lists in their blocks
|
|
3250
|
+
rules = $.SUBRULE($.declarationList, { ARGS: [ctx] });
|
|
3251
|
+
$.CONSUME(T.RCurly);
|
|
3252
|
+
if (!$.RECORDING_PHASE) {
|
|
3253
|
+
return new AtRule({
|
|
3254
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
3255
|
+
prelude: preludeNodes.length ? $.wrap(new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), this.context), 'both') : undefined,
|
|
3256
|
+
rules
|
|
3257
|
+
}, undefined, $.endRule(), this.context);
|
|
3258
|
+
}
|
|
3259
|
+
};
|
|
3260
|
+
}
|
|
3261
|
+
export function nonNestedAtRule(T) {
|
|
3262
|
+
const $ = this;
|
|
3263
|
+
return (ctx = {}) => {
|
|
3264
|
+
$.startRule();
|
|
3265
|
+
let preludeNodes = [];
|
|
3266
|
+
let name = $.CONSUME(T.AtNonNested);
|
|
3267
|
+
$.MANY(() => preludeNodes.push($.wrap($.SUBRULE($.anyOuterValue, { ARGS: [ctx] }))));
|
|
3268
|
+
$.CONSUME(T.Semi);
|
|
3269
|
+
if (!$.RECORDING_PHASE) {
|
|
3270
|
+
return new AtRule({
|
|
3271
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
3272
|
+
prelude: $.wrap(new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), this.context))
|
|
3273
|
+
}, undefined, $.endRule(), this.context);
|
|
3274
|
+
}
|
|
3275
|
+
};
|
|
3276
|
+
}
|
|
3277
|
+
// unknownAtRule
|
|
3278
|
+
// : AT_RULE anyOuterValue* (SEMI | LCURLY anyInnerValue* RCURLY)
|
|
3279
|
+
// ;
|
|
3280
|
+
export function unknownAtRule(T) {
|
|
3281
|
+
const $ = this;
|
|
3282
|
+
const { AtKeyword, Semi, LCurly, RCurly, DotName, HashName, Ampersand, LSquare, SelectorPseudoClass, NthPseudoClass, Star, ColorIdentStart, PlainIdent, CustomProperty, LegacyPropIdent, Colon } = T;
|
|
3283
|
+
return (ctx = {}) => {
|
|
3284
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
3285
|
+
$.startRule();
|
|
3286
|
+
let preludeNodes;
|
|
3287
|
+
let valueNodes;
|
|
3288
|
+
let declRules;
|
|
3289
|
+
let endToken;
|
|
3290
|
+
let innerBlockLocation;
|
|
3291
|
+
if (!RECORDING_PHASE) {
|
|
3292
|
+
preludeNodes = [];
|
|
3293
|
+
}
|
|
3294
|
+
let name = $.CONSUME(T.AtKeyword);
|
|
3295
|
+
$.MANY(() => {
|
|
3296
|
+
let val = $.SUBRULE($.anyOuterValue, { ARGS: [ctx] });
|
|
3297
|
+
if (!RECORDING_PHASE) {
|
|
3298
|
+
preludeNodes.push($.wrap(val, 'both'));
|
|
3299
|
+
}
|
|
3300
|
+
});
|
|
3301
|
+
$.OR([
|
|
3302
|
+
{ ALT: () => $.CONSUME(Semi) },
|
|
3303
|
+
{
|
|
3304
|
+
ALT: () => {
|
|
3305
|
+
if (!RECORDING_PHASE) {
|
|
3306
|
+
valueNodes = [];
|
|
3307
|
+
}
|
|
3308
|
+
$.CONSUME(LCurly);
|
|
3309
|
+
$.startRule();
|
|
3310
|
+
// 1) Fast selector/nested-at-rule start gate
|
|
3311
|
+
let t1 = $.LA(1).tokenType;
|
|
3312
|
+
let t2 = $.LA(2).tokenType;
|
|
3313
|
+
let assumeDeclList = (t1 === DotName
|
|
3314
|
+
|| t1 === HashName
|
|
3315
|
+
|| t1 === Ampersand
|
|
3316
|
+
|| t1 === LSquare
|
|
3317
|
+
|| t1 === SelectorPseudoClass
|
|
3318
|
+
|| t1 === NthPseudoClass
|
|
3319
|
+
|| t1 === Star
|
|
3320
|
+
|| t1 === ColorIdentStart
|
|
3321
|
+
|| t1 === AtKeyword
|
|
3322
|
+
// Also treat IDENT followed by '{' as a rule start (e.g., @-moz-document url-prefix() { a { ... } })
|
|
3323
|
+
|| ((t1 === PlainIdent
|
|
3324
|
+
|| t1 === CustomProperty
|
|
3325
|
+
|| t1 === LegacyPropIdent) && t2 === LCurly));
|
|
3326
|
+
if (!assumeDeclList) {
|
|
3327
|
+
assumeDeclList = (t1 === PlainIdent
|
|
3328
|
+
|| t1 === CustomProperty
|
|
3329
|
+
|| t1 === LegacyPropIdent) && t2 === Colon;
|
|
3330
|
+
}
|
|
3331
|
+
$.OR9([
|
|
3332
|
+
{
|
|
3333
|
+
GATE: () => assumeDeclList,
|
|
3334
|
+
ALT: () => {
|
|
3335
|
+
declRules = $.SUBRULE($.atRuleBody, { ARGS: [{ ...ctx, inner: true }] });
|
|
3336
|
+
}
|
|
3337
|
+
},
|
|
3338
|
+
{
|
|
3339
|
+
GATE: () => !assumeDeclList,
|
|
3340
|
+
ALT: () => {
|
|
3341
|
+
/** Fallback to raw capture */
|
|
3342
|
+
$.MANY9(() => {
|
|
3343
|
+
const value = $.SUBRULE($.anyInnerValue, { ARGS: [ctx] });
|
|
3344
|
+
if (!RECORDING_PHASE) {
|
|
3345
|
+
valueNodes.push($.wrap(value, 'both'));
|
|
3346
|
+
}
|
|
3347
|
+
});
|
|
3348
|
+
}
|
|
3349
|
+
}
|
|
3350
|
+
]);
|
|
3351
|
+
endToken = $.CONSUME(RCurly);
|
|
3352
|
+
if (!RECORDING_PHASE) {
|
|
3353
|
+
innerBlockLocation = $.endRule();
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
]);
|
|
3358
|
+
if (!RECORDING_PHASE) {
|
|
3359
|
+
// Build rules result: declaration list, or single-sequence fallback, or undefined
|
|
3360
|
+
let rules;
|
|
3361
|
+
if (declRules) {
|
|
3362
|
+
rules = declRules;
|
|
3363
|
+
}
|
|
3364
|
+
else {
|
|
3365
|
+
if (valueNodes?.length) {
|
|
3366
|
+
// Create a single Sequence from all inner nodes, so serialization treats it as one unit
|
|
3367
|
+
const seqLoc = $.getLocationFromNodes(valueNodes);
|
|
3368
|
+
const seq = new Sequence(valueNodes, undefined, seqLoc, this.context);
|
|
3369
|
+
// Use RawRules to avoid inserting newlines/indentation during serialization
|
|
3370
|
+
rules = new RawRules([seq], undefined, seqLoc, this.context);
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
return new AtRule({
|
|
3374
|
+
name: $.wrap(new Any(name.image, { role: 'atkeyword' }, $.getLocationInfo(name), this.context), true),
|
|
3375
|
+
prelude: preludeNodes.length ? new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), this.context) : undefined,
|
|
3376
|
+
rules
|
|
3377
|
+
}, undefined, $.endRule(), this.context);
|
|
3378
|
+
}
|
|
3379
|
+
};
|
|
3380
|
+
}
|
|
3381
|
+
/**
|
|
3382
|
+
@todo - add all tokens
|
|
3383
|
+
@see - https://stackoverflow.com/questions/55594491/antlr-4-parser-match-any-token
|
|
3384
|
+
|
|
3385
|
+
From - https://w3c.github.io/csswg-drafts/css-syntax-3/#typedef-any-value
|
|
3386
|
+
The <any-value> production is identical to <declaration-value>, but also allows
|
|
3387
|
+
top-level <semicolon-token> tokens and <delim-token> tokens with a value of "!".
|
|
3388
|
+
It represents the entirety of what valid CSS can be in any context.
|
|
3389
|
+
|
|
3390
|
+
Parts of the spec that allow any value should not display a warning or error
|
|
3391
|
+
for any unknown token.
|
|
3392
|
+
*/
|
|
3393
|
+
// anyOuterValue
|
|
3394
|
+
// : value
|
|
3395
|
+
// | COMMA
|
|
3396
|
+
// | '(' anyInnerValue* ')'
|
|
3397
|
+
// | '[' anyInnerValue* ']'
|
|
3398
|
+
// ;
|
|
3399
|
+
export function anyOuterValue(T) {
|
|
3400
|
+
const $ = this;
|
|
3401
|
+
let valueAlt = (ctx = {}) => [
|
|
3402
|
+
{ ALT: () => $.SUBRULE($.extraTokens, { ARGS: [ctx] }) },
|
|
3403
|
+
{ ALT: () => $.SUBRULE($.string, { ARGS: [ctx] }) },
|
|
3404
|
+
{
|
|
3405
|
+
ALT: () => {
|
|
3406
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
3407
|
+
$.startRule();
|
|
3408
|
+
let nodes;
|
|
3409
|
+
if (!RECORDING_PHASE) {
|
|
3410
|
+
nodes = [];
|
|
3411
|
+
}
|
|
3412
|
+
$.CONSUME(T.LParen);
|
|
3413
|
+
$.MANY(() => {
|
|
3414
|
+
let val = $.SUBRULE($.anyInnerValue, { ARGS: [ctx] });
|
|
3415
|
+
if (!RECORDING_PHASE) {
|
|
3416
|
+
nodes.push($.wrap(val));
|
|
3417
|
+
}
|
|
3418
|
+
});
|
|
3419
|
+
$.CONSUME(T.RParen);
|
|
3420
|
+
if (!RECORDING_PHASE) {
|
|
3421
|
+
let location = $.endRule();
|
|
3422
|
+
return new Paren(nodes.length ? new Sequence(nodes, undefined, $.getLocationFromNodes(nodes), this.context) : undefined, undefined, location, this.context);
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
},
|
|
3426
|
+
{
|
|
3427
|
+
ALT: () => {
|
|
3428
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
3429
|
+
$.startRule();
|
|
3430
|
+
let nodes;
|
|
3431
|
+
if (!RECORDING_PHASE) {
|
|
3432
|
+
nodes = [];
|
|
3433
|
+
}
|
|
3434
|
+
$.CONSUME(T.LSquare);
|
|
3435
|
+
$.MANY2(() => {
|
|
3436
|
+
let node = $.SUBRULE2($.anyInnerValue, { ARGS: [ctx] });
|
|
3437
|
+
if (!RECORDING_PHASE) {
|
|
3438
|
+
nodes.push($.wrap(node));
|
|
3439
|
+
}
|
|
3440
|
+
});
|
|
3441
|
+
$.CONSUME(T.RSquare);
|
|
3442
|
+
if (!RECORDING_PHASE) {
|
|
3443
|
+
let location = $.endRule();
|
|
3444
|
+
return new Paren($.wrap(new Sequence(nodes, undefined, $.getLocationFromNodes(nodes), this.context), true), undefined, location, this.context);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
];
|
|
3449
|
+
return (ctx = {}) => $.OR(valueAlt(ctx));
|
|
3450
|
+
}
|
|
3451
|
+
/**
|
|
3452
|
+
* Same as allowable outer values, but allows
|
|
3453
|
+
* semi-colons and curly blocks.
|
|
3454
|
+
*/
|
|
3455
|
+
// anyInnerValue
|
|
3456
|
+
// : anyOuterValue
|
|
3457
|
+
// | '{' anyInnerValue* '}'
|
|
3458
|
+
// | ID
|
|
3459
|
+
// | SEMI
|
|
3460
|
+
// | pseudoSelector
|
|
3461
|
+
// ;
|
|
3462
|
+
export function anyInnerValue(T) {
|
|
3463
|
+
const $ = this;
|
|
3464
|
+
let valueAlt = (ctx = {}) => [
|
|
3465
|
+
{ ALT: () => $.SUBRULE($.anyOuterValue, { ARGS: [ctx] }) },
|
|
3466
|
+
{
|
|
3467
|
+
ALT: () => {
|
|
3468
|
+
$.startRule();
|
|
3469
|
+
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
3470
|
+
let nodes;
|
|
3471
|
+
if (!RECORDING_PHASE) {
|
|
3472
|
+
nodes = [];
|
|
3473
|
+
}
|
|
3474
|
+
$.CONSUME(T.LCurly);
|
|
3475
|
+
$.MANY(() => {
|
|
3476
|
+
let node = $.SUBRULE2($.anyInnerValue, { ARGS: [ctx] });
|
|
3477
|
+
if (!RECORDING_PHASE) {
|
|
3478
|
+
nodes.push(node);
|
|
3479
|
+
}
|
|
3480
|
+
});
|
|
3481
|
+
$.CONSUME(T.RCurly);
|
|
3482
|
+
if (!RECORDING_PHASE) {
|
|
3483
|
+
let location = $.endRule();
|
|
3484
|
+
return new Block($.wrap(new Sequence(nodes, undefined, $.getLocationFromNodes(nodes), this.context), 'both'), { type: 'curly' }, location, this.context);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
},
|
|
3488
|
+
{
|
|
3489
|
+
ALT: () => {
|
|
3490
|
+
let semi = $.CONSUME(T.Semi);
|
|
3491
|
+
if (!$.RECORDING_PHASE) {
|
|
3492
|
+
return $.wrap(new Any(semi.image, { role: 'semi' }, $.getLocationInfo(semi), this.context));
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
];
|
|
3497
|
+
return (ctx = {}) => $.OR(valueAlt(ctx));
|
|
3498
|
+
}
|
|
3499
|
+
//# sourceMappingURL=productions.js.map
|