less 4.7.0 → 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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Less - Leaner CSS v4.7.0
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>
@@ -4638,6 +4638,64 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
4638
4638
  warn('A bare @variable in an at-rule prelude is deprecated. Use @{variable} interpolation instead.', index, 'DEPRECATED', 'variable-in-at-rule-prelude');
4639
4639
  }
4640
4640
 
4641
+ /**
4642
+ * Numeric-leading variable names are a Less extension rather than valid CSS
4643
+ * identifier syntax. Keep accepting them through Less 4, but make the Less 5
4644
+ * migration visible at every actual variable reference or definition.
4645
+ *
4646
+ * @param {string} name - either an @-prefixed name or the name inside @{...}
4647
+ * @param {number} index - source position of the variable token
4648
+ */
4649
+ function warnNumericVariableName(name, index) {
4650
+ if (/^(?:@@?)?[0-9]/.test(name)) {
4651
+ 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');
4652
+ }
4653
+ }
4654
+
4655
+ /**
4656
+ * A lone '-' is not a CSS identifier. Less historically accepts it as a
4657
+ * variable name; retain that in Less 4 only and warn on definitions,
4658
+ * ordinary references, and interpolation references.
4659
+ *
4660
+ * @param {string} name - either @-/@@-, or the name inside @{...}
4661
+ * @param {number} index - source position of the variable token
4662
+ */
4663
+ function warnDashOnlyVariableName(name, index) {
4664
+ const bareName = name.replace(/^@@?/, '');
4665
+ if (bareName === '-') {
4666
+ 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');
4667
+ }
4668
+ }
4669
+
4670
+ /**
4671
+ * A lone '-' is not a CSS identifier. Less historically accepts it as a mixin
4672
+ * name after '.' or '#'; retain that in Less 4 only.
4673
+ *
4674
+ * @param {string} name
4675
+ * @param {number} index
4676
+ */
4677
+ function warnDashOnlyMixinName(name, index) {
4678
+ if (name === '.-' || name === '#-') {
4679
+ 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');
4680
+ }
4681
+ }
4682
+
4683
+ /**
4684
+ * CSS @charset is a source-header declaration, not a general dynamic at-rule.
4685
+ * Less 4 keeps its historical interpolation behavior for compatibility, but
4686
+ * makes each dynamic spelling visible before Less 5 rejects it.
4687
+ *
4688
+ * @param {import('../tree/node.js').default} value
4689
+ * @param {number} index - source position of the @charset token
4690
+ */
4691
+ function warnDynamicCharset(value, index) {
4692
+ if (value instanceof tree.Variable ||
4693
+ (value instanceof tree.Quoted &&
4694
+ (value.containsVariables() || value.value.match(value.propRegex)))) {
4695
+ 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');
4696
+ }
4697
+ }
4698
+
4641
4699
  function expect(arg, msg) {
4642
4700
  // some older browsers return typeof 'function' for RegExp
4643
4701
  const result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
@@ -5091,7 +5149,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
5091
5149
  }
5092
5150
 
5093
5151
  function condition() {
5094
- return [expect(parsers.condition, 'expected condition')];
5152
+ return [expect(() => parsers.condition(false, true), 'expected condition')];
5095
5153
  }
5096
5154
  },
5097
5155
 
@@ -5216,6 +5274,8 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
5216
5274
 
5217
5275
  parserInput.save();
5218
5276
  if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
5277
+ warnNumericVariableName(name, index);
5278
+ warnDashOnlyVariableName(name, index);
5219
5279
  ch = parserInput.currentChar();
5220
5280
  if ((ch === '(' && !parserInput.prevChar().match(/^\s/))
5221
5281
  || (ch === '[' && !parserInput.prevChar().match(/^\s/))) {
@@ -5238,6 +5298,8 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
5238
5298
  const index = parserInput.i;
5239
5299
 
5240
5300
  if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
5301
+ warnNumericVariableName(curly[1], index);
5302
+ warnDashOnlyVariableName(curly[1], index);
5241
5303
  return new(tree.Variable)(`@${curly[1]}`, index + currentIndex, fileInfo);
5242
5304
  }
5243
5305
  },
@@ -5360,7 +5422,11 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
5360
5422
  variable: function () {
5361
5423
  let name;
5362
5424
 
5363
- if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; }
5425
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) {
5426
+ warnNumericVariableName(name[1], parserInput.i - name[0].length);
5427
+ warnDashOnlyVariableName(name[1], parserInput.i - name[0].length);
5428
+ return name[1];
5429
+ }
5364
5430
  },
5365
5431
 
5366
5432
  //
@@ -5390,6 +5456,8 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
5390
5456
  }
5391
5457
 
5392
5458
  if (!inValue) {
5459
+ warnNumericVariableName(name[1], i);
5460
+ warnDashOnlyVariableName(name[1], i);
5393
5461
  name = name[1];
5394
5462
  }
5395
5463
 
