Skip to content

Commit dac5bfd

Browse files
committed
fix(issue:3777) operation on invalid type resolved
* Fixes issue less#3777 where using CSS custom property in an operation resulted in a parsing error.
1 parent a963f11 commit dac5bfd

File tree

5 files changed

+82
-4
lines changed

5 files changed

+82
-4
lines changed

packages/less/src/less/parser/parser.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,18 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
796796
if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; }
797797
},
798798

799+
//
800+
// The custom property part of a variable definition.
801+
//
802+
// --fink:
803+
//
804+
customProperty: function () {
805+
var name;
806+
if (parserInput.currentChar() === '-' && (name = parserInput.$re(/^(--[\w-]+)\s*:/))) {
807+
return name[1];
808+
}
809+
},
810+
799811
//
800812
// Call a variable value to retrieve a detached ruleset
801813
// or a value from a detached ruleset's rules.
@@ -1578,7 +1590,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
15781590

15791591
parserInput.save();
15801592

1581-
name = this.variable() || this.ruleProperty();
1593+
name = this.variable() || this.customProperty() || this.ruleProperty();
15821594
if (name) {
15831595
isVariable = typeof name === 'string';
15841596

@@ -1597,7 +1609,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
15971609
merge = !isVariable && name.length > 1 && name.pop().value;
15981610

15991611
// Custom property values get permissive parsing
1600-
if (name[0].value && name[0].value.slice(0, 2) === '--') {
1612+
if (isVariable && name.startsWith('--')) {
16011613
if (parserInput.$char(';')) {
16021614
value = new Anonymous('');
16031615
} else {

packages/less/src/less/tree/operation.js

+58-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import Node from './node';
22
import Color from './color';
33
import Dimension from './dimension';
44
import * as Constants from '../constants';
5+
import Variable from './variable';
6+
import Call from './call';
57
const MATH = Constants.Math;
68

79

@@ -18,8 +20,51 @@ Operation.prototype = Object.assign(new Node(), {
1820
this.operands = visitor.visitArray(this.operands);
1921
},
2022

23+
find: function (obj, fun) {
24+
for (var i_2 = 0, r = void 0; i_2 < obj.length; i_2++) {
25+
r = fun.call(obj, obj[i_2]);
26+
if (r) {
27+
return r;
28+
}
29+
}
30+
return null;
31+
},
32+
33+
evalVariable: function (context, operand) {
34+
if (operand.name === 'var' && operand.args.length === 1) {
35+
var varName = operand.args[0].toCSS();
36+
var variable = this.find(context.frames, function (frame) {
37+
var v = frame.variable(varName);
38+
if (v) {
39+
if (v.important) {
40+
var importantScope = context.importantScope[context.importantScope.length - 1];
41+
importantScope.important = v.important;
42+
}
43+
// If in calc, wrap vars in a function call to cascade evaluate args first
44+
if (context.inCalc) {
45+
return (new Call('_SELF', [v.value])).eval(context);
46+
}
47+
else {
48+
return v.value.eval(context);
49+
}
50+
}
51+
});
52+
if (variable) {
53+
return variable;
54+
}
55+
}
56+
},
57+
2158
eval(context) {
22-
let a = this.operands[0].eval(context), b = this.operands[1].eval(context), op;
59+
var a = this.evalVariable(context, this.operands[0])
60+
if (!a) {
61+
a = this.operands[0].eval(context)
62+
}
63+
var b = this.evalVariable(context, this.operands[1]);
64+
if (!b) {
65+
b = this.operands[1].eval(context);
66+
}
67+
var op;
2368

2469
if (context.isMathOn(this.op)) {
2570
op = this.op === './' ? '/' : this.op;
@@ -29,6 +74,18 @@ Operation.prototype = Object.assign(new Node(), {
2974
if (b instanceof Dimension && a instanceof Color) {
3075
b = b.toColor();
3176
}
77+
if (a instanceof Dimension && b instanceof Call && b.name === 'var') {
78+
if (b.args && b.args.length === 1) {
79+
b = new Variable(b.args[0].toCSS(), 0, {});
80+
return a.operate(context, op, b);
81+
}
82+
}
83+
if (b instanceof Dimension && a instanceof Call && a.name === 'var') {
84+
if (a.args && a.args.length === 1) {
85+
a = new Variable(a.args[0].toCSS(), 0, {});
86+
return b.operate(context, op, a);
87+
}
88+
}
3289
if (!a.operate || !b.operate) {
3390
if (
3491
(a instanceof Operation || b instanceof Operation)

packages/less/src/less/tree/ruleset.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ Ruleset.prototype = Object.assign(new Node(), {
286286
variables() {
287287
if (!this._variables) {
288288
this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) {
289-
if (r instanceof Declaration && r.variable === true) {
289+
if (r instanceof Declaration && (r.variable === true || (typeof r.name ==='string' && r.name.startsWith('--')))) {
290290
hash[r.name] = r;
291291
}
292292
// when evaluating variables in an import statement, imports have not been eval'd

packages/test-data/css/_main/custom-property.css

+4
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
--basic-deg: 20deg;
33
--basic-deg-tan: tan(var(--basic-deg));
44
}
5+
.test2 {
6+
--some-var: 5px;
7+
prop: min(100% - var(--some-var), 10px);
8+
}

packages/test-data/less/_main/custom-property.less

+5
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@
22
--basic-deg: 20deg;
33
--basic-deg-tan: tan(var(--basic-deg));
44
}
5+
6+
.test2 {
7+
--some-var: 5px;
8+
prop: min(100% - var(--some-var), 10px);
9+
}

0 commit comments

Comments
 (0)