From 88e126bfc53932b8090fe506dd9ad154d6fff81a Mon Sep 17 00:00:00 2001 From: Matthew Dean Date: Thu, 11 Jul 2019 14:17:24 -0700 Subject: [PATCH] Fixes #3346 #3338 #3345 (#3363) * Don't escape quotes in an each #3354 * Fixes #3346 - ambiguous mixin call / color parsing * Fixes #3338 - better error message * Fixes #3338 - missed mix() function * Fixes #3345 --- lib/less/functions/color.js | 52 ++++++++++++------- lib/less/functions/list.js | 3 +- lib/less/functions/string.js | 3 +- lib/less/parser/parser.js | 9 +++- lib/less/tree/quoted.js | 2 +- test/css/functions-each.css | 10 ++++ test/css/namespacing/namespacing-1.css | 4 ++ test/less/colors.less | 2 +- .../errors/color-func-invalid-color-2.less | 2 + .../errors/color-func-invalid-color-2.txt | 3 ++ test/less/functions-each.less | 32 ++++++++++++ test/less/namespacing/namespacing-1.less | 10 ++++ 12 files changed, 106 insertions(+), 26 deletions(-) create mode 100644 test/less/errors/color-func-invalid-color-2.less create mode 100644 test/less/errors/color-func-invalid-color-2.txt diff --git a/lib/less/functions/color.js b/lib/less/functions/color.js index 33f28bc59..a9b5198e8 100644 --- a/lib/less/functions/color.js +++ b/lib/less/functions/color.js @@ -20,6 +20,22 @@ function hsla(origColor, hsl) { return color; } } +function toHSL(color) { + if (color.toHSL) { + return color.toHSL(); + } else { + throw new Error('Argument cannot be evaluated to a color'); + } +} + +function toHSV(color) { + if (color.toHSV) { + return color.toHSV(); + } else { + throw new Error('Argument cannot be evaluated to a color'); + } +} + function number(n) { if (n instanceof Dimension) { return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); @@ -146,22 +162,22 @@ colorFunctions = { }, hue: function (color) { - return new Dimension(color.toHSL().h); + return new Dimension(toHSL(color).h); }, saturation: function (color) { - return new Dimension(color.toHSL().s * 100, '%'); + return new Dimension(toHSL(color).s * 100, '%'); }, lightness: function (color) { - return new Dimension(color.toHSL().l * 100, '%'); + return new Dimension(toHSL(color).l * 100, '%'); }, hsvhue: function(color) { - return new Dimension(color.toHSV().h); + return new Dimension(toHSV(color).h); }, hsvsaturation: function (color) { - return new Dimension(color.toHSV().s * 100, '%'); + return new Dimension(toHSV(color).s * 100, '%'); }, hsvvalue: function (color) { - return new Dimension(color.toHSV().v * 100, '%'); + return new Dimension(toHSV(color).v * 100, '%'); }, red: function (color) { return new Dimension(color.rgb[0]); @@ -173,7 +189,7 @@ colorFunctions = { return new Dimension(color.rgb[2]); }, alpha: function (color) { - return new Dimension(color.toHSL().a); + return new Dimension(toHSL(color).a); }, luma: function (color) { return new Dimension(color.luma() * color.alpha * 100, '%'); @@ -192,7 +208,7 @@ colorFunctions = { if (!color.rgb) { return null; } - var hsl = color.toHSL(); + var hsl = toHSL(color); if (typeof method !== 'undefined' && method.value === 'relative') { hsl.s += hsl.s * amount.value / 100; @@ -204,7 +220,7 @@ colorFunctions = { return hsla(color, hsl); }, desaturate: function (color, amount, method) { - var hsl = color.toHSL(); + var hsl = toHSL(color); if (typeof method !== 'undefined' && method.value === 'relative') { hsl.s -= hsl.s * amount.value / 100; @@ -216,7 +232,7 @@ colorFunctions = { return hsla(color, hsl); }, lighten: function (color, amount, method) { - var hsl = color.toHSL(); + var hsl = toHSL(color); if (typeof method !== 'undefined' && method.value === 'relative') { hsl.l += hsl.l * amount.value / 100; @@ -228,7 +244,7 @@ colorFunctions = { return hsla(color, hsl); }, darken: function (color, amount, method) { - var hsl = color.toHSL(); + var hsl = toHSL(color); if (typeof method !== 'undefined' && method.value === 'relative') { hsl.l -= hsl.l * amount.value / 100; @@ -240,7 +256,7 @@ colorFunctions = { return hsla(color, hsl); }, fadein: function (color, amount, method) { - var hsl = color.toHSL(); + var hsl = toHSL(color); if (typeof method !== 'undefined' && method.value === 'relative') { hsl.a += hsl.a * amount.value / 100; @@ -252,7 +268,7 @@ colorFunctions = { return hsla(color, hsl); }, fadeout: function (color, amount, method) { - var hsl = color.toHSL(); + var hsl = toHSL(color); if (typeof method !== 'undefined' && method.value === 'relative') { hsl.a -= hsl.a * amount.value / 100; @@ -264,14 +280,14 @@ colorFunctions = { return hsla(color, hsl); }, fade: function (color, amount) { - var hsl = color.toHSL(); + var hsl = toHSL(color); hsl.a = amount.value / 100; hsl.a = clamp(hsl.a); return hsla(color, hsl); }, spin: function (color, amount) { - var hsl = color.toHSL(); + var hsl = toHSL(color); var hue = (hsl.h + amount.value) % 360; hsl.h = hue < 0 ? 360 + hue : hue; @@ -283,16 +299,12 @@ colorFunctions = { // http://sass-lang.com // mix: function (color1, color2, weight) { - if (!color1.toHSL || !color2.toHSL) { - console.log(color2.type); - console.dir(color2); - } if (!weight) { weight = new Dimension(50); } var p = weight.value / 100.0; var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; + var a = toHSL(color1).a - toHSL(color2).a; var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; var w2 = 1 - w1; diff --git a/lib/less/functions/list.js b/lib/less/functions/list.js index 864b798f4..8c2523398 100644 --- a/lib/less/functions/list.js +++ b/lib/less/functions/list.js @@ -5,6 +5,7 @@ var Comment = require('../tree/comment'), Ruleset = require('../tree/ruleset'), Selector = require('../tree/selector'), Element = require('../tree/element'), + Quote = require('../tree/quoted'), functionRegistry = require('./function-registry'); var getItemsFromNode = function(node) { @@ -59,7 +60,7 @@ functionRegistry.addMultiple({ each: function(list, rs) { var rules = [], newRules, iterator; - if (list.value) { + if (list.value && !(list instanceof Quote)) { if (Array.isArray(list.value)) { iterator = list.value; } else { diff --git a/lib/less/functions/string.js b/lib/less/functions/string.js index 119309842..a04311cc2 100644 --- a/lib/less/functions/string.js +++ b/lib/less/functions/string.js @@ -1,11 +1,12 @@ var Quoted = require('../tree/quoted'), Anonymous = require('../tree/anonymous'), + Quote = require('../tree/quoted'), JavaScript = require('../tree/javascript'), functionRegistry = require('./function-registry'); functionRegistry.addMultiple({ e: function (str) { - return new Anonymous(str instanceof JavaScript ? str.evaluated : str.value); + return new Quote('"', str instanceof JavaScript ? str.evaluated : str.value, true); }, escape: function (str) { return new Anonymous( diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 847d0cc92..1faa1116a 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -635,10 +635,15 @@ var Parser = function Parser(context, imports, fileInfo) { // color: function () { var rgb; + parserInput.save(); - if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3,4})/))) { - return new(tree.Color)(rgb[1], undefined, rgb[0]); + if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3,4})([\w.#\[])?/))) { + if (!rgb[2]) { + parserInput.forget(); + return new(tree.Color)(rgb[1], undefined, rgb[0]); + } } + parserInput.restore(); }, colorKeyword: function () { diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index c19d74146..080552ac7 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -38,7 +38,7 @@ Quoted.prototype.eval = function (context) { function iterativeReplace(value, regexp, replacementFnc) { var evaluatedValue = value; do { - value = evaluatedValue; + value = evaluatedValue.toString(); evaluatedValue = value.replace(regexp, replacementFnc); } while (value !== evaluatedValue); return evaluatedValue; diff --git a/test/css/functions-each.css b/test/css/functions-each.css index d8743275f..89b90f30b 100644 --- a/test/css/functions-each.css +++ b/test/css/functions-each.css @@ -80,3 +80,13 @@ .bar { content: blue; } +span { + content: 'foo'; + content: 'bar'; +} +div { + content: 'foo'; +} +.a .w-1 { + width: 90 100 110; +} diff --git a/test/css/namespacing/namespacing-1.css b/test/css/namespacing/namespacing-1.css index 7a4253bde..f2097696f 100644 --- a/test/css/namespacing/namespacing-1.css +++ b/test/css/namespacing/namespacing-1.css @@ -14,3 +14,7 @@ #ns1 { foo: uno; } +.button { + color: grey; + border-color: #AAA #CCC; +} diff --git a/test/less/colors.less b/test/less/colors.less index 90063c84f..dd4d1bf92 100644 --- a/test/less/colors.less +++ b/test/less/colors.less @@ -113,4 +113,4 @@ test-12: hsla(#5F59, 0.5); --semi-transparent-dark-background: #001e00ee; --semi-transparent-dark-background-2: rgba(0, 30, 0, 238); // invalid opacity will be capped -} \ No newline at end of file +} diff --git a/test/less/errors/color-func-invalid-color-2.less b/test/less/errors/color-func-invalid-color-2.less new file mode 100644 index 000000000..5152ae62d --- /dev/null +++ b/test/less/errors/color-func-invalid-color-2.less @@ -0,0 +1,2 @@ +// https://github.com/less/less.js/issues/3338 +@base-color: darken(var(--baseColor, red), 50%); \ No newline at end of file diff --git a/test/less/errors/color-func-invalid-color-2.txt b/test/less/errors/color-func-invalid-color-2.txt new file mode 100644 index 000000000..39dd5887c --- /dev/null +++ b/test/less/errors/color-func-invalid-color-2.txt @@ -0,0 +1,3 @@ +RuntimeError: error evaluating function `darken`: Argument cannot be evaluated to a color in {path}color-func-invalid-color-2.less on line 2, column 14: +1 // https://github.com/less/less.js/issues/3338 +2 @base-color: darken(var(--baseColor, red), 50%); diff --git a/test/less/functions-each.less b/test/less/functions-each.less index c480252c8..6d800c4f1 100644 --- a/test/less/functions-each.less +++ b/test/less/functions-each.less @@ -129,3 +129,35 @@ each(@one[@two], { content: @value; } }); + +// https://github.com/less/less.js/issues/3354 +.log(@msgs) { + each(@msgs; { + content: @value; + }); +} + +@messages: 'foo', 'bar'; + +span { + .log(@messages); +} + +div { + .log('foo'); +} + +// https://github.com/less/less.js/issues/3345 +.mixin-create-width-style() { + @list: e("90 100 110"); + + each(@list, { + .w-@{key} { + width: @value; + } + }) +} + +.a { + .mixin-create-width-style(); +} \ No newline at end of file diff --git a/test/less/namespacing/namespacing-1.less b/test/less/namespacing/namespacing-1.less index 1618e4a6b..b5890e532 100644 --- a/test/less/namespacing/namespacing-1.less +++ b/test/less/namespacing/namespacing-1.less @@ -39,4 +39,14 @@ .vars() { sub: tres; } +} + +// https://github.com/less/less.js/issues/3346 +#DEF() { + .colors() { primary: grey; } +} + +.button { + color: #DEF.colors[primary]; + border-color: #AAA #CCC; } \ No newline at end of file