less 4.7.0 → 4.8.1

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.7.0
2
+ * Less - Leaner CSS v4.8.1
3
3
  * http://lesscss.org
4
4
  *
5
5
  * Copyright (c) 2009-2026, Alexis Sellier <self@cloudhead.net>
@@ -4470,6 +4470,64 @@
4470
4470
  warn('A bare @variable in an at-rule prelude is deprecated. Use @{variable} interpolation instead.', index, 'DEPRECATED', 'variable-in-at-rule-prelude');
4471
4471
  }
4472
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
+
4473
4531
  function expect(arg, msg) {
4474
4532
  // some older browsers return typeof 'function' for RegExp
4475
4533
  const result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
@@ -4923,7 +4981,7 @@
4923
4981
  }
4924
4982
 
4925
4983
  function condition() {
4926
- return [expect(parsers.condition, 'expected condition')];
4984
+ return [expect(() => parsers.condition(false, true), 'expected condition')];
4927
4985
  }
4928
4986
  },
4929
4987
 
@@ -5048,6 +5106,8 @@
5048
5106
 
5049
5107
  parserInput.save();
5050
5108
  if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
5109
+ warnNumericVariableName(name, index);
5110
+ warnDashOnlyVariableName(name, index);
5051
5111
  ch = parserInput.currentChar();
5052
5112
  if ((ch === '(' && !parserInput.prevChar().match(/^\s/))
5053
5113
  || (ch === '[' && !parserInput.prevChar().match(/^\s/))) {
@@ -5070,6 +5130,8 @@
5070
5130
  const index = parserInput.i;
5071
5131
 
5072
5132
  if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
5133
+ warnNumericVariableName(curly[1], index);
5134
+ warnDashOnlyVariableName(curly[1], index);
5073
5135
  return new(tree.Variable)(`@${curly[1]}`, index + currentIndex, fileInfo);
5074
5136
  }
5075
5137
  },
@@ -5192,7 +5254,11 @@
5192
5254
  variable: function () {
5193
5255
  let name;
5194
5256
 
5195
- 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
+ }
5196
5262
  },
5197
5263
 
5198
5264
  //
@@ -5222,6 +5288,8 @@
5222
5288
  }
5223
5289
 
5224
5290
  if (!inValue) {
5291
+ warnNumericVariableName(name[1], i);
5292
+ warnDashOnlyVariableName(name[1], i);
5225
5293
  name = name[1];
5226
5294
  }
5227
5295
 
@@ -5378,6 +5446,9 @@
5378
5446
  if (inValue || parsers.end()) {
5379
5447
  parserInput.forget();
5380
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
+ }
5381
5452
  if (lookups) {
5382
5453
  return new tree.NamespaceValue(mixin, lookups);
5383
5454
  }
@@ -5574,6 +5645,7 @@
5574
5645
  let ruleset;
5575
5646
  let cond;
5576
5647
  let variadic = false;
5648
+ const index = parserInput.i;
5577
5649
  if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
5578
5650
  parserInput.peek(/^[^{]*\}/)) {
5579
5651
  return;
@@ -5609,6 +5681,7 @@
5609
5681
 
5610
5682
  if (ruleset) {
5611
5683
  parserInput.forget();
5684
+ warnDashOnlyMixinName(name, index);
5612
5685
  return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
5613
5686
  } else {
5614
5687
  parserInput.restore();
@@ -6657,6 +6730,9 @@
6657
6730
  if (!value) {
6658
6731
  error(`expected ${name} identifier`);
6659
6732
  }
6733
+ if (nonVendorSpecificName === '@charset') {
6734
+ warnDynamicCharset(value, index);
6735
+ }
6660
6736
  } else if (hasExpression) {
6661
6737
  // `@namespace` may carry an interpolated `@{ns}` prefix (or a
6662
6738
  // deprecated bare `@ns`). Parse that prefix directly so `@{ns}`
@@ -6861,7 +6937,7 @@
6861
6937
  return condition || a;
6862
6938
  }
6863
6939
  },