@@ -5546,6 +5614,9 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
5546
5614
  if (inValue || parsers.end()) {
5547
5615
  parserInput.forget();
5548
5616
  const mixin = new(tree.mixin.Call)(elements, args, index + currentIndex, fileInfo, !lookups && important);
5617
+ for (const element of elements) {
5618
+ warnDashOnlyMixinName(element.value, element._index - currentIndex);
5619
+ }
5549
5620
  if (lookups) {
5550
5621
  return new tree.NamespaceValue(mixin, lookups);
5551
5622
  }
@@ -5742,6 +5813,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
5742
5813
  let ruleset;
5743
5814
  let cond;
5744
5815
  let variadic = false;
5816
+ const index = parserInput.i;
5745
5817
  if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
5746
5818
  parserInput.peek(/^[^{]*\}/)) {
5747
5819
  return;
@@ -5777,6 +5849,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
5777
5849
 
5778
5850
  if (ruleset) {
5779
5851
  parserInput.forget();
5852
+ warnDashOnlyMixinName(name, index);
5780
5853
  return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
5781
5854
  } else {
5782
5855
  parserInput.restore();
@@ -6825,6 +6898,9 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6825
6898
  if (!value) {
6826
6899
  error(`expected ${name} identifier`);
6827
6900
  }
6901
+ if (nonVendorSpecificName === '@charset') {
6902
+ warnDynamicCharset(value, index);
6903
+ }
6828
6904
  } else if (hasExpression) {
6829
6905
  // `@namespace` may carry an interpolated `@{ns}` prefix (or a
6830
6906
  // deprecated bare `@ns`). Parse that prefix directly so `@{ns}`
@@ -7029,7 +7105,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
7029
7105
  return condition || a;
7030
7106
  }
7031
7107
  },
