@jesscss/less-parser 2.0.0-alpha.6 → 2.0.0-alpha.8

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.
Files changed (62) hide show
  1. package/README.md +149 -36
  2. package/lib/builders.d.ts +381 -0
  3. package/lib/builders.d.ts.map +1 -0
  4. package/lib/cst.cjs +14 -0
  5. package/lib/cst.d.ts +6 -0
  6. package/lib/cst.d.ts.map +1 -0
  7. package/lib/cst.js +12 -0
  8. package/lib/functional-parser.cjs +2654 -0
  9. package/lib/functional-parser.d.ts +18 -0
  10. package/lib/functional-parser.d.ts.map +1 -0
  11. package/lib/functional-parser.js +2589 -0
  12. package/lib/grammar.cjs +35057 -0
  13. package/lib/grammar.d.ts +2 -0
  14. package/lib/grammar.d.ts.map +1 -0
  15. package/lib/grammar.js +35056 -0
  16. package/lib/index.cjs +17 -4096
  17. package/lib/index.d.ts +9 -149
  18. package/lib/index.d.ts.map +1 -1
  19. package/lib/index.js +6 -4091
  20. package/lib/jess.cjs +3968 -0
  21. package/lib/jess.d.ts +7 -0
  22. package/lib/jess.d.ts.map +1 -0
  23. package/lib/jess.js +3962 -0
  24. package/lib/lessParser.d.ts +38 -0
  25. package/lib/lessParser.d.ts.map +1 -0
  26. package/lib/lessRecursiveParser.d.ts +99 -0
  27. package/lib/lessRecursiveParser.d.ts.map +1 -0
  28. package/lib/lessTokens.d.ts +23 -0
  29. package/lib/lessTokens.d.ts.map +1 -0
  30. package/lib/productions/guards.d.ts +113 -0
  31. package/lib/productions/guards.d.ts.map +1 -0
  32. package/lib/productions/index.d.ts +5 -0
  33. package/lib/productions/index.d.ts.map +1 -0
  34. package/lib/productions/root.d.ts +38 -0
  35. package/lib/productions/root.d.ts.map +1 -0
  36. package/lib/productions/selectors.d.ts +41 -0
  37. package/lib/productions/selectors.d.ts.map +1 -0
  38. package/lib/productions/values.d.ts +35 -0
  39. package/lib/productions/values.d.ts.map +1 -0
  40. package/lib/utils.d.ts +9 -0
  41. package/lib/utils.d.ts.map +1 -0
  42. package/package.json +40 -9
  43. package/src/__tests__/debug-log.ts +35 -0
  44. package/src/__tests__/wall5-parse.test.ts +67 -0
  45. package/src/builders.ts +3190 -0
  46. package/src/cst.ts +25 -0
  47. package/src/functional-parser.ts +162 -0
  48. package/src/grammar.ts +870 -0
  49. package/src/index.ts +19 -0
  50. package/src/jess.ts +6 -0
  51. package/src/lessParser.ts +120 -0
  52. package/src/lessRecursiveParser.ts +279 -0
  53. package/src/lessTokens.ts +350 -0
  54. package/src/productions/guards.ts +1066 -0
  55. package/src/productions/index.ts +29 -0
  56. package/src/productions/root.ts +1613 -0
  57. package/src/productions/selectors.ts +1309 -0
  58. package/src/productions/values.ts +1449 -0
  59. package/src/utils.ts +178 -0
  60. package/lib/index.d.cts +0 -150
  61. package/lib/index.d.cts.map +0 -1
  62. package/lib/index.js.map +0 -1