6864
- condition: function (needsParens) {
6940
+ condition: function (needsParens, allowConditionOperands) {
6865
6941
  let result;
6866
6942
  let logical;
6867
6943
  let next;
@@ -6869,13 +6945,13 @@
6869
6945
  return parserInput.$str('or');
6870
6946
  }
6871
6947
 
6872
- result = this.conditionAnd(needsParens);
6948
+ result = this.conditionAnd(needsParens, allowConditionOperands);
6873
6949
  if (!result) {
6874
6950
  return ;
6875
6951
  }
6876
6952
  logical = or();
6877
6953
  if (logical) {
6878
- next = this.condition(needsParens);
6954
+ next = this.condition(needsParens, allowConditionOperands);
6879
6955
  if (next) {
6880
6956
  result = new(tree.Condition)(logical, result, next);
6881
6957
  } else {
@@ -6884,13 +6960,13 @@
6884
6960
  }
6885
6961
  return result;
6886
6962
  },
6887
- conditionAnd: function (needsParens) {
6963
+ conditionAnd: function (needsParens, allowConditionOperands) {
6888
6964
  let result;
6889
6965
  let logical;
6890
6966
  let next;
6891
6967
  const self = this;
6892
6968
  function insideCondition() {
6893
- const cond = self.negatedCondition(needsParens) || self.parenthesisCondition(needsParens);
6969
+ const cond = self.negatedCondition(needsParens, allowConditionOperands) || self.parenthesisCondition(needsParens, allowConditionOperands);
6894
6970
  if (!cond && !needsParens) {
6895
6971
  return self.atomicCondition(needsParens);
6896
6972
  }
@@ -6904,9 +6980,12 @@
6904
6980
  if (!result) {
6905
6981
  return ;
6906
6982
  }
6983
+ if (allowConditionOperands) {
6984
+ result = this.atomicCondition(needsParens, result, allowConditionOperands) || result;
6985
+ }
6907
6986
  logical = and();
6908
6987
  if (logical) {
6909
- next = this.conditionAnd(needsParens);
6988
+ next = this.conditionAnd(needsParens, allowConditionOperands);
6910
6989
  if (next) {
6911
6990
  result = new(tree.Condition)(logical, result, next);
6912
6991
  } else {
@@ -6915,9 +6994,9 @@
6915
6994
  }
6916
6995
  return result;
6917
6996
  },
6918
- negatedCondition: function (needsParens) {
6997
+ negatedCondition: function (needsParens, allowConditionOperands) {
6919
6998
  if (parserInput.$str('not')) {
6920
- const result = this.parenthesisCondition(needsParens);
6999
+ const result = this.parenthesisCondition(needsParens, allowConditionOperands);
6921
7000
  if (result) {
6922
7001
  result.negate = !result.negate;
6923
7002
  return result;
@@ -6934,11 +7013,11 @@
6934
7013
  }
6935
7014
  }
6936
7015
  },
6937
- parenthesisCondition: function (needsParens) {
7016
+ parenthesisCondition: function (needsParens, allowConditionOperands) {
6938
7017
  function tryConditionFollowedByParenthesis(me) {
6939
7018
  let body;
6940
7019
  parserInput.save();
6941
- body = me.condition(needsParens);
7020
+ body = me.condition(needsParens, allowConditionOperands);
6942
7021
  if (!body) {
6943
7022
  parserInput.restore();
6944
7023
  return ;
@@ -6975,7 +7054,7 @@
6975
7054
  parserInput.forget();
6976
7055
  return body;
6977
7056
  },
6978
- atomicCondition: function (needsParens, preparsedCond) {
7057
+ atomicCondition: function (needsParens, preparsedCond, allowConditionOperands) {
6979
7058
  const entities = this.entities;
6980
7059
  const index = parserInput.i;
6981
7060
  let a;
@@ -6984,7 +7063,7 @@
6984
7063
  let op;
6985
7064
 
6986
7065
  const cond = (function() {
6987
- return this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
7066
+ return (allowConditionOperands && this.parenthesisCondition(needsParens)) || this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
6988
7067
  }).bind(this);
6989
7068
 
6990
7069
  if (preparsedCond) {
@@ -7145,6 +7224,11 @@
7145
7224
  }
7146
7225
  for (k = 0; k < name.length; k++) {
7147
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
+ }
7148
7232
  name[k] = (s.charAt(0) !== '@' && s.charAt(0) !== '$') ?
7149
7233
  new(tree.Keyword)(s) :
7150
7234
  (s.charAt(0) === '@' ?
@@ -11097,35 +11181,47 @@
11097
11181
  */
11098
11182
  matchArgs(args, context) {
11099
11183
  const allArgsCnt = (args && args.length) || 0;
11100
- let len;
11101
- const optionalParameters = this.optionalParameters;
11102
- const requiredArgsCnt = !args ? 0 : args.reduce(function (/** @type {number} */ count, /** @type {MixinArg} */ p) {
11103
- if (optionalParameters.indexOf(p.name) < 0) {
11104
- return count + 1;
11105
- } else {
11106
- 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;
11107
11193
  }
11108
- }, 0);
11109
11194
 
11110
- if (!this.variadic) {
11111
- if (requiredArgsCnt < this.required) {
11195
+ const paramIndex = this.params.findIndex((param, index) => param.name === arg.name && !evaldArguments[index]);
11196
+ if (paramIndex < 0) {
11112
11197
  return false;
11113
11198
  }
11114
- if (allArgsCnt > this.params.length) {
11115
- return false;
11199
+ evaldArguments[paramIndex] = arg;
11200
+ }
11201
+
11202
+ for (let i = 0; i < this.params.length; i++) {
11203
+ if (evaldArguments[i]) {
11204
+ continue;
11116
11205
  }
11117
- } else {
11118
- 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) {
11119
11213
  return false;
11120
11214
  }
11121
11215
  }
11122
11216
 
11123
- // check patterns
11124
- len = Math.min(requiredArgsCnt, this.arity);
11217
+ if (positionalIndex < positionalArgs.length) {
11218
+ return false;
11219
+ }
11125
11220
 
11126
- for (let i = 0; i < len; i++) {
11221
+ // check patterns
11222
+ for (let i = 0; i < this.arity; i++) {
11127
11223
  if (!this.params[i].name && !this.params[i].variadic) {
11128
- 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} */ ({}))) {
11129
11225
  return false;
11130
11226
  }
11131
11227
  }
@@ -11281,7 +11377,7 @@
11281
11377
  for (m = 0; m < expandedValues.length; m++) {
11282
11378
  args.push({value: expandedValues[m]});
11283
11379
  }
11284
- } else {
11380
+ } else if (argValue.type === 'Expression' && Array.isArray(argValue.value) && argValue.value.length === 0) ; else {
11285
11381
  args.push({name: arg.name, value: argValue});
11286
11382
  }
11287
11383
  }
@@ -12516,8 +12612,15 @@
12516
12612
  }
12517
12613
  };
12518
12614
 
12519
- const MathHelper = (fn, unit, n) => {
12615
+ const MathHelper = (fn, unit, cssEvaluable, n) => {
12520
12616
  if (!(n instanceof Dimension)) {
12617
+ // A runtime CSS value such as var()/env() stays an unevaluated call and
12618
+ // cannot be resolved to a number at compile time. Only functions with a
12619
+ // CSS equivalent may be left for the browser; the rest (percentage, ceil,
12620
+ // floor, round) have no CSS form and keep erroring on such input.
12621
+ if (cssEvaluable && n && n.type === 'Call') {
12622
+ return undefined;
12623
+ }
12521
12624
  throw { type: 'Argument', message: 'argument must be a number' };
12522
12625
  }
12523
12626
  if (unit === null) {
@@ -12529,29 +12632,30 @@
12529
12632
  };
12530
12633
 
12531
12634
  const mathFunctions = {
12532
- // name, unit
12533
- ceil: null,
12534
- floor: null,
12535
- sqrt: null,
12536
- abs: null,
12537
- tan: '',
12538
- sin: '',
12539
- cos: '',
12540
- atan: 'rad',
12541
- asin: 'rad',
12542
- acos: 'rad'
12635
+ // name: [unit, has a CSS equivalent that the browser can evaluate]
12636
+ ceil: [null, false],
12637
+ floor: [null, false],
12638
+ sqrt: [null, true],
12639
+ abs: [null, true],
12640
+ tan: ['', true],
12641
+ sin: ['', true],
12642
+ cos: ['', true],
12643
+ atan: ['rad', true],
12644
+ asin: ['rad', true],
12645
+ acos: ['rad', true]
12543
12646
  };
12544
12647
 
12545
12648
  for (const f in mathFunctions) {
12546
12649
  // eslint-disable-next-line no-prototype-builtins
12547
12650
  if (mathFunctions.hasOwnProperty(f)) {
12548
- mathFunctions[f] = MathHelper.bind(null, Math[f], mathFunctions[f]);
12651
+ const [unit, cssEvaluable] = mathFunctions[f];
12652
+ mathFunctions[f] = MathHelper.bind(null, Math[f], unit, cssEvaluable);
12549
12653
  }
12550
12654
  }
12551
12655
 
12552
12656
  mathFunctions.round = (n, f) => {
12553
12657
  const fraction = typeof f === 'undefined' ? 0 : f.value;
12554
- return MathHelper(num => num.toFixed(fraction), null, n);
12658
+ return MathHelper(num => num.toFixed(fraction), null, false, n);
12555
12659
  };
12556
12660
 
12557
12661
  const minMax = function (isMin, args) {
@@ -12640,7 +12744,7 @@
12640
12744
  return new Dimension(Math.pow(x.value, y.value), x.unit);
12641
12745
  },
12642
12746
  percentage: function (n) {
12643
- const result = MathHelper(num => num * 100, '%', n);
12747
+ const result = MathHelper(num => num * 100, '%', false, n);
12644
12748
 
12645
12749
  return result;
12646
12750
  }
@@ -14292,7 +14396,7 @@
14292
14396
  };
14293
14397
 
14294
14398
  var name = "less";
14295
- var version = "4.7.0";
14399
+ var version = "4.8.1";
14296
14400
  var description = "Leaner CSS";
14297
14401
  var homepage = "http://lesscss.org";
14298
14402
  var author = {