less 4.6.7 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/less.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Less - Leaner CSS v4.6.7
2
+ * Less - Leaner CSS v4.8.0
3
3
  * http://lesscss.org
4
4
  *
5
5
  * Copyright (c) 2009-2026, Alexis Sellier <self@cloudhead.net>
@@ -4100,12 +4100,23 @@
4100
4100
  /**
4101
4101
  * Permissive parsing. Ignores everything except matching {} [] () and quotes
4102
4102
  * until matching token (outside of blocks)
4103
- */
4104
- parserInput.$parseUntil = tok => {
4103
+ *
4104
+ * @param {string|RegExp} tok - stop token
4105
+ * @param {boolean} [detectBareVar] - when set, also record the position of the
4106
+ * first bare `@variable` reference (not `@{interpolation}`) that appears at
4107
+ * PAREN depth 0 — i.e. a structural reference, not a declaration value inside
4108
+ * `(...)`. Reuses this single pass (which already skips strings/comments) so
4109
+ * callers don't re-scan the text. Exposed as `.bareVarIndex` on the returned
4110
+ * group array (or null). `[...]`/`{...}` do NOT shield a reference — only
4111
+ * `(...)` (a declaration-value group) does.
4112
+ */
4113
+ parserInput.$parseUntil = (tok, detectBareVar) => {
4105
4114
  let quote = '';
4106
4115
  let returnVal = null;
4107
4116
  let inComment = false;
4108
4117
  let blockDepth = 0;
4118
+ let parenDepth = 0;
4119
+ let bareVarIndex = null;
4109
4120
  const blockStack = [];
4110
4121
  const parseGroups = [];
4111
4122
  const length = input.length;
@@ -4145,6 +4156,12 @@
4145
4156
  i++;
4146
4157
  continue;
4147
4158
  }
4159
+ if (detectBareVar && bareVarIndex === null && nextChar === '@' && parenDepth === 0) {
4160
+ // A bare `@ident` (not `@{interpolation}`) outside any `(...)` —
4161
+ // a structural reference. Strings/comments are already skipped above.
4162
+ const after = input.charAt(i + 1);
4163
+ if (after && /[-\w]/.test(after)) { bareVarIndex = i; }
4164
+ }
4148
4165
  switch (nextChar) {
4149
4166
  case '\\':
4150
4167
  i++;
@@ -4180,6 +4197,7 @@
4180
4197
  case '(':
4181
4198
  blockStack.push(')');
4182
4199
  blockDepth++;
4200
+ parenDepth++;
4183
4201
  break;
4184
4202
  case '[':
4185
4203
  blockStack.push(']');
@@ -4191,6 +4209,7 @@
4191
4209
  const expected = blockStack.pop();
4192
4210
  if (nextChar === expected) {
4193
4211
  blockDepth--;
4212
+ if (nextChar === ')' && parenDepth > 0) { parenDepth--; }
4194
4213
  } else {
4195
4214
  // move the parser to the error and return expected
4196
4215
  skipWhitespace(i - startPos);
@@ -4206,6 +4225,7 @@
4206
4225
  }
4207
4226
  } while (loop);
4208
4227
 
4228
+ if (Array.isArray(returnVal)) { returnVal.bareVarIndex = bareVarIndex; }
4209
4229
  return returnVal ? returnVal : null;
4210
4230
  };
4211
4231
 
@@ -4406,6 +4426,10 @@
4406
4426
 
4407
4427
  const deprecationHandler = new DeprecationHandler();
4408
4428
 