@@ -0,0 +1,2654 @@
1
+ const require_grammar = require("./grammar.cjs");
2
+ let _jesscss_css_parser = require("@jesscss/css-parser");
3
+ let _jesscss_css_parser_jess = require("@jesscss/css-parser/jess");
4
+ let _jesscss_core = require("@jesscss/core");
5
+ //#region src/lessTokens.ts
6
+ const AMPERSAND_TEMPLATE_CONTENTS_REGEX = /(?:[.#-]|\d)(?:[.#\w\u0080-\uffff-]|&)*|(?:[.#\w\u0080-\uffff-]|&)*&(?:[.#\w\u0080-\uffff-]|&)*/;
7
+ function $preBuildFragments() {
8
+ const fragments = (0, _jesscss_css_parser.rawCssFragments)().map((f) => [...f]);
9
+ fragments.unshift(["lineComment", "\\/\\/[^\\n\\r]*"]);
10
+ fragments.push(["interpolated", "[@$]\\{(?:{{nmchar}}*)\\}"]);
11
+ return fragments;
12
+ }
13
+ function $preBuildTokens() {
14
+ const tokens = (0, _jesscss_css_parser.rawCssTokens)();
15
+ /**
16
+ * Keyed by what to insert after
17
+ *
18
+ * @todo - Move merge utility to css-parser
19
+ */
20
+ const merges = {
21
+ Assign: [{
22
+ name: "Ellipsis",
23
+ pattern: /\.\.\./,
24
+ categories: ["BlockMarker"]
25
+ }, (
26
+ /**
27
+ * Less's historical parser unfortunately allows
28
+ * at-keywords that are not valid in CSS. One is
29
+ * that Less allows at-keywords to begin with numbers.
30
+ * Another is that it allows an at-rule that only
31
+ * contains a single dash. So we capture this as
32
+ * a separate token.
33
+ *
34
+ * We also do this later in the token stack so that we
35
+ * don't accidentally grab something like
36
+ * @-webkit-keyframes while looking for @-.
37
+ */
38
+ {
39
+ name: "AtKeywordLessExtension",
40
+ pattern: "@(?:-|\\d(?:{{nmchar}})*)",
41
+ categories: ["BlockMarker", "AtName"]
42
+ })],
43
+ PlainIdent: [
44
+ {
45
+ name: "Interpolated",
46
+ pattern: _jesscss_css_parser.LexerType.NA
47
+ },
48
+ {
49
+ name: "LineComment",
50
+ pattern: "{{lineComment}}",
51
+ label: _jesscss_css_parser.SKIPPED_LABEL
52
+ },
53
+ {
54
+ name: "PlusAssign",
55
+ pattern: "\\+{{whitespace}}*:",
56
+ categories: ["BlockMarker", "Assign"]
57
+ },
58
+ {
59
+ name: "UnderscoreAssign",
60
+ pattern: "\\+{{whitespace}}*_{{whitespace}}*:",
61
+ categories: ["BlockMarker", "Assign"]
62
+ },
63
+ {
64
+ name: "AnonMixinStart",
65
+ pattern: /[.#]\(/,
66
+ categories: ["BlockMarker"]
67
+ },
68
+ {
69
+ name: "GtEqAlias",
70
+ pattern: /=>/,
71
+ categories: ["CompareOperator"]
72
+ },
73
+ {
74
+ name: "LtEqAlias",
75
+ pattern: /=</,
76
+ categories: ["CompareOperator"]
77
+ },
78
+ {
79
+ name: "Extend",
80
+ pattern: /:extend\(/,
81
+ categories: ["BlockMarker"]
82
+ },
83
+ {
84
+ name: "VarOrProp",
85
+ pattern: _jesscss_css_parser.LexerType.NA
86
+ },
87
+ {
88
+ name: "NestedReference",
89
+ pattern: ["([@$]+{{ident}}?){2,}", _jesscss_css_parser.groupCapture],
90
+ start_chars_hint: ["@", "$"],
91
+ categories: ["VarOrProp"],
92
+ line_breaks: true
93
+ },
94
+ {
95
+ name: "PropertyReference",
96
+ pattern: "\\${{ident}}",
97
+ categories: ["VarOrProp"]
98
+ },
99
+ (
100
+ /** Can be used in unit function or mod operation */
101
+ {
102
+ name: "Percent",
103
+ pattern: /%/
104
+ }),
105
+ {
106
+ name: "DefaultGuardIdent",
107
+ pattern: /default/,
108
+ longer_alt: "PlainIdent",
109
+ categories: ["Ident"]
110
+ },
111
+ {
112
+ name: "DefaultGuardFunc",
113
+ pattern: /default(?:\(\))/
114
+ }
115
+ ],
116
+ Ampersand: [{
117
+ name: "AmpersandLParen",
118
+ pattern: /&\(/,
119
+ push_mode: "AmpersandTemplate",
120
+ categories: [
121
+ "Selector",
122
+ "NestedRuleStart",
123
+ "BlockMarker"
124
+ ]
125
+ }, {
126
+ name: "AllFlag",
127
+ pattern: /!all/,
128
+ categories: ["BlockMarker"]
129
+ }],
130
+ UrlStart: [
131
+ (
132
+ /**
133
+ * Keywords that we don't identify as idents
134
+ * should be manually added to other places where an ident is valid.
135
+ */
136
+ {
137
+ name: "When",
138
+ pattern: /when/i,
139
+ longer_alt: "PlainIdent",
140
+ categories: ["BlockMarker"]
141
+ }),
142
+ {
143
+ name: "FormatFunction",
144
+ pattern: /%\(/,
145
+ categories: ["BlockMarker", "FunctionStart"]
146
+ },
147
+ {
148
+ name: "IfFunction",
149
+ pattern: /if\(/,
150
+ categories: ["BlockMarker", "FunctionStart"]
151
+ },
152
+ {
153
+ name: "BooleanFunction",
154
+ pattern: /boolean\(/,
155
+ categories: ["BlockMarker", "FunctionStart"]
156
+ },
157
+ {
158
+ name: "JavaScript",
159
+ pattern: /~?`[^`]*`/,
160
+ line_breaks: true
161
+ }
162
+ ],
163
+ Signed: [
164
+ {
165
+ name: "InterpolatedIdent",
166
+ pattern: "(?:{{ident}}|-)?{{interpolated}}(?:{{interpolated}}|{{nmchar}})*",
167
+ categories: [
168
+ "Interpolated",
169
+ "Selector",
170
+ "Ident"
171
+ ]
172
+ },
173
+ {
174
+ name: "InterpolatedCustomProperty",
175
+ pattern: "--{{ident}}?{{interpolated}}(?:{{interpolated}}|{{nmchar}})*",
176
+ categories: ["Interpolated"]
177
+ },
178
+ (
179
+ /**
180
+ * Unfortunately, there's grammatical ambiguity between
181
+ * interpolated props and a naked interpolated selector name,
182
+ * making this awkward token necessary.
183
+ */
184
+ {
185
+ name: "InterpolatedSelector",
186
+ pattern: ["[.#]{{ident}}?{{interpolated}}(?:{{interpolated}}|{{nmchar}})*", _jesscss_css_parser.groupCapture],
187
+ categories: ["Interpolated", "Selector"],
188
+ start_chars_hint: [".", "#"],
189
+ line_breaks: true
190
+ })
191
+ ]
192
+ };
193
+ let defaultTokens = tokens.modes.Default.slice();
194
+ let tokenLength = defaultTokens.length;
195
+ for (let i = 0; i < tokenLength; i++) {
196
+ let token = defaultTokens[i];
197
+ const { name } = token;
198
+ const copyToken = () => {
199
+ token = structuredClone(token);
200
+ };
201
+ let alterations = true;
202
+ switch (name) {
203
+ case "Ampersand":
204
+ copyToken();
205
+ /**
206
+ * Captures not just ampersands, but "ampersand merges", where
207
+ * the intent of the author was to merge the parent selector with a token
208
+ * suffix or prefix.
209
+ *
210
+ * e.g.
211
+ * 1. &-foo
212
+ * 2. &(foo)
213
+ * 3. &1
214
+ * 4. .foo-&
215
+ */
216
+ token.pattern = "(?:[.#](?:{{ident}}-)?&|&){{nmchar}}*";
217
+ token.start_chars_hint = [
218
+ "&",
219
+ ".",
220
+ "#"
221
+ ];
222
+ break;
223
+ case "DotName":
224
+ case "HashName":
225
+ copyToken();
226
+ token.longer_alt = "Ampersand";
227
+ break;
228
+ case "Divide":
229
+ copyToken();
230
+ token.pattern = /\.?\//;
231
+ break;
232
+ case "SingleQuoteStart":
233
+ copyToken();
234
+ token.pattern = /~?'/;
235
+ break;
236
+ case "DoubleQuoteStart":
237
+ copyToken();
238
+ token.pattern = /~?"/;
239
+ break;
240
+ default: alterations = false;
241
+ }
242
+ if (alterations) defaultTokens[i] = token;
243
+ const merge = merges[name];
244
+ if (merge) {
245
+ /** Insert after current token */
246
+ defaultTokens = defaultTokens.slice(0, i + 1).concat(merge, defaultTokens.slice(i + 1));
247
+ tokens.modes.Default = defaultTokens;
248
+ const mergeLength = merge.length;
249
+ tokenLength += mergeLength;
250
+ i += mergeLength;
251
+ }
252
+ }
253
+ tokens.modes.AmpersandTemplate = [
254
+ {
255
+ name: "AmpersandTemplateEnd",
256
+ pattern: /\)/,
257
+ pop_mode: true,
258
+ categories: ["FunctionLikeEnd"]
259
+ },
260
+ {
261
+ name: "AmpersandTemplateContents",
262
+ pattern: AMPERSAND_TEMPLATE_CONTENTS_REGEX,
263
+ categories: ["Selector"]
264
+ },
265
+ "SingleQuoteStart",
266
+ "DoubleQuoteStart",
267
+ "WS"
268
+ ];
269
+ return tokens;
270
+ }
271
+ const Fragments = $preBuildFragments();
272
+ const Tokens = $preBuildTokens();
273
+ const lessFragments = () => Fragments;
274
+ const lessTokens = () => Tokens;
275
+ //#endregion
276
+ //#region src/utils.ts
277
+ const INTERPOLATION_REGEX = /([$@])\{([^}]+)\}/g;
278
+ const createInterpolatedReference = (prefix, varName, location, context) => {
279
+ const isProperty = prefix === "$";
280
+ return new _jesscss_core.Reference({ key: isProperty ? new _jesscss_core.Quoted(varName, { quote: "'" }, location) : varName }, {
281
+ type: isProperty ? "index" : "variable",
282
+ role: "ident"
283
+ }, location);
284
+ };
285
+ const getInterpolatedNode = (name, location, context) => {
286
+ const replacements = [];
287
+ let source = name;
288
+ let result;
289
+ INTERPOLATION_REGEX.lastIndex = 0;
290
+ while ((result = INTERPOLATION_REGEX.exec(name)) !== null) {
291
+ const [match, prefix, varName] = result;
292
+ source = source.replace(match, _jesscss_core.INTERPOLATION_PLACEHOLDER);
293
+ replacements.push(createInterpolatedReference(prefix ?? "", varName ?? "", location, context));
294
+ }
295
+ return new _jesscss_core.Interpolated({
296
+ source,
297
+ replacements
298
+ }, { role: "ident" }, location);
299
+ };
300
+ const normalizeMixinReferenceKey = (selector) => {
301
+ if ((0, _jesscss_core.isNode)(selector, _jesscss_core.N.BasicSelector) || selector instanceof _jesscss_core.InterpolatedSelector) return {
302
+ key: selector.valueOf(),
303
+ rawKey: selector
304
+ };
305
+ if ((0, _jesscss_core.isNode)(selector, _jesscss_core.N.CompoundSelector)) return {
306
+ key: selector.value.map((node) => node.valueOf()),
307
+ rawKey: selector
308
+ };
309
+ if ((0, _jesscss_core.isNode)(selector, _jesscss_core.N.ComplexSelector)) {
310
+ const path = [];
311
+ let canUsePath = true;
312
+ for (const node of selector.value) {
313
+ if ((0, _jesscss_core.isNode)(node, _jesscss_core.N.BasicSelector) || node instanceof _jesscss_core.InterpolatedSelector) {
314
+ path.push(node.valueOf());
315
+ continue;
316
+ }
317
+ if ((0, _jesscss_core.isNode)(node, _jesscss_core.N.CompoundSelector)) {
318
+ path.push(...node.value.map((child) => child.valueOf()));
319
+ continue;
320
+ }
321
+ if ((0, _jesscss_core.isNode)(node, _jesscss_core.N.Combinator) && (node.value === ">" || node.value === " ")) continue;
322
+ canUsePath = false;
323
+ break;
324
+ }
325
+ if (canUsePath && path.length > 0) return {
326
+ key: path,
327
+ rawKey: selector
328
+ };
329
+ }
330
+ return {
331
+ key: selector.valueOf(),
332
+ rawKey: selector
333
+ };
334
+ };
335
+ const getInterpolatedOrString = (name, location, context) => {
336
+ const matches = [];
337
+ INTERPOLATION_REGEX.lastIndex = 0;
338
+ let result;
339
+ while ((result = INTERPOLATION_REGEX.exec(name)) !== null) {
340
+ const [fullMatch, prefix, varName] = result;
341
+ if (varName && prefix) matches.push({
342
+ fullMatch,
343
+ prefix,
344
+ varName,
345
+ index: result.index
346
+ });
347
+ }
348
+ if (matches.length > 0) {
349
+ let source = name;
350
+ const replacements = [];
351
+ let offset = 0;
352
+ for (let i = 0; i < matches.length; i++) {
353
+ const match = matches[i];
354
+ const adjustedIndex = match.index - offset;
355
+ const beforeMatch = source.substring(0, adjustedIndex);
356
+ const afterMatch = source.substring(adjustedIndex + match.fullMatch.length);
357
+ source = beforeMatch + _jesscss_core.INTERPOLATION_PLACEHOLDER + afterMatch;
358
+ offset += match.fullMatch.length - _jesscss_core.INTERPOLATION_PLACEHOLDER.length;
359
+ const ref = createInterpolatedReference(match.prefix, match.varName, location, context);
360
+ replacements.push(ref);
361
+ }
362
+ return new _jesscss_core.Interpolated({
363
+ source,
364
+ replacements
365
+ }, { role: "ident" }, location);
366
+ }
367
+ const atPos = name.indexOf("@", 1);
368
+ const dollarPos = name.indexOf("$", 1);
369
+ if (atPos === -1 && dollarPos === -1) if (name.startsWith("@") || name.startsWith("$")) return name.slice(1);
370
+ else return name;
371
+ const nextPos = atPos !== -1 ? atPos : dollarPos;
372
+ const start = name.slice(1, nextPos);
373
+ const end = name.slice(nextPos);
374
+ const type = end.startsWith("@") ? "variable" : "index";
375
+ const endResult = getInterpolatedOrString(end, location, context);
376
+ if (typeof endResult === "string") {
377
+ const endKey = type === "index" ? new _jesscss_core.Quoted(endResult, { quote: "'" }, location) : endResult;
378
+ return new _jesscss_core.Interpolated({
379
+ source: start + _jesscss_core.INTERPOLATION_PLACEHOLDER,
380
+ replacements: [new _jesscss_core.Reference({ key: endKey }, {
381
+ type,
382
+ role: "ident"
383
+ }, location)]
384
+ }, { role: "ident" });
385
+ } else
386
+ /**
387
+ * endResult is already an Interpolated node, so we need to handle this
388
+ * differently.
389
+ *
390
+ * @todo - test deep nesting
391
+ */
392
+ return new _jesscss_core.Interpolated({
393
+ source: start + _jesscss_core.INTERPOLATION_PLACEHOLDER,
394
+ replacements: [type === "index" ? new _jesscss_core.Quoted(endResult, { quote: "'" }, location) : endResult]
395
+ }, { role: "ident" });
396
+ };
397
+ //#endregion
398
+ //#region src/builders.ts
399
+ const KNOWN_AT_RULE_VAR_NAME_RE = /^(?:(?:-moz-)?document|(?:-[a-z]+-)?keyframes|(?:-ms-)?viewport|import|media|supports|layer|container|scope|page|font-face|starting-style|property|counter-style|color-profile|font-palette-values|namespace)$/i;
400
+ function spanToLocation(span) {
401
+ return {
402
+ start: span.start,
403
+ end: span.end
404
+ };
405
+ }
406
+ function nodeChildren(children) {
407
+ return children.filter((c) => c._tag === "node");
408
+ }
409
+ var LessGrammar = class LessGrammar extends _jesscss_css_parser_jess.CssParser {
410
+ /** Math mode governing when arithmetic operates / when `/` divides. Less default. */
411
+ mathMode = "parens-division";
412
+ /** Bare ident/keyword token in value or guard position. */
413
+ _lessKeyword(text, loc) {
414
+ return this._valueKeyword(text, loc);
415
+ }
416
+ _isKeywordLike(node) {
417
+ return !!node && typeof node === "object" && (node.type === "Keyword" || node.type === "Any");
418
+ }
419
+ _isEmptyKeywordLike(node) {
420
+ return !node || this._isKeywordLike(node) && !String(node.value ?? "").trim();
421
+ }
422
+ buildNode(type, span, children, _state, _rawChildren, fields, triviaLog = []) {
423
+ const loc = spanToLocation(span);
424
+ const raw = _rawChildren;
425
+ switch (type) {
426
+ case "VarDeclaration": return this._buildVarDeclaration(children, raw, loc);
427
+ case "Reference": return this._buildReference(children, loc);
428
+ case "LessAmpersand": return this._buildAmpersand(children, loc);
429
+ case "ComplexSelector": return this._buildComplexSelector(raw, loc);
430
+ case "SelectorList": return this._buildSelectorList(raw, loc);
431
+ case "Ruleset": return this._buildRuleset(children, raw, loc);
432
+ case "Declaration":
433
+ this._warnDeprecatedValue(span);
434
+ return this._buildLessDeclaration(raw, loc);
435
+ case "CustomDeclaration":
436
+ this._warnCustomPropVars(span);
437
+ return this._buildLessCustomDecl(children, loc);
438
+ case "Block": return this._buildLessCustomBlock(children, loc);
439
+ case "AtRuleBlock":
440
+ this._warnAtRulePreludeVars(span);
441
+ return this._buildAtRuleBlock(children, loc);
442
+ case "QueryAtRuleBlock":
443
+ this._warnAtRulePreludeVars(span);
444
+ return this._buildLessQueryAtRuleBlock(children, raw, loc);
445
+ case "NamedColor": return this._buildNamedColor(children, loc);
446
+ case "Comparison": return this._buildComparison(raw, loc);
447
+ case "GuardDefault": return new _jesscss_core.DefaultGuard("default()", {}, loc);
448
+ case "GuardInParens": return this._buildGuardInParens(children, loc);
449
+ case "GuardTerm": return this._buildGuardTerm(raw, loc);
450
+ case "GuardAnd": return this._buildGuardJoin(children, loc, "and");
451
+ case "GuardOr": return this._buildGuardJoin(children, loc, "or");
452
+ case "Guard": return this._buildGuard(children, loc);
453
+ case "CondArgTerm": return this._buildCondArgTerm(raw, loc);
454
+ case "CondArgAnd": return this._buildCondArgJoin(children, loc, "and");
455
+ case "CondArgOr": return this._buildCondArgJoin(children, loc, "or");
456
+ case "UnicodeRange": return this._lessKeyword(this._source.slice(span.start, span.end), loc);
457
+ case "PseudoSelector": return this._buildLessPseudo(type, span, children, _state, raw, fields, triviaLog, loc);
458
+ case "InterpolatedSelector": return this._buildInterpolatedSelector(children, loc);
459
+ case "VarCall": return this._buildVarCall(children, raw, loc);
460
+ case "MixinCall": return this._buildMixinCall(children, raw, loc);
461
+ case "Rest": return this._buildRest(children, loc);
462
+ case "NamedArg": return this._buildNamedArg(raw, loc);
463
+ case "MixinArgs": return this._buildMixinArgs(raw, loc);
464
+ case "AnonymousMixinDefinition": return this._buildAnonMixin(children, loc);
465
+ case "DetachedRuleset": return this._buildDetachedRuleset(children, loc);
466
+ case "For": return this._buildEachFor(children, loc);
467
+ case "FormatCall": return this._buildFormatCall(raw, loc);
468
+ case "MixinOrQualifiedRule": return this._buildMixinOrQualified(children, loc);
469
+ case "Negative": return new _jesscss_core.Negative(this._negativeOperand(children), void 0, loc);
470
+ case "OperationTop": return this._buildOperation(children, loc, this.mathMode === "always");
471
+ case "EscapedValue": return this._buildEscapedValue(children, loc);
472
+ case "InterpValue": return this._buildInterpValue(raw, loc);
473
+ case "NsAccessor": return this._buildNsAccessor(children, loc);
474
+ case "AtRuleStatement": return this._buildAtRuleStatement(children, loc);
475
+ case "ExtendTarget": return this._buildExtendTarget(children, raw, loc);
476
+ case "ExtendPseudo": return this._buildExtendPseudo(children, loc);
477
+ case "ExtendStatement": return this._buildExtendStatement(children, raw, loc);
478
+ default: return super.buildNode(type, span, children, _state, raw, fields, triviaLog);
479
+ }
480
+ }
481
+ /**
482
+ * The operand of a `Negative` (`-value`). The grammar emits the leading `-`
483
+ * as a leaf followed by the operand, which may itself be a node or a bare
484
+ * string terminal (e.g. `-@color` → `var(--color)`'s inner `-color-accent`).
485
+ * Prefer a node child; otherwise take the operand leaf's text so `Negative`
486
+ * coerces it to the canonical node form rather than receiving `undefined`.
487
+ */
488
+ _negativeOperand(children) {
489
+ const node = nodeChildren(children)[0];
490
+ if (node) return node;
491
+ return children.find((c) => c._tag === "leaf" && c.value !== "-")?.value ?? "";
492
+ }
493
+ _buildVarDeclaration(children, rawChildren, loc) {
494
+ const items = (0, _jesscss_css_parser_jess.spannedComponents)(rawChildren);
495
+ const rawName = typeof items[0]?.comp === "string" ? items[0].comp : "";
496
+ const name = rawName.startsWith("@") ? rawName.slice(1) : rawName;
497
+ if (/^-?\d/.test(name)) this._warn(`Variable name "@${name}" starts with a digit; digit-leading variable names are deprecated.`, "digit-leading-variable");
498
+ const colonIdx = items.findIndex((i) => i.comp === ":");
499
+ if (items[colonIdx + 1]?.comp === "{") {
500
+ const ruleNodes = nodeChildren(children);
501
+ const openBrace = items[colonIdx + 1];
502
+ const closeBrace = items[items.length - 1].comp === "}" ? items[items.length - 1] : void 0;
503
+ if (ruleNodes.length === 0 && closeBrace) {
504
+ if (this._source.slice(openBrace.span.end, closeBrace.span.start).trim() !== "") {
505
+ const rawBlock = this._source.slice(openBrace.span.start, closeBrace.span.end);
506
+ return new _jesscss_core.VarDeclaration({
507
+ name: (name || void 0) ?? name,
508
+ value: new _jesscss_core.Quoted(rawBlock, {}, loc)
509
+ }, {}, loc);
510
+ }
511
+ }
512
+ const mixin = new _jesscss_core.Mixin({ rules: ruleNodes }, {}, loc);
513
+ return new _jesscss_core.VarDeclaration({
514
+ name: (name || void 0) ?? name,
515
+ value: mixin
516
+ }, {}, loc);
517
+ }
518
+ let end = items.length;
519
+ let bangIdx = -1;
520
+ for (let i = colonIdx + 1; i < items.length; i++) {
521
+ const c = items[i].comp;
522
+ if (c === "!") {
523
+ end = i;
524
+ bangIdx = i;
525
+ break;
526
+ }
527
+ if (c === "important" || c === ";") {
528
+ end = i;
529
+ break;
530
+ }
531
+ }
532
+ const valItems = items.slice(colonIdx + 1, end);
533
+ if (valItems.length > 0 && valItems[valItems.length - 1].comp === ",") valItems.pop();
534
+ if (valItems.length) {
535
+ const vText = this._source.slice(valItems[0].span.start, valItems[valItems.length - 1].span.end);
536
+ if (/(?:^|[\s,])\.-?[_a-zA-Z]/.test(vText)) this._warn(`Unquoted selector capture in variable "@${name}" is deprecated; wrap the value in quotes or ~"...".`, "unquoted-selector-capture");
537
+ }
538
+ const nsRef = this._tryParseNamespaceRef(valItems, loc);
539
+ let { value: rawValue } = nsRef ? { value: nsRef } : this._assembleLessValue(valItems, loc);
540
+ if (!nsRef && valItems.length > 0) {
541
+ const lastSpan = valItems[valItems.length - 1].span;
542
+ const afterVal = this._source.slice(lastSpan.end);
543
+ const rv = rawValue;
544
+ const rvKey = rv?.key;
545
+ const isEmptyKey = rvKey === "" || rvKey === void 0 || rvKey && typeof rvKey === "object" && rvKey.type === "Quoted" && (rvKey.value === "" || rvKey.valueOf?.() === "");
546
+ const grammarPartialAccessor = rv && rv.type === "Reference" && rv.target !== void 0 && isEmptyKey;
547
+ const accMatch = /^\s*\[([^\]]+)\]/.exec(afterVal);
548
+ if (accMatch) {
549
+ const accText = accMatch[1].trim();
550
+ const accessorKey = this._decodeAccessorKey(accText, loc);
551
+ const accessorRefOptions = typeof accessorKey === "number" ? { type: "index" } : {};
552
+ if (grammarPartialAccessor) rawValue = new _jesscss_core.Reference({
553
+ target: rv.target,
554
+ key: accessorKey
555
+ }, accessorRefOptions, loc);
556
+ else rawValue = new _jesscss_core.Reference({
557
+ target: rawValue,
558
+ key: accessorKey
559
+ }, accessorRefOptions, loc);
560
+ const afterAcc = afterVal.slice(accMatch[0].length);
561
+ if (/^\s*\(\s*\)/.test(afterAcc)) rawValue = new _jesscss_core.Call({ name: rawValue }, {}, loc);
562
+ }
563
+ }
564
+ const value = typeof rawValue === "string" && rawValue ? this._lessKeyword(rawValue, loc) : rawValue;
565
+ let important;
566
+ if (bangIdx >= 0) {
567
+ const bang = items[bangIdx];
568
+ const kw = items[bangIdx + 1];
569
+ const impEnd = kw && typeof kw.comp === "string" && kw.comp.toLowerCase() === "important" ? kw.span.end : bang.span.end;
570
+ important = this._source.slice(bang.span.start, impEnd);
571
+ }
572
+ return new _jesscss_core.VarDeclaration({
573
+ name: (name || void 0) ?? name,
574
+ value,
575
+ important
576
+ }, {}, loc);
577
+ }
578
+ /**
579
+ * Build a Less variable reference and its accessor/call chain. Faithful 1:1
580
+ * port of the Chevrotain `varReference` + `lookupOrCall` productions
581
+ * (productions/values.ts, productions/guards.ts): a `@var` base glued to a
582
+ * left-folded chain of `[index]` accessors and `(call)`s. The grammar's
583
+ * noTrivia() guarantees the chain is adjacent (no whitespace between segments).
584
+ */
585
+ _buildReference(children, loc) {
586
+ const ls = children.filter((c) => c._tag === "leaf");
587
+ const varName = ls[0]?.value ?? "";
588
+ const isVar = varName.startsWith("@");
589
+ let base = varName.startsWith("$") ? new _jesscss_core.Reference({ key: new _jesscss_core.Quoted(varName.slice(1), { quote: "'" }, loc) }, { type: "index" }, loc) : new _jesscss_core.Reference(isVar ? varName.slice(1) : varName, isVar ? { type: "variable" } : {}, loc);
590
+ let i = 1;
591
+ while (i < ls.length) {
592
+ const tok = ls[i].value;
593
+ if (tok === "[") if (ls[i + 1]?.value === "]") {
594
+ base = new _jesscss_core.Reference({
595
+ target: base,
596
+ key: -1
597
+ }, { type: "index" }, loc);
598
+ i += 2;
599
+ } else {
600
+ base = this._applyReferenceAccessor(base, ls[i + 1].value, loc);
601
+ i += 3;
602
+ }
603
+ else if (tok === "(") {
604
+ const payload = { name: base };
605
+ if (ls[i + 1]?.value === ")") i += 2;
606
+ else {
607
+ const args = this._buildRefCallArgs(ls[i + 1].value, loc);
608
+ if (args) payload.args = args;
609
+ i += 3;
610
+ }
611
+ base = new _jesscss_core.Call(payload, {}, loc);
612
+ } else break;
613
+ }
614
+ return base;
615
+ }
616
+ /**
617
+ * Build a namespace INDEXED-accessor value: a `.`/`#` selector-path head glued to
618
+ * a `[accessor]` (and any further `[accessor]`/`(call)` chain), e.g.
619
+ * `#ns.options[val1]`. The grammar (NsAccessor) captures this as ONE value operand
620
+ * so it survives arithmetic folding; here we reassemble it into the mixin-ruleset
621
+ * name Reference + accessor chain — the SAME shape the declaration-value
622
+ * _assembleSegment path produces for a lone `#ns.options[val1]`. Call-headed forms
623
+ * (`.mixin()`, `.mixin()[k]`) do NOT reach here (they keep the GluedParen path).
624
+ * Leaves: [ headText, '[', key?, ']', '(', content?, ')' … ].
625
+ */
626
+ _buildNsAccessor(children, loc) {
627
+ const ls = children.filter((c) => c._tag === "leaf");
628
+ const headText = (ls[0]?.value ?? "").trim();
629
+ const pathSegs = headText.match(/[#.][^#.>+~\s]*/g) ?? [headText];
630
+ const nameKey = pathSegs.length === 1 ? pathSegs[0] : pathSegs;
631
+ const rawKey = pathSegs.length > 1 ? pathSegs.join("") : void 0;
632
+ let base = new _jesscss_core.Reference({
633
+ key: nameKey,
634
+ ...rawKey ? { rawKey } : {}
635
+ }, {
636
+ type: "mixin-ruleset",
637
+ role: "name"
638
+ }, loc);
639
+ let i = 1;
640
+ while (i < ls.length) {
641
+ const tok = ls[i].value;
642
+ if (tok === "[") if (ls[i + 1]?.value === "]") {
643
+ base = new _jesscss_core.Reference({
644
+ target: base,
645
+ key: -1
646
+ }, { type: "index" }, loc);
647
+ i += 2;
648
+ } else {
649
+ base = this._applyReferenceAccessor(base, ls[i + 1].value, loc);
650
+ i += 3;
651
+ }
652
+ else if (tok === "(") {
653
+ const payload = { name: base };
654
+ if (ls[i + 1]?.value === ")") i += 2;
655
+ else {
656
+ const args = this._buildRefCallArgs(ls[i + 1].value, loc);
657
+ if (args) payload.args = args;
658
+ i += 3;
659
+ }
660
+ base = new _jesscss_core.Call(payload, {}, loc);
661
+ } else break;
662
+ }
663
+ return base;
664
+ }
665
+ /**
666
+ * Apply one `[key]` accessor to `base`, reproducing lookupOrCall's key logic:
667
+ * type is `variable` when the key token starts with `@`, else `index`; the key
668
+ * text runs through getInterpolatedOrString (handling `$@x`/`@{x}` interpolation),
669
+ * and index keys are wrapped in a Quoted.
670
+ */
671
+ _applyReferenceAccessor(base, keyStr, loc) {
672
+ const type = keyStr.startsWith("@") ? "variable" : "index";
673
+ let result = getInterpolatedOrString(keyStr, loc);
674
+ if (type === "index") result = new _jesscss_core.Quoted(result, { quote: "'" }, loc);
675
+ return new _jesscss_core.Reference({
676
+ target: base,
677
+ key: result
678
+ }, { type }, loc);
679
+ }
680
+ /**
681
+ * Build mixin-call args for a `(…)` segment in a reference chain from the raw
682
+ * captured content. Comma-separated values become a List; an empty segment
683
+ * yields null (no args). Sub-structure here is intentionally shallow — the
684
+ * accessor-chain call form is rare and no consumer inspects nested arg shape.
685
+ */
686
+ _buildRefCallArgs(content, loc) {
687
+ const trimmed = content.trim();
688
+ if (!trimmed) return null;
689
+ return new _jesscss_core.List(trimmed.split(",").map((p) => p.trim()).filter(Boolean).map((p) => p.startsWith("@") ? new _jesscss_core.Reference(p.slice(1), { type: "variable" }, loc) : this._lessKeyword(p, loc)), void 0, loc);
690
+ }
691
+ _buildNamedColor(children, loc) {
692
+ return new _jesscss_core.Color({ node: children.filter((c) => c._tag === "leaf")[0]?.value ?? "" }, {}, loc);
693
+ }
694
+ _normalizeCompareOp(op) {
695
+ switch (op) {
696
+ case "=>":
697
+ case ">=": return ">=";
698
+ case "=<":
699
+ case "<=": return "<=";
700
+ case ">": return ">";
701
+ case "<": return "<";
702
+ default: return "=";
703
+ }
704
+ }
705
+ /** A guard comparison operator leaf (`=`, `<`, `>=`, `=<`, `=~`, …). */
706
+ _isCompareOpLeaf(text) {
707
+ return />=|<=|=>|=<|=~|[<>=]/.test(text);
708
+ }
709
+ /**
710
+ * Split a guard term's ordered components into `left [op right]`. A bare-keyword
711
+ * operand (`foo`, `true`) is a leaf string — not a node — so `nodeChildren`
712
+ * alone drops it; walk the ordered stream so string operands become real
713
+ * keyword nodes (their guard truthiness is decided at eval by `Condition`).
714
+ */
715
+ _guardComparison(raw, loc) {
716
+ const items = (0, _jesscss_css_parser_jess.spannedComponents)(raw);
717
+ let op;
718
+ const operands = [];
719
+ for (const it of items) if (typeof it.comp === "string") {
720
+ if (this._isCompareOpLeaf(it.comp)) {
721
+ op = this._normalizeCompareOp(it.comp);
722
+ continue;
723
+ }
724
+ operands.push(this._lessKeyword(it.comp, loc));
725
+ } else operands.push(it.comp);
726
+ const left = this._maybeDefaultGuard(operands[0] ?? this._lessKeyword("", loc), loc);
727
+ const right = operands[1] !== void 0 ? this._maybeDefaultGuard(operands[1], loc) : void 0;
728
+ return {
729
+ left,
730
+ op,
731
+ right
732
+ };
733
+ }
734
+ _buildComparison(raw, loc) {
735
+ const { left, op, right } = this._guardComparison(raw, loc);
736
+ if (op && right) return new _jesscss_core.Condition([
737
+ left,
738
+ op,
739
+ right
740
+ ], {}, loc);
741
+ return new _jesscss_core.Condition([left], {}, loc);
742
+ }
743
+ /**
744
+ * Coerce a `_assembleValue` result — a single Component or a raw space-group
745
+ * array — into ONE Node, so it can be a Condition operand. A single string
746
+ * becomes a keyword; a space-group array becomes a `Sequence` (the same coercion
747
+ * the List serializer applies to a raw group).
748
+ */
749
+ _condOperandNode(comps, loc) {
750
+ const { value } = this._assembleValue(comps, loc);
751
+ if (Array.isArray(value)) return new _jesscss_core.Sequence(value.map((c) => typeof c === "string" ? this._lessKeyword(c, loc) : c), void 0, loc);
752
+ if (typeof value === "string") return this._lessKeyword(value, loc);
753
+ return value;
754
+ }
755
+ /**
756
+ * `CondArgTerm` — a name-independent condition-argument term: optional leading
757
+ * `not`, a bounded value operand, and an optional `<op> right` comparison. Builds
758
+ * a `Condition` (comparison → `[left, op, right]`; bare `not` → `{negate:true}`)
759
+ * or, when neither a `not` nor a `compareOp` is present, the plain operand value
760
+ * (byte-identical to an ordinary value arg). Multi-token operands (`1px solid`)
761
+ * survive as a `Sequence`, unlike the single-operand guard path.
762
+ */
763
+ _buildCondArgTerm(raw, loc) {
764
+ const items = (0, _jesscss_css_parser_jess.spannedComponents)(raw);
765
+ const hasNot = items.length > 0 && items[0].comp === "not";
766
+ const rest = hasNot ? items.slice(1) : items;
767
+ const opIdx = rest.findIndex((it) => typeof it.comp === "string" && this._isCompareOpLeaf(it.comp));
768
+ let term;
769
+ if (opIdx >= 0) term = new _jesscss_core.Condition([
770
+ this._condOperandNode(rest.slice(0, opIdx), loc),
771
+ this._normalizeCompareOp(rest[opIdx].comp),
772
+ this._condOperandNode(rest.slice(opIdx + 1), loc)
773
+ ], {}, loc);
774
+ else term = this._condOperandNode(rest, loc);
775
+ if (hasNot) return new _jesscss_core.Condition([term], { negate: true }, loc);
776
+ return term;
777
+ }
778
+ /**
779
+ * Fold a left-associative `and`/`or` chain of condition-arg terms into Conditions.
780
+ *
781
+ * Less accepts a bare `and`/`or` join in value-position condition args (`if(@a > 5
782
+ * and @b < 2, …)`) — verified against less@4.6.7 (`if`/`boolean` route their arg
783
+ * through `condition()` with no `needsParens`, so bare comparisons split on `and`/
784
+ * `or`). We keep accepting the bare form for Less parity, but NORMALIZE the AST so
785
+ * each join operand is `Paren`-wrapped: `@a > 5 and @b < 2` builds the SAME tree as
786
+ * the explicitly-parenthesised `(@a > 5) and (@b < 2)`. This is a structural
787
+ * normalisation only — `Paren(Condition)` evaluates to the same boolean as the bare
788
+ * `Condition`, so rendered CSS is byte-identical. A single unjoined operand (one
789
+ * node) is untouched (no synthetic Paren).
790
+ */
791
+ _buildCondArgJoin(children, loc, op) {
792
+ const nodes = nodeChildren(children);
793
+ if (nodes.length === 0) return this._lessKeyword("", loc);
794
+ if (nodes.length === 1) return nodes[0];
795
+ const wrap = (n) => n.type === "Paren" ? n : new _jesscss_core.Paren(n, {}, loc);
796
+ let left = wrap(nodes[0]);
797
+ for (let i = 1; i < nodes.length; i++) left = new _jesscss_core.Condition([
798
+ left,
799
+ op,
800
+ wrap(nodes[i])
801
+ ], {}, loc);
802
+ return left;
803
+ }
804
+ /** Coerce a default() call/reference into a DefaultGuard, mirroring isDefaultGuardCall. */
805
+ _maybeDefaultGuard(node, loc) {
806
+ const n = node;
807
+ if (n?.type === "Call") {
808
+ const name = n.name;
809
+ const nameStr = String(typeof name === "object" && name !== null && "valueOf" in name ? name.valueOf() : name ?? "");
810
+ if (nameStr === "default" || nameStr === "??") return new _jesscss_core.DefaultGuard("default()", {}, loc);
811
+ }
812
+ return node;
813
+ }
814
+ /** guardInParens: `(` guardOr `)` → Paren, or a bare default() → Paren(DefaultGuard). */
815
+ _buildGuardInParens(children, loc) {
816
+ let inner = nodeChildren(children)[0] ?? this._lessKeyword("", loc);
817
+ inner = this._maybeDefaultGuard(inner, loc);
818
+ const innerAny = inner;
819
+ if (innerAny?.type === "Paren" && innerAny.value?.type === "DefaultGuard") return inner;
820
+ return new _jesscss_core.Paren(inner, {}, loc);
821
+ }
822
+ /** A single guard term: optional `not`, then a paren-guard or a comparison/value. */
823
+ _buildGuardTerm(raw, loc) {
824
+ const hasNot = (0, _jesscss_css_parser_jess.spannedComponents)(raw).some((i) => typeof i.comp === "string" && i.comp === "not");
825
+ const rest = raw.filter((rc) => !(rc._tag === "leaf" && rc.value === "not"));
826
+ const nodes = nodeChildren(rest);
827
+ let term;
828
+ if (nodes.length >= 1 && nodes[0].type === "Paren") term = nodes[0];
829
+ else {
830
+ const { left, op, right } = this._guardComparison(rest, loc);
831
+ term = op && right ? new _jesscss_core.Condition([
832
+ left,
833
+ op,
834
+ right
835
+ ], {}, loc) : left;
836
+ }
837
+ if (hasNot) return new _jesscss_core.Condition([term], { negate: true }, loc);
838
+ return term;
839
+ }
840
+ /** Fold a left-associative chain of terms joined by `and` / `or`. */
841
+ _buildGuardJoin(children, loc, op) {
842
+ const nodes = nodeChildren(children);
843
+ if (nodes.length === 0) return this._lessKeyword("", loc);
844
+ let left = nodes[0];
845
+ for (let i = 1; i < nodes.length; i++) left = new _jesscss_core.Condition([
846
+ left,
847
+ op,
848
+ nodes[i]
849
+ ], {}, loc);
850
+ return left;
851
+ }
852
+ /** guard: `when` guardOr — returns the single guardOr child. */
853
+ _buildGuard(children, loc) {
854
+ return nodeChildren(children)[0] ?? this._lessKeyword("", loc);
855
+ }
856
+ /**
857
+ * Deprecated Less `%(format, args…)` string formatting, LOWERED at build time.
858
+ *
859
+ * Jess already has full string interpolation, so `%()` is redundant Less-4 legacy —
860
+ * we emit a `percent-format` deprecation warning and, when the format is a string
861
+ * literal, splice its `%[sda]` directives into the canonical `Interpolated` node
862
+ * (the same one `@{var}` string interpolation builds), wrapped in a `Quoted` that
863
+ * preserves the literal's quote char / escaped flag:
864
+ * - `%s` → bare interpolation slot (a Quoted arg inserts with its quotes stripped);
865
+ * - `%d`/`%a` → identical bare slot (d/a are the same in Less);
866
+ * - `%S`/`%D`/`%A` → the arg WRAPPED in `escape(…)` (URL-encode);
867
+ * - `%%` → a literal `%`.
868
+ * A dynamic (non-literal) format (`%(hello)`, `%(e("…"), …)`) can't be lowered at
869
+ * parse time, so it falls back to a best-effort runtime `%` Call with the same warning.
870
+ */
871
+ _buildFormatCall(raw, loc) {
872
+ this._warn("%() string formatting is deprecated — use string interpolation", "percent-format");
873
+ const argList = this._assembleArgs(this._betweenParens((0, _jesscss_css_parser_jess.spannedComponents)(raw)), loc);
874
+ const args = argList.value ?? [];
875
+ const format = args[0];
876
+ const rest = args.slice(1);
877
+ if (format instanceof _jesscss_core.Quoted && typeof format.value === "string") {
878
+ const { source, replacements } = this._lowerFormatString(format.value, rest, loc);
879
+ return new _jesscss_core.Quoted(new _jesscss_core.Interpolated({
880
+ source,
881
+ replacements
882
+ }, { role: "ident" }, loc), {
883
+ quote: format.quote,
884
+ escaped: format.escaped
885
+ }, loc);
886
+ }
887
+ return new _jesscss_core.Call({
888
+ name: new _jesscss_core.Reference("%", {
889
+ type: "function",
890
+ fallbackValue: true
891
+ }, loc),
892
+ args: argList
893
+ }, { silentFail: true }, loc);
894
+ }
895
+ /**
896
+ * Turn a printf-style format string into an `Interpolated` source + replacements.
897
+ * `%[sda]` → one `INTERPOLATION_PLACEHOLDER` slot consuming the next positional arg
898
+ * (uppercase → wrapped in `escape(…)` to URL-encode); `%%` → a literal `%`.
899
+ */
900
+ _lowerFormatString(formatText, restArgs, loc) {
901
+ let source = "";
902
+ const replacements = [];
903
+ let argIndex = 0;
904
+ for (let i = 0; i < formatText.length; i++) {
905
+ const ch = formatText[i];
906
+ if (ch !== "%") {
907
+ source += ch;
908
+ continue;
909
+ }
910
+ const next = formatText[i + 1];
911
+ if (next === "%") {
912
+ source += "%";
913
+ i++;
914
+ continue;
915
+ }
916
+ if (next && /[sda]/i.test(next)) {
917
+ const arg = restArgs[argIndex++];
918
+ if (arg) {
919
+ source += _jesscss_core.INTERPOLATION_PLACEHOLDER;
920
+ if (/[A-Z]/.test(next)) {
921
+ const escapeRef = new _jesscss_core.Reference("escape", {
922
+ type: "function",
923
+ fallbackValue: true
924
+ }, loc);
925
+ const escapeArgs = new _jesscss_core.List([arg], {}, loc);
926
+ replacements.push(new _jesscss_core.Call({
927
+ name: escapeRef,
928
+ args: escapeArgs
929
+ }, { silentFail: true }, loc));
930
+ } else replacements.push(arg);
931
+ } else source += ch + next;
932
+ i++;
933
+ continue;
934
+ }
935
+ source += ch;
936
+ }
937
+ return {
938
+ source,
939
+ replacements
940
+ };
941
+ }
942
+ _buildInterpolatedSelector(children, loc) {
943
+ const ls = children.filter((c) => c._tag === "leaf");
944
+ const replacements = [];
945
+ let source = "";
946
+ for (const l of ls) if (l.value.startsWith("@{")) {
947
+ const varName = l.value.slice(2, -1);
948
+ replacements.push(new _jesscss_core.Reference(varName, { role: "ident" }, loc));
949
+ source += _jesscss_core.INTERPOLATION_PLACEHOLDER;
950
+ } else source += l.value;
951
+ return new _jesscss_core.InterpolatedSelector(new _jesscss_core.Interpolated({
952
+ source,
953
+ replacements
954
+ }, { role: "ident" }, loc), {}, loc);
955
+ }
956
+ _buildLessPseudo(type, span, children, state, raw, fields, triviaLog, loc) {
957
+ const pseudo = super.buildNode(type, span, children, state, raw, fields, triviaLog);
958
+ let pseudoArg = pseudo.arg;
959
+ if (pseudoArg === void 0) {
960
+ const open = children.findIndex((c) => c._tag === "leaf" && c.value === "(");
961
+ if (open >= 0) {
962
+ const inner = children[open + 1];
963
+ const isClose = inner?._tag === "leaf" && inner.value === ")";
964
+ if (inner !== void 0 && !isClose) {
965
+ const recovered = typeof inner === "string" ? this._lessKeyword(inner, loc) : inner._tag === "leaf" ? this._lessKeyword(inner.value, loc) : inner;
966
+ pseudo.arg = recovered;
967
+ pseudoArg = recovered;
968
+ }
969
+ }
970
+ }
971
+ if (Array.isArray(pseudoArg)) pseudo.arg = pseudoArg.map((item) => typeof item === "string" && item !== " " ? this._lessKeyword(item, loc) : item);
972
+ return pseudo;
973
+ }
974
+ _buildLessDeclaration(raw, loc) {
975
+ const items = (0, _jesscss_css_parser_jess.spannedComponents)(raw);
976
+ const decl = this._buildDeclaration(raw, loc);
977
+ const colonIdx = items.findIndex((i) => i.comp === ":");
978
+ const merge = colonIdx > 0 ? items[colonIdx - 1]?.comp : void 0;
979
+ const assign = merge === "+_" ? "+_:" : merge === "+" ? "+,:" : ":";
980
+ const d = decl;
981
+ d._options = {
982
+ ...d._options ?? {},
983
+ assign
984
+ };
985
+ if (typeof d.name === "string" && d.name) {
986
+ const nameStr = d.name;
987
+ decl.name = nameStr.includes("@{") || nameStr.includes("${") ? getInterpolatedNode(nameStr, loc) : nameStr;
988
+ }
989
+ const dvRaw = decl.value;
990
+ if (dvRaw && typeof dvRaw === "object" && dvRaw.type === "Operation") {
991
+ const f = dvRaw;
992
+ if ((0, _jesscss_core.shouldOperateWithMathFrames)({
993
+ mathMode: this.mathMode,
994
+ parenFrames: [],
995
+ calcFrames: 0
996
+ }, f.operator, f.left, f.right)) decl.value = new _jesscss_core.Expression(dvRaw, { parens: true }, loc);
997
+ return decl;
998
+ }
999
+ const dvList = decl.value;
1000
+ if (dvList && dvList.type === "List" && dvList.options?.sep === "/" && this.mathMode === "always") {
1001
+ const items = dvList.value;
1002
+ if (items.length >= 2 && items.every((it) => this._isDivisionLike(it))) {
1003
+ let op = items[0];
1004
+ for (let i = 1; i < items.length; i++) op = new _jesscss_core.Operation([
1005
+ op,
1006
+ "/",
1007
+ items[i]
1008
+ ], void 0, loc);
1009
+ const f = op;
1010
+ decl.value = (0, _jesscss_core.shouldOperateWithMathFrames)({
1011
+ mathMode: this.mathMode,
1012
+ parenFrames: [],
1013
+ calcFrames: 0
1014
+ }, f.operator, f.left, f.right) ? new _jesscss_core.Expression(op, { parens: true }, loc) : op;
1015
+ return decl;
1016
+ }
1017
+ }
1018
+ const dv = decl.value;
1019
+ const dvIsArray = Array.isArray(dv);
1020
+ const dvHasNode = dvIsArray && dv.some((p) => !!p && typeof p === "object" && "type" in p);
1021
+ if (dvIsArray && dvHasNode) {
1022
+ const src = this._source.slice(loc.start, loc.end);
1023
+ const colonPos = src.indexOf(":");
1024
+ const rawVal = colonPos >= 0 ? src.slice(colonPos + 1).trim().replace(/;\s*$/, "").trim() : src;
1025
+ if (/^progid:/i.test(rawVal)) decl.value = this._buildLegacyMSFilter(rawVal, loc);
1026
+ }
1027
+ return decl;
1028
+ }
1029
+ /**
1030
+ * Port of `processLegacyMSFilterToken` (lessRecursiveParser.ts): a `progid:…`
1031
+ * filter value string → Interpolated(role=any) with `@var` runs templated out,
1032
+ * or a plain Keyword when the run has no variables.
1033
+ */
1034
+ _buildLegacyMSFilter(source, loc) {
1035
+ source = source.replace(/\s*=\s*/g, "=");
1036
+ const varRe = /@([_a-zA-Z\xA0-￿][-_a-zA-Z0-9\xA0-￿]*)/g;
1037
+ const matches = [...source.matchAll(varRe)];
1038
+ if (matches.length === 0) return this._lessKeyword(source, loc);
1039
+ return new _jesscss_core.Interpolated({
1040
+ source: source.replace(varRe, (_full, _name, offset, fullSource) => {
1041
+ const key = fullSource.slice(0, offset).match(/([A-Za-z]+)=$/)?.[1];
1042
+ if (key && /colorstr$/i.test(key)) return `"${_jesscss_core.INTERPOLATION_PLACEHOLDER}"`;
1043
+ return _jesscss_core.INTERPOLATION_PLACEHOLDER;
1044
+ }),
1045
+ replacements: matches.map((match) => createInterpolatedReference("@", match[1], loc))
1046
+ }, { role: "any" }, loc);
1047
+ }
1048
+ _buildAmpersand(children, loc) {
1049
+ const ls = children.filter((c) => c._tag === "leaf");
1050
+ if (!ls.some((l) => l.value === "(")) {
1051
+ const image = ls[0]?.value ?? "&";
1052
+ return new _jesscss_core.Ampersand(this._ampersandTemplateValue(image), {}, loc);
1053
+ }
1054
+ const trimmed = (ls.find((l) => l.value !== "&" && l.value !== "(" && l.value !== ")")?.value ?? "").trim();
1055
+ return new _jesscss_core.Ampersand(trimmed === "nil" ? "" : trimmed.replace(/^(['"])([\s\S]*)\1$/, "$2"), {}, loc);
1056
+ }
1057
+ /** The append value of a `&`-led ampersand token: the suffix after `&` (`&-bar` →
1058
+ * `-bar`, `&1` → `1`), or undefined for a bare `&`. */
1059
+ _ampersandTemplateValue(image) {
1060
+ return image === "&" ? void 0 : image.slice(1) || void 0;
1061
+ }
1062
+ /**
1063
+ * A single extend target inside `extend( … )`: a complex selector plus its
1064
+ * optional `all` / `!all` flag (selectors.ts `complexSelector`'s OPTION2 flag).
1065
+ * Produced as an `Extend` carrier the surrounding pseudo/statement groups.
1066
+ */
1067
+ _buildExtendTarget(_children, raw, loc) {
1068
+ const comps = (0, _jesscss_css_parser_jess.spannedComponents)(raw);
1069
+ const isFlag = (c) => typeof c === "string" && /^!?all$/.test(c);
1070
+ const flag = comps.some((c) => isFlag(c.comp)) ? _jesscss_core.ExtendFlag.All : _jesscss_core.ExtendFlag.Exact;
1071
+ const targetComp = comps.find((c) => !isFlag(c.comp))?.comp;
1072
+ return new _jesscss_core.Extend({
1073
+ target: typeof targetComp === "string" ? targetComp : targetComp ?? "&",
1074
+ flag
1075
+ }, {}, loc);
1076
+ }
1077
+ /**
1078
+ * `:extend( … )` pseudo form (selectors.ts `extend`): groups its ExtendTarget
1079
+ * children. Targets sharing one flag collapse to a single Extend whose target is
1080
+ * a SelectorList (or the lone selector); mixed flags stay as one Extend each,
1081
+ * returned in a List. Mirrors mergeExtends' target-and-flag grouping.
1082
+ */
1083
+ _buildExtendPseudo(children, loc) {
1084
+ const targets = nodeChildren(children).filter((n) => n.type === "Extend");
1085
+ const firstFlag = targets[0].flag;
1086
+ if (targets.every((t) => t.flag === firstFlag)) return new _jesscss_core.Extend({
1087
+ target: targets.length === 1 ? targets[0].target : this._makeSelectorList(targets.map((t) => t.target), loc),
1088
+ flag: firstFlag
1089
+ }, {}, loc);
1090
+ return new _jesscss_core.List(targets.map((t) => new _jesscss_core.Extend({
1091
+ target: t.target,
1092
+ flag: t.flag
1093
+ }, {}, loc)), {}, loc);
1094
+ }
1095
+ /**
1096
+ * `&:extend( … );` (or bare `:extend( … );`) statement form (selectors.ts
1097
+ * `ampersandExtend`). The ExtendPseudo child already carries the grouped
1098
+ * Extend(s); the leading `&` is just the statement marker.
1099
+ */
1100
+ _buildExtendStatement(children, _raw, _loc) {
1101
+ return nodeChildren(children).find((n) => n.type === "Extend" || n.type === "List");
1102
+ }
1103
+ /**
1104
+ * Decode a single `[key]` accessor into its Less lookup-key AST — the ONE shared
1105
+ * decoder for every accessor-chain builder path (var-decl value, declaration value,
1106
+ * at-rule prelude, namespace ref). Accepts either a raw authored key STRING (the
1107
+ * text inside the brackets, e.g. `@@foo`, `$@x`, `bar`, ``), or a SquareParen `Paren`
1108
+ * node whose inner content the grammar already parsed (a raw string or a `@var`
1109
+ * Reference). Returns the key exactly as the reference lookupOrCall production would:
1110
+ *
1111
+ * `[]` → -1 (index, empty)
1112
+ * `[@@name]` → Reference{type:variable,key:name} (dynamic variable lookup)
1113
+ * `[$@name]` / `[@$name]` → Quoted(Interpolated(@name)) (dynamic property lookup)
1114
+ * `[@name]` → 'name' (static variable lookup; bare string key)
1115
+ * `[$name]` → Quoted('name') (property lookup; `$` marker dropped)
1116
+ * `[name]` → Quoted('name') (index)
1117
+ *
1118
+ * Exactly one `@`/`$` marker is the lookup marker and is never kept.
1119
+ */
1120
+ _decodeAccessorKey(rawTextOrNode, loc) {
1121
+ let rawText;
1122
+ let innerVal;
1123
+ if (typeof rawTextOrNode === "string") rawText = rawTextOrNode.trim();
1124
+ else {
1125
+ innerVal = rawTextOrNode.node ?? rawTextOrNode.value;
1126
+ if (!innerVal || this._isEmptyKeywordLike(innerVal)) return -1;
1127
+ if (typeof innerVal === "string") rawText = innerVal.trim();
1128
+ else if (typeof innerVal === "object" && innerVal.type === "Reference" && typeof innerVal.key === "string") rawText = "@" + innerVal.key;
1129
+ else if (this._isKeywordLike(innerVal) && typeof innerVal.value === "string") rawText = innerVal.value.trim();
1130
+ }
1131
+ if (rawText === void 0 || rawText === "") {
1132
+ if (rawText === "") return -1;
1133
+ return innerVal?.key ?? innerVal;
1134
+ }
1135
+ if (rawText.startsWith("@@")) return new _jesscss_core.Reference(rawText.slice(2), { type: "variable" }, loc);
1136
+ if (rawText.startsWith("$@") || rawText.startsWith("@$")) return new _jesscss_core.Quoted(new _jesscss_core.Interpolated({
1137
+ source: _jesscss_core.INTERPOLATION_PLACEHOLDER,
1138
+ replacements: [new _jesscss_core.Reference(rawText.slice(2), { role: "ident" }, loc)]
1139
+ }, { role: "ident" }, loc), {}, loc);
1140
+ if (rawText.startsWith("@")) return rawText.slice(1);
1141
+ return new _jesscss_core.Quoted(rawText.replace(/^\$/, ""), {}, loc);
1142
+ }
1143
+ _assembleSegment(seg, loc) {
1144
+ const result = super._assembleSegment(seg, loc);
1145
+ const isNsNameEarly = (c) => typeof c === "string" && /^[#.]-?[_a-zA-Z]/.test(c.trim());
1146
+ if (!Array.isArray(result)) {
1147
+ if (isNsNameEarly(result)) {
1148
+ const segs = result.trim().match(/[#.][^#.]*/g) ?? [result.trim()];
1149
+ const nameKey = segs.length === 1 ? segs[0] : segs;
1150
+ const rawKey = segs.length > 1 ? segs.join("") : void 0;
1151
+ return new _jesscss_core.Reference({
1152
+ key: nameKey,
1153
+ ...rawKey ? { rawKey } : {}
1154
+ }, {
1155
+ type: "mixin-ruleset",
1156
+ role: "name"
1157
+ }, loc);
1158
+ }
1159
+ return result;
1160
+ }
1161
+ if (result.length < 2) return result;
1162
+ const comps = result;
1163
+ const isNsName = (c) => typeof c === "string" && /^[#.]-?[_a-zA-Z€-￿]/.test(c.trim());
1164
+ const isSquareParen = (c) => !!c && typeof c === "object" && c.type === "Paren" && c._options?.delimiter === "square";
1165
+ const isRoundParen = (c) => !!c && typeof c === "object" && c.type === "Paren" && c._options?.delimiter !== "square";
1166
+ if (!isNsName(comps[0])) return comps;
1167
+ const splitNsToken = (s) => s.match(/[#.][^#.]*/g) ?? [s];
1168
+ let i = 0;
1169
+ const pathSegs = [];
1170
+ while (i < comps.length && isNsName(comps[i])) {
1171
+ pathSegs.push(...splitNsToken(comps[i].trim()));
1172
+ i++;
1173
+ }
1174
+ const rawPathText = pathSegs.join("");
1175
+ const nameKey = pathSegs.length === 1 ? pathSegs[0] : pathSegs;
1176
+ const rawKey = pathSegs.length > 1 ? rawPathText : void 0;
1177
+ let base = new _jesscss_core.Reference({
1178
+ key: nameKey,
1179
+ ...rawKey ? { rawKey } : {}
1180
+ }, {
1181
+ type: "mixin-ruleset",
1182
+ role: "name"
1183
+ }, loc);
1184
+ if (i >= comps.length || !isSquareParen(comps[i]) && !isRoundParen(comps[i])) return i === comps.length ? base : comps;
1185
+ while (i < comps.length) {
1186
+ const item = comps[i];
1187
+ if (isSquareParen(item)) {
1188
+ const innerKey = this._decodeAccessorKey(item, loc);
1189
+ const accType = typeof innerKey === "string" || innerKey != null && typeof innerKey === "object" && innerKey.type === "Reference" ? "variable" : "index";
1190
+ base = new _jesscss_core.Reference({
1191
+ target: base,
1192
+ key: innerKey
1193
+ }, { type: accType }, loc);
1194
+ i++;
1195
+ } else if (isRoundParen(item)) {
1196
+ const innerContent = item.value ?? item.node;
1197
+ const argsNode = this._isEmptyKeywordLike(innerContent) ? null : this._parenToArgs(item, loc);
1198
+ const callPayload = { name: base };
1199
+ if (argsNode) callPayload.args = argsNode;
1200
+ base = new _jesscss_core.Call(callPayload, {}, loc);
1201
+ i++;
1202
+ } else break;
1203
+ }
1204
+ return i === comps.length ? base : comps;
1205
+ }
1206
+ _buildEscapedValue(children, loc) {
1207
+ const inner = nodeChildren(children)[0];
1208
+ if (!inner) return this._lessKeyword("", loc);
1209
+ if (inner instanceof _jesscss_core.Quoted) return new _jesscss_core.Quoted(inner.value, {
1210
+ quote: inner.quote,
1211
+ escaped: true
1212
+ }, loc);
1213
+ const n = inner;
1214
+ n._options = {
1215
+ ...n._options ?? {},
1216
+ escaped: true
1217
+ };
1218
+ return inner;
1219
+ }
1220
+ _buildInterpValue(raw, loc) {
1221
+ const result = getInterpolatedOrString((0, _jesscss_css_parser_jess.spannedComponents)(raw).map((i) => typeof i.comp === "string" ? i.comp : "").join(""), loc);
1222
+ if (typeof result === "string") return this._lessKeyword(result, loc);
1223
+ return result;
1224
+ }
1225
+ /**
1226
+ * A quoted string value. Unlike plain CSS, Less interpolates `@{var}` / `${prop}`
1227
+ * inside quoted (and escaped `~"…"`) strings and inside `@import` paths. When the
1228
+ * raw content holds an interpolation, split it into an `Interpolated` value the same
1229
+ * way the reference parser's `processStringInterpolation` does (source with
1230
+ * INTERPOLATION_PLACEHOLDER, `@var`/`$prop` references in `replacements`); otherwise
1231
+ * fall through to the plain CSS builder (bare-string value).
1232
+ */
1233
+ /**
1234
+ * The Less `Url` grammar tokenizes the inner string as bare leaves (no child
1235
+ * node), so the css base builder wraps a quoted url body in a raw-string
1236
+ * `Quoted` — which never interpolates `@{var}`/`${prop}`. Less 4.x DOES resolve
1237
+ * interpolation inside a QUOTED url body (`url("@{base}/@{i}.svg")`), the same as
1238
+ * any other quoted string, so route the quoted inner through the same
1239
+ * interpolation-aware construction `_buildQuoted` uses. Unquoted url bodies stay
1240
+ * verbatim (Less 4.x leaves `url(@{x})` literal), as does a quoted body with no
1241
+ * interpolation.
1242
+ */
1243
+ _buildUrl(children, loc) {
1244
+ if (nodeChildren(children)[0]) return super._buildUrl(children, loc);
1245
+ const inner = children.filter((c) => c._tag === "leaf").filter((l) => !/^url\($/i.test(l.value) && l.value !== ")").map((l) => l.value).join("").trim();
1246
+ const quote = inner[0];
1247
+ if ((quote === "\"" || quote === "'") && inner.at(-1) === quote) {
1248
+ const body = inner.slice(1, -1);
1249
+ if (body.includes("@{") || body.includes("${")) return new _jesscss_core.Url(new _jesscss_core.Quoted(this._buildStringInterpolation(body, loc), { quote }, loc), void 0, loc);
1250
+ }
1251
+ return super._buildUrl(children, loc);
1252
+ }
1253
+ _buildQuoted(children, loc) {
1254
+ const text = children.filter((c) => c._tag === "leaf").map((l) => l.value).join("");
1255
+ const inner = text.slice(1, -1);
1256
+ if (inner.includes("@{") || inner.includes("${")) return new _jesscss_core.Quoted(this._buildStringInterpolation(inner, loc), { quote: text[0] }, loc);
1257
+ return super._buildQuoted(children, loc);
1258
+ }
1259
+ /**
1260
+ * Build an escaped `~'…'` Quoted (at-rule prelude position), interpolating any
1261
+ * `@{var}` / `${prop}` in its body the same way `_buildQuoted` does — so
1262
+ * `~'@{a} / @{b}'` renders its substituted values instead of literal text.
1263
+ */
1264
+ _buildEscapedQuoted(inner, quote, loc) {
1265
+ return new _jesscss_core.Quoted(inner.includes("@{") || inner.includes("${") ? this._buildStringInterpolation(inner, loc) : inner, {
1266
+ quote,
1267
+ escaped: true
1268
+ }, loc);
1269
+ }
1270
+ /**
1271
+ * Split a quoted-string body on `@{…}` / `${…}` interpolations into an
1272
+ * `Interpolated` (source + reference replacements). Port of the reference parser's
1273
+ * `processStringInterpolation`/`findInterpolations` (productions/values.ts): brace
1274
+ * matching is nesting-aware, and a nested-interpolated name resolves through a
1275
+ * variable Reference wrapped in an Expression.
1276
+ */
1277
+ _buildStringInterpolation(value, loc) {
1278
+ const matches = this._findInterpolations(value);
1279
+ const replacements = [];
1280
+ let source = value;
1281
+ let offset = 0;
1282
+ for (const match of matches) {
1283
+ const adjustedStart = match.start - offset;
1284
+ const adjustedEnd = match.end - offset;
1285
+ source = source.slice(0, adjustedStart) + _jesscss_core.INTERPOLATION_PLACEHOLDER + source.slice(adjustedEnd);
1286
+ offset += match.end - match.start - _jesscss_core.INTERPOLATION_PLACEHOLDER.length;
1287
+ if (match.content.includes("@{") || match.content.includes("${")) {
1288
+ const nestedRef = new _jesscss_core.Reference({ key: this._buildStringInterpolation(match.content, loc) }, {
1289
+ type: "variable",
1290
+ role: "ident"
1291
+ }, loc);
1292
+ replacements.push(new _jesscss_core.Expression(nestedRef, void 0, loc));
1293
+ } else replacements.push(createInterpolatedReference(match.prefix, match.content, loc));
1294
+ }
1295
+ return new _jesscss_core.Interpolated({
1296
+ source,
1297
+ replacements
1298
+ }, { role: "ident" }, loc);
1299
+ }
1300
+ /**
1301
+ * Locate `@{…}` / `${…}` interpolation runs in a string, counting nested braces so
1302
+ * `@{@{x}}` and `@{fn(a, b)}` are matched whole. Returns start/end/prefix/content.
1303
+ */
1304
+ _findInterpolations(value) {
1305
+ const matches = [];
1306
+ let i = 0;
1307
+ while (i < value.length) if ((value[i] === "@" || value[i] === "$") && value[i + 1] === "{") {
1308
+ const prefix = value[i];
1309
+ const start = i;
1310
+ i += 2;
1311
+ let braceCount = 1;
1312
+ const contentStart = i;
1313
+ while (i < value.length && braceCount > 0) {
1314
+ if (value[i] === "{") braceCount++;
1315
+ else if (value[i] === "}") braceCount--;
1316
+ i++;
1317
+ }
1318
+ if (braceCount === 0) matches.push({
1319
+ start,
1320
+ end: i,
1321
+ prefix,
1322
+ content: value.slice(contentStart, i - 1)
1323
+ });
1324
+ } else i++;
1325
+ return matches;
1326
+ }
1327
+ _buildCall(rawChildren, loc) {
1328
+ const call = super._buildCall(rawChildren, loc);
1329
+ const key = typeof call.name === "string" ? call.name : "";
1330
+ this._checkMixedArgDelimiters(call.args, "function", loc);
1331
+ const loweredArgs = this._lowerSemiArgs(call.args, loc);
1332
+ return new _jesscss_core.Call({
1333
+ name: new _jesscss_core.Reference(key, {
1334
+ type: "function",
1335
+ fallbackValue: true
1336
+ }, loc),
1337
+ args: loweredArgs
1338
+ }, { silentFail: true }, loc);
1339
+ }
1340
+ _buildLessCustomDecl(children, loc) {
1341
+ const ls = children.filter((c) => c._tag === "leaf");
1342
+ const propNameText = ls[0]?.value ?? "";
1343
+ const name = propNameText.includes("@") || propNameText.includes("$") ? getInterpolatedNode(propNameText, loc) : propNameText;
1344
+ const valueNodes = nodeChildren(children);
1345
+ if (valueNodes.length > 0) return new _jesscss_core.CustomDeclaration({
1346
+ name,
1347
+ value: valueNodes.length === 1 ? valueNodes[0] : valueNodes
1348
+ }, void 0, loc);
1349
+ const valueText = ls.slice(2).filter((l) => l.value !== ";").map((l) => l.value).join("").trim();
1350
+ return new _jesscss_core.CustomDeclaration({
1351
+ name,
1352
+ value: this._lessKeyword(valueText, loc)
1353
+ }, void 0, loc);
1354
+ }
1355
+ /**
1356
+ * `--foo: { color: @a; }` — a curly-brace custom-property value whose body
1357
+ * opportunistically structured as a declaration list (customCurlyBlock in the
1358
+ * grammar), so nested `@var`/calls evaluate normally instead of staying opaque
1359
+ * text. Wrapped in a Block(type: 'curly') so `{`/`}` re-render around it.
1360
+ */
1361
+ _buildLessCustomBlock(children, loc) {
1362
+ return new _jesscss_core.Block(new _jesscss_core.Sequence(nodeChildren(children), void 0, loc), { type: "curly" }, loc);
1363
+ }
1364
+ _warnDeprecatedValue(span) {
1365
+ const text = this._source.slice(span.start, span.end);
1366
+ if (/\d\s*\.\//.test(text)) this._warn("The ./ operator is deprecated and will be removed.", "dot-slash-operator");
1367
+ }
1368
+ _warnCustomPropVars(span) {
1369
+ const text = this._source.slice(span.start, span.end);
1370
+ const colon = text.indexOf(":");
1371
+ const value = colon >= 0 ? text.slice(colon + 1) : text;
1372
+ const at = value.match(/@[a-zA-Z][\w-]*/);
1373
+ if (at && !value.includes("@{")) this._warn(`"${at[0]}" in custom property values is treated as literal text. Use @{${at[0].slice(1)}} for interpolation.`, "variable-in-unknown-value");
1374
+ const dollar = value.match(/\$[a-zA-Z][\w-]*/);
1375
+ if (dollar && !value.includes("${")) this._warn(`"${dollar[0]}" in custom property values is treated as literal text. Use \${${dollar[0].slice(1)}} for interpolation.`, "property-in-unknown-value");
1376
+ }
1377
+ _warnAtRulePreludeVars(span) {
1378
+ const text = this._source.slice(span.start, span.end);
1379
+ const varName = this._firstTopLevelBareAtVar(text);
1380
+ if (varName !== null) this._warn(`A bare "@${varName}" in an at-rule prelude is deprecated. Use @{${varName}} interpolation instead.`, "variable-in-at-rule-prelude");
1381
+ }
1382
+ /**
1383
+ * The first bare `@ident` reference in an at-rule prelude that is deprecated
1384
+ * under Less 4.x PR #4462 (`variable-in-at-rule-prelude`), or null when there
1385
+ * is none. A bare `@var` in a *structural* (top-level) prelude position still
1386
+ * resolves but is deprecated in favour of `@{var}` interpolation; the scan
1387
+ * therefore ignores, mirroring `hasTopLevelBareVariable` / `warnBareAtRuleVariable`:
1388
+ * - the leading at-rule name itself (`@media`, `@-moz-document`, …);
1389
+ * - `@{ident}` interpolation — the supported migration target;
1390
+ * - a `@var` inside `(...)` — a declaration/feature value (e.g. the `@size`
1391
+ * in `@media (min-width: @size)`), which stays valid;
1392
+ * - `@`/`(` characters inside string literals, which are not structural.
1393
+ */
1394
+ _firstTopLevelBareAtVar(text) {
1395
+ let depth = 0;
1396
+ let i = /^\s*@-?[\w-]+/.exec(text)?.[0].length ?? 0;
1397
+ for (; i < text.length; i++) {
1398
+ const c = text[i];
1399
+ if (c === "\"" || c === "'") {
1400
+ i++;
1401
+ while (i < text.length && text[i] !== c) i++;
1402
+ continue;
1403
+ }
1404
+ if (c === "@") {
1405
+ if (text[i + 1] === "{") {
1406
+ i += 2;
1407
+ while (i < text.length && text[i] !== "}") i++;
1408
+ continue;
1409
+ }
1410
+ if (depth === 0) {
1411
+ const m = /^@(-?[a-zA-Z\x80-￿][\w-]*)/.exec(text.slice(i));
1412
+ if (m) return m[1];
1413
+ }
1414
+ continue;
1415
+ }
1416
+ if (c === "{") break;
1417
+ if (c === "(") depth++;
1418
+ else if (c === ")" && depth > 0) depth--;
1419
+ }
1420
+ return null;
1421
+ }
1422
+ _buildMixinCall(children, raw, loc) {
1423
+ const ls = children.filter((c) => c._tag === "leaf");
1424
+ const markImportant = ls.some((l) => l.value === "!");
1425
+ const nameParts = [];
1426
+ for (const l of ls) {
1427
+ if (l.value === "(" || l.value === ";" || l.value === "!") break;
1428
+ nameParts.push(l.value);
1429
+ }
1430
+ const name = nameParts.join("");
1431
+ const argsList = nodeChildren(children).find((n) => n.type === "List");
1432
+ const hasArgs = argsList && argsList.value?.length;
1433
+ if (argsList === void 0) this._warn("Calling a mixin without parentheses is deprecated", "mixin-call-no-parens");
1434
+ else {
1435
+ const src = this._source.slice(loc.start, loc.end);
1436
+ if (/^\S+\s+\(/.test(src)) this._warn("Whitespace between a mixin name and parentheses is deprecated", "mixin-call-whitespace");
1437
+ }
1438
+ return new _jesscss_core.Call({
1439
+ name: new _jesscss_core.Reference({ key: name }, {
1440
+ type: "mixin-ruleset",
1441
+ role: "name"
1442
+ }, loc),
1443
+ args: hasArgs ? this._convertArgsForCall(argsList, loc) : void 0
1444
+ }, { markImportant }, loc);
1445
+ }
1446
+ /**
1447
+ * `@name(...)` (no `:`) → a detached-ruleset variable CALL. Faithful port of
1448
+ * `varDeclarationOrCall`'s LParen branch (selectors.ts): build a `Reference`
1449
+ * over the var name (`type: 'variable', role: 'name'`), wrap in a `Call` with
1450
+ * the (optional) args, and wrap THAT in an `Expression` (a top-level variable
1451
+ * call is an expression, not a parenthesized one). `!important` sets
1452
+ * `markImportant` on the Call, mirroring the production.
1453
+ */
1454
+ _buildVarCall(children, raw, loc) {
1455
+ const ls = children.filter((c) => c._tag === "leaf");
1456
+ const markImportant = ls.some((l) => l.value === "!");
1457
+ const rawName = ls.find((l) => l.value.startsWith("@"))?.value ?? "";
1458
+ const name = rawName.startsWith("@") ? rawName.slice(1) : rawName;
1459
+ const nameRef = new _jesscss_core.Reference({ key: this._lessKeyword(name, loc) }, {
1460
+ type: "variable",
1461
+ role: "name"
1462
+ }, loc);
1463
+ const argsList = nodeChildren(children).find((n) => n.type === "List");
1464
+ const hasArgs = argsList && argsList.value?.length;
1465
+ if (!hasArgs && KNOWN_AT_RULE_VAR_NAME_RE.test(name)) this._warn("Using known at-rule names as variables is deprecated", "at-rule-variable");
1466
+ return new _jesscss_core.Expression(new _jesscss_core.Call({
1467
+ name: nameRef,
1468
+ args: hasArgs ? this._convertArgsForCall(argsList, loc) : void 0
1469
+ }, markImportant ? { markImportant: true } : void 0, loc), void 0, loc);
1470
+ }
1471
+ /** `...` or `@name...` variadic arg → `Rest`. Definition-shape (string name); a
1472
+ * CALL turns it into `Rest(Reference)` via `_convertArgsForCall`. */
1473
+ _buildRest(raw, loc) {
1474
+ const nameItem = (0, _jesscss_css_parser_jess.spannedComponents)(raw).find((i) => typeof i.comp === "string" && i.comp.startsWith("@"));
1475
+ return new _jesscss_core.Rest(nameItem ? String(nameItem.comp).slice(1) : "", {}, loc);
1476
+ }
1477
+ /** `@name: value` named arg/param → `VarDeclaration`. The value is assembled by the
1478
+ * shared value builder (`_assembleValue`) — the same machinery as a declaration
1479
+ * value, so trivia and Keyword-ification are handled and no manual trimming is
1480
+ * needed. Named args flow through function calls too; the runtime decides whether
1481
+ * the target accepts them. */
1482
+ _buildNamedArg(raw, loc) {
1483
+ const items = (0, _jesscss_css_parser_jess.spannedComponents)(raw);
1484
+ const colonIdx = items.findIndex((i) => i.comp === ":");
1485
+ const nameItem = items.find((i) => typeof i.comp === "string" && i.comp.startsWith("@"));
1486
+ const name = String(nameItem?.comp ?? "").slice(1);
1487
+ const valueItems = colonIdx >= 0 ? items.slice(colonIdx + 1) : [];
1488
+ const { value } = this._assembleValue(valueItems, loc);
1489
+ let paramValue;
1490
+ if (typeof value === "string") paramValue = this._valueKeyword(value, loc);
1491
+ else if (Array.isArray(value)) paramValue = new _jesscss_core.Sequence(value.map((c) => this._argComponent(c, loc)), void 0, loc);
1492
+ else paramValue = value;
1493
+ return new _jesscss_core.VarDeclaration({
1494
+ name,
1495
+ value: paramValue
1496
+ }, {}, loc);
1497
+ }
1498
+ /** Mixin-call args are assembled by the SAME builder as function-call args
1499
+ * (`_assembleArgs` via `_betweenParens`) — identical comma/semicolon and value
1500
+ * handling. Named args are `VarDeclaration`s and variadic args `Rest`, which pass
1501
+ * through as single components. A bare `@name` is a `Reference` (the call shape);
1502
+ * the mixin-DEFINITION builder reinterprets a lone `@name` as a param. */
1503
+ _buildMixinArgs(raw, loc) {
1504
+ const inner = this._betweenParens((0, _jesscss_css_parser_jess.spannedComponents)(raw));
1505
+ const args = this._assembleArgs(inner, loc);
1506
+ this._checkMixedArgDelimiters(args, "mixin", loc);
1507
+ return this._lowerSemiArgs(args, loc);
1508
+ }
1509
+ /** Less forbids mixing the COMMA and SEMICOLON argument separators: once a
1510
+ * semicolon separates args, a comma is a value-list separator, so a semicolon-group
1511
+ * may not hold 2+ named params (`@a: 1, @b: 2`). `_assembleArgs` renders such a group
1512
+ * as a List of ≥2 VarDeclarations. (This is purely about the `,` vs `;` argument
1513
+ * separators — a `/` inside a value is unrelated and never checked.) Applies to BOTH
1514
+ * mixin and function calls (args are unified). */
1515
+ _checkMixedArgDelimiters(args, kind, loc) {
1516
+ const list = args;
1517
+ if (list?.type !== "List" || list.options?.sep !== ";" || !Array.isArray(list.value)) return;
1518
+ for (const el of list.value) {
1519
+ const group = el;
1520
+ if (group?.type === "List" && Array.isArray(group.value) && group.value.filter((n) => n?.type === "VarDeclaration").length >= 2) {
1521
+ this._error(`Cannot mix ; and , as delimiter types in ${kind} arguments`, loc.start);
1522
+ break;
1523
+ }
1524
+ }
1525
+ }
1526
+ /**
1527
+ * Lower Less `;`-separated call args to the unified Jess representation: the outer
1528
+ * args `List{ sep: ';' }` becomes comma-separated, and each element that is itself
1529
+ * a comma-`List` (a `;`-group that held a comma-list) is wrapped in an escaped
1530
+ * `Paren` — the same shape Jess authors write as `~(1, 2)`. So
1531
+ * `.mixin(1, 2; 3, 4)` and Jess `mixin(~(1, 2), ~(3, 4))` converge on one AST.
1532
+ *
1533
+ * The escaped `Paren` evaluates to its inner value STRIPPED (paren.ts §escaped),
1534
+ * so `~(1, 2)` binds/renders identically to the bare list `1, 2` — representation
1535
+ * only, semantics unchanged. Scalar (non-List) elements pass through untouched.
1536
+ *
1537
+ * MUST run AFTER `_checkMixedArgDelimiters` (which inspects the `;`-List).
1538
+ */
1539
+ _lowerSemiArgs(args, loc) {
1540
+ const list = args;
1541
+ if (!list || list.type !== "List" || list.options?.sep !== ";" || !Array.isArray(list.value)) return args;
1542
+ return new _jesscss_core.List(list.value.map((el) => {
1543
+ if (el?.type === "List") return new _jesscss_core.Paren(el, { escaped: true }, loc);
1544
+ return el;
1545
+ }), void 0, loc);
1546
+ }
1547
+ /**
1548
+ * Mixin-DEFINITION param conversion. With combinator-composed args a bare `@name`
1549
+ * value parses as a `Reference{variable}` (the CALL shape); in a DEFINITION it is a
1550
+ * param, so convert it to `VarDeclaration(name, Nil)`. Named params (`@a: 1`),
1551
+ * variadic (`Rest`) and pattern-match values stay as-is. Returns a NEW List (the
1552
+ * def/call split must not mutate a shared node).
1553
+ */
1554
+ _convertArgsForDefinition(argsList, loc) {
1555
+ if (!argsList || argsList.type !== "List") return argsList;
1556
+ const list = argsList;
1557
+ const value = list.value;
1558
+ if (!value || value.length === 0) return argsList;
1559
+ let changed = false;
1560
+ const converted = value.map((node) => {
1561
+ if (node.type === "Reference" && node.options?.type === "variable") {
1562
+ const key = node.key;
1563
+ const name = typeof key === "string" ? key : String(key?.valueOf?.() ?? "");
1564
+ changed = true;
1565
+ return new _jesscss_core.VarDeclaration({
1566
+ name,
1567
+ value: new _jesscss_core.Nil("", {}, loc)
1568
+ }, {}, loc);
1569
+ }
1570
+ return node;
1571
+ });
1572
+ if (!changed) return argsList;
1573
+ return new _jesscss_core.List(converted, list.options, loc);
1574
+ }
1575
+ /**
1576
+ * Mixin-CALL argument conversion (reference `convertArgsForCall`, root.ts).
1577
+ * `_buildMixinArgs` builds bare `@name` args as definition-style VarDeclarations
1578
+ * (Nil value) — correct for a DEFINITION param, but in a CALL a bare `@name` is a
1579
+ * variable being PASSED, i.e. a `Reference{type:variable}`. Named args (`@a: 1`)
1580
+ * and value args stay as-is; a `Rest('name')` becomes `Rest(Reference{variable})`.
1581
+ * Returns a NEW List (the def/call split must not mutate a shared node).
1582
+ */
1583
+ _convertArgsForCall(argsList, loc) {
1584
+ if (!argsList || argsList.type !== "List") return argsList;
1585
+ const list = argsList;
1586
+ const value = list.value;
1587
+ if (!value || value.length === 0) return argsList;
1588
+ let changed = false;
1589
+ const converted = value.map((node) => {
1590
+ if (node.type === "VarDeclaration") {
1591
+ const decl = node;
1592
+ const val = decl.value;
1593
+ if (!val || val.type === "Nil") {
1594
+ const key = decl.name.valueOf();
1595
+ changed = true;
1596
+ return new _jesscss_core.Reference({ key }, { type: "variable" }, loc);
1597
+ }
1598
+ return node;
1599
+ }
1600
+ if (node.type === "Rest") {
1601
+ const restVal = node.value;
1602
+ if (typeof restVal === "string") {
1603
+ changed = true;
1604
+ return new _jesscss_core.Rest(new _jesscss_core.Reference({ key: restVal }, { type: "variable" }, loc), {}, loc);
1605
+ }
1606
+ return node;
1607
+ }
1608
+ return node;
1609
+ });
1610
+ if (!changed) return argsList;
1611
+ return new _jesscss_core.List(converted, list.options, loc);
1612
+ }
1613
+ _buildAnonMixin(children, loc) {
1614
+ const nodes = nodeChildren(children);
1615
+ const argsList = nodes.find((n) => n.type === "List");
1616
+ const rules = nodes.filter((n) => n !== argsList);
1617
+ const defParams = this._convertArgsForDefinition(argsList, loc);
1618
+ return new _jesscss_core.Mixin({
1619
+ params: defParams?.value?.length ? defParams : void 0,
1620
+ rules
1621
+ }, void 0, loc);
1622
+ }
1623
+ /**
1624
+ * `each(<iterable>, { … })` → a `For` control node (the $for shape), not a Call.
1625
+ * The value(s) before the comma are the iterable; the callback block's body becomes
1626
+ * the loop rules. A literal block callback carries no captured params here, so the
1627
+ * pattern defaults to the Less `[value, key, index]` triple.
1628
+ */
1629
+ /** A bare detached ruleset `{ … }` in value / argument position → a Mixin holding
1630
+ * its rules (same shape `@var: { … }` produces in `_buildVarDeclaration`). */
1631
+ _buildDetachedRuleset(children, loc) {
1632
+ return new _jesscss_core.Mixin({ rules: nodeChildren(children) }, {}, loc);
1633
+ }
1634
+ _buildEachFor(children, loc) {
1635
+ const nodes = nodeChildren(children);
1636
+ const callback = nodes.find((n) => n.type === "Mixin");
1637
+ const iterableNodes = nodes.filter((n) => n !== callback);
1638
+ const paramsList = (callback?.params)?.type === "List" ? callback.params : void 0;
1639
+ const ruleNodes = callback?.rules ?? [];
1640
+ const iterable = iterableNodes.length === 1 ? iterableNodes[0] : new _jesscss_core.List(iterableNodes, void 0, loc);
1641
+ return new _jesscss_core.For({
1642
+ pattern: this._eachPattern(paramsList, loc),
1643
+ iterable: {
1644
+ kind: "node",
1645
+ value: iterable
1646
+ },
1647
+ rules: ruleNodes
1648
+ }, void 0, loc);
1649
+ }
1650
+ _eachPattern(paramsList, loc) {
1651
+ const params = (paramsList?.value ?? []).filter((p) => p?.type === "VarDeclaration");
1652
+ if (params.length === 1) return {
1653
+ kind: "single",
1654
+ value: params[0]
1655
+ };
1656
+ if (params.length >= 2) return {
1657
+ kind: "tuple",
1658
+ values: [params[0], ...params.slice(1)]
1659
+ };
1660
+ const paramVar = (name) => new _jesscss_core.VarDeclaration({
1661
+ name,
1662
+ value: this._lessKeyword("", loc)
1663
+ }, { paramVar: true }, loc);
1664
+ return {
1665
+ kind: "tuple",
1666
+ values: [
1667
+ paramVar("value"),
1668
+ paramVar("key"),
1669
+ paramVar("index")
1670
+ ]
1671
+ };
1672
+ }
1673
+ _buildMixinOrQualified(children, loc) {
1674
+ const ls = children.filter((c) => c._tag === "leaf");
1675
+ const nodes = nodeChildren(children);
1676
+ const hasBlock = ls.some((l) => l.value === "{");
1677
+ const markImportant = ls.some((l) => l.value === "!");
1678
+ const nameParts = [];
1679
+ for (const l of ls) {
1680
+ if (l.value === "(" || l.value === "{" || l.value === "}" || l.value === ";" || l.value === ")" || l.value === "!") break;
1681
+ nameParts.push(l.value);
1682
+ }
1683
+ const name = nameParts.join("");
1684
+ const argsList = nodes.find((n) => n.type === "List");
1685
+ const guard = nodes.find((n) => n.type === "Paren" || n.type === "Condition" || n.type === "DefaultGuard");
1686
+ const hasExplicitParens = argsList !== void 0;
1687
+ if (hasBlock) {
1688
+ const rawRuleNodes = nodes.filter((n) => n !== argsList && n !== guard);
1689
+ const braceIdx = this._source.indexOf("{", loc.start);
1690
+ const bodyStart = braceIdx >= 0 ? braceIdx + 1 : loc.start;
1691
+ const closeIdx = this._source.lastIndexOf("}", loc.end - 1);
1692
+ const bodyEnd = closeIdx >= bodyStart ? closeIdx : loc.end;
1693
+ const ruleNodes = this._liftStandaloneComments(rawRuleNodes, bodyStart, bodyEnd, loc);
1694
+ if (hasExplicitParens) {
1695
+ const hasDefault = (guard !== void 0 ? guard.toTrimmedString?.() ?? "" : "").includes("default");
1696
+ const defParams = this._convertArgsForDefinition(argsList, loc);
1697
+ return new _jesscss_core.Mixin({
1698
+ name,
1699
+ params: defParams?.value?.length ? defParams : void 0,
1700
+ rules: ruleNodes,
1701
+ guard
1702
+ }, { hasDefault: !!hasDefault }, loc);
1703
+ }
1704
+ const { cleanedSelector, extractedExtends } = this._extractExtendsFromSelectorText(name || "&", loc);
1705
+ const finalRules = extractedExtends.length > 0 ? [...extractedExtends, ...ruleNodes] : ruleNodes;
1706
+ return new _jesscss_core.Ruleset({
1707
+ selector: cleanedSelector || "&",
1708
+ rules: finalRules,
1709
+ guard
1710
+ }, void 0, loc);
1711
+ }
1712
+ const combinatorValues = new Set([
1713
+ ">",
1714
+ "+",
1715
+ "~"
1716
+ ]);
1717
+ const selectorTokens = nameParts.filter((p) => !combinatorValues.has(p.trim()) && p.trim() !== "");
1718
+ const hasComplexPath = selectorTokens.length > 1;
1719
+ const refKey = hasComplexPath ? selectorTokens : name;
1720
+ const rawKey = hasComplexPath ? new _jesscss_core.ComplexSelector(nameParts, void 0, loc) : void 0;
1721
+ const ref = name.startsWith(".") || name.startsWith("#") ? new _jesscss_core.Reference({
1722
+ key: refKey,
1723
+ ...rawKey ? { rawKey } : {}
1724
+ }, {
1725
+ type: "mixin-ruleset",
1726
+ role: "name"
1727
+ }, loc) : new _jesscss_core.Reference({ key: refKey }, {
1728
+ type: "function",
1729
+ silentFail: true,
1730
+ fallbackValue: true
1731
+ }, loc);
1732
+ const hasArgs2 = argsList && argsList.value?.length;
1733
+ const hasSemi = ls.some((l) => l.value === ";");
1734
+ if (hasSemi && !hasExplicitParens) this._warn("Calling a mixin without parentheses is deprecated", "mixin-call-no-parens");
1735
+ else if (hasSemi && hasExplicitParens) {
1736
+ const src = this._source.slice(loc.start, loc.end);
1737
+ if (/^\S+\s+\(/.test(src)) this._warn("Whitespace between a mixin name and parentheses is deprecated", "mixin-call-whitespace");
1738
+ }
1739
+ return new _jesscss_core.Call({
1740
+ name: ref,
1741
+ args: hasArgs2 ? this._convertArgsForCall(argsList, loc) : void 0
1742
+ }, { markImportant }, loc);
1743
+ }
1744
+ _extractExtendsFromSelectorText(selectorText, _loc) {
1745
+ return {
1746
+ cleanedSelector: selectorText,
1747
+ extractedExtends: []
1748
+ };
1749
+ }
1750
+ _selectorHasNestedExtend(sel) {
1751
+ if (!sel || typeof sel === "string") return false;
1752
+ if (sel.type === "PseudoSelector") {
1753
+ const arg = sel.arg;
1754
+ return arg ? this._treeHasExtend(arg) : false;
1755
+ }
1756
+ if (sel instanceof _jesscss_core.CompoundSelector || sel instanceof _jesscss_core.ComplexSelector) return sel.value.some((p) => this._selectorHasNestedExtend(p));
1757
+ if ((0, _jesscss_core.isSelectorListLike)(sel)) return (0, _jesscss_core.selectorListItems)(sel).some((p) => this._selectorHasNestedExtend(p));
1758
+ return false;
1759
+ }
1760
+ _treeHasExtend(node) {
1761
+ if (node instanceof _jesscss_core.Extend) return true;
1762
+ if (node instanceof _jesscss_core.CompoundSelector || node instanceof _jesscss_core.ComplexSelector) return node.value.some((p) => this._treeHasExtend(p));
1763
+ if ((0, _jesscss_core.isSelectorListLike)(node)) return (0, _jesscss_core.selectorListItems)(node).some((p) => this._treeHasExtend(p));
1764
+ return false;
1765
+ }
1766
+ /**
1767
+ * A guarded ruleset (`sel when …`) is parsed by the shared CSS builder, which
1768
+ * has no `when` concept, so the Guard CST child folds into the body as the
1769
+ * first rule — always a Paren/Condition/DefaultGuard. Lift it into the
1770
+ * ruleset's `guard` field (rebuilt through the canonical Ruleset ctor so the
1771
+ * guard is adopted) so it gates output instead of rendering as a `{ true }`
1772
+ * body. Non-guarded rulesets never begin their body with one of these node
1773
+ * types, so the leading-node check is unambiguous.
1774
+ */
1775
+ _liftRulesetGuard(base, loc) {
1776
+ const rules = base.rules;
1777
+ if (!Array.isArray(rules) || rules.length === 0) return base;
1778
+ const first = rules[0];
1779
+ if (first?.type !== "Paren" && first?.type !== "Condition" && first?.type !== "DefaultGuard") return base;
1780
+ return new _jesscss_core.Ruleset({
1781
+ selector: base.selector,
1782
+ rules: rules.slice(1),
1783
+ guard: first
1784
+ }, void 0, loc);
1785
+ }
1786
+ _buildRuleset(children, rawChildren, loc) {
1787
+ let base = super._buildRuleset(children, rawChildren, loc);
1788
+ const selector = base.selector;
1789
+ if (!selector) return base;
1790
+ base = this._liftRulesetGuard(base, loc);
1791
+ if (typeof selector === "string") return base;
1792
+ if (this._selectorHasNestedExtend(selector)) this._error(":extend() is not allowed inside a pseudo-class selector", loc.start);
1793
+ const baseRules = Array.isArray(base.rules) ? base.rules : [];
1794
+ const baseGuard = base.guard;
1795
+ const withGuard = (rs) => {
1796
+ if (baseGuard !== void 0) rs.guard = baseGuard;
1797
+ return rs;
1798
+ };
1799
+ const extendKey = (e) => {
1800
+ const ext = e;
1801
+ return `${String(ext.target?.valueOf?.() ?? ext.target)}:${ext.flag}`;
1802
+ };
1803
+ if (!(0, _jesscss_core.isSelectorListLike)(selector)) {
1804
+ const { cleanedSelector, extractedExtends } = this._extractExtendsFromSelector(selector, loc);
1805
+ if (extractedExtends.length === 0) return base;
1806
+ return withGuard(new _jesscss_core.Ruleset({
1807
+ selector: cleanedSelector,
1808
+ rules: [...extractedExtends, ...baseRules]
1809
+ }, void 0, loc));
1810
+ }
1811
+ const perSelector = [];
1812
+ let anyExtends = false;
1813
+ for (const item of (0, _jesscss_core.selectorListItems)(selector)) {
1814
+ const { cleanedSelector: cs, extractedExtends: ee } = this._extractExtendsFromSelector(item, loc);
1815
+ perSelector.push({
1816
+ clean: cs,
1817
+ extends: ee
1818
+ });
1819
+ if (ee.length > 0) anyExtends = true;
1820
+ }
1821
+ if (!anyExtends) return base;
1822
+ const allExtendKeys = perSelector.map((s) => s.extends.map(extendKey).sort().join("|"));
1823
+ if (allExtendKeys.every((k) => k === allExtendKeys[0])) {
1824
+ const uniqueExtends = perSelector[0].extends;
1825
+ const cleanedItems = perSelector.map((s) => s.clean).filter((c) => c !== void 0);
1826
+ return withGuard(new _jesscss_core.Ruleset({
1827
+ selector: cleanedItems.length === 1 ? cleanedItems[0] : this._makeSelectorList(cleanedItems, loc),
1828
+ rules: [...uniqueExtends, ...baseRules]
1829
+ }, void 0, loc));
1830
+ }
1831
+ const wrapperRules = [];
1832
+ const cleanedItems = [];
1833
+ for (const { clean, extends: exts } of perSelector) {
1834
+ for (const ext of exts) {
1835
+ const extNode = ext;
1836
+ wrapperRules.push(new _jesscss_core.Extend({
1837
+ target: extNode.target,
1838
+ flag: extNode.flag,
1839
+ selector: clean
1840
+ }, {}, loc));
1841
+ }
1842
+ if (clean !== void 0) cleanedItems.push(clean);
1843
+ }
1844
+ const combinedSel = cleanedItems.length === 1 ? cleanedItems[0] : this._makeSelectorList(cleanedItems, loc);
1845
+ wrapperRules.push(withGuard(new _jesscss_core.Ruleset({
1846
+ selector: combinedSel,
1847
+ rules: baseRules
1848
+ }, void 0, loc)));
1849
+ return new _jesscss_core.Rules(wrapperRules, void 0, loc);
1850
+ }
1851
+ _extractExtendsFromSelector(selector, loc) {
1852
+ if (!selector || typeof selector === "string") return {
1853
+ cleanedSelector: selector,
1854
+ extractedExtends: []
1855
+ };
1856
+ if (selector instanceof _jesscss_core.CompoundSelector) {
1857
+ const extractedExtends = [];
1858
+ const newParts = [];
1859
+ for (const part of selector.value) if (part instanceof _jesscss_core.Extend) extractedExtends.push(part);
1860
+ else if (part instanceof _jesscss_core.List) for (const item of part.value ?? []) if (item instanceof _jesscss_core.Extend) extractedExtends.push(item);
1861
+ else newParts.push(item);
1862
+ else newParts.push(part);
1863
+ if (extractedExtends.length === 0) return {
1864
+ cleanedSelector: selector,
1865
+ extractedExtends: []
1866
+ };
1867
+ return {
1868
+ cleanedSelector: newParts.length === 0 ? "&" : newParts.length === 1 ? newParts[0] : new _jesscss_core.CompoundSelector(newParts, void 0, loc),
1869
+ extractedExtends
1870
+ };
1871
+ }
1872
+ if (selector instanceof _jesscss_core.ComplexSelector) {
1873
+ const allExtends = [];
1874
+ const newParts = [];
1875
+ for (const part of selector.value) if (part instanceof _jesscss_core.Extend) allExtends.push(part);
1876
+ else if (part instanceof _jesscss_core.List) for (const item of part.value ?? []) if (item instanceof _jesscss_core.Extend) allExtends.push(item);
1877
+ else newParts.push(item);
1878
+ else if (part instanceof _jesscss_core.CompoundSelector) {
1879
+ const { cleanedSelector: cs, extractedExtends: ee } = this._extractExtendsFromSelector(part, loc);
1880
+ allExtends.push(...ee);
1881
+ if (cs !== void 0) newParts.push(cs);
1882
+ } else newParts.push(part);
1883
+ if (allExtends.length === 0) return {
1884
+ cleanedSelector: selector,
1885
+ extractedExtends: []
1886
+ };
1887
+ return {
1888
+ cleanedSelector: newParts.length === 1 ? newParts[0] : new _jesscss_core.ComplexSelector(newParts, void 0, loc),
1889
+ extractedExtends: allExtends
1890
+ };
1891
+ }
1892
+ if ((0, _jesscss_core.isSelectorListLike)(selector)) {
1893
+ const allExtends = [];
1894
+ const cleanedItems = [];
1895
+ let changed = false;
1896
+ for (const item of (0, _jesscss_core.selectorListItems)(selector)) {
1897
+ const { cleanedSelector: cs, extractedExtends: ee } = this._extractExtendsFromSelector(item, loc);
1898
+ allExtends.push(...ee);
1899
+ if (ee.length > 0) changed = true;
1900
+ if (cs !== void 0) cleanedItems.push(cs);
1901
+ }
1902
+ if (!changed) return {
1903
+ cleanedSelector: selector,
1904
+ extractedExtends: []
1905
+ };
1906
+ return {
1907
+ cleanedSelector: cleanedItems.length === 1 ? cleanedItems[0] : this._makeSelectorList(cleanedItems, loc),
1908
+ extractedExtends: allExtends
1909
+ };
1910
+ }
1911
+ return {
1912
+ cleanedSelector: selector,
1913
+ extractedExtends: []
1914
+ };
1915
+ }
1916
+ static _isCssUrl(url, opts) {
1917
+ if (opts.includes("inline") || opts.includes("less")) return false;
1918
+ return url.endsWith(".css") || url.startsWith("http://") || url.startsWith("https://") || url.startsWith("//");
1919
+ }
1920
+ _buildImportAtRuleFromPrelude(children, raw, loc, name) {
1921
+ const preludeText = this._source.slice(loc.start, loc.end);
1922
+ const optMatch = /^\s*\(([^)]+)\)/.exec(preludeText.replace(/^@import\s*/, ""));
1923
+ const opts = optMatch ? optMatch[1].split(",").map((s) => s.trim()) : [];
1924
+ const builtNodes = nodeChildren(children);
1925
+ const urlNode = builtNodes.find((n) => n.type === "Url");
1926
+ const quotedNode = builtNodes.find((n) => n.type === "Quoted");
1927
+ let pathNode;
1928
+ if (quotedNode) pathNode = new _jesscss_core.Quoted(quotedNode.value, { quote: quotedNode.quote ?? "\"" }, loc);
1929
+ else {
1930
+ const _qm = preludeText.match(/(['"])([^'"]+)\1/);
1931
+ if (_qm) {
1932
+ const quote = _qm[1] === "'" ? "'" : "\"";
1933
+ const innerNode = _qm[2];
1934
+ pathNode = new _jesscss_core.Quoted(innerNode, { quote }, loc);
1935
+ }
1936
+ }
1937
+ let mediaNode;
1938
+ {
1939
+ let rest = preludeText.replace(/^@-?[_a-zA-Z][-_a-zA-Z0-9]*\s*/, "");
1940
+ rest = rest.replace(/^\([^)]*\)\s*/, "");
1941
+ rest = urlNode ? rest.replace(/url\(\s*(['"])[^'"]*\1\s*\)\s*/i, "") : rest.replace(/(['"])[^'"]*\1\s*/, "");
1942
+ rest = rest.replace(/\bas\s+[^\s;(]+\s*/g, "");
1943
+ rest = rest.replace(/;\s*$/, "").trim();
1944
+ if (rest) mediaNode = this._buildAtRulePrelude(rest, loc);
1945
+ }
1946
+ const pathMatch2 = /['"]([^'"]+)['"]/.exec(preludeText);
1947
+ const pathStr = pathMatch2 ? pathMatch2[1] : "";
1948
+ const isCssImport = pathStr ? LessGrammar._isCssUrl(pathStr, opts) : false;
1949
+ if (!opts.includes("inline") && (isCssImport || opts.includes("css"))) {
1950
+ const preludeItems = [];
1951
+ const pathPrelude = urlNode ?? pathNode;
1952
+ if (pathPrelude) preludeItems.push(pathPrelude);
1953
+ if (mediaNode) preludeItems.push(mediaNode);
1954
+ let prelude;
1955
+ if (preludeItems.length === 1) prelude = preludeItems[0];
1956
+ else {
1957
+ const joined = preludeItems.map((item) => item.toTrimmedString()).join(" ");
1958
+ prelude = preludeItems.every((item) => item.structuralStaticFlag()) ? new _jesscss_core.Any(joined, void 0, loc) : joined;
1959
+ }
1960
+ return new _jesscss_core.AtRuleStatement({
1961
+ name,
1962
+ prelude
1963
+ }, void 0, loc);
1964
+ }
1965
+ const isForward = name === "@-export";
1966
+ const importType = isForward ? "compose" : "import";
1967
+ const importOpts = { once: !opts.includes("multiple") };
1968
+ if (opts.includes("reference")) importOpts.reference = true;
1969
+ if (opts.includes("multiple")) importOpts.multiple = true;
1970
+ if (opts.includes("optional")) importOpts.optional = true;
1971
+ if (opts.includes("inline")) importOpts.inline = true;
1972
+ if (opts.includes("less")) importOpts.type = "less";
1973
+ if (mediaNode) importOpts.postlude = mediaNode;
1974
+ if (isForward) importOpts.forward = true;
1975
+ const namespace = /\bas\s+([^\s;(]+)/.exec(preludeText)?.[1];
1976
+ const styleImportOptions = {
1977
+ type: importType,
1978
+ importOptions: importOpts
1979
+ };
1980
+ if (namespace) styleImportOptions.namespace = namespace;
1981
+ return new _jesscss_core.StyleImport({ path: pathNode ?? urlNode }, styleImportOptions, loc);
1982
+ }
1983
+ _buildAtRuleBlock(children, loc) {
1984
+ const ls = children.filter((c) => c._tag === "leaf");
1985
+ const nameLf = ls[0];
1986
+ const name = nameLf?.value ?? "";
1987
+ if ([
1988
+ "@import",
1989
+ "@-import",
1990
+ "@-export"
1991
+ ].includes(name)) return this._buildImportAtRuleFromPrelude(children, ls, loc, name);
1992
+ if (["@use", "@-use"].includes(name)) return this._buildUseAtRuleFromPrelude(children, loc, name);
1993
+ const nameSpan = nameLf?.span;
1994
+ const braceStart = ls.find((l) => l.value === "{")?.span?.start;
1995
+ let rawPreludeText;
1996
+ if (nameSpan && typeof braceStart === "number") {
1997
+ const sliced = this._source.slice(nameSpan.end, braceStart).trim();
1998
+ rawPreludeText = sliced.length > 0 ? sliced : void 0;
1999
+ } else rawPreludeText = ls.slice(1).find((l) => l.value !== "{" && l.value !== "}")?.value.trim();
2000
+ return this._buildAtRuleFromParts(name, rawPreludeText, nodeChildren(children), loc);
2001
+ }
2002
+ /**
2003
+ * Shared AtRule assembly used by both the flat `AtRuleBlock` builder and the
2004
+ * structured, committed `QueryAtRuleBlock` builder. `preludeText` is the raw
2005
+ * prelude source (already `{`/`}` stripped); routing it through
2006
+ * `_buildAtRulePrelude` keeps the emitted AST identical regardless of which
2007
+ * grammar rule matched.
2008
+ */
2009
+ _buildAtRuleFromParts(name, preludeText, ruleNodes, loc) {
2010
+ const nestableOpts = _jesscss_core.NESTABLE_AT_RULES.includes(name) ? { nestable: true } : void 0;
2011
+ return new _jesscss_core.AtRule({
2012
+ name,
2013
+ prelude: preludeText ? this._buildAtRulePrelude(preludeText, loc) : void 0,
2014
+ rules: ruleNodes
2015
+ }, nestableOpts, loc);
2016
+ }
2017
+ /**
2018
+ * Builder for the structured, committed `@media`/`@container`/`@supports`
2019
+ * query block. The grammar rule parses the prelude with real query structure
2020
+ * (so a stray/unbalanced bracket is rejected instead of swallowed) and commits
2021
+ * on `expect('{')`, but the AST is reconstructed from the prelude source text
2022
+ * via the shared `_buildAtRuleFromParts` path — so well-formed queries emit the
2023
+ * exact same AtRule the flat `AtRuleBlock` builder would.
2024
+ */
2025
+ _buildLessQueryAtRuleBlock(children, raw, loc) {
2026
+ const name = children.filter((c) => c._tag === "leaf")[0]?.value ?? "";
2027
+ const comps = (0, _jesscss_css_parser_jess.spannedComponents)(raw);
2028
+ const keywordEnd = comps[0]?.span.end ?? loc.start;
2029
+ const braceStart = comps.find((c) => c.comp === "{")?.span.start ?? loc.end;
2030
+ const preludeText = this._source.slice(keywordEnd, braceStart).trim();
2031
+ const bodyNodes = comps.filter((c) => typeof c.comp !== "string" && c.span.start >= braceStart).map((c) => c.comp);
2032
+ return this._buildAtRuleFromParts(name, preludeText || void 0, bodyNodes, loc);
2033
+ }
2034
+ _buildAtRulePrelude(text, loc) {
2035
+ const singleVarRe = /^@(-?[_a-zA-Z\x80-￿][-_a-zA-Z0-9\x80-￿]*)$/;
2036
+ const MEDIA_KEYWORDS = new Set([
2037
+ "and",
2038
+ "or",
2039
+ "not",
2040
+ "only",
2041
+ "all",
2042
+ "print",
2043
+ "screen",
2044
+ "speech"
2045
+ ]);
2046
+ const COMPARISON_OPS = new Set([
2047
+ ">",
2048
+ "<",
2049
+ ">=",
2050
+ "<=",
2051
+ "=",
2052
+ "!="
2053
+ ]);
2054
+ const escapedStrRe = /^~(['"])([\s\S]*)\1$/;
2055
+ const buildWord = (w) => {
2056
+ const es = escapedStrRe.exec(w);
2057
+ if (es) return this._buildEscapedQuoted(es[2], es[1], loc);
2058
+ const mv = singleVarRe.exec(w);
2059
+ if (mv) return new _jesscss_core.Reference(mv[1], {
2060
+ type: "index",
2061
+ role: "ident"
2062
+ }, loc);
2063
+ if (MEDIA_KEYWORDS.has(w.toLowerCase())) return this._lessKeyword(w, loc);
2064
+ if (COMPARISON_OPS.has(w)) return w;
2065
+ return this._lessKeyword(w, loc);
2066
+ };
2067
+ const varAccRe = /^@(-?[_a-zA-Z\x80-￿][-_a-zA-Z0-9\x80-￿]*)\[([^\]]*)\]$/;
2068
+ const buildAccessor = (varName, accInner) => {
2069
+ const varBase = new _jesscss_core.Reference({ key: varName }, {}, loc);
2070
+ const inner = accInner.trim();
2071
+ let accKey;
2072
+ let accType;
2073
+ if (inner === "") {
2074
+ accKey = -1;
2075
+ accType = "index";
2076
+ } else if (inner.startsWith("@")) {
2077
+ accKey = inner.slice(1);
2078
+ accType = "variable";
2079
+ } else {
2080
+ accKey = new _jesscss_core.Quoted(inner, {}, loc);
2081
+ accType = "index";
2082
+ }
2083
+ return new _jesscss_core.Reference({
2084
+ target: varBase,
2085
+ key: accKey
2086
+ }, { type: accType }, loc);
2087
+ };
2088
+ const prodOps = new Set(["*", "/"]);
2089
+ const buildFeatureValue = (raw) => {
2090
+ const propVal = raw.trim();
2091
+ if (escapedStrRe.test(propVal) || singleVarRe.test(propVal)) return buildWord(propVal);
2092
+ const vam = varAccRe.exec(propVal);
2093
+ if (vam) return buildAccessor(vam[1], vam[2] ?? "");
2094
+ const paren = /^\(([\s\S]*)\)$/.exec(propVal);
2095
+ if (paren) {
2096
+ const op = buildMathExpr(paren[1].trim());
2097
+ if (op) return new _jesscss_core.Expression(op, { parens: true }, loc);
2098
+ }
2099
+ const ratio = /^(\d+(?:\.\d+)?)\s*\/\s*(\d+(?:\.\d+)?)$/.exec(propVal);
2100
+ if (ratio) return this._lessKeyword(`${ratio[1]} / ${ratio[2]}`, loc);
2101
+ return this._lessKeyword(propVal, loc);
2102
+ };
2103
+ const buildMathExpr = (expr) => {
2104
+ const parts = expr.split(/\s+/).filter(Boolean);
2105
+ if (parts.length < 3 || parts.length % 2 === 0) return;
2106
+ const buildOperand = (t) => {
2107
+ const es = escapedStrRe.exec(t);
2108
+ if (es) return this._buildEscapedQuoted(es[2], es[1], loc);
2109
+ const mv = singleVarRe.exec(t);
2110
+ if (mv) return new _jesscss_core.Reference(mv[1], {
2111
+ type: "index",
2112
+ role: "ident"
2113
+ }, loc);
2114
+ const va = varAccRe.exec(t);
2115
+ if (va) return buildAccessor(va[1], va[2] ?? "");
2116
+ const dim = /^([+-]?(?:\d*\.\d+|\d+))([_a-zA-Z%][-_a-zA-Z0-9%]*)?$/.exec(t);
2117
+ if (dim) return dim[2] ? new _jesscss_core.Dimension({
2118
+ number: parseFloat(dim[1]),
2119
+ unit: dim[2]
2120
+ }, void 0, loc) : new _jesscss_core.Num(parseFloat(dim[1]), void 0, loc);
2121
+ };
2122
+ const nodes = parts.map((p, i) => i % 2 === 0 ? buildOperand(p) : /^[-+*/]$/.test(p) ? p : void 0);
2123
+ if (nodes.some((n) => n === void 0)) return;
2124
+ const foldPass = (matchOp) => {
2125
+ for (let i = 1; i < nodes.length - 1; i += 2) {
2126
+ const op = nodes[i];
2127
+ if (matchOp(op)) {
2128
+ const left = nodes[i - 1];
2129
+ const right = nodes[i + 1];
2130
+ const combined = new _jesscss_core.Operation([
2131
+ left,
2132
+ op,
2133
+ right
2134
+ ], void 0, loc);
2135
+ nodes.splice(i - 1, 3, combined);
2136
+ return true;
2137
+ }
2138
+ }
2139
+ return false;
2140
+ };
2141
+ while (foldPass((op) => prodOps.has(op)));
2142
+ while (foldPass((op) => op === "+" || op === "-"));
2143
+ return nodes.length === 1 ? nodes[0] : void 0;
2144
+ };
2145
+ const buildParen = (inner) => {
2146
+ const trimmed = inner.trim();
2147
+ const colonIdx = trimmed.indexOf(":");
2148
+ if (colonIdx > 0 && !/[><=!]/.test(trimmed.slice(0, colonIdx))) {
2149
+ const propName = trimmed.slice(0, colonIdx).trim();
2150
+ const valueNode = buildFeatureValue(trimmed.slice(colonIdx + 1).trim());
2151
+ if (propName.startsWith("--")) return new _jesscss_core.Paren(new _jesscss_core.QueryCondition([this._lessKeyword(`${propName}:`, loc), valueNode], void 0, loc), void 0, loc);
2152
+ return new _jesscss_core.Paren(new _jesscss_core.Declaration({
2153
+ name: propName,
2154
+ value: valueNode
2155
+ }, void 0, loc), void 0, loc);
2156
+ }
2157
+ return new _jesscss_core.Paren(new _jesscss_core.QueryCondition(trimmed.replace(/\(\s+/g, "(").replace(/\s+\)/g, ")").replace(/\s*(<=|>=|!=|[<>=])\s*/g, " $1 ").trim().split(/\s+/).filter(Boolean).map((w) => buildWord(w)), void 0, loc), void 0, loc);
2158
+ };
2159
+ const tokenize = (t) => {
2160
+ const tokens = [];
2161
+ let i = 0;
2162
+ while (i < t.length) if (t[i] === "(") {
2163
+ let depth = 1;
2164
+ let j = i + 1;
2165
+ while (j < t.length && depth > 0) {
2166
+ if (t[j] === "(") depth++;
2167
+ else if (t[j] === ")") depth--;
2168
+ j++;
2169
+ }
2170
+ tokens.push(buildParen(t.slice(i + 1, j - 1)));
2171
+ i = j;
2172
+ } else if (t[i] === "\"" || t[i] === "'" || t[i] === "~" && (t[i + 1] === "\"" || t[i + 1] === "'")) {
2173
+ const start = i;
2174
+ if (t[i] === "~") i++;
2175
+ const quote = t[i];
2176
+ i++;
2177
+ while (i < t.length && t[i] !== quote) i++;
2178
+ i = Math.min(i + 1, t.length);
2179
+ tokens.push(buildWord(t.slice(start, i)));
2180
+ } else if (/\s/.test(t[i])) i++;
2181
+ else {
2182
+ let j = i;
2183
+ while (j < t.length && !/\s/.test(t[j]) && t[j] !== "(") j++;
2184
+ tokens.push(buildWord(t.slice(i, j)));
2185
+ i = j;
2186
+ }
2187
+ return tokens;
2188
+ };
2189
+ const splitCommas = (t) => {
2190
+ const parts = [];
2191
+ let depth = 0;
2192
+ let start = 0;
2193
+ for (let i = 0; i < t.length; i++) if (t[i] === "(") depth++;
2194
+ else if (t[i] === ")") depth--;
2195
+ else if (t[i] === "," && depth === 0) {
2196
+ parts.push(t.slice(start, i).trim());
2197
+ start = i + 1;
2198
+ }
2199
+ parts.push(t.slice(start).trim());
2200
+ return parts.filter(Boolean);
2201
+ };
2202
+ const nsMediaRe = /^([#.][^(\[,\s]*)(\([^)]*\))?(\[[^\]]*\])?$/;
2203
+ const buildItem = (t) => {
2204
+ const trimmed = t.trim();
2205
+ const mv = singleVarRe.exec(trimmed);
2206
+ if (mv) return new _jesscss_core.QueryCondition([new _jesscss_core.Reference(mv[1], {
2207
+ type: "index",
2208
+ role: "ident"
2209
+ }, loc)], void 0, loc);
2210
+ const vam = /^@(-?[_a-zA-Z\x80-￿][-_a-zA-Z0-9\x80-￿]*)(\[([^\]]*)\])$/.exec(trimmed);
2211
+ if (vam) {
2212
+ const varBase = new _jesscss_core.Reference({ key: vam[1] }, {}, loc);
2213
+ const accInner = (vam[3] ?? "").trim();
2214
+ let accKey;
2215
+ let accType;
2216
+ if (accInner === "") {
2217
+ accKey = -1;
2218
+ accType = "index";
2219
+ } else if (accInner.startsWith("@")) {
2220
+ accKey = accInner.slice(1);
2221
+ accType = "variable";
2222
+ } else {
2223
+ accKey = new _jesscss_core.Quoted(accInner, {}, loc);
2224
+ accType = "index";
2225
+ }
2226
+ return new _jesscss_core.Expression(new _jesscss_core.Reference({
2227
+ target: varBase,
2228
+ key: accKey
2229
+ }, { type: accType }, loc), void 0, loc);
2230
+ }
2231
+ const nsm = nsMediaRe.exec(trimmed);
2232
+ if (nsm) {
2233
+ const nsPath = nsm[1];
2234
+ const argsText = nsm[2];
2235
+ const accText = nsm[3];
2236
+ const segments = nsPath.match(/[#.][^#.]*/g) ?? [nsPath];
2237
+ const nameKey = segments.length === 1 ? segments[0] : segments;
2238
+ const rawKey = segments.length > 1 ? nsPath : void 0;
2239
+ let base = new _jesscss_core.Reference({
2240
+ key: nameKey,
2241
+ ...rawKey ? { rawKey } : {}
2242
+ }, {
2243
+ type: "mixin-ruleset",
2244
+ role: "name"
2245
+ }, loc);
2246
+ if (argsText) {
2247
+ const argsInner = argsText.slice(1, -1).trim();
2248
+ let argsNode = null;
2249
+ if (argsInner) {
2250
+ const argRefMatch = /^([.#][^\[\]()\s]+)(\[([^\]]*)\])?$/.exec(argsInner);
2251
+ if (argRefMatch) {
2252
+ let argBase = new _jesscss_core.Reference({ key: argRefMatch[1] }, { role: "name" }, loc);
2253
+ if (argRefMatch[2] !== void 0) {
2254
+ const argAcc = argRefMatch[3] ?? "";
2255
+ argBase = new _jesscss_core.Reference({
2256
+ target: argBase,
2257
+ key: argAcc === "" ? -1 : argAcc
2258
+ }, {}, loc);
2259
+ }
2260
+ argsNode = new _jesscss_core.List([argBase], void 0, loc);
2261
+ }
2262
+ }
2263
+ const callPayload = { name: base };
2264
+ if (argsNode) callPayload.args = argsNode;
2265
+ base = new _jesscss_core.Call(callPayload, {}, loc);
2266
+ }
2267
+ if (accText) {
2268
+ const inner = accText.slice(1, -1).trim();
2269
+ const key = inner.startsWith("@") ? inner.slice(1) : inner === "" ? -1 : inner;
2270
+ base = new _jesscss_core.Reference({
2271
+ target: base,
2272
+ key
2273
+ }, { type: "variable" }, loc);
2274
+ }
2275
+ return new _jesscss_core.Expression(base, void 0, loc);
2276
+ }
2277
+ const tokens = tokenize(t);
2278
+ const nameMatch = /^(@?-?[_a-zA-Z\x80-\uffff][-_a-zA-Z0-9\x80-\uffff]*)\s+(?:\(|not(?![-\w]))/i.exec(trimmed);
2279
+ return new _jesscss_core.QueryCondition(tokens, !!nameMatch && tokens.length > 1 && ![
2280
+ "not",
2281
+ "and",
2282
+ "or",
2283
+ "only"
2284
+ ].includes(nameMatch[1].replace(/^@/, "").toLowerCase()) ? { leadingContainerName: true } : void 0, loc);
2285
+ };
2286
+ const commaItems = splitCommas(text);
2287
+ if (commaItems.length === 1) return buildItem(commaItems[0]);
2288
+ return new _jesscss_core.List(commaItems.map(buildItem), void 0, loc);
2289
+ }
2290
+ _buildAtRuleStatement(children, loc) {
2291
+ const ls = children.filter((c) => c._tag === "leaf");
2292
+ const name = ls[0]?.value ?? "";
2293
+ if ([
2294
+ "@import",
2295
+ "@-import",
2296
+ "@-export"
2297
+ ].includes(name)) return this._buildImportAtRuleFromPrelude(children, ls, loc, name);
2298
+ if (["@use", "@-use"].includes(name)) return this._buildUseAtRuleFromPrelude(children, loc, name);
2299
+ return super._buildAtRuleStatement(children, loc);
2300
+ }
2301
+ _buildUseAtRuleFromPrelude(children, loc, name) {
2302
+ const preludeText = this._source.slice(loc.start, loc.end);
2303
+ const quotedNode = nodeChildren(children).find((n) => n.type === "Quoted");
2304
+ let rawPath = "";
2305
+ let pathNode;
2306
+ if (quotedNode) {
2307
+ const quote = quotedNode.quote ?? "\"";
2308
+ const innerVal = quotedNode.value;
2309
+ const inner = typeof innerVal === "string" ? innerVal : innerVal?.value ?? String(innerVal?.valueOf?.() ?? "");
2310
+ rawPath = inner;
2311
+ pathNode = new _jesscss_core.Quoted(inner, { quote }, loc);
2312
+ } else {
2313
+ const qm = /(['"])((?:[^'"\\]|\\.)*)\1/.exec(preludeText);
2314
+ if (qm) {
2315
+ const quote = qm[1] === "'" ? "'" : "\"";
2316
+ const inner = qm[2];
2317
+ rawPath = inner;
2318
+ pathNode = new _jesscss_core.Quoted(inner, { quote }, loc);
2319
+ }
2320
+ }
2321
+ const explicitNs = /\bas\s+([^\s;]+)/.exec(preludeText)?.[1];
2322
+ if (/\.[cm]?[jt]sx?$/.test(rawPath) || rawPath.startsWith("#")) {
2323
+ let ns = explicitNs;
2324
+ if (!ns) ns = (rawPath.split("/").pop() ?? "").replace(/\.[^.]+$/, "").replace(/[^a-zA-Z0-9_$]/g, "_");
2325
+ return new _jesscss_core.JsImport({ path: pathNode }, { namespace: ns }, loc);
2326
+ }
2327
+ return new _jesscss_core.AtRule({
2328
+ name,
2329
+ prelude: pathNode,
2330
+ rules: []
2331
+ }, void 0, loc);
2332
+ }
2333
+ _parenToArgs(paren, loc) {
2334
+ const inner = paren.value ?? paren.node;
2335
+ if (!inner) return null;
2336
+ const items = [];
2337
+ const isSeq = inner && typeof inner === "object" && inner.type === "Sequence";
2338
+ if (isSeq || Array.isArray(inner)) {
2339
+ const seqItems = (isSeq ? inner.value ?? [] : inner).map((it) => typeof it === "string" ? this._lessKeyword(it.trim(), loc) : it);
2340
+ let j = 0;
2341
+ while (j < seqItems.length) {
2342
+ const item = seqItems[j];
2343
+ if (item.type === "Reference" && j + 2 < seqItems.length && seqItems[j + 1]?.value === ":") {
2344
+ const nameAny = item.key ?? "";
2345
+ const valNode = seqItems[j + 2];
2346
+ const vd = new _jesscss_core.VarDeclaration({
2347
+ name: nameAny,
2348
+ value: this._isKeywordLike(valNode) ? this._lessKeyword(String(valNode.value ?? "").trim(), loc) : this._lessKeyword(String(valNode.value ?? ""), loc)
2349
+ }, {}, loc);
2350
+ items.push(vd);
2351
+ j += 3;
2352
+ } else {
2353
+ items.push(item);
2354
+ j++;
2355
+ }
2356
+ }
2357
+ } else if (inner && inner.type === "List") for (const it of inner.value ?? []) items.push(it);
2358
+ else if (inner) {
2359
+ if (!this._isEmptyKeywordLike(inner)) items.push(inner);
2360
+ }
2361
+ if (items.length === 0) return null;
2362
+ return new _jesscss_core.List(items, void 0, loc);
2363
+ }
2364
+ _tryParseNamespaceRef(valItems, loc) {
2365
+ const isSel = (s) => typeof s === "string" && /^[#.]-?[_a-zA-Z\u0080-\uffff]/.test(s.trim());
2366
+ const isCombinator = (s) => typeof s === "string" && /^[>+~|]$|^\|\|$/.test(String(s).trim());
2367
+ const isJessNodeVal = (x) => !!x && typeof x === "object" && "type" in x;
2368
+ const first = valItems[0]?.comp;
2369
+ const isVarRef = (x) => !!x && typeof x === "object" && x.type === "Reference";
2370
+ if (!isSel(first) && !isVarRef(first)) return null;
2371
+ if (isVarRef(first) && valItems.length < 2) return null;
2372
+ const hasSquareAfterVar = isVarRef(first) && valItems.slice(1).some((vi) => isJessNodeVal(vi.comp) && vi.comp.type === "Paren" && vi.comp._options?.delimiter === "square");
2373
+ if (isVarRef(first) && !hasSquareAfterVar) return null;
2374
+ let i = 0;
2375
+ let base = null;
2376
+ let pendingSegments = [];
2377
+ let hasMidCall = false;
2378
+ const flushPendingAsRef = () => {
2379
+ const k = pendingSegments.length === 1 ? pendingSegments[0] : pendingSegments;
2380
+ pendingSegments = [];
2381
+ if (base === null) return new _jesscss_core.Reference({ key: k }, {
2382
+ type: "mixin-ruleset",
2383
+ role: "name"
2384
+ }, loc);
2385
+ return new _jesscss_core.Reference({
2386
+ target: base,
2387
+ key: k
2388
+ }, {
2389
+ type: "mixin-ruleset",
2390
+ role: "name"
2391
+ }, loc);
2392
+ };
2393
+ while (i < valItems.length) {
2394
+ const c = valItems[i].comp;
2395
+ if (isVarRef(c) && base === null && pendingSegments.length === 0) {
2396
+ const rv = c;
2397
+ base = rv.target !== void 0 && (rv.key === "" || rv.key === void 0 || rv.key && typeof rv.key === "object" && rv.key.type === "Quoted" && !String(rv.key.value ?? "").trim()) ? rv.target : c;
2398
+ i++;
2399
+ } else if (isSel(c)) {
2400
+ const seg = c.trim();
2401
+ const splitSeg = seg.match(/[#.][^#.]*/g) ?? [seg];
2402
+ for (const s of splitSeg) pendingSegments.push(s);
2403
+ i++;
2404
+ } else if (isCombinator(c) && i + 1 < valItems.length && isSel(valItems[i + 1]?.comp)) i++;
2405
+ else if (isJessNodeVal(c) && c.type === "Paren" && c._options?.delimiter === "square") {
2406
+ if (pendingSegments.length > 0) base = flushPendingAsRef();
2407
+ if (base === null) break;
2408
+ const innerKey = this._decodeAccessorKey(c, loc);
2409
+ base = new _jesscss_core.Reference({
2410
+ target: base,
2411
+ key: innerKey
2412
+ }, { type: typeof innerKey === "number" ? "index" : "variable" }, loc);
2413
+ i++;
2414
+ } else if (isJessNodeVal(c) && c.type === "Paren") {
2415
+ if (pendingSegments.length === 0 && base === null) break;
2416
+ if (pendingSegments.length > 0) base = flushPendingAsRef();
2417
+ const argsNode = this._parenToArgs(c, loc);
2418
+ const callPayload = { name: base };
2419
+ if (argsNode) callPayload.args = argsNode;
2420
+ base = new _jesscss_core.Call(callPayload, {}, loc);
2421
+ hasMidCall = true;
2422
+ i++;
2423
+ } else break;
2424
+ }
2425
+ if (i !== valItems.length) return null;
2426
+ if (pendingSegments.length === 0 && base === null) return null;
2427
+ if (pendingSegments.length > 0) base = flushPendingAsRef();
2428
+ if (valItems.length > 0) {
2429
+ const lastSpan = valItems[valItems.length - 1].span;
2430
+ let afterVal = this._source.slice(lastSpan.end).trimStart();
2431
+ if (!hasMidCall && afterVal.startsWith("(")) {
2432
+ const closeIdx = afterVal.indexOf(")", 1);
2433
+ if (closeIdx > 0) {
2434
+ const callInner = afterVal.slice(1, closeIdx).trim();
2435
+ afterVal = afterVal.slice(closeIdx + 1).trimStart();
2436
+ let argsNode = null;
2437
+ if (callInner) {
2438
+ const argRefMatch = /^([.#][^\[\]()\s]+)(\[([^\]]*)\])?$/.exec(callInner);
2439
+ if (argRefMatch) {
2440
+ const argSel = argRefMatch[1];
2441
+ const argAccContent = argRefMatch[3];
2442
+ let argBase = new _jesscss_core.Reference({ key: argSel }, { role: "name" }, loc);
2443
+ if (argRefMatch[2] !== void 0) {
2444
+ const argAccKey = argAccContent === void 0 || argAccContent === "" ? -1 : argAccContent.startsWith("@") ? argAccContent.slice(1) : argAccContent;
2445
+ argBase = new _jesscss_core.Reference({
2446
+ target: argBase,
2447
+ key: argAccKey
2448
+ }, {}, loc);
2449
+ }
2450
+ argsNode = new _jesscss_core.List([argBase], void 0, loc);
2451
+ }
2452
+ }
2453
+ const callPayload = { name: base };
2454
+ if (argsNode) callPayload.args = argsNode;
2455
+ base = new _jesscss_core.Call(callPayload, {}, loc);
2456
+ }
2457
+ }
2458
+ const accMatch = /^\[([^\]]*)\]/.exec(afterVal);
2459
+ if (accMatch) {
2460
+ const accText = accMatch[1].trim();
2461
+ let accessorKey;
2462
+ if (accText === "") accessorKey = -1;
2463
+ else if (accText.startsWith("@")) accessorKey = accText.slice(1);
2464
+ else accessorKey = new _jesscss_core.Quoted(accText, {}, loc);
2465
+ base = new _jesscss_core.Reference({
2466
+ target: base,
2467
+ key: accessorKey
2468
+ }, {}, loc);
2469
+ const afterAcc = afterVal.slice(accMatch[0].length).trimStart();
2470
+ if (/^\(\s*\)/.test(afterAcc)) base = new _jesscss_core.Call({ name: base }, {}, loc);
2471
+ } else if (!hasMidCall) {
2472
+ if (/^\(\s*\)/.exec(afterVal)) base = new _jesscss_core.Call({ name: base }, {}, loc);
2473
+ }
2474
+ }
2475
+ return base;
2476
+ }
2477
+ _assembleLessValue(valItems, loc) {
2478
+ const parts = [];
2479
+ for (const item of valItems) {
2480
+ const c = item.comp;
2481
+ if (typeof c === "string") {
2482
+ if (c.trim()) parts.push(this._lessKeyword(c.trim(), loc));
2483
+ } else parts.push(c);
2484
+ }
2485
+ if (parts.length === 0) return { value: "" };
2486
+ if (parts.length === 1) return { value: parts[0] };
2487
+ return { value: parts };
2488
+ }
2489
+ };
2490
+ //#endregion
2491
+ //#region src/functional-parser.ts
2492
+ var BuilderHost = class extends LessGrammar {
2493
+ /**
2494
+ * The TreeContext threaded in by the caller (the plugin's per-file context),
2495
+ * held for the duration of one parse. Currently a pass-through: it's carried
2496
+ * back out in the result so a caller can hand one context in and read it back.
2497
+ * Grammar rules (e.g. a future `@compose`/`@use` rule) can read/mutate it —
2498
+ * e.g. set `context.opts.strict` — and the mutation is visible to the caller
2499
+ * since it's the same object.
2500
+ */
2501
+ context;
2502
+ setSource(src) {
2503
+ this._source = src;
2504
+ }
2505
+ resetWarnings() {
2506
+ this._warnings = [];
2507
+ this._errors = [];
2508
+ this._liftedCommentRanges = [];
2509
+ }
2510
+ getWarnings() {
2511
+ return this._warnings.slice();
2512
+ }
2513
+ getErrors() {
2514
+ return this._errors.slice();
2515
+ }
2516
+ /** `ctx.build` host: every structural `node(type, …)` builds through this,
2517
+ * reusing LessGrammar's (Less + inherited CSS) `buildNode` verbatim. */
2518
+ captureTriviaForNode(type) {
2519
+ return type === "CompoundSelector";
2520
+ }
2521
+ build(type, children, fields, span, rawChildren, triviaLog) {
2522
+ return this.buildNode(type, {
2523
+ start: span.start,
2524
+ end: span.end
2525
+ }, children, void 0, rawChildren, fields, triviaLog);
2526
+ }
2527
+ };
2528
+ const host = new BuilderHost();
2529
+ function parseLessFn(input, rule = "Stylesheet", mathMode = "parens-division", context) {
2530
+ host.mathMode = context?.options?.mathMode ?? mathMode;
2531
+ host.context = context;
2532
+ const g = require_grammar.lessGrammar;
2533
+ const result = (0, _jesscss_css_parser_jess.runFunctionalParse)(input, g[rule], host, { trivia: g.rw });
2534
+ const threaded = host.context;
2535
+ host.context = void 0;
2536
+ return {
2537
+ ...result,
2538
+ context: threaded
2539
+ };
2540
+ }
2541
+ /**
2542
+ * Index of the first backtick in CODE position (i.e. real inline JS), or -1.
2543
+ * Skips `//` line comments, `/* … *​/` block comments, and quoted strings so a
2544
+ * backtick inside a comment/string (common in Less doc comments) is not
2545
+ * mistaken for inline JavaScript.
2546
+ */
2547
+ function firstInlineJsBacktick(text) {
2548
+ for (let i = 0; i < text.length; i++) {
2549
+ const c = text[i];
2550
+ if (c === "`") return i;
2551
+ if (c === "/" && text[i + 1] === "/") {
2552
+ const nl = text.indexOf("\n", i + 2);
2553
+ if (nl === -1) return -1;
2554
+ i = nl;
2555
+ } else if (c === "/" && text[i + 1] === "*") {
2556
+ const end = text.indexOf("*/", i + 2);
2557
+ if (end === -1) return -1;
2558
+ i = end + 1;
2559
+ } else if (c === "\"" || c === "'") {
2560
+ i++;
2561
+ while (i < text.length && text[i] !== c) {
2562
+ if (text[i] === "\\") i++;
2563
+ i++;
2564
+ }
2565
+ }
2566
+ }
2567
+ return -1;
2568
+ }
2569
+ /** Functional Less parser — call .parse(text) to get a Jess AST. */
2570
+ var LessParser = class {
2571
+ _mathMode;
2572
+ constructor(config) {
2573
+ this._mathMode = config?.mathMode ?? "parens-division";
2574
+ }
2575
+ parse = (text, rule = "Stylesheet", options) => {
2576
+ const backtick = firstInlineJsBacktick(text);
2577
+ if (backtick !== -1) return {
2578
+ tree: (0, _jesscss_core.nil)(),
2579
+ errors: [(0, _jesscss_css_parser_jess.toParseError)("Inline JavaScript using backticks is not supported. Use @use / @-use to import a script module instead.", backtick, text)],
2580
+ warnings: [],
2581
+ trivia: (0, _jesscss_css_parser_jess.buildLazyTriviaMap)([], text),
2582
+ liftedCommentRanges: [],
2583
+ context: options?.context
2584
+ };
2585
+ return parseLessFn(text, rule, this._mathMode, options?.context);
2586
+ };
2587
+ };
2588
+ //#endregion
2589
+ Object.defineProperty(exports, "Fragments", {
2590
+ enumerable: true,
2591
+ get: function() {
2592
+ return Fragments;
2593
+ }
2594
+ });
2595
+ Object.defineProperty(exports, "LessGrammar", {
2596
+ enumerable: true,
2597
+ get: function() {
2598
+ return LessGrammar;
2599
+ }
2600
+ });
2601
+ Object.defineProperty(exports, "LessParser", {
2602
+ enumerable: true,
2603
+ get: function() {
2604
+ return LessParser;
2605
+ }
2606
+ });
2607
+ Object.defineProperty(exports, "Tokens", {
2608
+ enumerable: true,
2609
+ get: function() {
2610
+ return Tokens;
2611
+ }
2612
+ });
2613
+ Object.defineProperty(exports, "createInterpolatedReference", {
2614
+ enumerable: true,
2615
+ get: function() {
2616
+ return createInterpolatedReference;
2617
+ }
2618
+ });
2619
+ Object.defineProperty(exports, "getInterpolatedNode", {
2620
+ enumerable: true,
2621
+ get: function() {
2622
+ return getInterpolatedNode;
2623
+ }
2624
+ });
2625
+ Object.defineProperty(exports, "getInterpolatedOrString", {
2626
+ enumerable: true,
2627
+ get: function() {
2628
+ return getInterpolatedOrString;
2629
+ }
2630
+ });
2631
+ Object.defineProperty(exports, "lessFragments", {
2632
+ enumerable: true,
2633
+ get: function() {
2634
+ return lessFragments;
2635
+ }
2636
+ });
2637
+ Object.defineProperty(exports, "lessTokens", {
2638
+ enumerable: true,
2639
+ get: function() {
2640
+ return lessTokens;
2641
+ }
2642
+ });
2643
+ Object.defineProperty(exports, "normalizeMixinReferenceKey", {
2644
+ enumerable: true,
2645
+ get: function() {
2646
+ return normalizeMixinReferenceKey;
2647
+ }
2648
+ });
2649
+ Object.defineProperty(exports, "parseLessFn", {
2650
+ enumerable: true,
2651
+ get: function() {
2652
+ return parseLessFn;
2653
+ }
2654
+ });