@jesscss/less-parser 2.0.0-alpha.8 → 2.0.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -10
- package/lib/ast/grammar.d.ts +2 -0
- package/lib/ast/grammar.d.ts.map +1 -0
- package/lib/grammar.cjs +39855 -31054
- package/lib/grammar.d.ts.map +1 -1
- package/lib/grammar.js +39855 -31054
- package/lib/index.cjs +49609 -15
- package/lib/index.d.ts +4 -9
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +49607 -5
- package/lib/parse-error.d.ts +15 -0
- package/lib/parse-error.d.ts.map +1 -0
- package/package.json +9 -24
- package/lib/builders.d.ts +0 -381
- package/lib/builders.d.ts.map +0 -1
- package/lib/functional-parser.cjs +0 -2654
- package/lib/functional-parser.d.ts +0 -18
- package/lib/functional-parser.d.ts.map +0 -1
- package/lib/functional-parser.js +0 -2589
- package/lib/jess.cjs +0 -3968
- package/lib/jess.d.ts +0 -7
- package/lib/jess.d.ts.map +0 -1
- package/lib/jess.js +0 -3962
- package/lib/lessParser.d.ts +0 -38
- package/lib/lessParser.d.ts.map +0 -1
- package/lib/lessRecursiveParser.d.ts +0 -99
- package/lib/lessRecursiveParser.d.ts.map +0 -1
- package/lib/lessTokens.d.ts +0 -23
- package/lib/lessTokens.d.ts.map +0 -1
- package/lib/productions/guards.d.ts +0 -113
- package/lib/productions/guards.d.ts.map +0 -1
- package/lib/productions/index.d.ts +0 -5
- package/lib/productions/index.d.ts.map +0 -1
- package/lib/productions/root.d.ts +0 -38
- package/lib/productions/root.d.ts.map +0 -1
- package/lib/productions/selectors.d.ts +0 -41
- package/lib/productions/selectors.d.ts.map +0 -1
- package/lib/productions/values.d.ts +0 -35
- package/lib/productions/values.d.ts.map +0 -1
- package/lib/utils.d.ts +0 -9
- package/lib/utils.d.ts.map +0 -1
- package/src/__tests__/debug-log.ts +0 -35
- package/src/__tests__/wall5-parse.test.ts +0 -67
- package/src/builders.ts +0 -3190
- package/src/cst.ts +0 -25
- package/src/functional-parser.ts +0 -162
- package/src/grammar.ts +0 -870
- package/src/index.ts +0 -19
- package/src/jess.ts +0 -6
- package/src/lessParser.ts +0 -120
- package/src/lessRecursiveParser.ts +0 -279
- package/src/lessTokens.ts +0 -350
- package/src/productions/guards.ts +0 -1066
- package/src/productions/index.ts +0 -29
- package/src/productions/root.ts +0 -1613
- package/src/productions/selectors.ts +0 -1309
- package/src/productions/values.ts +0 -1449
- package/src/utils.ts +0 -178
|
@@ -1,1066 +0,0 @@
|
|
|
1
|
-
// @ts-nocheck — Retired Chevrotain parser. Uses the legacy 6-tuple `.location`
|
|
2
|
-
// shape removed from Node in the provenance-side-table refactor; the functional
|
|
3
|
-
// Parséman grammar (grammar-rules.ts + builders.ts) is the maintained parser.
|
|
4
|
-
// Not type-checked.
|
|
5
|
-
import type { RuleContext } from '../lessRecursiveParser.js';
|
|
6
|
-
import type { TokenMap } from '../lessRecursiveParser.js';
|
|
7
|
-
import type { IToken } from 'chevrotain';
|
|
8
|
-
import { NoViableAltException } from 'chevrotain';
|
|
9
|
-
import { productions as cssProductions } from '@jesscss/css-parser/jess';
|
|
10
|
-
import {
|
|
11
|
-
type LocationInfo,
|
|
12
|
-
Node,
|
|
13
|
-
Any,
|
|
14
|
-
AtRuleStatement,
|
|
15
|
-
Condition,
|
|
16
|
-
type ConditionOperator,
|
|
17
|
-
DefaultGuard,
|
|
18
|
-
Paren,
|
|
19
|
-
List,
|
|
20
|
-
Sequence,
|
|
21
|
-
Call,
|
|
22
|
-
Reference,
|
|
23
|
-
Quoted,
|
|
24
|
-
Rest,
|
|
25
|
-
Nil,
|
|
26
|
-
VarDeclaration,
|
|
27
|
-
JsImport,
|
|
28
|
-
StyleImport,
|
|
29
|
-
type Url,
|
|
30
|
-
isNode,
|
|
31
|
-
N
|
|
32
|
-
} from '@jesscss/core';
|
|
33
|
-
import { getInterpolatedNode, getInterpolatedOrString } from '../utils.js';
|
|
34
|
-
|
|
35
|
-
/** Use `any` for `this` to avoid structural incompatibility between LessRecursiveParser and CssRecursiveParser */
|
|
36
|
-
type P = any;
|
|
37
|
-
type ProductionRule = (...args: any[]) => any;
|
|
38
|
-
|
|
39
|
-
function isScriptUsePath(path: string): boolean {
|
|
40
|
-
const filePart = path.replace(/[?#].*$/u, '');
|
|
41
|
-
return path === '#less'
|
|
42
|
-
|| path.startsWith('#less/')
|
|
43
|
-
|| /\.(?:js|mjs|cjs|ts|mts|cts|json)$/i.test(filePart);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function defaultNamespaceFromPath(path: string): string | undefined {
|
|
47
|
-
if (path === '#less') {
|
|
48
|
-
return 'less';
|
|
49
|
-
}
|
|
50
|
-
if (path.startsWith('#less/')) {
|
|
51
|
-
return path.split('/').filter(Boolean).pop();
|
|
52
|
-
}
|
|
53
|
-
const base = path.split('/').filter(Boolean).pop();
|
|
54
|
-
if (!base) {
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
|
57
|
-
const noExt = base.replace(/\.(less|css|jess|js|mjs|cjs|ts|mts|cts|json)$/i, '');
|
|
58
|
-
return noExt || undefined;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function getParenFrames(ctx: RuleContext | undefined): boolean[] {
|
|
62
|
-
return (ctx?.parenFrames as boolean[] | undefined) ?? [];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function isDefaultGuardCall(node: Node | undefined): node is Call {
|
|
66
|
-
if (!node || !isNode(node, N.Call)) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
const callName = node.name;
|
|
70
|
-
const callNameStr = String(
|
|
71
|
-
(typeof callName === 'object' && callName !== null && 'valueOf' in callName)
|
|
72
|
-
? callName.valueOf()
|
|
73
|
-
: callName ?? ''
|
|
74
|
-
);
|
|
75
|
-
if (callNameStr === 'default' || callNameStr === '??') {
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
if (callName instanceof Reference) {
|
|
79
|
-
const key = callName.key;
|
|
80
|
-
const keyStr = String(
|
|
81
|
-
(typeof key === 'object' && key !== null && 'valueOf' in key)
|
|
82
|
-
? key.valueOf()
|
|
83
|
-
: key ?? ''
|
|
84
|
-
);
|
|
85
|
-
return keyStr === 'default' || keyStr === '??';
|
|
86
|
-
}
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Save CSS production factory for super calls
|
|
91
|
-
const cssUnknownAtRule = cssProductions.unknownAtRule;
|
|
92
|
-
|
|
93
|
-
function isGuardComparisonToken(tt: unknown, T: TokenMap) {
|
|
94
|
-
return tt === T.CompareOperator
|
|
95
|
-
|| tt === T.Eq
|
|
96
|
-
|| tt === T.Gt
|
|
97
|
-
|| tt === T.GtEq
|
|
98
|
-
|| tt === T.GtEqAlias
|
|
99
|
-
|| tt === T.Lt
|
|
100
|
-
|| tt === T.LtEq
|
|
101
|
-
|| tt === T.LtEqAlias;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function normalizeComparisonOperator(op: string): ConditionOperator {
|
|
105
|
-
switch (op) {
|
|
106
|
-
case '=>':
|
|
107
|
-
case '>=':
|
|
108
|
-
return '>=';
|
|
109
|
-
case '=<':
|
|
110
|
-
case '<=':
|
|
111
|
-
return '<=';
|
|
112
|
-
case '=':
|
|
113
|
-
return '=';
|
|
114
|
-
case '>':
|
|
115
|
-
return '>';
|
|
116
|
-
case '<':
|
|
117
|
-
return '<';
|
|
118
|
-
default:
|
|
119
|
-
return '=';
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function guard(this: P, T: TokenMap) {
|
|
124
|
-
const $ = this;
|
|
125
|
-
return (ctx: RuleContext = {}) => {
|
|
126
|
-
$.CONSUME(T.When);
|
|
127
|
-
return $.OR([
|
|
128
|
-
{
|
|
129
|
-
GATE: () => !!ctx.inValueList,
|
|
130
|
-
ALT: () => $.SUBRULE($.comparison, { ARGS: [ctx] })
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
ALT: () => {
|
|
134
|
-
const node = $.SUBRULE($.guardOr, { ARGS: [{ ...ctx, allowComma: true }] });
|
|
135
|
-
return node;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
]);
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* 'or' expression
|
|
144
|
-
* Allows an (outer) comma like historical media queries
|
|
145
|
-
*/
|
|
146
|
-
export function guardOr(this: P, T: TokenMap) {
|
|
147
|
-
const $ = this;
|
|
148
|
-
return (ctx: RuleContext = {}) => {
|
|
149
|
-
$.startRule();
|
|
150
|
-
|
|
151
|
-
let left = $.SUBRULE($.guardAnd, { ARGS: [ctx] });
|
|
152
|
-
let right: Node | undefined;
|
|
153
|
-
$.MANY({
|
|
154
|
-
GATE: () => (ctx.allowComma && $.isType(T.Comma)) || $.isType(T.Or),
|
|
155
|
-
DEF: () => {
|
|
156
|
-
/**
|
|
157
|
-
* Nest expressions within expressions for correct
|
|
158
|
-
* order of operations.
|
|
159
|
-
*/
|
|
160
|
-
$.OR([
|
|
161
|
-
{ ALT: () => $.CONSUME(T.Comma) },
|
|
162
|
-
{ ALT: () => $.CONSUME(T.Or) }
|
|
163
|
-
]);
|
|
164
|
-
right = $.SUBRULE2($.guardAnd, { ARGS: [ctx] });
|
|
165
|
-
let location = $.endRule();
|
|
166
|
-
$.startRule();
|
|
167
|
-
left = new Condition(
|
|
168
|
-
[left, 'or', right!],
|
|
169
|
-
undefined,
|
|
170
|
-
location,
|
|
171
|
-
$.context
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
$.endRule();
|
|
176
|
-
return left;
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export function guardDefault(this: P, T: TokenMap) {
|
|
181
|
-
const $ = this;
|
|
182
|
-
return (ctx: RuleContext = {}) => {
|
|
183
|
-
let guard = $.OR([
|
|
184
|
-
{ ALT: () => $.CONSUME(T.DefaultGuardIdent) },
|
|
185
|
-
{ ALT: () => $.CONSUME(T.DefaultGuardFunc) }
|
|
186
|
-
]);
|
|
187
|
-
if ($.RECORDING_PHASE) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
ctx.hasDefault = true;
|
|
191
|
-
return new DefaultGuard(guard.image, undefined, $.getLocationInfo(guard));
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* 'and' and 'or' expressions
|
|
197
|
-
*
|
|
198
|
-
* In Media queries level 4, you cannot have
|
|
199
|
-
* `([expr]) or ([expr]) and ([expr])` because
|
|
200
|
-
* of evaluation order ambiguity.
|
|
201
|
-
* However, Less allows it.
|
|
202
|
-
*/
|
|
203
|
-
export function guardAnd(this: P, T: TokenMap): ProductionRule {
|
|
204
|
-
const $ = this;
|
|
205
|
-
return (ctx: RuleContext = {}) => {
|
|
206
|
-
let left: Node;
|
|
207
|
-
$.MANY_SEP({
|
|
208
|
-
SEP: T.And,
|
|
209
|
-
DEF: () => {
|
|
210
|
-
let not: IToken | undefined;
|
|
211
|
-
$.OPTION(() => not = $.CONSUME(T.Not));
|
|
212
|
-
let allowComma = ctx.allowComma;
|
|
213
|
-
ctx.allowComma = false;
|
|
214
|
-
let right: Node;
|
|
215
|
-
try {
|
|
216
|
-
right = $.OR([
|
|
217
|
-
{ ALT: () => $.SUBRULE($.guardInParens, { ARGS: [ctx] }) },
|
|
218
|
-
{
|
|
219
|
-
GATE: () => {
|
|
220
|
-
const tokenType = $.LA(1).tokenType;
|
|
221
|
-
return tokenType !== T.Not
|
|
222
|
-
&& tokenType !== T.DefaultGuardFunc
|
|
223
|
-
&& tokenType !== T.DefaultGuardIdent;
|
|
224
|
-
},
|
|
225
|
-
ALT: () => $.SUBRULE($.expressionSum, { ARGS: [ctx] })
|
|
226
|
-
}
|
|
227
|
-
]);
|
|
228
|
-
$.OPTION2({
|
|
229
|
-
GATE: () => isGuardComparisonToken($.LA(1).tokenType, T),
|
|
230
|
-
DEF: () => {
|
|
231
|
-
const op = $.CONSUME(T.CompareOperator);
|
|
232
|
-
const compareRight = $.SUBRULE2($.expressionSum, { ARGS: [ctx] });
|
|
233
|
-
if (!$.RECORDING_PHASE) {
|
|
234
|
-
right = new Condition(
|
|
235
|
-
[
|
|
236
|
-
right,
|
|
237
|
-
normalizeComparisonOperator(op.image),
|
|
238
|
-
compareRight
|
|
239
|
-
],
|
|
240
|
-
undefined,
|
|
241
|
-
$.getLocationFromNodes([right, compareRight]),
|
|
242
|
-
$.context
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
} finally {
|
|
248
|
-
ctx.allowComma = allowComma;
|
|
249
|
-
}
|
|
250
|
-
if (!$.RECORDING_PHASE) {
|
|
251
|
-
if (isDefaultGuardCall(right!)) {
|
|
252
|
-
ctx.hasDefault = true;
|
|
253
|
-
const location = Array.isArray(right!.location) && right!.location.length === 6
|
|
254
|
-
? right!.location as LocationInfo
|
|
255
|
-
: undefined;
|
|
256
|
-
right = new DefaultGuard('default()', undefined, location);
|
|
257
|
-
}
|
|
258
|
-
if (not) {
|
|
259
|
-
let [,,, endOffset, endLine, endColumn] = right.location!;
|
|
260
|
-
let [startOffset, startLine, startColumn] = $.getLocationInfo(not);
|
|
261
|
-
right = new Condition(
|
|
262
|
-
[right],
|
|
263
|
-
{ negate: true },
|
|
264
|
-
[startOffset!, startLine!, startColumn!, endOffset!, endLine!, endColumn!],
|
|
265
|
-
$.context
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
if (!left) {
|
|
269
|
-
left = right;
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
left = new Condition(
|
|
273
|
-
[left, 'and', right],
|
|
274
|
-
undefined,
|
|
275
|
-
$.getLocationFromNodes([left, right]),
|
|
276
|
-
$.context
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
return left!;
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
export function guardInParens(this: P, T: TokenMap) {
|
|
286
|
-
const $ = this;
|
|
287
|
-
return (ctx: RuleContext) => {
|
|
288
|
-
$.startRule();
|
|
289
|
-
let node = $.OR([
|
|
290
|
-
{ ALT: () => $.SUBRULE($.guardDefault, { ARGS: [ctx] }) },
|
|
291
|
-
{
|
|
292
|
-
ALT: () => {
|
|
293
|
-
$.CONSUME(T.LParen);
|
|
294
|
-
let node = $.SUBRULE($.guardInner, { ARGS: [ctx] });
|
|
295
|
-
$.CONSUME(T.RParen);
|
|
296
|
-
return node;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
]);
|
|
300
|
-
|
|
301
|
-
if (isDefaultGuardCall(node)) {
|
|
302
|
-
ctx.hasDefault = true;
|
|
303
|
-
const location = Array.isArray(node.location) && node.location.length === 6
|
|
304
|
-
? node.location as LocationInfo
|
|
305
|
-
: undefined;
|
|
306
|
-
node = new DefaultGuard('default()', undefined, location);
|
|
307
|
-
}
|
|
308
|
-
node = node;
|
|
309
|
-
return new Paren(node, undefined, $.endRule(), $.context);
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// The inner content of a guard inside parentheses
|
|
314
|
-
export function guardInner(this: P, _T: TokenMap) {
|
|
315
|
-
const $ = this;
|
|
316
|
-
return (ctx: RuleContext = {}) => {
|
|
317
|
-
return $.SUBRULE($.guardOr, { ARGS: [ctx] });
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
export function guardWithConditionValue(this: P, T: TokenMap) {
|
|
322
|
-
const $ = this;
|
|
323
|
-
return (ctx: RuleContext = {}) => {
|
|
324
|
-
if ($.isType(T.DefaultGuardIdent) || $.isType(T.DefaultGuardFunc)) {
|
|
325
|
-
$.OR([
|
|
326
|
-
{ ALT: () => $.CONSUME(T.DefaultGuardIdent) },
|
|
327
|
-
{ ALT: () => $.CONSUME(T.DefaultGuardFunc) }
|
|
328
|
-
]);
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
return $.SUBRULE($.guardInParens, { ARGS: [ctx] });
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
export function guardWithCondition(this: P, T: TokenMap) {
|
|
336
|
-
const $ = this;
|
|
337
|
-
return (ctx: RuleContext = {}) => {
|
|
338
|
-
$.SUBRULE($.guardWithConditionValue, { ARGS: [ctx] });
|
|
339
|
-
$.AT_LEAST_ONE(() => {
|
|
340
|
-
$.OR([
|
|
341
|
-
{ ALT: () => $.CONSUME(T.Or) },
|
|
342
|
-
{ ALT: () => $.CONSUME(T.And) },
|
|
343
|
-
{ ALT: () => $.CONSUME(T.Comma) }
|
|
344
|
-
]);
|
|
345
|
-
$.SUBRULE2($.guardWithConditionValue, { ARGS: [ctx] });
|
|
346
|
-
});
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Currently, Less only allows a single comparison expression,
|
|
352
|
-
* unlike Media Queries Level 4, which allows a left and right
|
|
353
|
-
* comparison.
|
|
354
|
-
*/
|
|
355
|
-
export function comparison(this: P, T: TokenMap) {
|
|
356
|
-
const $ = this;
|
|
357
|
-
return (ctx: RuleContext = {}) => {
|
|
358
|
-
let left = $.SUBRULE($.expressionSum, { ARGS: [ctx] });
|
|
359
|
-
const op = $.CONSUME(T.CompareOperator);
|
|
360
|
-
let right = $.SUBRULE2($.expressionSum, { ARGS: [ctx] });
|
|
361
|
-
if (isDefaultGuardCall(right)) {
|
|
362
|
-
ctx.hasDefault = true;
|
|
363
|
-
const location = Array.isArray(right.location) && right.location.length === 6
|
|
364
|
-
? right.location as LocationInfo
|
|
365
|
-
: undefined;
|
|
366
|
-
right = new DefaultGuard('default()', undefined, location);
|
|
367
|
-
}
|
|
368
|
-
left = new Condition(
|
|
369
|
-
[left, normalizeComparisonOperator(op.image), right],
|
|
370
|
-
undefined,
|
|
371
|
-
$.getLocationFromNodes([left, right]),
|
|
372
|
-
$.context
|
|
373
|
-
);
|
|
374
|
-
return left;
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Less (perhaps unwisely) allows bubbling of normally document-root
|
|
380
|
-
* at-rules, so we need to override CSS here.
|
|
381
|
-
*/
|
|
382
|
-
export function innerAtRule(this: P, _T: TokenMap) {
|
|
383
|
-
const $ = this;
|
|
384
|
-
return (ctx: RuleContext = {}): Node => {
|
|
385
|
-
return $.OR([
|
|
386
|
-
{ ALT: () => $.SUBRULE($.mediaAtRule, { ARGS: [{ ...ctx, inner: true }] }) },
|
|
387
|
-
{ ALT: () => $.SUBRULE($.supportsAtRule, { ARGS: [{ ...ctx, inner: true }] }) },
|
|
388
|
-
{ ALT: () => $.SUBRULE($.layerAtRule, { ARGS: [{ ...ctx, inner: true }] }) },
|
|
389
|
-
{ ALT: () => $.SUBRULE($.containerAtRule, { ARGS: [{ ...ctx, inner: true }] }) },
|
|
390
|
-
{ ALT: () => $.SUBRULE($.keyframesAtRule, { ARGS: [{ ...ctx, inner: true }] }) },
|
|
391
|
-
{ ALT: () => $.SUBRULE($.documentAtRule, { ARGS: [{ ...ctx, inner: true }] }) },
|
|
392
|
-
{ ALT: () => $.SUBRULE($.importAtRule, { ARGS: [ctx] }) },
|
|
393
|
-
{ ALT: () => $.SUBRULE($.pageAtRule, { ARGS: [ctx] }) },
|
|
394
|
-
{ ALT: () => $.SUBRULE($.fontFaceAtRule, { ARGS: [ctx] }) },
|
|
395
|
-
{ ALT: () => $.SUBRULE($.nestedAtRule, { ARGS: [ctx] }) },
|
|
396
|
-
{ ALT: () => $.SUBRULE($.nonNestedAtRule, { ARGS: [ctx] }) },
|
|
397
|
-
{ ALT: () => $.SUBRULE($.unknownAtRule, { ARGS: [{ ...ctx, inner: true }] }) }
|
|
398
|
-
]);
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Less override: allow variable reference as the first segment of a layer-name
|
|
404
|
-
* CSS: <ident> ('.' <ident>)*
|
|
405
|
-
* Less: (<var-ref> | <ident>) ('.' <ident>)*
|
|
406
|
-
*/
|
|
407
|
-
export function layerName(this: P, T: TokenMap) {
|
|
408
|
-
const $ = this;
|
|
409
|
-
return (ctx: RuleContext = {}) => {
|
|
410
|
-
const preludeCtx: RuleContext = { ...ctx, atRulePreludeBareVariableAs: 'index' };
|
|
411
|
-
$.startRule();
|
|
412
|
-
let RECORDING_PHASE = $.RECORDING_PHASE;
|
|
413
|
-
let nodes: Node[];
|
|
414
|
-
if (!RECORDING_PHASE) {
|
|
415
|
-
nodes = [];
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// First segment: variable reference or plain ident
|
|
419
|
-
const first = $.OR([
|
|
420
|
-
{ ALT: () => $.SUBRULE($.valueReference, { ARGS: [preludeCtx] }) },
|
|
421
|
-
{
|
|
422
|
-
GATE: () => $.isType(T.Ident),
|
|
423
|
-
ALT: () => $.CONSUME(T.Ident)
|
|
424
|
-
}
|
|
425
|
-
]);
|
|
426
|
-
|
|
427
|
-
if (!RECORDING_PHASE) {
|
|
428
|
-
if (first instanceof Node) {
|
|
429
|
-
nodes!.push(first);
|
|
430
|
-
} else {
|
|
431
|
-
nodes!.push($.processValueToken(first));
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Remaining segments: dot + ident (same as CSS)
|
|
436
|
-
$.MANY({
|
|
437
|
-
GATE: $.noSep.bind($),
|
|
438
|
-
DEF: () => {
|
|
439
|
-
const seg = $.CONSUME(T.DotName);
|
|
440
|
-
if (!RECORDING_PHASE) {
|
|
441
|
-
nodes!.push($.processValueToken(seg));
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
if (RECORDING_PHASE) {
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
const loc = $.endRule();
|
|
450
|
-
return new Sequence(nodes!, undefined, loc, $.context);
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Less override: allow variable reference for @keyframes name
|
|
456
|
-
* CSS: Ident | String
|
|
457
|
-
* Less: valueReference | Ident | String
|
|
458
|
-
*/
|
|
459
|
-
export function keyframesName(this: P, T: TokenMap): ProductionRule {
|
|
460
|
-
const $ = this;
|
|
461
|
-
return (ctx: RuleContext = {}) => {
|
|
462
|
-
const preludeCtx: RuleContext = { ...ctx, atRulePreludeBareVariableAs: 'index' };
|
|
463
|
-
let node: Node | undefined;
|
|
464
|
-
$.OR([
|
|
465
|
-
{ ALT: () => node = $.SUBRULE($.valueReference, { ARGS: [preludeCtx] }) },
|
|
466
|
-
{
|
|
467
|
-
GATE: () => $.isType(T.Ident) && !$.isType(T.InterpolatedIdent),
|
|
468
|
-
ALT: () => {
|
|
469
|
-
const tok = $.CONSUME(T.Ident);
|
|
470
|
-
node = $.processValueToken(tok);
|
|
471
|
-
} },
|
|
472
|
-
{ ALT: () => node = $.SUBRULE($.string, { ARGS: [] }) }
|
|
473
|
-
]);
|
|
474
|
-
return node!;
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* One of the rare rules that returns a token, because
|
|
480
|
-
* other rules will transform it differently.
|
|
481
|
-
*/
|
|
482
|
-
export function mixinName(this: P, T: TokenMap): ProductionRule {
|
|
483
|
-
const $ = this;
|
|
484
|
-
return (ctx: RuleContext = {}) => {
|
|
485
|
-
/** e.g. .mixin, #mixin */
|
|
486
|
-
let name = $.OR([
|
|
487
|
-
{ ALT: () => $.CONSUME(T.HashName) },
|
|
488
|
-
{ ALT: () => $.CONSUME(T.ColorIdentStart) },
|
|
489
|
-
{ ALT: () => $.CONSUME(T.DotName) },
|
|
490
|
-
{ ALT: () => $.CONSUME(T.InterpolatedIdent) },
|
|
491
|
-
{ ALT: () => $.CONSUME(T.InterpolatedSelector) }
|
|
492
|
-
]);
|
|
493
|
-
if ($.RECORDING_PHASE) {
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
const asReference = ctx.asReference;
|
|
497
|
-
let nameNode: Node;
|
|
498
|
-
let nameValue = name.image;
|
|
499
|
-
let location = $.getLocationInfo(name);
|
|
500
|
-
if (nameValue.includes('@') || nameValue.includes('$')) {
|
|
501
|
-
const interpolated = getInterpolatedNode(nameValue, location, $.context);
|
|
502
|
-
nameNode = interpolated;
|
|
503
|
-
if (asReference) {
|
|
504
|
-
if (isNode(ctx.node, N.Reference) && ctx.node.options.type === 'mixin-ruleset') {
|
|
505
|
-
nameNode = new Reference({ target: ctx.node, key: interpolated }, { type: 'mixin-ruleset', role: 'name' }, location, $.context);
|
|
506
|
-
} else {
|
|
507
|
-
const target = ctx.node as Node | undefined;
|
|
508
|
-
nameNode = new Reference({ target: target instanceof Call ? target : target instanceof Reference ? target : undefined, key: interpolated }, { type: 'mixin-ruleset', role: 'name' }, location, $.context);
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
} else {
|
|
512
|
-
if (asReference) {
|
|
513
|
-
// If target is a Reference with matching type, merge keys instead of nesting
|
|
514
|
-
if (isNode(ctx.node, N.Reference) && ctx.node.options.type === 'mixin-ruleset') {
|
|
515
|
-
const existingKey = ctx.node.key;
|
|
516
|
-
const existingRawKey = ctx.node.rawKey;
|
|
517
|
-
let mergedKeys: string[];
|
|
518
|
-
if (Array.isArray(existingKey)) {
|
|
519
|
-
mergedKeys = [...existingKey];
|
|
520
|
-
} else {
|
|
521
|
-
mergedKeys = [String(existingKey)];
|
|
522
|
-
}
|
|
523
|
-
mergedKeys.push(nameValue);
|
|
524
|
-
const rawPrefix = Array.isArray(existingRawKey)
|
|
525
|
-
? existingRawKey.join(' > ')
|
|
526
|
-
: typeof existingRawKey === 'string'
|
|
527
|
-
? existingRawKey
|
|
528
|
-
: Array.isArray(existingKey)
|
|
529
|
-
? existingKey.join(' > ')
|
|
530
|
-
: String(existingKey);
|
|
531
|
-
nameNode = new Reference(
|
|
532
|
-
{
|
|
533
|
-
key: mergedKeys.length === 1 ? mergedKeys[0]! : mergedKeys,
|
|
534
|
-
rawKey: `${rawPrefix} > ${nameValue}`
|
|
535
|
-
},
|
|
536
|
-
{ type: 'mixin-ruleset', role: 'name' },
|
|
537
|
-
location,
|
|
538
|
-
$.context
|
|
539
|
-
);
|
|
540
|
-
} else {
|
|
541
|
-
const target = ctx.node as Node | undefined;
|
|
542
|
-
nameNode = new Reference({ target: target instanceof Call ? target : target instanceof Reference ? target : undefined, key: nameValue }, { type: 'mixin-ruleset', role: 'name' }, location, $.context);
|
|
543
|
-
}
|
|
544
|
-
} else {
|
|
545
|
-
nameNode = new Any(nameValue, { role: 'name' }, $.getLocationInfo(name), $.context);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
return nameNode;
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Used within a value. These can be
|
|
554
|
-
* chained more recursively, unlike
|
|
555
|
-
* Less 1.x-4.x
|
|
556
|
-
* e.g. .mixin1() > .mixin2[@val1].ns() > .sub-mixin[@val2]
|
|
557
|
-
*
|
|
558
|
-
* This production intelligently decides whether to produce a Call or Reference
|
|
559
|
-
* based on whether there are parentheses at the end:
|
|
560
|
-
* - foo: #id; // Reference
|
|
561
|
-
* - foo: .class; // Reference
|
|
562
|
-
* - foo: #id > .scoped; // Reference
|
|
563
|
-
* - foo: #id > .scoped(); // Call
|
|
564
|
-
* - foo: #id[]; // Reference with accessor
|
|
565
|
-
* - foo: #id > .scoped[foo]; // Reference with accessor
|
|
566
|
-
* - foo: #id > .scoped[@ref](); // Call with accessor
|
|
567
|
-
*/
|
|
568
|
-
export function mixinReference(this: P, T: TokenMap) {
|
|
569
|
-
const $ = this;
|
|
570
|
-
return (ctx: RuleContext = {}) => {
|
|
571
|
-
let leftNode = $.SUBRULE($.mixinName, { ARGS: [{ ...ctx, asReference: true }] });
|
|
572
|
-
|
|
573
|
-
$.MANY({
|
|
574
|
-
GATE: () => {
|
|
575
|
-
let next = $.LA(1).tokenType;
|
|
576
|
-
return $.noSep() && (next === T.LParen || next === T.LSquare);
|
|
577
|
-
},
|
|
578
|
-
DEF: () => {
|
|
579
|
-
leftNode = $.SUBRULE($.lookupOrCall, { ARGS: [{ ...ctx, node: leftNode }] });
|
|
580
|
-
}
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
$.OPTION(() => {
|
|
584
|
-
$.OPTION2(() => $.CONSUME(T.Gt));
|
|
585
|
-
leftNode = $.SUBRULE($.mixinReference, { ARGS: [{ ...ctx, node: leftNode }] });
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
return leftNode;
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
export function mixinArgs(this: P, T: TokenMap): ProductionRule {
|
|
593
|
-
const $ = this;
|
|
594
|
-
return (ctx: RuleContext = {}) => {
|
|
595
|
-
let args: List | undefined;
|
|
596
|
-
// Check for whitespace before the opening paren (before consuming)
|
|
597
|
-
const hasWhitespace = !$.noSep();
|
|
598
|
-
const openingParenToken = hasWhitespace ? $.LA(1) : undefined;
|
|
599
|
-
|
|
600
|
-
$.CONSUME(T.LParen);
|
|
601
|
-
// Clear ctx.node when parsing arguments - arguments should start fresh, not inherit the parent node
|
|
602
|
-
// Calls intentionally push a `false` paren frame (matches `Call.evalNode`)
|
|
603
|
-
const argCtx: RuleContext = {
|
|
604
|
-
...ctx,
|
|
605
|
-
node: undefined,
|
|
606
|
-
allowComma: false,
|
|
607
|
-
parenFrames: [...getParenFrames(ctx), false],
|
|
608
|
-
detachedRulesetUsage: ctx.isDefinition ? 'default-param' : 'mixin-arg'
|
|
609
|
-
};
|
|
610
|
-
if (!$.isType(T.RParen)) {
|
|
611
|
-
args = $.SUBRULE($.mixinArgList, { ARGS: [argCtx] });
|
|
612
|
-
}
|
|
613
|
-
$.CONSUME(T.RParen);
|
|
614
|
-
|
|
615
|
-
// Check for whitespace warning AFTER consuming closing paren
|
|
616
|
-
// Now we can check what comes next to determine if it's actually a definition
|
|
617
|
-
if (hasWhitespace && openingParenToken) {
|
|
618
|
-
const nextAfterParens = $.LA(1).tokenType;
|
|
619
|
-
const isActuallyDefinition = nextAfterParens === T.LCurly || nextAfterParens === T.When;
|
|
620
|
-
// Only warn if it's NOT a definition (i.e., it's a mixin call)
|
|
621
|
-
if (!isActuallyDefinition) {
|
|
622
|
-
$.warnDeprecation(
|
|
623
|
-
'Whitespace between a mixin name and parentheses for a mixin call is deprecated',
|
|
624
|
-
openingParenToken,
|
|
625
|
-
'mixin-call-whitespace'
|
|
626
|
-
);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
return args;
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
export function lookupOrCall(this: P, T: TokenMap) {
|
|
635
|
-
const $ = this;
|
|
636
|
-
return (ctx: RuleContext = {}) => {
|
|
637
|
-
$.startRule();
|
|
638
|
-
return $.OR([
|
|
639
|
-
{
|
|
640
|
-
ALT: () => {
|
|
641
|
-
let keyToken: IToken | undefined;
|
|
642
|
-
$.CONSUME(T.LSquare);
|
|
643
|
-
$.OPTION(() => keyToken = $.OR2([
|
|
644
|
-
{ ALT: () => $.CONSUME(T.NestedReference) },
|
|
645
|
-
{ ALT: () => $.CONSUME(T.AtKeyword) },
|
|
646
|
-
{ ALT: () => $.CONSUME(T.PropertyReference) },
|
|
647
|
-
{ ALT: () => $.CONSUME(T.InterpolatedIdent) },
|
|
648
|
-
{
|
|
649
|
-
GATE: () => !$.isType(T.NestedReference)
|
|
650
|
-
&& !$.isType(T.AtKeyword)
|
|
651
|
-
&& !$.isType(T.PropertyReference)
|
|
652
|
-
&& !$.isType(T.InterpolatedIdent)
|
|
653
|
-
&& $.isType(T.Ident),
|
|
654
|
-
ALT: () => $.CONSUME(T.Ident)
|
|
655
|
-
}
|
|
656
|
-
]));
|
|
657
|
-
$.CONSUME(T.RSquare);
|
|
658
|
-
if ($.RECORDING_PHASE) {
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
let ref: Reference;
|
|
662
|
-
const targetNode = ctx.node;
|
|
663
|
-
const target = targetNode instanceof Call ? targetNode : targetNode instanceof Reference ? targetNode : undefined;
|
|
664
|
-
if (keyToken) {
|
|
665
|
-
let tokenStr = keyToken.image;
|
|
666
|
-
let type: 'variable' | 'index' = tokenStr.startsWith('@') ? 'variable' : 'index';
|
|
667
|
-
if (keyToken.tokenType === T.NestedReference) {
|
|
668
|
-
tokenStr = keyToken.image;
|
|
669
|
-
if (!tokenStr.startsWith('$') && !tokenStr.startsWith('@')) {
|
|
670
|
-
tokenStr = '$' + tokenStr;
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
let rawResult = getInterpolatedOrString(tokenStr, $.getLocationInfo(keyToken), $.context);
|
|
674
|
-
let result: typeof rawResult | Quoted = rawResult;
|
|
675
|
-
if (type === 'index') {
|
|
676
|
-
result = new Quoted(rawResult, { quote: '\'' }, $.getLocationInfo(keyToken), $.context);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
const targetType = isNode(target, N.Reference) ? target.options.type : undefined;
|
|
680
|
-
const shouldMergeKeys = targetType === 'mixin' || targetType === 'mixin-ruleset';
|
|
681
|
-
if (isNode(target, N.Reference) && target.options.type === type && typeof result === 'string' && shouldMergeKeys) {
|
|
682
|
-
const existingKey = target.key;
|
|
683
|
-
let mergedKeys: string[];
|
|
684
|
-
if (Array.isArray(existingKey)) {
|
|
685
|
-
mergedKeys = [...existingKey];
|
|
686
|
-
} else {
|
|
687
|
-
mergedKeys = [String(existingKey)];
|
|
688
|
-
}
|
|
689
|
-
mergedKeys.push(result);
|
|
690
|
-
ref = new Reference(
|
|
691
|
-
{ key: mergedKeys.length === 1 ? mergedKeys[0]! : mergedKeys },
|
|
692
|
-
{ type },
|
|
693
|
-
$.endRule(),
|
|
694
|
-
$.context
|
|
695
|
-
);
|
|
696
|
-
} else {
|
|
697
|
-
ref = new Reference({ target, key: result }, { type }, $.endRule(), $.context);
|
|
698
|
-
}
|
|
699
|
-
} else {
|
|
700
|
-
ref = new Reference({ target, key: -1 }, { type: 'index' }, $.endRule(), $.context);
|
|
701
|
-
}
|
|
702
|
-
/** Reference targets will technically precede the reference, so we need to update the location to the target start location */
|
|
703
|
-
if (target) {
|
|
704
|
-
let [targetStartOffset, targetStartLine, targetStartColumn] = target.location!;
|
|
705
|
-
ref.location.start = targetStartOffset;
|
|
706
|
-
ref.location[1] = targetStartLine;
|
|
707
|
-
ref.location[2] = targetStartColumn;
|
|
708
|
-
}
|
|
709
|
-
return ref;
|
|
710
|
-
}
|
|
711
|
-
},
|
|
712
|
-
{
|
|
713
|
-
ALT: () => {
|
|
714
|
-
let args = $.SUBRULE($.mixinArgs, { ARGS: [ctx] });
|
|
715
|
-
if ($.RECORDING_PHASE) {
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
return new Call({ name: ctx.node!, args }, undefined, $.endRule(), $.context);
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
]);
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
/**
|
|
726
|
-
* @see https://lesscss.org/features/#mixins-feature-mixins-parametric-feature
|
|
727
|
-
*
|
|
728
|
-
* This rule is recursive to allow chevrotain-allstar (hopefully) to lookahead
|
|
729
|
-
* and find semi-colon separators vs. commas.
|
|
730
|
-
*/
|
|
731
|
-
export function mixinArgList(this: P, T: TokenMap): ProductionRule {
|
|
732
|
-
const $ = this;
|
|
733
|
-
return (ctx: RuleContext = {}) => {
|
|
734
|
-
$.startRule();
|
|
735
|
-
const first = $.SUBRULE($.mixinArg, { ARGS: [ctx] });
|
|
736
|
-
|
|
737
|
-
let commaNodes: Node[] | undefined = [first];
|
|
738
|
-
const semiNodes: Node[] = [];
|
|
739
|
-
let isSemiList = false;
|
|
740
|
-
|
|
741
|
-
const collapseCommaNodesIntoSemiNodes = (semi: IToken) => {
|
|
742
|
-
if (!commaNodes) {
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
if (commaNodes.length > 1) {
|
|
746
|
-
const [head, ...rest] = commaNodes;
|
|
747
|
-
let hasDeclarations = false;
|
|
748
|
-
if (head instanceof VarDeclaration) {
|
|
749
|
-
const headValue = head.value instanceof Node ? head.value : undefined;
|
|
750
|
-
const nodes = headValue ? [headValue, ...rest] : [...rest];
|
|
751
|
-
hasDeclarations = rest.some(n => n instanceof VarDeclaration);
|
|
752
|
-
const value = new List(nodes, undefined, $.getLocationFromNodes(nodes), $.context);
|
|
753
|
-
semiNodes.push(new VarDeclaration({
|
|
754
|
-
name: head.name,
|
|
755
|
-
value,
|
|
756
|
-
important: head.important
|
|
757
|
-
}, head.options, head.location, $.context));
|
|
758
|
-
} else {
|
|
759
|
-
hasDeclarations = commaNodes.some(n => n instanceof VarDeclaration);
|
|
760
|
-
semiNodes.push(new List(commaNodes, undefined, $.getLocationFromNodes(commaNodes), $.context));
|
|
761
|
-
}
|
|
762
|
-
if (hasDeclarations) {
|
|
763
|
-
const indexOfSemi = $.input.indexOf(semi);
|
|
764
|
-
const previousToken = $.input[indexOfSemi - 1]!;
|
|
765
|
-
$.SAVE_ERROR(
|
|
766
|
-
new NoViableAltException(
|
|
767
|
-
'Cannot mix ; and , as delimiter types',
|
|
768
|
-
semi,
|
|
769
|
-
previousToken
|
|
770
|
-
)
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
} else {
|
|
774
|
-
semiNodes.push(commaNodes[0]!);
|
|
775
|
-
}
|
|
776
|
-
commaNodes = undefined;
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
while ($.isType(T.Comma) || $.isType(T.Semi)) {
|
|
780
|
-
if ($.isType(T.Comma)) {
|
|
781
|
-
const comma = $.CONSUME(T.Comma);
|
|
782
|
-
const node = $.SUBRULE2($.mixinArg, { ARGS: [ctx] });
|
|
783
|
-
if (commaNodes) {
|
|
784
|
-
commaNodes.push(node);
|
|
785
|
-
} else {
|
|
786
|
-
$.SAVE_ERROR(
|
|
787
|
-
new NoViableAltException(
|
|
788
|
-
'Cannot mix ; and , as delimiter types',
|
|
789
|
-
comma,
|
|
790
|
-
$.LA(0)
|
|
791
|
-
)
|
|
792
|
-
);
|
|
793
|
-
semiNodes.push(node);
|
|
794
|
-
}
|
|
795
|
-
continue;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
const semi = $.CONSUME(T.Semi);
|
|
799
|
-
isSemiList = true;
|
|
800
|
-
collapseCommaNodesIntoSemiNodes(semi);
|
|
801
|
-
|
|
802
|
-
if ($.isType(T.RParen)) {
|
|
803
|
-
break;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
const prevAllow = ctx.allowComma;
|
|
807
|
-
ctx.allowComma = true;
|
|
808
|
-
const node = $.SUBRULE3($.mixinArg, { ARGS: [ctx] });
|
|
809
|
-
ctx.allowComma = prevAllow;
|
|
810
|
-
semiNodes.push(node);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
let location = $.endRule();
|
|
814
|
-
let nodes = isSemiList ? semiNodes : commaNodes!;
|
|
815
|
-
let sep: ';' | ',' = isSemiList ? ';' : ',';
|
|
816
|
-
const result: List = new List(nodes, { sep }, location, $.context);
|
|
817
|
-
return result;
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
/**
|
|
822
|
-
* Less is more lenient about at-keywords. See lessTokens.ts for more details.
|
|
823
|
-
*/
|
|
824
|
-
export function varName(this: P, T: TokenMap) {
|
|
825
|
-
const $ = this;
|
|
826
|
-
return () => {
|
|
827
|
-
// AtKeywordLessExtension is categorized as AtName in lessTokens.ts, so consuming
|
|
828
|
-
// AtName alone preserves behavior while avoiding OR ambiguity warnings.
|
|
829
|
-
return $.CONSUME(T.AtName);
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* Originally, we were creating alternatives for mixin calls and mixin definitions
|
|
835
|
-
* that could mostly overlap, which led to longer parsing. Instead, we parse
|
|
836
|
-
* as if it could be either, and then we disambiguate at the end.
|
|
837
|
-
*/
|
|
838
|
-
export function mixinArg(this: P, T: TokenMap) {
|
|
839
|
-
const $ = this;
|
|
840
|
-
return (ctx: RuleContext = {}) => {
|
|
841
|
-
const firstToken = $.LA(1);
|
|
842
|
-
const atStart = $.matchToken(firstToken, T.AtName);
|
|
843
|
-
const tt2 = $.LA(2).tokenType;
|
|
844
|
-
const tt3 = $.LA(3).tokenType;
|
|
845
|
-
const hasWsAfterName = tt2 === T.WS;
|
|
846
|
-
const nextTokenType = hasWsAfterName ? tt3 : tt2;
|
|
847
|
-
|
|
848
|
-
if (atStart && nextTokenType === T.Ellipsis) {
|
|
849
|
-
$.startRule();
|
|
850
|
-
const name = $.CONSUME(T.AtName);
|
|
851
|
-
if (hasWsAfterName) {
|
|
852
|
-
$.CONSUME(T.WS);
|
|
853
|
-
}
|
|
854
|
-
$.CONSUME(T.Ellipsis);
|
|
855
|
-
if ($.RECORDING_PHASE) {
|
|
856
|
-
return;
|
|
857
|
-
}
|
|
858
|
-
return new Rest(name.image.slice(1), undefined, $.endRule(), $.context);
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
if (atStart && nextTokenType === T.Colon) {
|
|
862
|
-
$.startRule();
|
|
863
|
-
const name = $.CONSUME2(T.AtName);
|
|
864
|
-
if (hasWsAfterName) {
|
|
865
|
-
$.CONSUME2(T.WS);
|
|
866
|
-
}
|
|
867
|
-
$.CONSUME(T.Colon);
|
|
868
|
-
const value = $.SUBRULE3($.callArgument, { ARGS: [{ ...ctx, allowComma: !!ctx.allowComma, detachedRulesetUsage: 'default-param' }] });
|
|
869
|
-
|
|
870
|
-
const location = $.endRule();
|
|
871
|
-
if ($.RECORDING_PHASE) {
|
|
872
|
-
return;
|
|
873
|
-
}
|
|
874
|
-
return new VarDeclaration({
|
|
875
|
-
name: name.image.slice(1),
|
|
876
|
-
value
|
|
877
|
-
}, { paramVar: true }, location, $.context);
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
if (atStart && (nextTokenType === T.RParen || nextTokenType === T.Comma || nextTokenType === T.Semi)) {
|
|
881
|
-
$.startRule();
|
|
882
|
-
const name = $.CONSUME3(T.AtName);
|
|
883
|
-
if ($.RECORDING_PHASE) {
|
|
884
|
-
return;
|
|
885
|
-
}
|
|
886
|
-
const location = $.endRule();
|
|
887
|
-
if (ctx.isDefinition) {
|
|
888
|
-
return new VarDeclaration({
|
|
889
|
-
name: name.image.slice(1),
|
|
890
|
-
value: new Nil(undefined, undefined, location, $.context)
|
|
891
|
-
}, { paramVar: true }, location, $.context);
|
|
892
|
-
}
|
|
893
|
-
return new Any(name.image.slice(1), { role: 'name' }, location, $.context);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
if ($.isType(T.Ellipsis)) {
|
|
897
|
-
const ellipsis = $.CONSUME2(T.Ellipsis);
|
|
898
|
-
return new Rest(undefined, undefined, $.getLocationInfo(ellipsis), $.context);
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
return $.SUBRULE($.callArgument, { ARGS: [ctx] });
|
|
902
|
-
};
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
export function callArgument(this: P, T: TokenMap) {
|
|
906
|
-
const $ = this;
|
|
907
|
-
return (ctx: RuleContext = {}) => {
|
|
908
|
-
return $.OR([
|
|
909
|
-
{
|
|
910
|
-
GATE: () => $.isType(T.AnonMixinStart) || $.isType(T.LCurly),
|
|
911
|
-
ALT: () => $.SUBRULE($.anonymousMixinDefinition, { ARGS: [ctx] })
|
|
912
|
-
},
|
|
913
|
-
{
|
|
914
|
-
GATE: () => !ctx.allowComma,
|
|
915
|
-
ALT: () => $.SUBRULE($.valueSequence, { ARGS: [ctx] })
|
|
916
|
-
},
|
|
917
|
-
{
|
|
918
|
-
GATE: () => !!ctx.allowComma,
|
|
919
|
-
ALT: () => $.SUBRULE($.valueList, { ARGS: [ctx] })
|
|
920
|
-
}
|
|
921
|
-
]);
|
|
922
|
-
};
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
/**
|
|
926
|
-
* Override unknownAtRule to handle @-export for stylesheet forwarding.
|
|
927
|
-
* @-export is like @-compose but with forward semantics and no `with` support.
|
|
928
|
-
*/
|
|
929
|
-
export function unknownAtRule(this: P, T: TokenMap) {
|
|
930
|
-
const $ = this;
|
|
931
|
-
return (ctx: RuleContext = {}) => {
|
|
932
|
-
const img = $.LA(1).image;
|
|
933
|
-
if (img === '@use' || img === '@-use') {
|
|
934
|
-
return $.SUBRULE($.useAtRule, { ARGS: [ctx] });
|
|
935
|
-
}
|
|
936
|
-
if (img === '@-export') {
|
|
937
|
-
return $.SUBRULE($.exportAtRule, { ARGS: [ctx] });
|
|
938
|
-
}
|
|
939
|
-
return cssUnknownAtRule.call($, T)(ctx);
|
|
940
|
-
};
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
/**
|
|
944
|
-
* Parse Less v5 script-module imports.
|
|
945
|
-
*
|
|
946
|
-
* Stylesheet composition uses `@compose`; `@use` / `@-use` only become
|
|
947
|
-
* JsImport nodes for script-style paths and aliases like `#less/math`.
|
|
948
|
-
*/
|
|
949
|
-
export function useAtRule(this: P, T: TokenMap) {
|
|
950
|
-
const $ = this;
|
|
951
|
-
return (ctx: RuleContext = {}) => {
|
|
952
|
-
$.startRule();
|
|
953
|
-
const name = $.CONSUME(T.AtName); // '@use' or '@-use'
|
|
954
|
-
|
|
955
|
-
const pathNode: Quoted = $.SUBRULE($.string, { ARGS: [ctx] });
|
|
956
|
-
|
|
957
|
-
let namespace: string | undefined;
|
|
958
|
-
$.OPTION({
|
|
959
|
-
GATE: () =>
|
|
960
|
-
$.LA(1).tokenType === T.PlainIdent
|
|
961
|
-
&& $.LA(1).image === 'as',
|
|
962
|
-
DEF: () => {
|
|
963
|
-
$.CONSUME(T.PlainIdent);
|
|
964
|
-
$.OR2([
|
|
965
|
-
{ ALT: () => {
|
|
966
|
-
namespace = $.CONSUME2(T.PlainIdent).image;
|
|
967
|
-
} },
|
|
968
|
-
{ ALT: () => {
|
|
969
|
-
namespace = $.CONSUME(T.Star).image;
|
|
970
|
-
} }
|
|
971
|
-
]);
|
|
972
|
-
}
|
|
973
|
-
});
|
|
974
|
-
|
|
975
|
-
$.CONSUME(T.Semi);
|
|
976
|
-
|
|
977
|
-
const location = $.endRule();
|
|
978
|
-
if ($.RECORDING_PHASE) {
|
|
979
|
-
return;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
const rawPath = pathNode.valueOf();
|
|
983
|
-
if (isScriptUsePath(rawPath)) {
|
|
984
|
-
return new JsImport(
|
|
985
|
-
{ path: pathNode },
|
|
986
|
-
{ namespace: namespace ?? defaultNamespaceFromPath(rawPath) },
|
|
987
|
-
location,
|
|
988
|
-
$.context
|
|
989
|
-
);
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
const preludeNodes: Node[] = [pathNode];
|
|
993
|
-
if (namespace) {
|
|
994
|
-
preludeNodes.push(
|
|
995
|
-
new Any('as', { role: 'ident' }, undefined, $.context),
|
|
996
|
-
new Any(namespace, { role: namespace === '*' ? 'operator' : 'ident' }, undefined, $.context)
|
|
997
|
-
);
|
|
998
|
-
}
|
|
999
|
-
return new AtRuleStatement(
|
|
1000
|
-
{
|
|
1001
|
-
name: name.image,
|
|
1002
|
-
prelude: new Sequence(preludeNodes, undefined, $.getLocationFromNodes(preludeNodes), $.context)
|
|
1003
|
-
},
|
|
1004
|
-
undefined,
|
|
1005
|
-
location,
|
|
1006
|
-
$.context
|
|
1007
|
-
);
|
|
1008
|
-
};
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
/**
|
|
1012
|
-
* Parse @-export './foo.jess' [as <namespace>]
|
|
1013
|
-
*
|
|
1014
|
-
* Creates a StyleImport with forward semantics (members not visible locally but transitive).
|
|
1015
|
-
* Does NOT support `with` (unlike @-compose).
|
|
1016
|
-
* Participates in evaldTrees caching like @-compose.
|
|
1017
|
-
*/
|
|
1018
|
-
export function exportAtRule(this: P, T: TokenMap) {
|
|
1019
|
-
const $ = this;
|
|
1020
|
-
return (ctx: RuleContext = {}) => {
|
|
1021
|
-
$.startRule();
|
|
1022
|
-
$.CONSUME(T.AtKeyword); // '@-export'
|
|
1023
|
-
|
|
1024
|
-
// Parse the path (string or url)
|
|
1025
|
-
const pathNode: Quoted | Url = $.OR([
|
|
1026
|
-
{ ALT: () => $.SUBRULE($.urlFunction, { ARGS: [ctx] }) },
|
|
1027
|
-
{ ALT: () => $.SUBRULE($.string, { ARGS: [ctx] }) }
|
|
1028
|
-
]);
|
|
1029
|
-
|
|
1030
|
-
// Optional "as <namespace>"
|
|
1031
|
-
let namespace: string | undefined;
|
|
1032
|
-
$.OPTION(() => {
|
|
1033
|
-
const la = $.LA(1);
|
|
1034
|
-
if (!((la.tokenType === T.PlainIdent || la.tokenType === T.Ident) && la.image === 'as')) {
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
// Consume "as"
|
|
1038
|
-
if ($.isType(T.Ident)) {
|
|
1039
|
-
$.CONSUME(T.Ident);
|
|
1040
|
-
} else {
|
|
1041
|
-
$.CONSUME(T.PlainIdent);
|
|
1042
|
-
}
|
|
1043
|
-
// Consume namespace identifier
|
|
1044
|
-
const nsTok: IToken = $.isType(T.Ident)
|
|
1045
|
-
? $.CONSUME(T.Ident)
|
|
1046
|
-
: $.CONSUME(T.PlainIdent);
|
|
1047
|
-
namespace = nsTok.image;
|
|
1048
|
-
});
|
|
1049
|
-
|
|
1050
|
-
$.CONSUME(T.Semi);
|
|
1051
|
-
|
|
1052
|
-
const loc = $.endRule();
|
|
1053
|
-
return new StyleImport(
|
|
1054
|
-
{ path: pathNode },
|
|
1055
|
-
{
|
|
1056
|
-
type: 'compose',
|
|
1057
|
-
namespace,
|
|
1058
|
-
importOptions: {
|
|
1059
|
-
forward: true
|
|
1060
|
-
}
|
|
1061
|
-
},
|
|
1062
|
-
loc,
|
|
1063
|
-
$.context
|
|
1064
|
-
);
|
|
1065
|
-
};
|
|
1066
|
-
}
|