4429
+ // Tracks `${deprecationId}@${index}` pairs already warned about, so a source
4430
+ // position that gets re-parsed via parser backtracking only warns once.
4431
+ const warnedDeprecations = new Set();
4432
+
4409
4433
  /**
4410
4434
  * @param {string} msg
4411
4435
  * @param {number} index
@@ -4415,6 +4439,11 @@
4415
4439
  function warn(msg, index, type, deprecationId) {
4416
4440
  if (context.quiet) { return; }
4417
4441
  if (deprecationId && context.quietDeprecations) { return; }
4442
+ if (deprecationId) {
4443
+ const key = `${deprecationId}@${index ?? parserInput.i}`;
4444
+ if (warnedDeprecations.has(key)) { return; }
4445
+ warnedDeprecations.add(key);
4446
+ }
4418
4447
  if (deprecationId && !deprecationHandler.shouldWarn(deprecationId)) { return; }
4419
4448
 
4420
4449
  logger$1.warn(
@@ -4430,6 +4459,75 @@
4430
4459
  );
4431
4460
  }
4432
4461
 
4462
+ /**
4463
+ * Warn that a bare `@variable` reference is being used in a non-value
4464
+ * position (an at-rule prelude, name, or identifier), where it still
4465
+ * resolves today but is deprecated in favour of `@{variable}` interpolation.
4466
+ *
4467
+ * @param {number} index - source position of the bare reference
4468
+ */
4469
+ function warnBareAtRuleVariable(index) {
4470
+ warn('A bare @variable in an at-rule prelude is deprecated. Use @{variable} interpolation instead.', index, 'DEPRECATED', 'variable-in-at-rule-prelude');
4471
+ }
4472
+
4473
+ /**
4474
+ * Numeric-leading variable names are a Less extension rather than valid CSS
4475
+ * identifier syntax. Keep accepting them through Less 4, but make the Less 5
4476
+ * migration visible at every actual variable reference or definition.
4477
+ *
4478
+ * @param {string} name - either an @-prefixed name or the name inside @{...}
4479
+ * @param {number} index - source position of the variable token
4480
+ */
4481
+ function warnNumericVariableName(name, index) {
4482
+ if (/^(?:@@?)?[0-9]/.test(name)) {
4483
+ warn('Variable names beginning with a number are deprecated and will be removed in Less 5.x. Rename the variable to start with a valid identifier character.', index, 'DEPRECATED', 'numeric-variable-name');
4484
+ }
4485
+ }
4486
+
4487
+ /**
4488
+ * A lone '-' is not a CSS identifier. Less historically accepts it as a
4489
+ * variable name; retain that in Less 4 only and warn on definitions,
4490
+ * ordinary references, and interpolation references.
4491
+ *
4492
+ * @param {string} name - either @-/@@-, or the name inside @{...}
4493
+ * @param {number} index - source position of the variable token
4494
+ */
4495
+ function warnDashOnlyVariableName(name, index) {
4496
+ const bareName = name.replace(/^@@?/, '');
4497
+ if (bareName === '-') {
4498
+ warn('The dash-only variable names @- and @{-} are deprecated and will be removed in Less 5.x. Rename the variable to use a valid identifier.', index, 'DEPRECATED', 'dash-only-variable-name');
4499
+ }
4500
+ }
4501
+
4502
+ /**
4503
+ * A lone '-' is not a CSS identifier. Less historically accepts it as a mixin
4504
+ * name after '.' or '#'; retain that in Less 4 only.
4505
+ *
4506
+ * @param {string} name
4507
+ * @param {number} index
4508
+ */
4509
+ function warnDashOnlyMixinName(name, index) {
4510
+ if (name === '.-' || name === '#-') {
4511
+ warn('The dash-only mixin names .-() and #-() are deprecated and will be removed in Less 5.x. Rename the mixin to use a valid CSS identifier.', index, 'DEPRECATED', 'dash-only-mixin-name');
4512
+ }
4513
+ }
4514
+
4515
+ /**
4516
+ * CSS @charset is a source-header declaration, not a general dynamic at-rule.
4517
+ * Less 4 keeps its historical interpolation behavior for compatibility, but
4518
+ * makes each dynamic spelling visible before Less 5 rejects it.
4519
+ *
4520
+ * @param {import('../tree/node.js').default} value
4521
+ * @param {number} index - source position of the @charset token
4522
+ */
4523
+ function warnDynamicCharset(value, index) {
4524
+ if (value instanceof tree.Variable ||
4525
+ (value instanceof tree.Quoted &&
4526
+ (value.containsVariables() || value.value.match(value.propRegex)))) {
4527
+ warn('Dynamic @charset interpolation is deprecated and will be removed in Less 5.x. Use a static quoted encoding declaration instead.', index, 'DEPRECATED', 'dynamic-charset');
4528
+ }
4529
+ }
4530
+
4433
4531
  function expect(arg, msg) {
4434
4532
  // some older browsers return typeof 'function' for RegExp
4435
4533
  const result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
@@ -4883,7 +4981,7 @@
4883
4981
  }
