less 4.6.6 → 4.7.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.6
2
+ * Less - Leaner CSS v4.7.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,17 @@
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
+
4433
4473
  function expect(arg, msg) {
4434
4474
  // some older browsers return typeof 'function' for RegExp
4435
4475
  const result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
@@ -5980,7 +6020,7 @@
5980
6020
  if (parserInput.$char(';')) {
5981
6021
  value = new Anonymous('');
5982
6022
  } else {
5983
- value = this.permissiveValue(/[;}]/, true);
6023
+ value = this.permissiveValue(/[;}]/);
5984
6024
  }
5985
6025
  }
5986
6026
  // Try to store values as anonymous
@@ -6039,8 +6079,12 @@
6039
6079
  * math is allowed.
6040
6080
  *
6041
6081
  * @param {RexExp} untilTokens - Characters to stop parsing at
6082
+ * @param {boolean} [deprecateVariables] - when set, this is an at-rule
6083
+ * prelude (non-value position); accept `@{var}` interpolation and warn
6084
+ * on a bare `@var` reference (which resolves today but is deprecated).
6042
6085
  */
6043
- permissiveValue: function (untilTokens) {
6086
+ permissiveValue: function (untilTokens, deprecateVariables) {
6087
+ const entities = this.entities;
6044
6088
  let i;
6045
6089
  let e;
6046
6090
  let done;
@@ -6067,7 +6111,20 @@
6067
6111
  value.push(e);
6068
6112
  continue;
6069
6113
  }
6070
- e = this.entity();
6114
+ if (deprecateVariables) {
6115
+ // In an at-rule prelude, `@{var}` interpolation is the supported
6116
+ // form; consume it here so its `{` is not mistaken for a block.
6117
+ e = entities.variableCurly();
6118
+ if (!e) {
6119
+ const varIndex = parserInput.i;
6120
+ e = this.entity();
6121
+ if (e && e.type === 'Variable') {
6122
+ warnBareAtRuleVariable(varIndex);
6123
+ }
6124
+ }
6125
+ } else {
6126
+ e = this.entity();
6127
+ }
6071
6128
  if (e) {
6072
6129
  value.push(e);
6073
6130
  }
@@ -6094,7 +6151,7 @@
6094
6151
  }
6095
6152
  parserInput.save();
6096
6153
 
6097
- value = parserInput.$parseUntil(tok);
6154
+ value = parserInput.$parseUntil(tok, deprecateVariables);
6098
6155
 
6099
6156
  if (value) {
6100
6157
  if (typeof value === 'string') {
@@ -6104,6 +6161,14 @@
6104
6161
  parserInput.forget();
6105
6162
  return new tree.Anonymous('', index);
6106
6163
  }
6164
+ // At-rule prelude: `$parseUntil` (deprecateVariables) records the
6165
+ // first bare `@var` it saw outside any `(...)` in its single pass —
6166
+ // a structural reference (`[...]`/`{...}` don't shield it, only a
6167
+ // declaration-value `(...)` does). Warn once here rather than
6168
+ // re-scanning the text.
6169
+ if (deprecateVariables && value.bareVarIndex !== null && value.bareVarIndex !== undefined) {
6170
+ warnBareAtRuleVariable(value.bareVarIndex);
6171
+ }
6107
6172
  /** @type {string} */
6108
6173
  let item;
6109
6174
  for (i = 0; i < value.length; i++) {
@@ -6120,11 +6185,14 @@
6120
6185
  const quote = new tree.Quoted('\'', item, true, index, fileInfo);
6121
6186
  const variableRegex = /@([\w-]+)/g;
6122
6187
  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');
6188
+ // At-rule preludes are handled once above via
6189
+ // `value.bareVarIndex`; the `variable-in-unknown-value`
6190
+ // notice is for unknown declaration values only.
6191
+ if (!deprecateVariables && variableRegex.test(item)) {
6192
+ warn('@variable in unknown values will not be evaluated as variables in the future. Use @{variable}', index, 'DEPRECATED', 'variable-in-unknown-value');
6125
6193
  }
6126
6194
  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');
6195
+ warn('$property in unknown values will not be evaluated as property references in the future. Use ${property}', index, 'DEPRECATED', 'property-in-unknown-value');
6128
6196
  }
6129
6197
  quote.variableRegex = /@([\w-]+)|@{([\w-]+)}/g;
6130
6198
  quote.propRegex = /\$([\w-]+)|\${([\w-]+)}/g;
@@ -6233,7 +6301,17 @@
6233
6301
  }
