less 4.6.7 → 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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Less - Leaner CSS v4.6.7
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>
@@ -31,6 +31,7 @@ class SourceMapGeneratorFallback {
31
31
  return null;
32
32
  }
33
33
  }
34
+
34
35
  var environment = {
35
36
  encodeBase64: function encodeBase64(str) {
36
37
  // Avoid Buffer constructor on newer versions of Node.js.
@@ -40,9 +41,9 @@ var environment = {
40
41
  mimeLookup: function (filename) {
41
42
  try {
42
43
  const mimeModule = require$6('mime');
43
- return mimeModule ? mimeModule.lookup(filename) : "application/octet-stream";
44
+ return mimeModule ? mimeModule.lookup(filename) : 'application/octet-stream';
44
45
  } catch (e) {
45
- return "application/octet-stream";
46
+ return 'application/octet-stream';
46
47
  }
47
48
  },
48
49
  charsetLookup: function (mime) {
@@ -4267,12 +4268,23 @@ var getParserInput = () => {
4267
4268
  /**
4268
4269
  * Permissive parsing. Ignores everything except matching {} [] () and quotes
4269
4270
  * until matching token (outside of blocks)
4270
- */
4271
- parserInput.$parseUntil = tok => {
4271
+ *
4272
+ * @param {string|RegExp} tok - stop token
4273
+ * @param {boolean} [detectBareVar] - when set, also record the position of the
4274
+ * first bare `@variable` reference (not `@{interpolation}`) that appears at
4275
+ * PAREN depth 0 — i.e. a structural reference, not a declaration value inside
4276
+ * `(...)`. Reuses this single pass (which already skips strings/comments) so
4277
+ * callers don't re-scan the text. Exposed as `.bareVarIndex` on the returned
4278
+ * group array (or null). `[...]`/`{...}` do NOT shield a reference — only
4279
+ * `(...)` (a declaration-value group) does.
4280
+ */
4281
+ parserInput.$parseUntil = (tok, detectBareVar) => {
4272
4282
  let quote = '';
4273
4283
  let returnVal = null;
4274
4284
  let inComment = false;
4275
4285
  let blockDepth = 0;
4286
+ let parenDepth = 0;
4287
+ let bareVarIndex = null;
4276
4288
  const blockStack = [];
4277
4289
  const parseGroups = [];
4278
4290
  const length = input.length;
@@ -4312,6 +4324,12 @@ var getParserInput = () => {
4312
4324
  i++;
4313
4325
  continue;
4314
4326
  }
4327
+ if (detectBareVar && bareVarIndex === null && nextChar === '@' && parenDepth === 0) {
4328
+ // A bare `@ident` (not `@{interpolation}`) outside any `(...)` —
4329
+ // a structural reference. Strings/comments are already skipped above.
4330
+ const after = input.charAt(i + 1);
4331
+ if (after && /[-\w]/.test(after)) { bareVarIndex = i; }
4332
+ }
4315
4333
  switch (nextChar) {
4316
4334
  case '\\':
4317
4335
  i++;
@@ -4347,6 +4365,7 @@ var getParserInput = () => {
4347
4365
  case '(':
4348
4366
  blockStack.push(')');
4349
4367
  blockDepth++;
4368
+ parenDepth++;
4350
4369
  break;
4351
4370
  case '[':
4352
4371
  blockStack.push(']');
@@ -4358,6 +4377,7 @@ var getParserInput = () => {
4358
4377
  const expected = blockStack.pop();
4359
4378
  if (nextChar === expected) {
4360
4379
  blockDepth--;
4380
+ if (nextChar === ')' && parenDepth > 0) { parenDepth--; }
4361
4381
  } else {
4362
4382
  // move the parser to the error and return expected
4363
4383
  skipWhitespace(i - startPos);
@@ -4373,6 +4393,7 @@ var getParserInput = () => {
4373
4393
  }
4374
4394
  } while (loop);
4375
4395
 
4396
+ if (Array.isArray(returnVal)) { returnVal.bareVarIndex = bareVarIndex; }
4376
4397
  return returnVal ? returnVal : null;
4377
4398
  };
4378
4399
 
@@ -4573,6 +4594,10 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
4573
4594
 
4574
4595
  const deprecationHandler = new DeprecationHandler();
4575
4596
 
4597
+ // Tracks `${deprecationId}@${index}` pairs already warned about, so a source
4598
+ // position that gets re-parsed via parser backtracking only warns once.
4599
+ const warnedDeprecations = new Set();
4600
+
4576
4601
  /**
4577
4602
  * @param {string} msg
4578
4603
  * @param {number} index
@@ -4582,6 +4607,11 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
4582
4607
  function warn(msg, index, type, deprecationId) {
4583
4608
  if (context.quiet) { return; }
4584
4609
  if (deprecationId && context.quietDeprecations) { return; }
4610
+ if (deprecationId) {
4611
+ const key = `${deprecationId}@${index ?? parserInput.i}`;
4612
+ if (warnedDeprecations.has(key)) { return; }
4613
+ warnedDeprecations.add(key);
4614
+ }
4585
4615
  if (deprecationId && !deprecationHandler.shouldWarn(deprecationId)) { return; }
4586
4616
 
4587
4617
  logger.warn(
@@ -4597,6 +4627,17 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
4597
4627
  );
4598
4628
  }
4599
4629
 
4630
+ /**
4631
+ * Warn that a bare `@variable` reference is being used in a non-value
4632
+ * position (an at-rule prelude, name, or identifier), where it still
4633
+ * resolves today but is deprecated in favour of `@{variable}` interpolation.
4634
+ *
4635
+ * @param {number} index - source position of the bare reference
4636
+ */
4637
+ function warnBareAtRuleVariable(index) {
4638
+ warn('A bare @variable in an at-rule prelude is deprecated. Use @{variable} interpolation instead.', index, 'DEPRECATED', 'variable-in-at-rule-prelude');
4639
+ }
4640
+
4600
4641
  function expect(arg, msg) {
4601
4642
  // some older browsers return typeof 'function' for RegExp
4602
4643
  const result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
@@ -6147,7 +6188,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6147
6188
  if (parserInput.$char(';')) {
6148
6189
  value = new Anonymous('');
6149
6190
  } else {
6150
- value = this.permissiveValue(/[;}]/, true);
6191
+ value = this.permissiveValue(/[;}]/);
6151
6192
  }
6152
6193
  }
6153
6194
  // Try to store values as anonymous
@@ -6206,8 +6247,12 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6206
6247
  * math is allowed.
6207
6248
  *
6208
6249
  * @param {RexExp} untilTokens - Characters to stop parsing at
6250
+ * @param {boolean} [deprecateVariables] - when set, this is an at-rule
6251
+ * prelude (non-value position); accept `@{var}` interpolation and warn
6252
+ * on a bare `@var` reference (which resolves today but is deprecated).
6209
6253
  */
6210
- permissiveValue: function (untilTokens) {
6254
+ permissiveValue: function (untilTokens, deprecateVariables) {
6255
+ const entities = this.entities;
6211
6256
  let i;
6212
6257
  let e;
6213
6258
  let done;
@@ -6234,7 +6279,20 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6234
6279
  value.push(e);
6235
6280
  continue;
6236
6281
  }
6237
- e = this.entity();
6282
+ if (deprecateVariables) {
6283
+ // In an at-rule prelude, `@{var}` interpolation is the supported
6284
+ // form; consume it here so its `{` is not mistaken for a block.
6285
+ e = entities.variableCurly();
6286
+ if (!e) {
6287
+ const varIndex = parserInput.i;
6288
+ e = this.entity();
6289
+ if (e && e.type === 'Variable') {
6290
+ warnBareAtRuleVariable(varIndex);
6291
+ }
6292
+ }
6293
+ } else {
6294
+ e = this.entity();
6295
+ }
6238
6296
  if (e) {
6239
6297
  value.push(e);
6240
6298
  }
@@ -6261,7 +6319,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6261
6319
  }
6262
6320
  parserInput.save();
6263
6321
 
6264
- value = parserInput.$parseUntil(tok);
6322
+ value = parserInput.$parseUntil(tok, deprecateVariables);
6265
6323
 
6266
6324
  if (value) {
6267
6325
  if (typeof value === 'string') {
@@ -6271,6 +6329,14 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6271
6329
  parserInput.forget();
6272
6330
  return new tree.Anonymous('', index);
6273
6331
  }
6332
+ // At-rule prelude: `$parseUntil` (deprecateVariables) records the
6333
+ // first bare `@var` it saw outside any `(...)` in its single pass —
6334
+ // a structural reference (`[...]`/`{...}` don't shield it, only a
6335
+ // declaration-value `(...)` does). Warn once here rather than
6336
+ // re-scanning the text.
6337
+ if (deprecateVariables && value.bareVarIndex !== null && value.bareVarIndex !== undefined) {
6338
+ warnBareAtRuleVariable(value.bareVarIndex);
6339
+ }
6274
6340
  /** @type {string} */
6275
6341
  let item;
6276
6342
  for (i = 0; i < value.length; i++) {
@@ -6287,11 +6353,14 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6287
6353
  const quote = new tree.Quoted('\'', item, true, index, fileInfo);
6288
6354
  const variableRegex = /@([\w-]+)/g;
6289
6355
  const propRegex = /\$([\w-]+)/g;
6290
- if (variableRegex.test(item)) {
6291
- warn('@[ident] in unknown values will not be evaluated as variables in the future. Use @{[ident]}', index, 'DEPRECATED', 'variable-in-unknown-value');
6356
+ // At-rule preludes are handled once above via
6357
+ // `value.bareVarIndex`; the `variable-in-unknown-value`
6358
+ // notice is for unknown declaration values only.
6359
+ if (!deprecateVariables && variableRegex.test(item)) {
6360
+ warn('@variable in unknown values will not be evaluated as variables in the future. Use @{variable}', index, 'DEPRECATED', 'variable-in-unknown-value');
6292
6361
  }
6293
6362
  if (propRegex.test(item)) {
6294
- warn('$[ident] in unknown values will not be evaluated as property references in the future. Use ${[ident]}', index, 'DEPRECATED', 'property-in-unknown-value');
6363
+ warn('$property in unknown values will not be evaluated as property references in the future. Use ${property}', index, 'DEPRECATED', 'property-in-unknown-value');
6295
6364
  }
6296
6365
  quote.variableRegex = /@([\w-]+)|@{([\w-]+)}/g;
6297
6366
  quote.propRegex = /\$([\w-]+)|\${([\w-]+)}/g;
@@ -6400,7 +6469,17 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6400
6469
  }
6401
6470
  parserInput.restore();
6402
6471
 
6403
- e = entities.declarationCall.bind(this)() || cssKeyword() || entities.keyword() || entities.variable() || entities.mixinLookup();
6472
+ e = entities.declarationCall.bind(this)() || cssKeyword() || entities.keyword() || entities.variableCurly();
6473
+ if (!e) {
6474
+ const varIndex = parserInput.i;
6475
+ const bareVariable = entities.variable();
6476
+ if (bareVariable) {
6477
+ warnBareAtRuleVariable(varIndex);
6478
+ e = bareVariable;
6479
+ } else {
6480
+ e = entities.mixinLookup();
6481
+ }
6482
+ }
6404
6483
  if (e) {
6405
6484
  nodes.push(e);
6406
6485
  if (e.type === 'Variable' ||
@@ -6411,7 +6490,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6411
6490
  let closed = false;
6412
6491
  p = this.property();
6413
6492
  parserInput.save();
6414
- if (!p && syntaxOptions.queryInParens && parserInput.$re(/^[0-9a-z-]*\s*([<>]=|<=|>=|[<>]|=)/)) {
6493
+ if (!p && syntaxOptions.queryInParens && parserInput.$re(/^(?:[^()]|\([^()]*\))*\s*([<>]=|<=|>=|[<>]|=)/)) {
6415
6494
  parserInput.restore();
6416
6495
  p = this.condition();
6417
6496
 
@@ -6491,7 +6570,17 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6491
6570
  features[features.length - 1].noSpacing = false;
6492
6571
  }
6493
6572
  } else {
6494
- e = entities.variable() || entities.mixinLookup();
6573
+ e = entities.variableCurly();
6574
+ if (!e) {
6575
+ const varIndex = parserInput.i;
6576
+ const bareVariable = entities.variable();
6577
+ if (bareVariable) {
6578
+ warnBareAtRuleVariable(varIndex);
6579
+ e = bareVariable;
6580
+ } else {
6581
+ e = entities.mixinLookup();
6582
+ }
6583
+ }
6495
6584
  if (e) {
6496
6585
  features.push(e);
6497
6586
  if (!parserInput.$char(',')) { break; }
@@ -6605,8 +6694,24 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6605
6694
  return null;
6606
6695
  }
6607
6696
  },
6697
+ /**
6698
+ * An entity in a non-value at-rule position (an at-rule identifier,
6699
+ * name, or keyword-list item — e.g. the name in `@keyframes @foo`).
6700
+ * `@{foo}` interpolation is the supported form; a bare `@foo` still
6701
+ * resolves but is deprecated.
6702
+ */
6703
+ atRuleEntity: function () {
6704
+ const curly = this.entities.variableCurly();
6705
+ if (curly) { return curly; }
6706
+ const index = parserInput.i;
6707
+ const e = this.entity();
6708
+ if (e && e.type === 'Variable') {
6709
+ warnBareAtRuleVariable(index);
6710
+ }
6711
+ return e;
6712
+ },
6608
6713
  atruleUnknown: function (value, name, hasBlock) {
6609
- value = this.permissiveValue(/^[{;]/);
6714
+ value = this.permissiveValue(/^[{;]/, true);
6610
6715
  hasBlock = (parserInput.currentChar() === '{');
6611
6716
  if (!value) {
6612
6717
  if (!hasBlock && parserInput.currentChar() !== ';') {
@@ -6622,16 +6727,16 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6622
6727
  rules = this.blockRuleset();
6623
6728
  parserInput.save();
6624
6729
  if (!rules && !isRooted) {
6625
- value = this.entity();
6730
+ value = this.atRuleEntity();
6626
6731
  rules = this.blockRuleset();
6627
6732
  }
6628
6733
  if (!rules && !isRooted) {
6629
6734
  parserInput.restore();
6630
6735
  var e = [];
6631
- value = this.entity();
6736
+ value = this.atRuleEntity();
6632
6737
  while (parserInput.$char(',')) {
6633
6738
  e.push(value);
6634
- value = this.entity();
6739
+ value = this.atRuleEntity();
6635
6740
  }
6636
6741
  if (value && e.length > 0) {
6637
6742
  e.push(value);
@@ -6716,12 +6821,25 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
6716
6821
  parserInput.commentStore.length = 0;
6717
6822
 
6718
6823
  if (hasIdentifier) {
6719
- value = this.entity();
6824
+ value = this.atRuleEntity();
6720
6825
  if (!value) {
6721
6826
  error(`expected ${name} identifier`);
6722
6827
  }
6723
6828
  } else if (hasExpression) {
6829
+ // `@namespace` may carry an interpolated `@{ns}` prefix (or a
6830
+ // deprecated bare `@ns`). Parse that prefix directly so `@{ns}`
6831
+ // is accepted here without treating value positions as
6832
+ // interpolation contexts, then read the namespace URL.
6833
+ let prefix = this.entities.variableCurly();
6834
+ if (!prefix && parserInput.peek(/^@@?[\w-]/)) {
6835
+ const prefixIndex = parserInput.i;
6836
+ prefix = this.entities.variable();
6837
+ if (prefix) { warnBareAtRuleVariable(prefixIndex); }
6838
+ }
6724
6839
  value = this.expression();
6840
+ if (prefix) {
6841
+ value = value ? new(tree.Expression)([prefix, ...value.value]) : prefix;
6842
+ }
6725
6843
  if (!value) {
6726
6844
  error(`expected ${name} expression`);
6727
6845
  }
@@ -8784,16 +8902,16 @@ const NestableAtRulePrototype = {
8784
8902
  self.features = new Value(self.permute(/** @type {Node[][]} */ (/** @type {unknown} */ (path))).map(
8785
8903
  /** @param {Node | Node[]} path */
8786
8904
  path => {
8787
- path = /** @type {Node[]} */ (path).map(
8905
+ path = /** @type {Node[]} */ (path).map(
8788
8906
  /** @param {Node & { toCSS?: Function }} fragment */
8789
- fragment => fragment.toCSS ? fragment : new Anonymous(/** @type {string} */ (/** @type {unknown} */ (fragment))));
8907
+ fragment => fragment.toCSS ? fragment : new Anonymous(/** @type {string} */ (/** @type {unknown} */ (fragment))));
8790
8908
 
8791
- for (i = /** @type {Node[]} */ (path).length - 1; i > 0; i--) {
8909
+ for (i = /** @type {Node[]} */ (path).length - 1; i > 0; i--) {
8792
8910
  /** @type {Node[]} */ (path).splice(i, 0, new Anonymous('and'));
8793
- }
8911
+ }
8794
8912
 
8795
- return new Expression(/** @type {Node[]} */ (path));
8796
- }));
8913
+ return new Expression(/** @type {Node[]} */ (path));
8914
+ }));
8797
8915
  self.setParent(self.features, self);
8798
8916
 
8799
8917
  // Fake a tree-node that doesn't output anything.
@@ -14064,23 +14182,41 @@ var imageSize = environment => {
14064
14182
  throw fileSync.error;
14065
14183
  }
14066
14184
 
14067
- const sizeOf = require$1('image-size');
14068
- return sizeOf ? sizeOf(fileSync.filename) : {width: 0, height: 0};
14185
+ let probe;
14186
+ try {
14187
+ probe = require$1('probe-image-size/sync');
14188
+ } catch (_) {
14189
+ return { width: 0, height: 0 };
14190
+ }
14191
+
14192
+ const size = probe(nodeFs.readFileSync(fileSync.filename));
14193
+
14194
+ if (!size) {
14195
+ throw {
14196
+ type: 'File',
14197
+ message: `Unrecognised image format for '${filePath}'`
14198
+ };
14199
+ }
14200
+
14201
+ return {
14202
+ width: size.width,
14203
+ height: size.height
14204
+ };
14069
14205
  }
14070
14206
 
14071
14207
  const imageFunctions = {
14072
- 'image-size': function(filePathNode) {
14208
+ 'image-size': function (filePathNode) {
14073
14209
  const size = imageSize(this, filePathNode);
14074
14210
  return new Expression([
14075
14211
  new Dimension(size.width, 'px'),
14076
14212
  new Dimension(size.height, 'px')
14077
14213
  ]);
14078
14214
  },
14079
- 'image-width': function(filePathNode) {
14215
+ 'image-width': function (filePathNode) {
14080
14216
  const size = imageSize(this, filePathNode);
14081
14217
  return new Dimension(size.width, 'px');
14082
14218
  },
14083
- 'image-height': function(filePathNode) {
14219
+ 'image-height': function (filePathNode) {
14084
14220
  const size = imageSize(this, filePathNode);
14085
14221
  return new Dimension(size.height, 'px');
14086
14222
  }
@@ -14090,7 +14226,7 @@ var imageSize = environment => {
14090
14226
  };
14091
14227
 
14092
14228
  createRequire();
14093
- const version = "4.6.7";
14229
+ const version = "4.7.0";
14094
14230
 
14095
14231
  const less = createFromEnvironment(environment, [new FileManager(), new UrlFileManager()], version);
14096
14232