4884
4982
 
4885
4983
  function condition() {
4886
- return [expect(parsers.condition, 'expected condition')];
4984
+ return [expect(() => parsers.condition(false, true), 'expected condition')];
4887
4985
  }
4888
4986
  },
4889
4987
 
@@ -5008,6 +5106,8 @@
5008
5106
 
5009
5107
  parserInput.save();
5010
5108
  if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
5109
+ warnNumericVariableName(name, index);
5110
+ warnDashOnlyVariableName(name, index);
5011
5111
  ch = parserInput.currentChar();
5012
5112
  if ((ch === '(' && !parserInput.prevChar().match(/^\s/))
5013
5113
  || (ch === '[' && !parserInput.prevChar().match(/^\s/))) {
@@ -5030,6 +5130,8 @@
5030
5130
  const index = parserInput.i;
5031
5131
 
5032
5132
  if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
5133
+ warnNumericVariableName(curly[1], index);
5134
+ warnDashOnlyVariableName(curly[1], index);
5033
5135
  return new(tree.Variable)(`@${curly[1]}`, index + currentIndex, fileInfo);
5034
5136
  }
5035
5137
  },
@@ -5152,7 +5254,11 @@
5152
5254
  variable: function () {
5153
5255
  let name;
5154
5256
 
5155
- if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; }
5257
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) {
5258
+ warnNumericVariableName(name[1], parserInput.i - name[0].length);
5259
+ warnDashOnlyVariableName(name[1], parserInput.i - name[0].length);
5260
+ return name[1];
5261
+ }
5156
5262
  },
5157
5263
 
5158
5264
  //
@@ -5182,6 +5288,8 @@
5182
5288
  }
5183
5289
 
5184
5290
  if (!inValue) {
5291
+ warnNumericVariableName(name[1], i);
5292
+ warnDashOnlyVariableName(name[1], i);
5185
5293
  name = name[1];
5186
5294
  }
5187
5295
 
@@ -5338,6 +5446,9 @@
5338
5446
  if (inValue || parsers.end()) {
5339
5447
  parserInput.forget();
5340
5448
  const mixin = new(tree.mixin.Call)(elements, args, index + currentIndex, fileInfo, !lookups && important);
5449
+ for (const element of elements) {
5450
+ warnDashOnlyMixinName(element.value, element._index - currentIndex);
5451
+ }
5341
5452
  if (lookups) {
5342
5453
  return new tree.NamespaceValue(mixin, lookups);
5343
5454
  }
@@ -5534,6 +5645,7 @@
5534
5645
  let ruleset;
5535
5646
  let cond;
5536
5647
  let variadic = false;
5648
+ const index = parserInput.i;
5537
5649
  if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
5538
5650
  parserInput.peek(/^[^{]*\}/)) {
5539
5651
  return;
@@ -5569,6 +5681,7 @@
5569
5681
 
5570
5682
  if (ruleset) {
5571
5683
  parserInput.forget();
5684
+ warnDashOnlyMixinName(name, index);
5572
5685
  return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
5573
5686
  } else {
5574
5687
  parserInput.restore();
@@ -5980,7 +6093,7 @@
5980
6093
  if (parserInput.$char(';')) {
5981
6094
  value = new Anonymous('');
5982
6095
  } else {
5983
- value = this.permissiveValue(/[;}]/, true);
6096
+ value = this.permissiveValue(/[;}]/);
5984
6097
  }
5985
6098
  }
5986
6099
  // Try to store values as anonymous
@@ -6039,8 +6152,12 @@
6039
6152
  * math is allowed.
6040
6153
  *