7032
- condition: function (needsParens) {
7108
+ condition: function (needsParens, allowConditionOperands) {
7033
7109
  let result;
7034
7110
  let logical;
7035
7111
  let next;
@@ -7037,13 +7113,13 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
7037
7113
  return parserInput.$str('or');
7038
7114
  }
7039
7115
 
7040
- result = this.conditionAnd(needsParens);
7116
+ result = this.conditionAnd(needsParens, allowConditionOperands);
7041
7117
  if (!result) {
7042
7118
  return ;
7043
7119
  }
7044
7120
  logical = or();
7045
7121
  if (logical) {
7046
- next = this.condition(needsParens);
7122
+ next = this.condition(needsParens, allowConditionOperands);
7047
7123
  if (next) {
7048
7124
  result = new(tree.Condition)(logical, result, next);
7049
7125
  } else {
@@ -7052,13 +7128,13 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
7052
7128
  }
7053
7129
  return result;
7054
7130
  },
7055
- conditionAnd: function (needsParens) {
7131
+ conditionAnd: function (needsParens, allowConditionOperands) {
7056
7132
  let result;
7057
7133
  let logical;
7058
7134
  let next;
7059
7135
  const self = this;
7060
7136
  function insideCondition() {
7061
- const cond = self.negatedCondition(needsParens) || self.parenthesisCondition(needsParens);
7137
+ const cond = self.negatedCondition(needsParens, allowConditionOperands) || self.parenthesisCondition(needsParens, allowConditionOperands);
7062
7138
  if (!cond && !needsParens) {
7063
7139
  return self.atomicCondition(needsParens);
7064
7140
  }
@@ -7072,9 +7148,12 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
7072
7148
  if (!result) {
7073
7149
  return ;
7074
7150
  }
7151
+ if (allowConditionOperands) {
7152
+ result = this.atomicCondition(needsParens, result, allowConditionOperands) || result;
7153
+ }
7075
7154
  logical = and();
7076
7155
  if (logical) {
7077
- next = this.conditionAnd(needsParens);
7156
+ next = this.conditionAnd(needsParens, allowConditionOperands);
7078
7157
  if (next) {
7079
7158
  result = new(tree.Condition)(logical, result, next);
7080
7159
  } else {
@@ -7083,9 +7162,9 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
7083
7162
  }
7084
7163
  return result;
7085
7164
  },
7086
- negatedCondition: function (needsParens) {
7165
+ negatedCondition: function (needsParens, allowConditionOperands) {
7087
7166
  if (parserInput.$str('not')) {
7088
- const result = this.parenthesisCondition(needsParens);
7167
+ const result = this.parenthesisCondition(needsParens, allowConditionOperands);
7089
7168
  if (result) {
7090
7169
  result.negate = !result.negate;
7091
7170
  return result;
@@ -7102,11 +7181,11 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
7102
7181
  }
7103
7182
  }
7104
7183
  },
7105
- parenthesisCondition: function (needsParens) {
7184
+ parenthesisCondition: function (needsParens, allowConditionOperands) {
7106
7185
  function tryConditionFollowedByParenthesis(me) {
7107
7186
  let body;
7108
7187
  parserInput.save();
7109
- body = me.condition(needsParens);
7188
+ body = me.condition(needsParens, allowConditionOperands);
7110
7189
  if (!body) {
7111
7190
  parserInput.restore();
7112
7191
  return ;
@@ -7143,7 +7222,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
7143
7222
  parserInput.forget();
7144
7223
  return body;
7145
7224
  },
7146
- atomicCondition: function (needsParens, preparsedCond) {
7225
+ atomicCondition: function (needsParens, preparsedCond, allowConditionOperands) {
7147
7226
  const entities = this.entities;
7148
7227
  const index = parserInput.i;
7149
7228
  let a;
@@ -7152,7 +7231,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
7152
7231
  let op;
7153
7232
 
7154
7233
  const cond = (function() {
7155
- return this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
7234
+ return (allowConditionOperands && this.parenthesisCondition(needsParens)) || this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
7156
7235
  }).bind(this);
7157
7236
 
7158
7237
  if (preparsedCond) {
@@ -7313,6 +7392,11 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
7313
7392
  }
7314
7393
  for (k = 0; k < name.length; k++) {
7315
7394
  s = name[k];
7395
+ if (s.charAt(0) === '@') {
7396
+ const variableName = s.slice(2, -1);
7397
+ warnNumericVariableName(variableName, index[k]);
7398
+ warnDashOnlyVariableName(variableName, index[k]);
7399
+ }
7316
7400
  name[k] = (s.charAt(0) !== '@' && s.charAt(0) !== '$') ?
7317
7401
  new(tree.Keyword)(s) :
7318
7402
  (s.charAt(0) === '@' ?
@@ -11265,35 +11349,47 @@ class Definition extends Ruleset {
11265
11349
  */
11266
11350
  matchArgs(args, context) {
11267
11351
  const allArgsCnt = (args && args.length) || 0;
11268
- let len;
11269
- const optionalParameters = this.optionalParameters;
11270
- const requiredArgsCnt = !args ? 0 : args.reduce(function (/** @type {number} */ count, /** @type {MixinArg} */ p) {
11271
- if (optionalParameters.indexOf(p.name) < 0) {
11272
- return count + 1;
11273
- } else {
11274
- return count;
11352
+ const evaldArguments = new Array(this.params.length);
11353
+ const positionalArgs = [];
11354
+ let positionalIndex = 0;
11355
+
11356
+ for (let i = 0; i < allArgsCnt; i++) {
11357
+ const arg = /** @type {MixinArg[]} */ (args)[i];
11358
+ if (!arg.name) {
11359
+ positionalArgs.push(arg);
11360
+ continue;
11275
11361
  }
11276
- }, 0);
11277
11362
 
11278
- if (!this.variadic) {
11279
- if (requiredArgsCnt < this.required) {
11363
+ const paramIndex = this.params.findIndex((param, index) => param.name === arg.name && !evaldArguments[index]);
11364
+ if (paramIndex < 0) {
11280
11365
  return false;
11281
11366
  }
11282
- if (allArgsCnt > this.params.length) {
11283
- return false;
11367
+ evaldArguments[paramIndex] = arg;
11368
+ }
11369
+
11370
+ for (let i = 0; i < this.params.length; i++) {
11371
+ if (evaldArguments[i]) {
11372
+ continue;
11284
11373
  }
11285
- } else {
11286
- if (requiredArgsCnt < (this.required - 1)) {
11374
+ if (this.params[i].variadic) {
11375
+ positionalIndex = positionalArgs.length;
11376
+ continue;
11377
+ }
11378
+ if (positionalIndex < positionalArgs.length) {
11379
+ evaldArguments[i] = positionalArgs[positionalIndex++];
11380
+ } else if (!this.params[i].name || !this.params[i].value) {
11287
11381
  return false;
11288
11382
  }
11289
11383
  }
11290
11384
 
11291
- // check patterns
11292
- len = Math.min(requiredArgsCnt, this.arity);
11385
+ if (positionalIndex < positionalArgs.length) {
11386
+ return false;
11387
+ }
11293
11388
 
11294
- for (let i = 0; i < len; i++) {
11389
+ // check patterns
11390
+ for (let i = 0; i < this.arity; i++) {
11295
11391
  if (!this.params[i].name && !this.params[i].variadic) {
11296
- if (/** @type {MixinArg[]} */ (args)[i].value.eval(context).toCSS(/** @type {EvalContext} */ ({})) != /** @type {Node} */ (this.params[i].value).eval(context).toCSS(/** @type {EvalContext} */ ({}))) {
11392
+ if (evaldArguments[i].value.eval(context).toCSS(/** @type {EvalContext} */ ({})) != /** @type {Node} */ (this.params[i].value).eval(context).toCSS(/** @type {EvalContext} */ ({}))) {
11297
11393
  return false;
11298
11394
  }
11299
11395
  }
@@ -14226,7 +14322,7 @@ var imageSize = environment => {
14226
14322
  };
14227
14323
 
14228
14324
  createRequire();
14229
- const version = "4.7.0";
14325
+ const version = "4.8.0";
14230
14326
 
14231
14327
  const less = createFromEnvironment(environment, [new FileManager(), new UrlFileManager()], version);
14232
14328