diff --git a/packages/postcss-minify-gradients/src/__tests__/index.js b/packages/postcss-minify-gradients/src/__tests__/index.js index 2090aae00..941bc3113 100644 --- a/packages/postcss-minify-gradients/src/__tests__/index.js +++ b/packages/postcss-minify-gradients/src/__tests__/index.js @@ -60,6 +60,13 @@ test( 'background:repeating-linear-gradient(0deg,#ffe500,#121)' ); +test( + 'repeating-linear: should convert "to top" to 0deg (uppercase)', + processCSS, + 'background:REPEATING-LINEAR-GRADIENT(TO TOP,#ffe500,#121)', + 'background:REPEATING-LINEAR-GRADIENT(0deg,#ffe500,#121)' +); + test( 'repeating-linear: should convert "to right" to 90deg', processCSS, @@ -100,6 +107,13 @@ test( 'background:linear-gradient(45deg,#ffe500 50%,#121 0)' ); +test( + 'linear: should reduce length values if they are the same (uppercase)', + processCSS, + 'background:LINEAR-GRADIENT(45DEG,#FFE500 50%,#121 50%)', + 'background:LINEAR-GRADIENT(45DEG,#FFE500 50%,#121 0)' +); + test( 'linear: should reduce length values if they are less', processCSS, @@ -134,12 +148,18 @@ test( 'BACKGROUND:REPEATING-RADIAL-GRADIENT(#121,#121 5PX,#FFE500 0,#FFE500 10PX)' ); +test( + 'repeating-radial: should reduce length values if they are the same (last is zero)', + processCSS, + 'BACKGROUND:REPEATING-RADIAL-GRADIENT(#121,#121 5PX,#FFE500 5PX,#FFE500 0)', + 'BACKGROUND:REPEATING-RADIAL-GRADIENT(#121,#121 5PX,#FFE500 0,#FFE500 0)' +); + test( 'radial: should correctly account for "at"', passthroughCSS, 'background:radial-gradient(at 50% 0%,rgba(74,74,74,.15),transparent 40%);' ); - test( 'radial: should correctly account for uppercase "at"', passthroughCSS, @@ -154,6 +174,12 @@ test( 'background:radial-gradient(at 50% 0%,rgba(74,74,74,.15),transparent 40%, red 0);' ); +test( + 'radial: should correctly account for "at" (2) (uppercase)', + processCSS, + 'background:radial-gradient(AT 50% 0%,RGBA(74,74,74,.15),TRANSPARENT 40%, RED 40%);', + 'background:radial-gradient(AT 50% 0%,RGBA(74,74,74,.15),TRANSPARENT 40%, RED 0);' +); test( 'radial: should correctly account with prefix "-webkit" (1)', @@ -162,6 +188,28 @@ test( 'background: -webkit-radial-gradient(50% 26%, circle, #fff, rgba(255, 255, 255, 0) 24%)' ); + +test( + 'radial: should correctly account with prefix "-webkit" (1) (uppercase)', + processCSS, + 'background: -WEBKIT-RADIAL-GRADIENT(50% 26%, circle, #fff, rgba(255, 255, 255, 0) 24%)', + 'background: -WEBKIT-RADIAL-GRADIENT(50% 26%, circle, #fff, rgba(255, 255, 255, 0) 24%)' +); + +test( + 'radial: should correctly account with prefix "-webkit" (1) with px', + processCSS, + 'background: -webkit-radial-gradient(50% 26px, circle, #fff, rgba(255, 255, 255, 0) 24%)', + 'background: -webkit-radial-gradient(50% 26px, circle, #fff, rgba(255, 255, 255, 0) 24%)' +); + +test( + 'radial: should correctly account with prefix "-webkit" (1) with px (uppercase)', + processCSS, + 'background: -webkit-radial-gradient(50% 26PX, circle, #fff, rgba(255, 255, 255, 0) 24%)', + 'background: -webkit-radial-gradient(50% 26PX, circle, #fff, rgba(255, 255, 255, 0) 24%)' +); + test( 'radial: should correctly account with prefix "-webkit" (2)', processCSS, @@ -169,6 +217,13 @@ test( 'background: -webkit-radial-gradient(center, 30% 30%, white 20%, black 0)' ); +test( + 'radial: should correctly account with prefix "-webkit" (2) (uppercase)', + processCSS, + 'background: -WEBKIT-RADIAL-GRADIENT(CENTER, 30% 30%, WHITE 20%, BLACK 10%)', + 'background: -WEBKIT-RADIAL-GRADIENT(CENTER, 30% 30%, WHITE 20%, BLACK 0)' +); + test( 'radial: should correctly account with prefix "-webkit" (3)', processCSS, @@ -183,6 +238,20 @@ test( 'background: -webkit-radial-gradient(white 50%, black 0)' ); +test( + 'radial: should correctly account with prefix "-webkit" (4) (uppercase)', + processCSS, + 'background: -WEBKIT-RADIAL-GRADIENT(WHITE 50%, BLACK 40%)', + 'background: -WEBKIT-RADIAL-GRADIENT(WHITE 50%, BLACK 0)' +); + +test( + 'radial: should correctly account with prefix "-webkit" (4) with px (uppercase)', + processCSS, + 'background: -WEBKIT-RADIAL-GRADIENT(WHITE 50px, BLACK 400PX)', + 'background: -WEBKIT-RADIAL-GRADIENT(WHITE 50px, BLACK 400PX)' +); + test( 'radial: should correctly account with prefix "-webkit" (5)', processCSS, @@ -190,6 +259,13 @@ test( 'background: -webkit-radial-gradient(white calc(30%), black calc(50%))' ); +test( + 'radial: should correctly account with prefix "-webkit" (5) (calc uppercase)', + processCSS, + 'background: -webkit-radial-gradient(white CALC(30%), black calc(50%))', + 'background: -webkit-radial-gradient(white CALC(30%), black calc(50%))' +); + test( 'should not mangle floating point numbers', processCSS, @@ -197,6 +273,20 @@ test( 'background:linear-gradient(#fff,#fff 2em,#ccc 0,#ccc 2.1em,#fff 0)' ); +test( + 'should not mangle floating point numbers (uppercase)', + processCSS, + 'background:LINEAR-GRADIENT(#FFF,#FFF 2EM,#CCC 2EM,#CCC 2.1EM,#FFF 2.1EM)', + 'background:LINEAR-GRADIENT(#FFF,#FFF 2EM,#CCC 0,#CCC 2.1EM,#FFF 0)' +); + +test( + 'should not mangle floating point numbers 1 (uppercase)', + processCSS, + 'background:lInEaR-gRaDiEnT(#fFf,#fFf 2Em,#cCc 2eM,#cCc 2.1eM,#fFf 2.1EM)', + 'background:lInEaR-gRaDiEnT(#fFf,#fFf 2Em,#cCc 0,#cCc 2.1eM,#fFf 0)' +); + test( 'should not remove the trailing zero if it is the last stop', passthroughCSS, diff --git a/packages/postcss-minify-gradients/src/index.js b/packages/postcss-minify-gradients/src/index.js index b7c6856f8..43cef8ed7 100644 --- a/packages/postcss-minify-gradients/src/index.js +++ b/packages/postcss-minify-gradients/src/index.js @@ -11,18 +11,23 @@ const angles = { }; function isLessThan (a, b) { - return a.unit === b.unit && parseFloat(a.number) >= parseFloat(b.number); + return a.unit.toLowerCase() === b.unit.toLowerCase() && parseFloat(a.number) >= parseFloat(b.number); } function optimise (decl) { - if (!~decl.value.toLowerCase().indexOf('gradient')) { + const value = decl.value; + + if (!~value.toLowerCase().indexOf('gradient')) { return; } - decl.value = valueParser(decl.value).walk(node => { + + decl.value = valueParser(value).walk(node => { if (node.type !== 'function' || !node.nodes.length) { return false; } + const lowerCasedValue = node.value.toLowerCase(); + if ( lowerCasedValue === 'linear-gradient' || lowerCasedValue === 'repeating-linear-gradient' || @@ -30,72 +35,96 @@ function optimise (decl) { lowerCasedValue === '-webkit-repeating-linear-gradient' ) { let args = getArguments(node); + if (node.nodes[0].value.toLowerCase() === 'to' && args[0].length === 3) { node.nodes = node.nodes.slice(2); node.nodes[0].value = angles[node.nodes[0].value.toLowerCase()]; } + let lastStop = null; + args.forEach((arg, index) => { if (!arg[2]) { return; } + let isFinalStop = index === args.length - 1; let thisStop = unit(arg[2].value); + if (lastStop === null) { lastStop = thisStop; + if (!isFinalStop && lastStop && lastStop.number === '0' && lastStop.unit.toLowerCase() !== 'deg') { arg[1].value = arg[2].value = ''; } + return; } - if (isLessThan(lastStop, thisStop)) { + + if (lastStop && thisStop && isLessThan(lastStop, thisStop)) { arg[2].value = 0; } + lastStop = thisStop; + if (isFinalStop && arg[2].value === '100%') { arg[1].value = arg[2].value = ''; } }); + return false; } + if ( lowerCasedValue === 'radial-gradient' || lowerCasedValue === 'repeating-radial-gradient' ) { let args = getArguments(node); let lastStop; + const hasAt = args[0].find(n => n.value.toLowerCase() === 'at'); + args.forEach((arg, index) => { if (!arg[2] || !index && hasAt) { return; } + let thisStop = unit(arg[2].value); + if (!lastStop) { lastStop = thisStop; + return; } - if (isLessThan(lastStop, thisStop)) { + + if (lastStop && thisStop && isLessThan(lastStop, thisStop)) { arg[2].value = 0; } + lastStop = thisStop; }); + return false; } + if ( lowerCasedValue === '-webkit-radial-gradient' || lowerCasedValue === '-webkit-repeating-radial-gradient' ) { let args = getArguments(node); let lastStop; + args.forEach((arg) => { let color; let stop; + if (arg[2] !== undefined) { if (arg[0].type === 'function') { color = `${arg[0].value}(${stringify(arg[0].nodes)})`; } else { color = arg[0].value; } + if (arg[2].type === 'function') { stop = `${arg[2].value}(${stringify(arg[2].nodes)})`; } else { @@ -105,25 +134,35 @@ function optimise (decl) { if (arg[0].type === 'function') { color = `${arg[0].value}(${stringify(arg[0].nodes)})`; } + color = arg[0].value; } + + color = color.toLowerCase(); + const colorStop = stop || stop === 0 ? - isColorStop(color, stop) : + isColorStop(color, stop.toLowerCase()) : isColorStop(color); if (!colorStop || !arg[2]) { return; } + let thisStop = unit(arg[2].value); + if (!lastStop) { lastStop = thisStop; + return; } - if (isLessThan(lastStop, thisStop)) { + + if (lastStop && thisStop && isLessThan(lastStop, thisStop)) { arg[2].value = 0; } + lastStop = thisStop; }); + return false; } }).toString();