6234
6302
  parserInput.restore();
6235
6303
 
6236
- e = entities.declarationCall.bind(this)() || cssKeyword() || entities.keyword() || entities.variable() || entities.mixinLookup();
6304
+ e = entities.declarationCall.bind(this)() || cssKeyword() || entities.keyword() || entities.variableCurly();
6305
+ if (!e) {
6306
+ const varIndex = parserInput.i;
6307
+ const bareVariable = entities.variable();
6308
+ if (bareVariable) {
6309
+ warnBareAtRuleVariable(varIndex);
6310
+ e = bareVariable;
6311
+ } else {
6312
+ e = entities.mixinLookup();
6313
+ }
6314
+ }
6237
6315
  if (e) {
6238
6316
  nodes.push(e);
6239
6317
  if (e.type === 'Variable' ||
@@ -6244,7 +6322,7 @@
6244
6322
  let closed = false;
6245
6323
  p = this.property();
6246
6324
  parserInput.save();
6247
- if (!p && syntaxOptions.queryInParens && parserInput.$re(/^[0-9a-z-]*\s*([<>]=|<=|>=|[<>]|=)/)) {
6325
+ if (!p && syntaxOptions.queryInParens && parserInput.$re(/^(?:[^()]|\([^()]*\))*\s*([<>]=|<=|>=|[<>]|=)/)) {
6248
6326
  parserInput.restore();
6249
6327
  p = this.condition();
6250
6328
 
@@ -6324,7 +6402,17 @@
6324
6402
  features[features.length - 1].noSpacing = false;
6325
6403
  }
6326
6404
  } else {
6327
- e = entities.variable() || entities.mixinLookup();
6405
+ e = entities.variableCurly();
6406
+ if (!e) {
6407
+ const varIndex = parserInput.i;
6408
+ const bareVariable = entities.variable();
6409
+ if (bareVariable) {
6410
+ warnBareAtRuleVariable(varIndex);
6411
+ e = bareVariable;
6412
+ } else {
6413
+ e = entities.mixinLookup();
6414
+ }
6415
+ }
6328
6416
  if (e) {
6329
6417
  features.push(e);
6330
6418
  if (!parserInput.$char(',')) { break; }
@@ -6438,8 +6526,24 @@
6438
6526
  return null;
6439
6527
  }
6440
6528
  },
6529
+ /**
6530
+ * An entity in a non-value at-rule position (an at-rule identifier,
6531
+ * name, or keyword-list item — e.g. the name in `@keyframes @foo`).
6532
+ * `@{foo}` interpolation is the supported form; a bare `@foo` still
6533
+ * resolves but is deprecated.
6534
+ */
6535
+ atRuleEntity: function () {
6536
+ const curly = this.entities.variableCurly();
6537
+ if (curly) { return curly; }
6538
+ const index = parserInput.i;
6539
+ const e = this.entity();
6540
+ if (e && e.type === 'Variable') {
6541
+ warnBareAtRuleVariable(index);
6542
+ }
6543
+ return e;
6544
+ },
6441
6545
  atruleUnknown: function (value, name, hasBlock) {
6442
- value = this.permissiveValue(/^[{;]/);
6546
+ value = this.permissiveValue(/^[{;]/, true);
6443
6547
  hasBlock = (parserInput.currentChar() === '{');
6444
6548
  if (!value) {
6445
6549
  if (!hasBlock && parserInput.currentChar() !== ';') {
@@ -6455,16 +6559,16 @@
6455
6559
  rules = this.blockRuleset();
6456
6560
  parserInput.save();
6457
6561
  if (!rules && !isRooted) {
6458
- value = this.entity();
6562
+ value = this.atRuleEntity();
6459
6563
  rules = this.blockRuleset();
6460
6564
  }
6461
6565
  if (!rules && !isRooted) {
6462
6566
  parserInput.restore();
6463
6567
  var e = [];
6464
- value = this.entity();
6568
+ value = this.atRuleEntity();
6465
6569
  while (parserInput.$char(',')) {
6466
6570
  e.push(value);
6467
- value = this.entity();
6571
+ value = this.atRuleEntity();
6468
6572
  }
6469
6573
  if (value && e.length > 0) {
6470
6574
  e.push(value);
@@ -6549,12 +6653,25 @@
6549
6653
  parserInput.commentStore.length = 0;
6550
6654
 
6551
6655
  if (hasIdentifier) {
6552
- value = this.entity();
6656
+ value = this.atRuleEntity();
6553
6657
  if (!value) {
6554
6658
  error(`expected ${name} identifier`);
6555
6659
  }
6556
6660
  } else if (hasExpression) {
6661
+ // `@namespace` may carry an interpolated `@{ns}` prefix (or a
6662
+ // deprecated bare `@ns`). Parse that prefix directly so `@{ns}`
6663
+ // is accepted here without treating value positions as
6664
+ // interpolation contexts, then read the namespace URL.
6665
+ let prefix = this.entities.variableCurly();
6666
+ if (!prefix && parserInput.peek(/^@@?[\w-]/)) {
6667
+ const prefixIndex = parserInput.i;
6668
+ prefix = this.entities.variable();
6669
+ if (prefix) { warnBareAtRuleVariable(prefixIndex); }
6670
+ }
6557
6671
  value = this.expression();
6672
+ if (prefix) {
6673
+ value = value ? new(tree.Expression)([prefix, ...value.value]) : prefix;
6674
+ }
6558
6675
  if (!value) {
6559
6676
  error(`expected ${name} expression`);
6560
6677
  }
@@ -8617,16 +8734,16 @@
8617
8734
  self.features = new Value(self.permute(/** @type {Node[][]} */ (/** @type {unknown} */ (path))).map(
8618
8735
  /** @param {Node | Node[]} path */
8619
8736
  path => {
8620
- path = /** @type {Node[]} */ (path).map(
8737
+ path = /** @type {Node[]} */ (path).map(
8621
8738
  /** @param {Node & { toCSS?: Function }} fragment */
8622
- fragment => fragment.toCSS ? fragment : new Anonymous(/** @type {string} */ (/** @type {unknown} */ (fragment))));
8739
+ fragment => fragment.toCSS ? fragment : new Anonymous(/** @type {string} */ (/** @type {unknown} */ (fragment))));
8623
8740
 
8624
- for (i = /** @type {Node[]} */ (path).length - 1; i > 0; i--) {
8741
+ for (i = /** @type {Node[]} */ (path).length - 1; i > 0; i--) {
8625
8742
  /** @type {Node[]} */ (path).splice(i, 0, new Anonymous('and'));
8626
- }
8743
+ }
8627
8744
 
8628
- return new Expression(/** @type {Node[]} */ (path));
8629
- }));
8745
+ return new Expression(/** @type {Node[]} */ (path));
8746
+ }));
8630
8747
  self.setParent(self.features, self);
8631
8748
 
8632
8749
  // Fake a tree-node that doesn't output anything.
@@ -14175,7 +14292,7 @@
14175
14292
  };
14176
14293
 
14177
14294
  var name = "less";
14178
- var version = "4.6.6";
14295
+ var version = "4.7.0";
14179
14296
  var description = "Leaner CSS";
14180
14297
  var homepage = "http://lesscss.org";
14181
14298
  var author = {
@@ -14245,7 +14362,7 @@
14245
14362
  var optionalDependencies = {
14246
14363
  errno: "^0.1.1",
14247
14364
  "graceful-fs": "^4.1.2",
14248
- "image-size": "~0.5.0",
14365
+ "probe-image-size": "^7.2.3",
14249
14366
  "make-dir": "^5.1.0",
14250
14367
  mime: "^1.4.1",
14251
14368
  needle: "^3.1.0",