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.
- package/dist/less-node.cjs +130 -34
- package/dist/less.cjs +130 -34
- package/dist/less.js +130 -34
- package/dist/less.min.js +2 -2
- package/dist/less.min.js.map +1 -1
- package/lib/less/deprecation.js +12 -0
- package/lib/less/parser/parser.js +98 -14
- package/lib/less/tree/mixin-definition.js +30 -18
- package/package.json +1 -1
package/lib/less/deprecation.js
CHANGED
|
@@ -22,6 +22,18 @@ const deprecations = {
|
|
|
22
22
|
'variable-in-at-rule-prelude': {
|
|
23
23
|
description: 'A bare @variable in an at-rule prelude (e.g. @media @foo) is deprecated. Use @{variable} interpolation instead.'
|
|
24
24
|
},
|
|
25
|
+
'numeric-variable-name': {
|
|
26
|
+
description: 'Variable names beginning with a number are deprecated and will be removed in Less 5.x.'
|
|
27
|
+
},
|
|
28
|
+
'dash-only-variable-name': {
|
|
29
|
+
description: 'The dash-only variable names @- and @{-} are deprecated and will be removed in Less 5.x.'
|
|
30
|
+
},
|
|
31
|
+
'dash-only-mixin-name': {
|
|
32
|
+
description: 'The dash-only mixin names .-() and #-() are deprecated and will be removed in Less 5.x.'
|
|
33
|
+
},
|
|
34
|
+
'dynamic-charset': {
|
|
35
|
+
description: 'Dynamic @charset interpolation is deprecated and will be removed in Less 5.x.'
|
|
36
|
+
},
|
|
25
37
|
'property-in-unknown-value': {
|
|
26
38
|
description: '$property in custom property values is treated as literal text.'
|
|
27
39
|
},
|
|
@@ -106,6 +106,64 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
106
106
|
warn('A bare @variable in an at-rule prelude is deprecated. Use @{variable} interpolation instead.', index, 'DEPRECATED', 'variable-in-at-rule-prelude');
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Numeric-leading variable names are a Less extension rather than valid CSS
|
|
111
|
+
* identifier syntax. Keep accepting them through Less 4, but make the Less 5
|
|
112
|
+
* migration visible at every actual variable reference or definition.
|
|
113
|
+
*
|
|
114
|
+
* @param {string} name - either an @-prefixed name or the name inside @{...}
|
|
115
|
+
* @param {number} index - source position of the variable token
|
|
116
|
+
*/
|
|
117
|
+
function warnNumericVariableName(name, index) {
|
|
118
|
+
if (/^(?:@@?)?[0-9]/.test(name)) {
|
|
119
|
+
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');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A lone '-' is not a CSS identifier. Less historically accepts it as a
|
|
125
|
+
* variable name; retain that in Less 4 only and warn on definitions,
|
|
126
|
+
* ordinary references, and interpolation references.
|
|
127
|
+
*
|
|
128
|
+
* @param {string} name - either @-/@@-, or the name inside @{...}
|
|
129
|
+
* @param {number} index - source position of the variable token
|
|
130
|
+
*/
|
|
131
|
+
function warnDashOnlyVariableName(name, index) {
|
|
132
|
+
const bareName = name.replace(/^@@?/, '');
|
|
133
|
+
if (bareName === '-') {
|
|
134
|
+
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');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* A lone '-' is not a CSS identifier. Less historically accepts it as a mixin
|
|
140
|
+
* name after '.' or '#'; retain that in Less 4 only.
|
|
141
|
+
*
|
|
142
|
+
* @param {string} name
|
|
143
|
+
* @param {number} index
|
|
144
|
+
*/
|
|
145
|
+
function warnDashOnlyMixinName(name, index) {
|
|
146
|
+
if (name === '.-' || name === '#-') {
|
|
147
|
+
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');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* CSS @charset is a source-header declaration, not a general dynamic at-rule.
|
|
153
|
+
* Less 4 keeps its historical interpolation behavior for compatibility, but
|
|
154
|
+
* makes each dynamic spelling visible before Less 5 rejects it.
|
|
155
|
+
*
|
|
156
|
+
* @param {import('../tree/node.js').default} value
|
|
157
|
+
* @param {number} index - source position of the @charset token
|
|
158
|
+
*/
|
|
159
|
+
function warnDynamicCharset(value, index) {
|
|
160
|
+
if (value instanceof tree.Variable ||
|
|
161
|
+
(value instanceof tree.Quoted &&
|
|
162
|
+
(value.containsVariables() || value.value.match(value.propRegex)))) {
|
|
163
|
+
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');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
109
167
|
function expect(arg, msg) {
|
|
110
168
|
// some older browsers return typeof 'function' for RegExp
|
|
111
169
|
const result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
|
|
@@ -559,7 +617,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
559
617
|
}
|
|
560
618
|
|
|
561
619
|
function condition() {
|
|
562
|
-
return [expect(parsers.condition, 'expected condition')];
|
|
620
|
+
return [expect(() => parsers.condition(false, true), 'expected condition')];
|
|
563
621
|
}
|
|
564
622
|
},
|
|
565
623
|
|
|
@@ -684,6 +742,8 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
684
742
|
|
|
685
743
|
parserInput.save();
|
|
686
744
|
if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
|
|
745
|
+
warnNumericVariableName(name, index);
|
|
746
|
+
warnDashOnlyVariableName(name, index);
|
|
687
747
|
ch = parserInput.currentChar();
|
|
688
748
|
if ((ch === '(' && !parserInput.prevChar().match(/^\s/))
|
|
689
749
|
|| (ch === '[' && !parserInput.prevChar().match(/^\s/))) {
|
|
@@ -706,6 +766,8 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
706
766
|
const index = parserInput.i;
|
|
707
767
|
|
|
708
768
|
if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) {
|
|
769
|
+
warnNumericVariableName(curly[1], index);
|
|
770
|
+
warnDashOnlyVariableName(curly[1], index);
|
|
709
771
|
return new(tree.Variable)(`@${curly[1]}`, index + currentIndex, fileInfo);
|
|
710
772
|
}
|
|
711
773
|
},
|
|
@@ -828,7 +890,11 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
828
890
|
variable: function () {
|
|
829
891
|
let name;
|
|
830
892
|
|
|
831
|
-
if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) {
|
|
893
|
+
if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) {
|
|
894
|
+
warnNumericVariableName(name[1], parserInput.i - name[0].length);
|
|
895
|
+
warnDashOnlyVariableName(name[1], parserInput.i - name[0].length);
|
|
896
|
+
return name[1];
|
|
897
|
+
}
|
|
832
898
|
},
|
|
833
899
|
|
|
834
900
|
//
|
|
@@ -858,6 +924,8 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
858
924
|
}
|
|
859
925
|
|
|
860
926
|
if (!inValue) {
|
|
927
|
+
warnNumericVariableName(name[1], i);
|
|
928
|
+
warnDashOnlyVariableName(name[1], i);
|
|
861
929
|
name = name[1];
|
|
862
930
|
}
|
|
863
931
|
|
|
@@ -1014,6 +1082,9 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
1014
1082
|
if (inValue || parsers.end()) {
|
|
1015
1083
|
parserInput.forget();
|
|
1016
1084
|
const mixin = new(tree.mixin.Call)(elements, args, index + currentIndex, fileInfo, !lookups && important);
|
|
1085
|
+
for (const element of elements) {
|
|
1086
|
+
warnDashOnlyMixinName(element.value, element._index - currentIndex);
|
|
1087
|
+
}
|
|
1017
1088
|
if (lookups) {
|
|
1018
1089
|
return new tree.NamespaceValue(mixin, lookups);
|
|
1019
1090
|
}
|
|
@@ -1210,6 +1281,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
1210
1281
|
let ruleset;
|
|
1211
1282
|
let cond;
|
|
1212
1283
|
let variadic = false;
|
|
1284
|
+
const index = parserInput.i;
|
|
1213
1285
|
if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') ||
|
|
1214
1286
|
parserInput.peek(/^[^{]*\}/)) {
|
|
1215
1287
|
return;
|
|
@@ -1245,6 +1317,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
1245
1317
|
|
|
1246
1318
|
if (ruleset) {
|
|
1247
1319
|
parserInput.forget();
|
|
1320
|
+
warnDashOnlyMixinName(name, index);
|
|
1248
1321
|
return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
|
|
1249
1322
|
} else {
|
|
1250
1323
|
parserInput.restore();
|
|
@@ -2293,6 +2366,9 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2293
2366
|
if (!value) {
|
|
2294
2367
|
error(`expected ${name} identifier`);
|
|
2295
2368
|
}
|
|
2369
|
+
if (nonVendorSpecificName === '@charset') {
|
|
2370
|
+
warnDynamicCharset(value, index);
|
|
2371
|
+
}
|
|
2296
2372
|
} else if (hasExpression) {
|
|
2297
2373
|
// `@namespace` may carry an interpolated `@{ns}` prefix (or a
|
|
2298
2374
|
// deprecated bare `@ns`). Parse that prefix directly so `@{ns}`
|
|
@@ -2497,7 +2573,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2497
2573
|
return condition || a;
|
|
2498
2574
|
}
|
|
2499
2575
|
},
|
|
2500
|
-
condition: function (needsParens) {
|
|
2576
|
+
condition: function (needsParens, allowConditionOperands) {
|
|
2501
2577
|
let result;
|
|
2502
2578
|
let logical;
|
|
2503
2579
|
let next;
|
|
@@ -2505,13 +2581,13 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2505
2581
|
return parserInput.$str('or');
|
|
2506
2582
|
}
|
|
2507
2583
|
|
|
2508
|
-
result = this.conditionAnd(needsParens);
|
|
2584
|
+
result = this.conditionAnd(needsParens, allowConditionOperands);
|
|
2509
2585
|
if (!result) {
|
|
2510
2586
|
return ;
|
|
2511
2587
|
}
|
|
2512
2588
|
logical = or();
|
|
2513
2589
|
if (logical) {
|
|
2514
|
-
next = this.condition(needsParens);
|
|
2590
|
+
next = this.condition(needsParens, allowConditionOperands);
|
|
2515
2591
|
if (next) {
|
|
2516
2592
|
result = new(tree.Condition)(logical, result, next);
|
|
2517
2593
|
} else {
|
|
@@ -2520,13 +2596,13 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2520
2596
|
}
|
|
2521
2597
|
return result;
|
|
2522
2598
|
},
|
|
2523
|
-
conditionAnd: function (needsParens) {
|
|
2599
|
+
conditionAnd: function (needsParens, allowConditionOperands) {
|
|
2524
2600
|
let result;
|
|
2525
2601
|
let logical;
|
|
2526
2602
|
let next;
|
|
2527
2603
|
const self = this;
|
|
2528
2604
|
function insideCondition() {
|
|
2529
|
-
const cond = self.negatedCondition(needsParens) || self.parenthesisCondition(needsParens);
|
|
2605
|
+
const cond = self.negatedCondition(needsParens, allowConditionOperands) || self.parenthesisCondition(needsParens, allowConditionOperands);
|
|
2530
2606
|
if (!cond && !needsParens) {
|
|
2531
2607
|
return self.atomicCondition(needsParens);
|
|
2532
2608
|
}
|
|
@@ -2540,9 +2616,12 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2540
2616
|
if (!result) {
|
|
2541
2617
|
return ;
|
|
2542
2618
|
}
|
|
2619
|
+
if (allowConditionOperands) {
|
|
2620
|
+
result = this.atomicCondition(needsParens, result, allowConditionOperands) || result;
|
|
2621
|
+
}
|
|
2543
2622
|
logical = and();
|
|
2544
2623
|
if (logical) {
|
|
2545
|
-
next = this.conditionAnd(needsParens);
|
|
2624
|
+
next = this.conditionAnd(needsParens, allowConditionOperands);
|
|
2546
2625
|
if (next) {
|
|
2547
2626
|
result = new(tree.Condition)(logical, result, next);
|
|
2548
2627
|
} else {
|
|
@@ -2551,9 +2630,9 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2551
2630
|
}
|
|
2552
2631
|
return result;
|
|
2553
2632
|
},
|
|
2554
|
-
negatedCondition: function (needsParens) {
|
|
2633
|
+
negatedCondition: function (needsParens, allowConditionOperands) {
|
|
2555
2634
|
if (parserInput.$str('not')) {
|
|
2556
|
-
const result = this.parenthesisCondition(needsParens);
|
|
2635
|
+
const result = this.parenthesisCondition(needsParens, allowConditionOperands);
|
|
2557
2636
|
if (result) {
|
|
2558
2637
|
result.negate = !result.negate;
|
|
2559
2638
|
return result;
|
|
@@ -2570,11 +2649,11 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2570
2649
|
}
|
|
2571
2650
|
}
|
|
2572
2651
|
},
|
|
2573
|
-
parenthesisCondition: function (needsParens) {
|
|
2652
|
+
parenthesisCondition: function (needsParens, allowConditionOperands) {
|
|
2574
2653
|
function tryConditionFollowedByParenthesis(me) {
|
|
2575
2654
|
let body;
|
|
2576
2655
|
parserInput.save();
|
|
2577
|
-
body = me.condition(needsParens);
|
|
2656
|
+
body = me.condition(needsParens, allowConditionOperands);
|
|
2578
2657
|
if (!body) {
|
|
2579
2658
|
parserInput.restore();
|
|
2580
2659
|
return ;
|
|
@@ -2611,7 +2690,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2611
2690
|
parserInput.forget();
|
|
2612
2691
|
return body;
|
|
2613
2692
|
},
|
|
2614
|
-
atomicCondition: function (needsParens, preparsedCond) {
|
|
2693
|
+
atomicCondition: function (needsParens, preparsedCond, allowConditionOperands) {
|
|
2615
2694
|
const entities = this.entities;
|
|
2616
2695
|
const index = parserInput.i;
|
|
2617
2696
|
let a;
|
|
@@ -2620,7 +2699,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2620
2699
|
let op;
|
|
2621
2700
|
|
|
2622
2701
|
const cond = (function() {
|
|
2623
|
-
return this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
|
|
2702
|
+
return (allowConditionOperands && this.parenthesisCondition(needsParens)) || this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
|
|
2624
2703
|
}).bind(this)
|
|
2625
2704
|
|
|
2626
2705
|
if (preparsedCond) {
|
|
@@ -2781,6 +2860,11 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
|
|
|
2781
2860
|
}
|
|
2782
2861
|
for (k = 0; k < name.length; k++) {
|
|
2783
2862
|
s = name[k];
|
|
2863
|
+
if (s.charAt(0) === '@') {
|
|
2864
|
+
const variableName = s.slice(2, -1);
|
|
2865
|
+
warnNumericVariableName(variableName, index[k]);
|
|
2866
|
+
warnDashOnlyVariableName(variableName, index[k]);
|
|
2867
|
+
}
|
|
2784
2868
|
name[k] = (s.charAt(0) !== '@' && s.charAt(0) !== '$') ?
|
|
2785
2869
|
new(tree.Keyword)(s) :
|
|
2786
2870
|
(s.charAt(0) === '@' ?
|
|
@@ -267,35 +267,47 @@ class Definition extends Ruleset {
|
|
|
267
267
|
*/
|
|
268
268
|
matchArgs(args, context) {
|
|
269
269
|
const allArgsCnt = (args && args.length) || 0;
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
270
|
+
const evaldArguments = new Array(this.params.length);
|
|
271
|
+
const positionalArgs = [];
|
|
272
|
+
let positionalIndex = 0;
|
|
273
|
+
|
|
274
|
+
for (let i = 0; i < allArgsCnt; i++) {
|
|
275
|
+
const arg = /** @type {MixinArg[]} */ (args)[i];
|
|
276
|
+
if (!arg.name) {
|
|
277
|
+
positionalArgs.push(arg);
|
|
278
|
+
continue;
|
|
277
279
|
}
|
|
278
|
-
}, 0);
|
|
279
280
|
|
|
280
|
-
|
|
281
|
-
if (
|
|
281
|
+
const paramIndex = this.params.findIndex((param, index) => param.name === arg.name && !evaldArguments[index]);
|
|
282
|
+
if (paramIndex < 0) {
|
|
282
283
|
return false;
|
|
283
284
|
}
|
|
284
|
-
|
|
285
|
-
|
|
285
|
+
evaldArguments[paramIndex] = arg;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
for (let i = 0; i < this.params.length; i++) {
|
|
289
|
+
if (evaldArguments[i]) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (this.params[i].variadic) {
|
|
293
|
+
positionalIndex = positionalArgs.length;
|
|
294
|
+
continue;
|
|
286
295
|
}
|
|
287
|
-
|
|
288
|
-
|
|
296
|
+
if (positionalIndex < positionalArgs.length) {
|
|
297
|
+
evaldArguments[i] = positionalArgs[positionalIndex++];
|
|
298
|
+
} else if (!this.params[i].name || !this.params[i].value) {
|
|
289
299
|
return false;
|
|
290
300
|
}
|
|
291
301
|
}
|
|
292
302
|
|
|
293
|
-
|
|
294
|
-
|
|
303
|
+
if (positionalIndex < positionalArgs.length) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
295
306
|
|
|
296
|
-
|
|
307
|
+
// check patterns
|
|
308
|
+
for (let i = 0; i < this.arity; i++) {
|
|
297
309
|
if (!this.params[i].name && !this.params[i].variadic) {
|
|
298
|
-
if (
|
|
310
|
+
if (evaldArguments[i].value.eval(context).toCSS(/** @type {EvalContext} */ ({})) != /** @type {Node} */ (this.params[i].value).eval(context).toCSS(/** @type {EvalContext} */ ({}))) {
|
|
299
311
|
return false;
|
|
300
312
|
}
|
|
301
313
|
}
|