6041
6154
  * @param {RexExp} untilTokens - Characters to stop parsing at
6155
+ * @param {boolean} [deprecateVariables] - when set, this is an at-rule
6156
+ * prelude (non-value position); accept `@{var}` interpolation and warn
6157
+ * on a bare `@var` reference (which resolves today but is deprecated).
6042
6158
  */
6043
- permissiveValue: function (untilTokens) {
6159
+ permissiveValue: function (untilTokens, deprecateVariables) {
6160
+ const entities = this.entities;
6044
6161
  let i;
6045
6162
  let e;
6046
6163
  let done;
@@ -6067,7 +6184,20 @@
6067
6184
  value.push(e);
6068
6185
  continue;
6069
6186
  }
6070
- e = this.entity();
6187
+ if (deprecateVariables) {
6188
+ // In an at-rule prelude, `@{var}` interpolation is the supported
6189
+ // form; consume it here so its `{` is not mistaken for a block.
6190
+ e = entities.variableCurly();
6191
+ if (!e) {
6192
+ const varIndex = parserInput.i;
6193
+ e = this.entity();
6194
+ if (e && e.type === 'Variable') {
6195
+ warnBareAtRuleVariable(varIndex);
6196
+ }
6197
+ }
6198
+ } else {
6199
+ e = this.entity();
6200
+ }
6071
6201
  if (e) {
6072
6202
  value.push(e);
6073
6203
  }
@@ -6094,7 +6224,7 @@
6094
6224
  }
6095
6225
  parserInput.save();
6096
6226
 
6097
- value = parserInput.$parseUntil(tok);
6227
+ value = parserInput.$parseUntil(tok, deprecateVariables);
6098
6228
 
6099
6229
  if (value) {
6100
6230
  if (typeof value === 'string') {
@@ -6104,6 +6234,14 @@
6104
6234
  parserInput.forget();
6105
6235
  return new tree.Anonymous('', index);
6106
6236
  }
6237
+ // At-rule prelude: `$parseUntil` (deprecateVariables) records the
6238
+ // first bare `@var` it saw outside any `(...)` in its single pass —
6239
+ // a structural reference (`[...]`/`{...}` don't shield it, only a
6240
+ // declaration-value `(...)` does). Warn once here rather than
6241
+ // re-scanning the text.
6242
+ if (deprecateVariables && value.bareVarIndex !== null && value.bareVarIndex !== undefined) {
6243
+ warnBareAtRuleVariable(value.bareVarIndex);
6244
+ }
6107
6245
  /** @type {string} */
6108
6246
  let item;
6109
6247
  for (i = 0; i < value.length; i++) {
@@ -6120,11 +6258,14 @@
6120
6258
  const quote = new tree.Quoted('\'', item, true, index, fileInfo);
6121
6259
  const variableRegex = /@([\w-]+)/g;
6122
6260
  const propRegex = /\$([\w-]+)/g;
6123
- if (variableRegex.test(item)) {
6124
- warn('@[ident] in unknown values will not be evaluated as variables in the future. Use @{[ident]}', index, 'DEPRECATED', 'variable-in-unknown-value');
6261
+ // At-rule preludes are handled once above via
6262
+ // `value.bareVarIndex`; the `variable-in-unknown-value`
6263
+ // notice is for unknown declaration values only.
6264
+ if (!deprecateVariables && variableRegex.test(item)) {
6265
+ warn('@variable in unknown values will not be evaluated as variables in the future. Use @{variable}', index, 'DEPRECATED', 'variable-in-unknown-value');
6125
6266
  }
6126
6267
  if (propRegex.test(item)) {
6127
- warn('$[ident] in unknown values will not be evaluated as property references in the future. Use ${[ident]}', index, 'DEPRECATED', 'property-in-unknown-value');
6268
+ warn('$property in unknown values will not be evaluated as property references in the future. Use ${property}', index, 'DEPRECATED', 'property-in-unknown-value');
6128
6269
  }
6129
6270
  quote.variableRegex = /@([\w-]+)|@{([\w-]+)}/g;
6130
6271
  quote.propRegex = /\$([\w-]+)|\${([\w-]+)}/g;
@@ -6233,7 +6374,17 @@
6233
6374
  }
6234
6375
  parserInput.restore();
6235
6376
 
6236
- e = entities.declarationCall.bind(this)() || cssKeyword() || entities.keyword() || entities.variable() || entities.mixinLookup();
6377
+ e = entities.declarationCall.bind(this)() || cssKeyword() || entities.keyword() || entities.variableCurly();
6378
+ if (!e) {
6379
+ const varIndex = parserInput.i;
6380
+ const bareVariable = entities.variable();
6381
+ if (bareVariable) {
6382
+ warnBareAtRuleVariable(varIndex);
6383
+ e = bareVariable;
6384
+ } else {
6385
+ e = entities.mixinLookup();
6386
+ }
6387
+ }
6237
6388
  if (e) {
6238
6389
  nodes.push(e);
6239
6390
  if (e.type === 'Variable' ||
@@ -6244,7 +6395,7 @@
6244
6395
  let closed = false;
6245
6396
  p = this.property();
6246
6397
  parserInput.save();
6247
- if (!p && syntaxOptions.queryInParens && parserInput.$re(/^[0-9a-z-]*\s*([<>]=|<=|>=|[<>]|=)/)) {
6398
+ if (!p && syntaxOptions.queryInParens && parserInput.$re(/^(?:[^()]|\([^()]*\))*\s*([<>]=|<=|>=|[<>]|=)/)) {
6248
6399
  parserInput.restore();
6249
6400
  p = this.condition();
6250
6401
 
@@ -6324,7 +6475,17 @@
6324
6475
  features[features.length - 1].noSpacing = false;
6325
6476
  }
6326
6477
  } else {
6327
- e = entities.variable() || entities.mixinLookup();
6478
+ e = entities.variableCurly();
6479
+ if (!e) {
6480
+ const varIndex = parserInput.i;
6481
+ const bareVariable = entities.variable();
6482
+ if (bareVariable) {
6483
+ warnBareAtRuleVariable(varIndex);
6484
+ e = bareVariable;
6485
+ } else {
6486
+ e = entities.mixinLookup();
6487
+ }
6488
+ }
6328
6489
  if (e) {
6329
6490
  features.push(e);
6330
6491
  if (!parserInput.$char(',')) { break; }
@@ -6438,8 +6599,24 @@
6438
6599
  return null;
6439
6600
  }
6440
6601
  },
6602
+ /**
6603
+ * An entity in a non-value at-rule position (an at-rule identifier,
6604
+ * name, or keyword-list item — e.g. the name in `@keyframes @foo`).
6605
+ * `@{foo}` interpolation is the supported form; a bare `@foo` still
6606
+ * resolves but is deprecated.
6607
+ */
6608
+ atRuleEntity: function () {
6609
+ const curly = this.entities.variableCurly();
6610
+ if (curly) { return curly; }
6611
+ const index = parserInput.i;
6612
+ const e = this.entity();
6613
+ if (e && e.type === 'Variable') {
6614
+ warnBareAtRuleVariable(index);
6615
+ }
6616
+ return e;
6617
+ },
6441
6618
  atruleUnknown: function (value, name, hasBlock) {
6442
- value = this.permissiveValue(/^[{;]/);
6619
+ value = this.permissiveValue(/^[{;]/, true);
6443
6620
  hasBlock = (parserInput.currentChar() === '{');
6444
6621
  if (!value) {
6445
6622
  if (!hasBlock && parserInput.currentChar() !== ';') {
@@ -6455,16 +6632,16 @@
6455
6632
  rules = this.blockRuleset();
6456
6633
  parserInput.save();
6457
6634
  if (!rules && !isRooted) {
6458
- value = this.entity();
6635
+ value = this.atRuleEntity();
6459
6636
  rules = this.blockRuleset();
6460
6637
  }
6461
6638
  if (!rules && !isRooted) {
6462
6639
  parserInput.restore();
6463
6640
  var e = [];
6464
- value = this.entity();
6641
+ value = this.atRuleEntity();
6465
6642
  while (parserInput.$char(',')) {
6466
6643
  e.push(value);
6467
- value = this.entity();
6644
+ value = this.atRuleEntity();
6468
6645
  }
6469
6646
  if (value && e.length > 0) {
6470
6647
  e.push(value);
@@ -6549,12 +6726,28 @@
6549
6726
  parserInput.commentStore.length = 0;
6550
6727
 
6551
6728
  if (hasIdentifier) {
6552
- value = this.entity();
6729
+ value = this.atRuleEntity();
6553
6730
  if (!value) {
6554
6731
  error(`expected ${name} identifier`);
6555
6732
  }
6733
+ if (nonVendorSpecificName === '@charset') {
6734
+ warnDynamicCharset(value, index);
6735
+ }
6556
6736
  } else if (hasExpression) {
6737
+ // `@namespace` may carry an interpolated `@{ns}` prefix (or a
6738
+ // deprecated bare `@ns`). Parse that prefix directly so `@{ns}`
6739
+ // is accepted here without treating value positions as
6740
+ // interpolation contexts, then read the namespace URL.
6741
+ let prefix = this.entities.variableCurly();
6742
+ if (!prefix && parserInput.peek(/^@@?[\w-]/)) {
6743
+ const prefixIndex = parserInput.i;
6744
+ prefix = this.entities.variable();
6745
+ if (prefix) { warnBareAtRuleVariable(prefixIndex); }
6746
+ }
6557
6747
  value = this.expression();
6748
+ if (prefix) {
6749
+ value = value ? new(tree.Expression)([prefix, ...value.value]) : prefix;
6750
+ }
6558
6751
  if (!value) {
6559
6752
  error(`expected ${name} expression`);
6560
6753
  }
@@ -6744,7 +6937,7 @@
6744
6937
  return condition || a;
6745
6938
  }
6746
6939
  },
6747
- condition: function (needsParens) {
6940
+ condition: function (needsParens, allowConditionOperands) {
6748
6941
  let result;
6749
6942
  let logical;
6750
6943
  let next;
@@ -6752,13 +6945,13 @@
6752
6945
  return parserInput.$str('or');
6753
6946
  }
6754
6947
 
6755
- result = this.conditionAnd(needsParens);
6948
+ result = this.conditionAnd(needsParens, allowConditionOperands);
6756
6949
  if (!result) {
6757
6950
  return ;
6758
6951
  }
6759
6952
  logical = or();
6760
6953
  if (logical) {
6761
- next = this.condition(needsParens);
6954
+ next = this.condition(needsParens, allowConditionOperands);
6762
6955
  if (next) {
6763
6956
  result = new(tree.Condition)(logical, result, next);
6764
6957
  } else {
@@ -6767,13 +6960,13 @@
6767
6960
  }
6768
6961
  return result;
6769
6962
  },
6770
- conditionAnd: function (needsParens) {
6963
+ conditionAnd: function (needsParens, allowConditionOperands) {
6771
6964
  let result;
6772
6965
  let logical;
6773
6966
  let next;
6774
6967
  const self = this;
6775
6968
  function insideCondition() {
6776
- const cond = self.negatedCondition(needsParens) || self.parenthesisCondition(needsParens);
6969
+ const cond = self.negatedCondition(needsParens, allowConditionOperands) || self.parenthesisCondition(needsParens, allowConditionOperands);
6777
6970
  if (!cond && !needsParens) {
6778
6971
  return self.atomicCondition(needsParens);
6779
6972
  }
@@ -6787,9 +6980,12 @@
6787
6980
  if (!result) {
6788
6981
  return ;
6789
6982
  }
6983
+ if (allowConditionOperands) {
6984
+ result = this.atomicCondition(needsParens, result, allowConditionOperands) || result;
6985
+ }
6790
6986
  logical = and();
6791
6987
  if (logical) {
6792
- next = this.conditionAnd(needsParens);
6988
+ next = this.conditionAnd(needsParens, allowConditionOperands);
6793
6989
  if (next) {
6794
6990
  result = new(tree.Condition)(logical, result, next);
6795
6991
  } else {
@@ -6798,9 +6994,9 @@
6798
6994
  }
6799
6995
  return result;
6800
6996
  },
6801
- negatedCondition: function (needsParens) {
6997
+ negatedCondition: function (needsParens, allowConditionOperands) {
6802
6998
  if (parserInput.$str('not')) {
6803
- const result = this.parenthesisCondition(needsParens);
6999
+ const result = this.parenthesisCondition(needsParens, allowConditionOperands);
6804
7000
  if (result) {
6805
7001
  result.negate = !result.negate;
6806
7002
  return result;
@@ -6817,11 +7013,11 @@
6817
7013
  }
6818
7014
  }
6819
7015
  },
6820
- parenthesisCondition: function (needsParens) {
7016
+ parenthesisCondition: function (needsParens, allowConditionOperands) {
6821
7017
  function tryConditionFollowedByParenthesis(me) {
6822
7018
  let body;
6823
7019
  parserInput.save();
6824
- body = me.condition(needsParens);
7020
+ body = me.condition(needsParens, allowConditionOperands);
6825
7021
  if (!body) {
6826
7022
  parserInput.restore();
6827
7023
  return ;
@@ -6858,7 +7054,7 @@
6858
7054
  parserInput.forget();
6859
7055
  return body;
6860
7056
  },
6861
- atomicCondition: function (needsParens, preparsedCond) {
7057
+ atomicCondition: function (needsParens, preparsedCond, allowConditionOperands) {
6862
7058
  const entities = this.entities;
6863
7059
  const index = parserInput.i;
6864
7060
  let a;
@@ -6867,7 +7063,7 @@
6867
7063
  let op;
6868
7064
 
6869
7065
  const cond = (function() {
6870
- return this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
7066
+ return (allowConditionOperands && this.parenthesisCondition(needsParens)) || this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
6871
7067
  }).bind(this);
6872
7068
 
6873
7069
  if (preparsedCond) {
@@ -7028,6 +7224,11 @@
7028
7224
  }
7029
7225
  for (k = 0; k < name.length; k++) {
7030
7226
  s = name[k];
7227
+ if (s.charAt(0) === '@') {
7228
+ const variableName = s.slice(2, -1);
7229
+ warnNumericVariableName(variableName, index[k]);
7230
+ warnDashOnlyVariableName(variableName, index[k]);
7231
+ }
7031
7232
  name[k] = (s.charAt(0) !== '@' && s.charAt(0) !== '$') ?
7032
7233
  new(tree.Keyword)(s) :
7033
7234
  (s.charAt(0) === '@' ?
@@ -8617,16 +8818,16 @@
8617
8818
  self.features = new Value(self.permute(/** @type {Node[][]} */ (/** @type {unknown} */ (path))).map(
8618
8819
  /** @param {Node | Node[]} path */
8619
8820
  path => {
8620
- path = /** @type {Node[]} */ (path).map(
8821
+ path = /** @type {Node[]} */ (path).map(
8621
8822
  /** @param {Node & { toCSS?: Function }} fragment */
8622
- fragment => fragment.toCSS ? fragment : new Anonymous(/** @type {string} */ (/** @type {unknown} */ (fragment))));
8823
+ fragment => fragment.toCSS ? fragment : new Anonymous(/** @type {string} */ (/** @type {unknown} */ (fragment))));
8623
8824
 
8624
- for (i = /** @type {Node[]} */ (path).length - 1; i > 0; i--) {
8825
+ for (i = /** @type {Node[]} */ (path).length - 1; i > 0; i--) {
8625
8826
  /** @type {Node[]} */ (path).splice(i, 0, new Anonymous('and'));
8626
- }
8827
+ }
8627
8828
 
8628
- return new Expression(/** @type {Node[]} */ (path));
8629
- }));
8829
+ return new Expression(/** @type {Node[]} */ (path));
8830
+ }));
8630
8831
  self.setParent(self.features, self);
8631
8832
 
8632
8833
  // Fake a tree-node that doesn't output anything.
@@ -10980,35 +11181,47 @@
10980
11181
  */
10981
11182
  matchArgs(args, context) {
10982
11183
  const allArgsCnt = (args && args.length) || 0;
10983
- let len;
10984
- const optionalParameters = this.optionalParameters;
10985
- const requiredArgsCnt = !args ? 0 : args.reduce(function (/** @type {number} */ count, /** @type {MixinArg} */ p) {
10986
- if (optionalParameters.indexOf(p.name) < 0) {
10987
- return count + 1;
10988
- } else {
10989
- return count;
11184
+ const evaldArguments = new Array(this.params.length);
11185
+ const positionalArgs = [];
11186
+ let positionalIndex = 0;
11187
+
11188
+ for (let i = 0; i < allArgsCnt; i++) {
11189
+ const arg = /** @type {MixinArg[]} */ (args)[i];
11190
+ if (!arg.name) {
11191
+ positionalArgs.push(arg);
11192
+ continue;
10990
11193
  }
10991
- }, 0);
10992
11194
 
10993
- if (!this.variadic) {
10994
- if (requiredArgsCnt < this.required) {
11195
+ const paramIndex = this.params.findIndex((param, index) => param.name === arg.name && !evaldArguments[index]);
11196
+ if (paramIndex < 0) {
10995
11197
  return false;
10996
11198
  }
10997
- if (allArgsCnt > this.params.length) {
10998
- return false;
11199
+ evaldArguments[paramIndex] = arg;
11200
+ }
11201
+
11202
+ for (let i = 0; i < this.params.length; i++) {
11203
+ if (evaldArguments[i]) {
11204
+ continue;
10999
11205
  }
11000
- } else {
11001
- if (requiredArgsCnt < (this.required - 1)) {
11206
+ if (this.params[i].variadic) {
11207
+ positionalIndex = positionalArgs.length;
11208
+ continue;
11209
+ }
11210
+ if (positionalIndex < positionalArgs.length) {
11211
+ evaldArguments[i] = positionalArgs[positionalIndex++];
11212
+ } else if (!this.params[i].name || !this.params[i].value) {
11002
11213
  return false;
11003
11214
  }
11004
11215
  }
11005
11216
 
11006
- // check patterns
11007
- len = Math.min(requiredArgsCnt, this.arity);
11217
+ if (positionalIndex < positionalArgs.length) {
11218
+ return false;
11219
+ }
11008
11220
 
11009
- for (let i = 0; i < len; i++) {
11221
+ // check patterns
11222
+ for (let i = 0; i < this.arity; i++) {
11010
11223
  if (!this.params[i].name && !this.params[i].variadic) {
11011
- if (/** @type {MixinArg[]} */ (args)[i].value.eval(context).toCSS(/** @type {EvalContext} */ ({})) != /** @type {Node} */ (this.params[i].value).eval(context).toCSS(/** @type {EvalContext} */ ({}))) {
11224
+ if (evaldArguments[i].value.eval(context).toCSS(/** @type {EvalContext} */ ({})) != /** @type {Node} */ (this.params[i].value).eval(context).toCSS(/** @type {EvalContext} */ ({}))) {
11012
11225
  return false;
11013
11226
  }
11014
11227
  }
@@ -14175,7 +14388,7 @@
14175
14388
  };
14176
14389
 
14177
14390
  var name = "less";
14178
- var version = "4.6.7";
14391
+ var version = "4.8.0";
14179
14392
  var description = "Leaner CSS";
14180
14393
  var homepage = "http://lesscss.org";
14181
14394
  var author = {
@@ -14245,7 +14458,7 @@
14245
14458
  var optionalDependencies = {
14246
14459
  errno: "^0.1.1",
14247
14460
  "graceful-fs": "^4.1.2",
14248
- "image-size": "~0.5.0",
14461
+ "probe-image-size": "^7.2.3",
14249
14462
  "make-dir": "^5.1.0",
14250
14463
  mime: "^1.4.1",
14251
14464
  needle: "^3.1.0",