From 7fabd9d03c81142c5854d0c0a50bee15ed86c219 Mon Sep 17 00:00:00 2001 From: Evilebot Tnawi Date: Mon, 29 Apr 2019 15:35:33 +0300 Subject: [PATCH 01/13] fix(postcss-minify-gradients): don't break gradients with `var` and `env` functions (#735) --- .../src/__tests__/index.js | 60 ++++ .../postcss-minify-gradients/src/index.js | 279 ++++++++++-------- 2 files changed, 221 insertions(+), 118 deletions(-) diff --git a/packages/postcss-minify-gradients/src/__tests__/index.js b/packages/postcss-minify-gradients/src/__tests__/index.js index 941bc3113..435584e84 100644 --- a/packages/postcss-minify-gradients/src/__tests__/index.js +++ b/packages/postcss-minify-gradients/src/__tests__/index.js @@ -317,6 +317,66 @@ test( 'background-image:var(--bg),linear-gradient(red,blue)' ); +test( + 'should pass through custom property references #2', + passthroughCSS, + 'background:linear-gradient(to var(--var), transparent, black) 0% 50% no-repeat' +); + +test( + 'should pass through custom property references #3', + passthroughCSS, + 'background:linear-gradient(var(--var), transparent, black) 0% 50% no-repeat' +); + +test( + 'should pass through custom property references #4', + passthroughCSS, + 'background:linear-gradient(var(--var), black) 0% 50% no-repeat' +); + +test( + 'should pass through custom property references #5', + passthroughCSS, + 'background:linear-gradient(var(--var)) 0% 50% no-repeat' +); + +test( + 'should pass through custom property references #6', + passthroughCSS, + 'background:linear-gradient(var(--var), rgba(255,0,0,0) 70.71%)' +); + +test( + 'should pass through custom property references #7', + passthroughCSS, + 'background:linear-gradient(to env(--var), transparent, black) 0% 50% no-repeat' +); + +test( + 'should pass through custom property references #8', + passthroughCSS, + 'background:linear-gradient(var(--var))' +); + +test( + 'should pass through custom property references #9', + passthroughCSS, + 'background:linear-gradient(var(--foo), var(--bar), var(--baz))' +); + +test( + 'should pass through env property references', + passthroughCSS, + 'background:linear-gradient(env(--var))' +); + +test( + 'should not throw error on broken syntax', + passthroughCSS, + 'background:' +); + test( 'should not operate on declarations without gradients', passthroughCSS, diff --git a/packages/postcss-minify-gradients/src/index.js b/packages/postcss-minify-gradients/src/index.js index 43cef8ed7..101218ff7 100644 --- a/packages/postcss-minify-gradients/src/index.js +++ b/packages/postcss-minify-gradients/src/index.js @@ -1,173 +1,216 @@ -import postcss from 'postcss'; -import valueParser, {unit, stringify} from 'postcss-value-parser'; -import getArguments from 'lerna:cssnano-util-get-arguments'; -import isColorStop from 'is-color-stop'; +import postcss from "postcss"; +import valueParser, {unit, stringify} from "postcss-value-parser"; +import getArguments from "lerna:cssnano-util-get-arguments"; +import isColorStop from "is-color-stop"; const angles = { - top: '0deg', - right: '90deg', - bottom: '180deg', - left: '270deg', + top: "0deg", + right: "90deg", + bottom: "180deg", + left: "270deg", }; function isLessThan (a, b) { - return a.unit.toLowerCase() === b.unit.toLowerCase() && parseFloat(a.number) >= parseFloat(b.number); + return ( + a.unit.toLowerCase() === b.unit.toLowerCase() && + parseFloat(a.number) >= parseFloat(b.number) + ); } function optimise (decl) { const value = decl.value; - if (!~value.toLowerCase().indexOf('gradient')) { + if (!value) { return; } - decl.value = valueParser(value).walk(node => { - if (node.type !== 'function' || !node.nodes.length) { - return false; - } + const normalizedValue = value.toLowerCase(); - const lowerCasedValue = node.value.toLowerCase(); + if (normalizedValue.includes("var(") || normalizedValue.includes("env(")) { + return; + } - if ( - lowerCasedValue === 'linear-gradient' || - lowerCasedValue === 'repeating-linear-gradient' || - lowerCasedValue === '-webkit-linear-gradient' || - lowerCasedValue === '-webkit-repeating-linear-gradient' - ) { - let args = getArguments(node); + if (!normalizedValue.includes("gradient")) { + return; + } - 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()]; + decl.value = valueParser(value) + .walk(node => { + if (node.type !== "function" || !node.nodes.length) { + return false; } - let lastStop = null; - - args.forEach((arg, index) => { - if (!arg[2]) { - return; + const lowerCasedValue = node.value.toLowerCase(); + + if ( + lowerCasedValue === "linear-gradient" || + lowerCasedValue === "repeating-linear-gradient" || + lowerCasedValue === "-webkit-linear-gradient" || + 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 isFinalStop = index === args.length - 1; - let thisStop = unit(arg[2].value); + let lastStop = null; - if (lastStop === null) { - lastStop = thisStop; - - if (!isFinalStop && lastStop && lastStop.number === '0' && lastStop.unit.toLowerCase() !== 'deg') { - arg[1].value = arg[2].value = ''; + args.forEach((arg, index) => { + if (!arg[2]) { + return; } - return; - } + let isFinalStop = index === args.length - 1; + let thisStop = unit(arg[2].value); - if (lastStop && thisStop && isLessThan(lastStop, thisStop)) { - arg[2].value = 0; - } + if (lastStop === null) { + lastStop = thisStop; - lastStop = thisStop; + if ( + !isFinalStop && + lastStop && + lastStop.number === "0" && + lastStop.unit.toLowerCase() !== "deg" + ) { + arg[1].value = arg[2].value = ""; + } - if (isFinalStop && arg[2].value === '100%') { - arg[1].value = arg[2].value = ''; - } - }); + return; + } - return false; - } + if ( + lastStop && + thisStop && + isLessThan(lastStop, thisStop) + ) { + arg[2].value = 0; + } - if ( - lowerCasedValue === 'radial-gradient' || - lowerCasedValue === 'repeating-radial-gradient' - ) { - let args = getArguments(node); - let lastStop; + lastStop = thisStop; - const hasAt = args[0].find(n => n.value.toLowerCase() === 'at'); + if (isFinalStop && arg[2].value === "100%") { + arg[1].value = arg[2].value = ""; + } + }); - args.forEach((arg, index) => { - if (!arg[2] || !index && hasAt) { - return; - } + return false; + } - let thisStop = unit(arg[2].value); + if ( + lowerCasedValue === "radial-gradient" || + lowerCasedValue === "repeating-radial-gradient" + ) { + let args = getArguments(node); + let lastStop; - if (!lastStop) { - lastStop = thisStop; + const hasAt = args[0].find(n => n.value.toLowerCase() === "at"); - return; - } + args.forEach((arg, index) => { + if (!arg[2] || (!index && hasAt)) { + return; + } - if (lastStop && thisStop && isLessThan(lastStop, thisStop)) { - arg[2].value = 0; - } + let thisStop = unit(arg[2].value); - lastStop = thisStop; - }); + if (!lastStop) { + lastStop = thisStop; - return false; - } + return; + } - if ( - lowerCasedValue === '-webkit-radial-gradient' || - lowerCasedValue === '-webkit-repeating-radial-gradient' - ) { - let args = getArguments(node); - let lastStop; + if ( + lastStop && + thisStop && + isLessThan(lastStop, thisStop) + ) { + arg[2].value = 0; + } - args.forEach((arg) => { - let color; - let stop; + lastStop = thisStop; + }); - if (arg[2] !== undefined) { - if (arg[0].type === 'function') { - color = `${arg[0].value}(${stringify(arg[0].nodes)})`; - } else { - color = arg[0].value; - } + return false; + } - if (arg[2].type === 'function') { - stop = `${arg[2].value}(${stringify(arg[2].nodes)})`; + 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 { + stop = arg[2].value; + } } else { - stop = arg[2].value; - } - } else { - if (arg[0].type === 'function') { - color = `${arg[0].value}(${stringify(arg[0].nodes)})`; - } + if (arg[0].type === "function") { + color = `${arg[0].value}(${stringify( + arg[0].nodes + )})`; + } - color = arg[0].value; - } + color = arg[0].value; + } - color = color.toLowerCase(); + color = color.toLowerCase(); - const colorStop = stop || stop === 0 ? - isColorStop(color, stop.toLowerCase()) : - isColorStop(color); + const colorStop = + stop || stop === 0 + ? isColorStop(color, stop.toLowerCase()) + : isColorStop(color); - if (!colorStop || !arg[2]) { - return; - } + if (!colorStop || !arg[2]) { + return; + } - let thisStop = unit(arg[2].value); + let thisStop = unit(arg[2].value); - if (!lastStop) { - lastStop = thisStop; + if (!lastStop) { + lastStop = thisStop; - return; - } + return; + } - if (lastStop && thisStop && isLessThan(lastStop, thisStop)) { - arg[2].value = 0; - } + if ( + lastStop && + thisStop && + isLessThan(lastStop, thisStop) + ) { + arg[2].value = 0; + } - lastStop = thisStop; - }); + lastStop = thisStop; + }); - return false; - } - }).toString(); + return false; + } + }) + .toString(); } -export default postcss.plugin('postcss-minify-gradients', () => { +export default postcss.plugin("postcss-minify-gradients", () => { return css => css.walkDecls(optimise); }); From 724104992e22d1b51e65383a9c6fbeb89a6a73f0 Mon Sep 17 00:00:00 2001 From: Evilebot Tnawi Date: Mon, 29 Apr 2019 15:40:01 +0300 Subject: [PATCH 02/13] fix(postcss-svgo): security problem with `js-yaml` (#736) --- packages/postcss-svgo/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/postcss-svgo/package.json b/packages/postcss-svgo/package.json index 2012a9a72..e66fe4e85 100644 --- a/packages/postcss-svgo/package.json +++ b/packages/postcss-svgo/package.json @@ -33,10 +33,10 @@ }, "repository": "cssnano/cssnano", "dependencies": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" + "is-svg": "^4.1.0", + "postcss": "^7.0.14", + "postcss-value-parser": "^3.3.1", + "svgo": "^1.2.2" }, "bugs": { "url": "https://github.com/cssnano/cssnano/issues" From 2e6cff1ba42f27eee9c09a3f0c60c22c19d1eb6d Mon Sep 17 00:00:00 2001 From: Evilebot Tnawi Date: Mon, 29 Apr 2019 15:56:14 +0300 Subject: [PATCH 03/13] test(postcss-colormin): var tests (#737) --- .../postcss-colormin/src/__tests__/index.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/postcss-colormin/src/__tests__/index.js b/packages/postcss-colormin/src/__tests__/index.js index 753a57429..7cbf1695e 100644 --- a/packages/postcss-colormin/src/__tests__/index.js +++ b/packages/postcss-colormin/src/__tests__/index.js @@ -246,6 +246,42 @@ test( 'h1{content:""}' ); +test( + 'should passthrough css variables', + passthroughDefault, + 'h1{color:var(--foo)}' +); + +test( + 'should passthrough css variables #2', + passthroughDefault, + 'h1{color:var(--foo) var(--bar)}' +); + +test( + 'should passthrough css variables #3', + passthroughDefault, + 'h1{color:rgb(var(--foo),255,255)}' +); + +test( + 'should passthrough css variables #4', + passthroughDefault, + 'h1{color:rgb(255,var(--bar),255)}' +); + +test( + 'should passthrough css variables #5', + passthroughDefault, + 'h1{color:rgb(255,255,var(--baz))}' +); + +test( + 'should passthrough css variables #6', + passthroughDefault, + 'h1{color:rgb(var(--foo))}' +); + test( 'should not convert this specific rgba value to "transparent" (old IE)', passthroughCSS, From 3580e7be5a4d32ed2b122d13b87380d2b150c705 Mon Sep 17 00:00:00 2001 From: Evilebot Tnawi Date: Mon, 29 Apr 2019 17:55:05 +0300 Subject: [PATCH 04/13] chore: integrate prettier (#738) --- .editorconfig | 7 +- .eslintrc.js | 72 + .prettierrc.js | 5 + .remarkrc.js | 40 +- README.md | 8 +- lerna.json | 8 +- package.json | 30 +- packages/css-size/css-size.d.ts | 3 +- packages/css-size/index.js | 3 +- packages/css-size/processors/nano.js | 6 +- packages/css-size/processors/noop.js | 4 +- packages/css-size/src/__tests__/index.js | 190 +-- packages/css-size/src/cli.js | 53 +- packages/css-size/src/index.js | 152 +- .../src/__tests__/autoprefixer.js | 40 +- .../src/__tests__/integrations.js | 30 +- packages/cssnano-preset-advanced/src/index.js | 28 +- .../src/__tests__/integrations.js | 30 +- packages/cssnano-preset-default/src/index.js | 86 +- .../src/__tests__/index.js | 78 +- .../cssnano-util-get-arguments/src/index.js | 21 +- .../src/__tests__/index.js | 18 +- packages/cssnano-util-get-match/src/index.js | 22 +- packages/cssnano-util-raw-cache/src/index.js | 30 +- .../src/__tests__/index.js | 148 +- .../cssnano-util-same-parent/src/index.js | 46 +- packages/cssnano/quickstart.js | 13 +- packages/cssnano/src/__tests__/_processCss.js | 12 +- .../cssnano/src/__tests__/_webpack.config.js | 38 +- packages/cssnano/src/__tests__/api.js | 30 +- packages/cssnano/src/__tests__/fixtures.js | 253 ++-- packages/cssnano/src/__tests__/issue26.js | 26 +- packages/cssnano/src/__tests__/issue315.js | 15 +- packages/cssnano/src/__tests__/issue420.js | 20 +- packages/cssnano/src/__tests__/issue579.js | 14 +- packages/cssnano/src/__tests__/knownIssues.js | 16 +- .../cssnano/src/__tests__/postcss-calc.js | 121 +- .../src/__tests__/postcss-convert-values.js | 160 +- .../src/__tests__/postcss-discard-comments.js | 140 +- .../__tests__/postcss-discard-duplicates.js | 56 +- .../__tests__/postcss-discard-overridden.js | 16 +- .../src/__tests__/postcss-merge-longhand.js | 270 ++-- .../src/__tests__/postcss-merge-rules.js | 296 ++-- .../__tests__/postcss-minify-font-weight.js | 24 +- .../src/__tests__/postcss-minify-gradients.js | 120 +- .../src/__tests__/postcss-minify-params.js | 8 +- .../src/__tests__/postcss-minify-selectors.js | 216 +-- .../src/__tests__/postcss-normalize-url.js | 170 +-- .../__tests__/postcss-reduce-transforms.js | 128 +- .../cssnano/src/__tests__/postcss-svgo.js | 32 +- packages/cssnano/src/__tests__/presets.js | 128 +- packages/cssnano/src/__tests__/webpack.js | 20 +- packages/cssnano/src/index.js | 165 ++- packages/example-cli-usage/postcss.config.js | 4 +- .../postcss-colormin/src/__tests__/colours.js | 262 ++-- .../postcss-colormin/src/__tests__/index.js | 326 ++--- packages/postcss-colormin/src/colours.js | 90 +- packages/postcss-colormin/src/generate.js | 38 +- packages/postcss-colormin/src/index.js | 134 +- .../postcss-colormin/src/lib/toShorthand.js | 14 +- .../src/__tests__/index.js | 493 +++---- packages/postcss-convert-values/src/index.js | 171 ++- .../postcss-convert-values/src/lib/convert.js | 102 +- .../src/__tests__/index.js | 291 ++-- .../postcss-discard-comments/src/index.js | 200 ++- .../src/lib/commentParser.js | 38 +- .../src/lib/commentRemover.js | 34 +- .../src/__tests__/index.js | 177 ++- .../postcss-discard-duplicates/src/index.js | 185 ++- .../src/__tests__/index.js | 153 +- packages/postcss-discard-empty/src/index.js | 38 +- .../src/__tests__/index.js | 93 +- .../postcss-discard-overridden/src/index.js | 72 +- .../src/__tests__/index.js | 173 ++- .../src/__tests__/namespace.js | 51 +- packages/postcss-discard-unused/src/index.js | 234 +-- .../src/__tests__/index.js | 257 ++-- packages/postcss-merge-idents/src/index.js | 166 ++- .../src/__tests__/api.js | 8 +- .../src/__tests__/borders.js | 1292 +++++++++-------- .../src/__tests__/boxBase.js | 337 +++-- .../src/__tests__/columns.js | 246 ++-- packages/postcss-merge-longhand/src/index.js | 16 +- .../src/lib/canExplode.js | 16 +- .../src/lib/canMerge.js | 40 +- .../src/lib/decl/borders.js | 1109 +++++++------- .../src/lib/decl/boxBase.js | 132 +- .../src/lib/decl/columns.js | 177 +-- .../src/lib/decl/index.js | 7 +- .../src/lib/getDecls.js | 6 +- .../src/lib/getLastNode.js | 2 +- .../src/lib/getRules.js | 14 +- .../src/lib/getValue.js | 4 +- .../src/lib/hasAllProps.js | 4 +- .../src/lib/insertCloned.js | 10 +- .../src/lib/isCustomProp.js | 3 +- .../src/lib/mergeRules.js | 54 +- .../src/lib/minifyTrbl.js | 22 +- .../src/lib/minifyWsc.js | 32 +- .../src/lib/parseTrbl.js | 18 +- .../src/lib/parseWsc.js | 89 +- .../postcss-merge-longhand/src/lib/remove.js | 4 +- .../src/lib/validateWsc.js | 98 +- .../src/__tests__/index.js | 888 +++++------ packages/postcss-merge-rules/src/index.js | 537 +++---- .../src/lib/ensureCompatibility.js | 228 +-- .../src/__tests__/index.js | 430 +++--- .../src/__tests__/minify-family.js | 172 +-- .../src/__tests__/minify-font.js | 60 +- .../src/__tests__/minify-weight.js | 8 +- .../postcss-minify-font-values/src/index.js | 44 +- .../src/lib/keywords.js | 79 +- .../src/lib/minify-family.js | 324 +++-- .../src/lib/minify-font.js | 66 +- .../src/lib/minify-weight.js | 12 +- .../src/lib/uniqs.js | 22 +- .../src/__tests__/index.js | 429 +++--- .../postcss-minify-gradients/src/index.js | 351 +++-- .../src/__tests__/index.js | 319 ++-- packages/postcss-minify-params/src/index.js | 157 +- .../src/__tests__/index.js | 582 ++++---- .../postcss-minify-selectors/src/index.js | 320 ++-- .../src/lib/canUnquote.js | 18 +- .../src/lib/unquote.js | 2 +- .../src/__tests__/index.js | 122 +- .../postcss-normalize-charset/src/index.js | 55 +- .../src/__tests__/index.js | 51 +- .../src/index.js | 64 +- .../src/lib/map.js | 38 +- .../src/__tests__/index.js | 297 ++-- .../postcss-normalize-positions/src/index.js | 179 +-- .../src/__tests__/index.js | 102 +- .../src/index.js | 128 +- .../src/lib/map.js | 12 +- .../src/__tests__/index.js | 251 ++-- .../postcss-normalize-string/src/index.js | 380 ++--- .../src/__tests__/index.js | 155 +- .../src/index.js | 100 +- .../src/lib/map.js | 10 +- .../src/__tests__/index.js | 141 +- .../postcss-normalize-unicode/src/index.js | 110 +- .../src/__tests__/index.js | 317 ++-- packages/postcss-normalize-url/src/index.js | 166 ++- .../src/__tests__/index.js | 34 +- .../postcss-normalize-whitespace/src/index.js | 156 +- .../src/__tests__/index.js | 673 ++++----- packages/postcss-ordered-values/src/index.js | 110 +- .../src/lib/addSpace.js | 4 +- .../src/lib/getValue.js | 42 +- .../src/rules/animation.js | 159 +- .../src/rules/border.js | 93 +- .../src/rules/boxShadow.js | 82 +- .../src/rules/flexFlow.js | 47 +- .../src/rules/transition.js | 101 +- .../src/__tests__/index.js | 547 +++---- packages/postcss-reduce-idents/src/index.js | 27 +- .../postcss-reduce-idents/src/lib/cache.js | 16 +- .../src/lib/counter-style.js | 159 +- .../postcss-reduce-idents/src/lib/counter.js | 165 ++- .../postcss-reduce-idents/src/lib/encode.js | 33 +- .../src/lib/grid-template.js | 125 +- .../postcss-reduce-idents/src/lib/isNum.js | 6 +- .../src/lib/keyframes.js | 120 +- .../src/__tests__/index.js | 125 +- .../postcss-reduce-initial/src/acquire.js | 99 +- packages/postcss-reduce-initial/src/index.js | 55 +- .../src/__tests__/index.js | 253 ++-- .../postcss-reduce-transforms/src/index.js | 302 ++-- packages/postcss-svgo/src/__tests__/index.js | 225 +-- packages/postcss-svgo/src/index.js | 195 +-- packages/postcss-svgo/src/lib/url.js | 20 +- .../src/__tests__/index.js | 29 +- .../postcss-unique-selectors/src/index.js | 8 +- .../postcss-zindex/src/__tests__/index.js | 113 +- packages/postcss-zindex/src/index.js | 61 +- packages/postcss-zindex/src/lib/layerCache.js | 57 +- .../stylehacks/src/__tests__/_processCSS.js | 20 +- packages/stylehacks/src/__tests__/api.js | 145 +- .../stylehacks/src/__tests__/bodyEmpty.js | 20 +- .../__tests__/htmlCombinatorCommentBody.js | 30 +- .../src/__tests__/htmlFirstChild.js | 20 +- .../stylehacks/src/__tests__/important.js | 21 +- packages/stylehacks/src/__tests__/issue8.js | 12 +- .../stylehacks/src/__tests__/leadingStar.js | 31 +- .../src/__tests__/leadingUnderscore.js | 90 +- .../stylehacks/src/__tests__/mediaSlash0.js | 20 +- .../src/__tests__/mediaSlash0Slash9.js | 20 +- .../stylehacks/src/__tests__/mediaSlash9.js | 20 +- packages/stylehacks/src/__tests__/slash9.js | 40 +- packages/stylehacks/src/__tests__/starHtml.js | 57 +- .../src/__tests__/trailingSlashComma.js | 42 +- .../stylehacks/src/dictionary/browsers.js | 10 +- .../stylehacks/src/dictionary/identifiers.js | 6 +- packages/stylehacks/src/dictionary/postcss.js | 4 +- packages/stylehacks/src/exists.js | 6 +- packages/stylehacks/src/index.js | 78 +- packages/stylehacks/src/isMixin.js | 14 +- packages/stylehacks/src/plugin.js | 84 +- packages/stylehacks/src/plugins/bodyEmpty.js | 48 +- .../src/plugins/htmlCombinatorCommentBody.js | 59 +- .../stylehacks/src/plugins/htmlFirstChild.js | 48 +- packages/stylehacks/src/plugins/important.js | 22 +- packages/stylehacks/src/plugins/index.js | 24 +- .../stylehacks/src/plugins/leadingStar.js | 74 +- .../src/plugins/leadingUnderscore.js | 38 +- .../stylehacks/src/plugins/mediaSlash0.js | 22 +- .../src/plugins/mediaSlash0Slash9.js | 22 +- .../stylehacks/src/plugins/mediaSlash9.js | 22 +- packages/stylehacks/src/plugins/slash9.js | 22 +- packages/stylehacks/src/plugins/starHtml.js | 50 +- .../src/plugins/trailingSlashComma.js | 36 +- site/App.js | 25 +- site/Html.js | 56 +- site/postcss.config.js | 64 +- site/src/components/Analytics/index.js | 84 +- site/src/components/Button/index.js | 43 +- site/src/components/Clipboard/index.js | 34 +- site/src/components/Container/index.js | 20 +- site/src/components/Content/index.js | 20 +- site/src/components/ContentBlock/index.js | 38 +- site/src/components/CssExample/index.js | 24 +- site/src/components/DefaultHeadMeta/index.js | 71 +- site/src/components/Footer/index.js | 40 +- site/src/components/Header/index.js | 105 +- site/src/components/Hero/index.js | 19 +- site/src/components/Loading/index.js | 52 +- site/src/components/Metadata/index.js | 27 +- site/src/components/PagePreview/index.js | 43 +- site/src/components/PagesList/index.js | 45 +- site/src/layouts/App/Index.js | 106 +- site/src/layouts/Blog/index.js | 34 +- site/src/layouts/Changelog/index.js | 44 +- site/src/layouts/Community/index.js | 42 +- site/src/layouts/CoverPage/index.js | 46 +- site/src/layouts/Guide/Guides.js | 53 +- site/src/layouts/Guide/NextButton.js | 51 +- site/src/layouts/Guide/index.js | 85 +- site/src/layouts/Home/index.js | 277 ++-- site/src/layouts/Optimisation/index.js | 241 +-- site/src/layouts/Optimisations/index.js | 34 +- site/src/layouts/Page/index.js | 101 +- site/src/layouts/PageError/index.js | 61 +- site/src/layouts/Playground/cssnano.js | 62 +- site/src/layouts/Playground/index.js | 184 +-- site/src/layouts/Post/index.js | 69 +- site/src/layouts/SupportUs/index.js | 44 +- site/src/metadata.js | 49 +- site/src/routes.js | 70 +- site/webpack.config.js | 205 +-- util/buildAliases.js | 33 +- util/buildFrameworks.js | 48 +- util/buildMetadata.js | 58 +- util/buildPackages.js | 276 ++-- util/buildSiteMarkdown.js | 553 +++---- util/contributorsSection.js | 30 +- util/frameworks.js | 15 +- util/getData.js | 10 +- util/getPackages.js | 18 +- util/getPresets.js | 6 +- util/installSection.js | 190 ++- util/pluginName.js | 4 +- util/sortAscending.js | 16 +- util/testHelpers.js | 109 +- yarn.lock | 1076 +++++++------- 264 files changed, 14971 insertions(+), 14584 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .prettierrc.js diff --git a/.editorconfig b/.editorconfig index 3f08c1a48..dffb18adf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,15 +2,12 @@ root = true [*] +charset = utf-8 indent_style = space -indent_size = 4 +indent_size = 2 end_of_line = lf -charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true -[*.{json,yml}] -indent_size = 2 - [*.md] trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..509703bbe --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,72 @@ +module.exports = { + root: true, + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module', + ecmaVersion: 2019, + jsx: true, + }, + env: { + browser: true, + node: true, + es6: true, + }, + plugins: ['prettier', 'import'], + extends: [ + 'eslint:recommended', + 'plugin:import/recommended', + 'plugin:react/recommended', + ], + settings: { + react: { + version: '16.4.1', // React version. "detect" automatically picks the version you have installed. + }, + 'import/ignore': ['node_modules', '.json$'], + }, + rules: { + 'prettier/prettier': ['error'], + camelcase: ['error'], + curly: ['error', 'all'], + 'dot-notation': ['error'], + eqeqeq: ['error'], + 'handle-callback-err': ['error'], + 'new-cap': ['error'], + 'no-alert': ['error'], + 'no-caller': ['error'], + 'no-eval': ['error'], + 'no-labels': ['error'], + 'no-lonely-if': ['error'], + 'no-new': ['error'], + 'no-proto': ['error'], + 'no-return-assign': ['error'], + 'no-self-compare': ['error'], + 'no-shadow': ['error'], + 'no-shadow-restricted-names': ['error'], + 'no-useless-call': ['error'], + 'no-var': ['error'], + 'no-void': ['error'], + 'no-warning-comments': ['error'], + 'no-with': ['error'], + radix: ['error'], + 'spaced-comment': ['error', 'always'], + strict: ['error', 'global'], + yoda: ['error', 'never'], + + // Import rules + // Search way how integrate with `lerna` + 'import/no-unresolved': 'off', + 'import/imports-first': ['error'], + 'import/newline-after-import': ['error'], + 'import/no-duplicates': ['error'], + 'import/no-mutable-exports': ['error'], + 'import/no-named-as-default': ['error'], + 'import/no-named-as-default-member': ['error'], + 'import/order': ['error'], + 'import/prefer-default-export': ['error'], + + // React + // Need enable in future + 'react/prop-types': ['off'], + 'react/display-name': ['off'], + }, +}; diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..1934550a6 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,5 @@ +module.exports = { + singleQuote: true, + trailingComma: 'es5', + arrowParens: 'always', +}; diff --git a/.remarkrc.js b/.remarkrc.js index 348e638d9..b81482ac2 100644 --- a/.remarkrc.js +++ b/.remarkrc.js @@ -1,28 +1,28 @@ const bookmarks = { - /* Packages */ - 'autoprefixer': 'https://github.com/postcss/autoprefixer', - 'browserslist': 'https://github.com/ai/browserslist', - 'css-size': 'https://npmjs.org/package/css-size', - /* Documentation/Online */ - 'node.js': 'https://nodejs.org', - 'npm': 'https://npmjs.com', - 'postcss': 'http://postcss.org', - /* Guides */ - 'guidePresets': '/guides/presets', - 'guideGettingStarted': '/guides/getting-started', - 'guideAdvancedTransforms': '/guides/advanced-transforms', - 'guideContributing': '/guides/contributing', + /* Packages */ + autoprefixer: 'https://github.com/postcss/autoprefixer', + browserslist: 'https://github.com/ai/browserslist', + 'css-size': 'https://npmjs.org/package/css-size', + /* Documentation/Online */ + 'node.js': 'https://nodejs.org', + npm: 'https://npmjs.com', + postcss: 'http://postcss.org', + /* Guides */ + guidePresets: '/guides/presets', + guideGettingStarted: '/guides/getting-started', + guideAdvancedTransforms: '/guides/advanced-transforms', + guideContributing: '/guides/contributing', }; exports.settings = { - bullet: '-', - fences: true, - listItemIndent: '1', - paddedTable: false, + bullet: '-', + fences: true, + listItemIndent: '1', + paddedTable: false, }; exports.plugins = [ - [require('remark-heading-gap'), {}], - [require('remark-bookmarks'), {bookmarks}], - [require('remark-frontmatter')] + [require('remark-heading-gap'), {}], + [require('remark-bookmarks'), { bookmarks }], + [require('remark-frontmatter')], ]; diff --git a/README.md b/README.md index 8b190e2b0..6b4bee546 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![NPM version](https://img.shields.io/npm/v/cssnano.svg)](https://www.npmjs.org/package/cssnano) [![Build Status](https://travis-ci.org/cssnano/cssnano.svg?branch=master)](https://travis-ci.org/cssnano/cssnano) -[![Build status](https://ci.appveyor.com/api/projects/status/t1chyvhobtju7jy8/branch/master?svg=true)](https://ci.appveyor.com/project/cssnano/cssnano/branch/master) +[![Build status](https://ci.appveyor.com/api/projects/status/t1chyvhobtju7jy8/branch/master?svg=true)](https://ci.appveyor.com/project/cssnano/cssnano/branch/master) [![Coverage Status](https://coveralls.io/repos/github/cssnano/cssnano/badge.svg?branch=master)](https://coveralls.io/github/cssnano/cssnano?branch=master) [![Gitter](https://img.shields.io/badge/Gitter-Join_the_PostCSS_chat-brightgreen.svg)](https://gitter.im/postcss/postcss) @@ -32,17 +32,15 @@ to provide different output depending on the browsers that you support. For further details check out the [website](http://cssnano.co/): -* [Installation guide for your build process](http://cssnano.co/guides/getting-started). -* [Full list of optimisations](http://cssnano.co/optimisations/). +- [Installation guide for your build process](http://cssnano.co/guides/getting-started). +- [Full list of optimisations](http://cssnano.co/optimisations/). You can now [try cssnano online](https://cssnano.co/playground/)! - ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md). - ## License MIT © [Ben Briggs](http://beneb.info) diff --git a/lerna.json b/lerna.json index ca526d88d..cb45fc17b 100644 --- a/lerna.json +++ b/lerna.json @@ -3,13 +3,9 @@ "npmClient": "yarn", "command": { "publish": { - "ignoreChanges": [ - "package-lock.json" - ] + "ignoreChanges": ["package-lock.json"] } }, - "packages": [ - "packages/*" - ], + "packages": ["packages/*"], "version": "independent" } diff --git a/package.json b/package.json index 878bbba3c..3e132293a 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,18 @@ "private": true, "scripts": { "all-contributors": "all-contributors add", - "lint": "eslint --ignore-path .gitignore packages/**/src util", - "fixlint": "eslint --fix --ignore-path .gitignore packages/**/src util", + "lint": "eslint . --cache --ignore-path .gitignore", + "fixlint": "yarn lint -- --fix", "postinstall": "lerna bootstrap", "build:packages": "babel-node ./util/buildPackages.js", "build:aliases": "babel-node ./util/buildAliases.js", "build:metadata": "babel-node ./util/buildMetadata.js", - "build:site": "remark site/content/**/*.md -o && npm run build:metadata && babel-node ./util/buildSiteMarkdown.js && cd site && npm run build", + "build:site": "remark site/content/**/*.md -o && yarn build:metadata && babel-node ./util/buildSiteMarkdown.js && cd site && yarn build", "build:integration": "babel-node ./util/buildFrameworks.js", - "pretest": "npm run lint", + "pretest": "yarn lint", "test-only": "cross-env BABEL_ENV=test ava", - "test-only:coverage": "nyc --reporter=lcov --reporter=text npm run test-only", - "test": "npm run test-only", + "test-only:coverage": "nyc --reporter=lcov --reporter=text yarn test-only", + "test": "yarn test-only", "deploy": "gh-pages -t -d site/dist", "publish": "lerna publish" }, @@ -24,6 +24,7 @@ "devDependencies": { "all-contributors-cli": "^5.0.0", "ava": "^0.25.0", + "babel-eslint": "^10.0.1", "babel-cli": "^6.0.0", "babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-module-resolver": "^2.5.0", @@ -34,10 +35,10 @@ "coveralls": "^3.0.1", "cross-env": "^5.1.6", "dox": "^0.9.0", - "eslint": "^3.0.0", - "eslint-config-cssnano": "^3.0.0", - "eslint-plugin-babel": "^3.3.0", - "eslint-plugin-import": "^2.0.0", + "eslint": "^5.16.0", + "eslint-plugin-import": "^2.17.2", + "eslint-plugin-react": "^7.12.4", + "eslint-plugin-prettier": "^3.0.1", "fs-extra": "^6.0.1", "get-pkg-repo": "^2.0.0", "gh-pages": "^1.2.0", @@ -46,6 +47,7 @@ "mdast-util-heading-range": "^2.0.1", "nyc": "^12.0.2", "postcss": "^7.0.0", + "prettier": "^1.17.0", "remark": "^9.0.0", "remark-behead": "^2.0.1", "remark-bookmarks": "^1.0.0", @@ -198,14 +200,6 @@ ] ] }, - "eslintConfig": { - "extends": "cssnano", - "rules": { - "import/no-unresolved": [ - 0 - ] - } - }, "browserslist": { "chrome58": [ "Chrome 58" diff --git a/packages/css-size/css-size.d.ts b/packages/css-size/css-size.d.ts index 06f31f5d3..a8b37ebea 100644 --- a/packages/css-size/css-size.d.ts +++ b/packages/css-size/css-size.d.ts @@ -1,6 +1,5 @@ export = cssSize; - declare function cssSize( css: string, options: cssSize.ProcessOptions, @@ -46,4 +45,4 @@ declare namespace cssSize { gzip: SizeInfo; brotli: SizeInfo; } -} \ No newline at end of file +} diff --git a/packages/css-size/index.js b/packages/css-size/index.js index 273a77e5b..c98f4af7e 100644 --- a/packages/css-size/index.js +++ b/packages/css-size/index.js @@ -1,4 +1,5 @@ -var cssSize = require("./dist/index.js"); +const cssSize = require('./dist/index.js'); + module.exports = cssSize.default; module.exports.table = cssSize.table; module.exports.numeric = cssSize.numeric; diff --git a/packages/css-size/processors/nano.js b/packages/css-size/processors/nano.js index 4fb693b10..ed0526c84 100644 --- a/packages/css-size/processors/nano.js +++ b/packages/css-size/processors/nano.js @@ -1,5 +1,5 @@ -let nano = require("cssnano"); +let nano = require('cssnano'); -module.exports = function (css, opts) { - return nano.process(css, opts); +module.exports = function(css, opts) { + return nano.process(css, opts); }; diff --git a/packages/css-size/processors/noop.js b/packages/css-size/processors/noop.js index 5580e1883..3b9a356cc 100644 --- a/packages/css-size/processors/noop.js +++ b/packages/css-size/processors/noop.js @@ -1,3 +1,3 @@ -module.exports = function (css) { - return Promise.resolve({css: css}); +module.exports = function(css) { + return Promise.resolve({ css: css }); }; diff --git a/packages/css-size/src/__tests__/index.js b/packages/css-size/src/__tests__/index.js index 3c42fa0fc..96d6733d1 100644 --- a/packages/css-size/src/__tests__/index.js +++ b/packages/css-size/src/__tests__/index.js @@ -1,80 +1,86 @@ -import {readFileSync as read} from 'fs'; -import {spawn} from 'child_process'; +import { readFileSync as read } from 'fs'; +import { spawn } from 'child_process'; import path from 'path'; import colors from 'colors/safe'; import test from 'ava'; -import size, {table, numeric} from '../'; +import size, { table, numeric } from '../'; let noopProcessorPath = path.resolve(__dirname, '../../processors/noop.js'); -function setup (args) { - return new Promise((resolve, reject) => { - process.chdir(__dirname); +function setup(args) { + return new Promise((resolve, reject) => { + process.chdir(__dirname); - let ps = spawn(process.execPath, [ - path.resolve(__dirname, '../../dist/cli.js'), - ].concat(args)); + let ps = spawn( + process.execPath, + [path.resolve(__dirname, '../../dist/cli.js')].concat(args) + ); - let out = ''; - let err = ''; + let out = ''; + let err = ''; - ps.stdout.on('data', buffer => (out += buffer)); - ps.stderr.on('data', buffer => (err += buffer)); + ps.stdout.on('data', (buffer) => (out += buffer)); + ps.stderr.on('data', (buffer) => (err += buffer)); - ps.on('exit', code => { - if (code !== 0) { - return reject(err); - } - resolve([out, code]); - }); + ps.on('exit', (code) => { + if (code !== 0) { + return reject(err); + } + resolve([out, code]); }); + }); } -test('cli', t => { - return setup(['test.css']).then(results => { - let out = results[0]; - t.truthy(~out.indexOf('43 B')); - t.truthy(~out.indexOf('34 B')); - t.truthy(~out.indexOf('9 B')); - t.truthy(~out.indexOf('79.07%')); - }); +test('cli', (t) => { + return setup(['test.css']).then((results) => { + let out = results[0]; + t.truthy(~out.indexOf('43 B')); + t.truthy(~out.indexOf('34 B')); + t.truthy(~out.indexOf('9 B')); + t.truthy(~out.indexOf('79.07%')); + }); }); -test('cli with processor argument', t => { - return setup(['-p', noopProcessorPath, 'test.css']).then(results => { - let out = results[0]; - t.truthy(~out.indexOf('100%')); - }); +test('cli with processor argument', (t) => { + return setup(['-p', noopProcessorPath, 'test.css']).then((results) => { + let out = results[0]; + t.truthy(~out.indexOf('100%')); + }); }); -test('api', t => { - return size(read(path.join(__dirname, 'test.css'), 'utf-8')).then(result => { - t.deepEqual(result, { - uncompressed: { - original: '23 B', - processed: '14 B', - difference: '9 B', - percent: '60.87%', - }, - gzip: { - original: '43 B', - processed: '34 B', - difference: '9 B', - percent: '79.07%', - }, - brotli: { - original: '27 B', - processed: '16 B', - difference: '11 B', - percent: '59.26%', - }, - }); - }); +test('api', (t) => { + return size(read(path.join(__dirname, 'test.css'), 'utf-8')).then( + (result) => { + t.deepEqual(result, { + uncompressed: { + original: '23 B', + processed: '14 B', + difference: '9 B', + percent: '60.87%', + }, + gzip: { + original: '43 B', + processed: '34 B', + difference: '9 B', + percent: '79.07%', + }, + brotli: { + original: '27 B', + processed: '16 B', + difference: '11 B', + percent: '59.26%', + }, + }); + } + ); }); -test('table', t => { - return table(read(path.join(__dirname, 'test.css'), 'utf-8')).then(result => { - t.deepEqual(colors.stripColors(result), ` +test('table', (t) => { + return table(read(path.join(__dirname, 'test.css'), 'utf-8')).then( + (result) => { + t.deepEqual( + colors.stripColors(result), + ` ┌────────────┬──────────────┬────────┬────────┐ │ │ Uncompressed │ Gzip │ Brotli │ ├────────────┼──────────────┼────────┼────────┤ @@ -85,41 +91,43 @@ test('table', t => { │ Difference │ 9 B │ 9 B │ 11 B │ ├────────────┼──────────────┼────────┼────────┤ │ Percent │ 60.87% │ 79.07% │ 59.26% │ -└────────────┴──────────────┴────────┴────────┘`.trim()); - }); +└────────────┴──────────────┴────────┴────────┘`.trim() + ); + } + ); }); -test('numeric', t => { - return numeric(read(path.join(__dirname, 'test.css'), 'utf-8')).then(result => { - t.deepEqual(result, { - uncompressed: { - original: 23, - processed: 14, - difference: 9, - percent: 0.6087, - }, - gzip: { - original: 43, - processed: 34, - difference: 9, - percent: 0.7907, - }, - brotli: { - original: 27, - processed: 16, - difference: 11, - percent: 0.5926, - }, - }); - }); +test('numeric', (t) => { + return numeric(read(path.join(__dirname, 'test.css'), 'utf-8')).then( + (result) => { + t.deepEqual(result, { + uncompressed: { + original: 23, + processed: 14, + difference: 9, + percent: 0.6087, + }, + gzip: { + original: 43, + processed: 34, + difference: 9, + percent: 0.7907, + }, + brotli: { + original: 27, + processed: 16, + difference: 11, + percent: 0.5926, + }, + }); + } + ); }); -test('api options', t => { - return size( - '@namespace islands url("http://bar.yandex.ru/ui/islands");', { - discardUnused: false, - } - ).then(result => { - t.deepEqual(result.gzip.processed, "67 B"); - }); +test('api options', (t) => { + return size('@namespace islands url("http://bar.yandex.ru/ui/islands");', { + discardUnused: false, + }).then((result) => { + t.deepEqual(result.gzip.processed, '67 B'); + }); }); diff --git a/packages/css-size/src/cli.js b/packages/css-size/src/cli.js index 4898d092c..544accec7 100644 --- a/packages/css-size/src/cli.js +++ b/packages/css-size/src/cli.js @@ -5,36 +5,39 @@ import process from 'process'; import path from 'path'; import read from 'read-file-stdin'; import minimist from 'minimist'; -import {table} from './'; +import { table } from './'; const opts = minimist(process.argv.slice(2), { - alias: { - h: 'help', - v: 'version', - p: 'processor', - }, + alias: { + h: 'help', + v: 'version', + p: 'processor', + }, }); - if (opts.version) { - console.log(require('../package.json').version); + // eslint-disable-next-line no-console + console.log(require('../package.json').version); } else { - let file = opts._[0]; + let file = opts._[0]; - if (file === 'help' || opts.help) { - fs.createReadStream(__dirname + '/../usage.txt') - .pipe(process.stdout) - .on('close', () => process.exit(1)); - } else { - read(file, (err, buf) => { - if (err) { - throw err; - } - let processor = null; - if (opts.processor) { - processor = require(path.resolve(process.cwd(), opts.processor)); - } - table(buf, opts.options, processor).then((results) => console.log(results)); - }); - } + if (file === 'help' || opts.help) { + fs.createReadStream(__dirname + '/../usage.txt') + .pipe(process.stdout) + .on('close', () => process.exit(1)); + } else { + read(file, (err, buf) => { + if (err) { + throw err; + } + let processor = null; + if (opts.processor) { + processor = require(path.resolve(process.cwd(), opts.processor)); + } + table(buf, opts.options, processor).then((results) => + // eslint-disable-next-line no-console + console.log(results) + ); + }); + } } diff --git a/packages/css-size/src/index.js b/packages/css-size/src/index.js index c2df263c6..87b4444b4 100644 --- a/packages/css-size/src/index.js +++ b/packages/css-size/src/index.js @@ -1,102 +1,114 @@ import prettyBytes from 'pretty-bytes'; -import {sync as gzip} from 'gzip-size'; -import {sync as brotli} from 'brotli-size'; +import { sync as gzip } from 'gzip-size'; +import { sync as brotli } from 'brotli-size'; import Table from 'cli-table'; import round from 'round-precision'; import nano from 'cssnano'; const getBinarySize = (string) => { - return Buffer.byteLength(string, 'utf8'); + return Buffer.byteLength(string, 'utf8'); }; const percentDifference = (original, minified) => { - return round((minified / original) * 100, 2) + '%'; + return round((minified / original) * 100, 2) + '%'; }; const cssSize = (css, opts, processor) => { - processor = processor || nano.process.bind(nano); - css = css.toString(); - return processor(css, opts).then(result => { - let sizes = computeSizes(css, result.css); - deltasAsStrings(sizes.uncompressed); - deltasAsStrings(sizes.gzip); - deltasAsStrings(sizes.brotli); - return sizes; - }); + processor = processor || nano.process.bind(nano); + css = css.toString(); + return processor(css, opts).then((result) => { + let sizes = computeSizes(css, result.css); + deltasAsStrings(sizes.uncompressed); + deltasAsStrings(sizes.gzip); + deltasAsStrings(sizes.brotli); + return sizes; + }); }; const deltasAsStrings = (sizes) => { - sizes.difference = prettyBytes(sizes.original - sizes.processed); - sizes.percent = percentDifference(sizes.original, sizes.processed); - sizes.original = prettyBytes(sizes.original); - sizes.processed = prettyBytes(sizes.processed); + sizes.difference = prettyBytes(sizes.original - sizes.processed); + sizes.percent = percentDifference(sizes.original, sizes.processed); + sizes.original = prettyBytes(sizes.original); + sizes.processed = prettyBytes(sizes.processed); }; const computeSizes = (original, minified) => { - return { - uncompressed: { - original: getBinarySize(original), - processed: getBinarySize(minified), - }, - gzip: { - original: gzip(original), - processed: gzip(minified), - }, - brotli: { - original: brotli(original), - processed: brotli(minified), - }, - }; + return { + uncompressed: { + original: getBinarySize(original), + processed: getBinarySize(minified), + }, + gzip: { + original: gzip(original), + processed: gzip(minified), + }, + brotli: { + original: brotli(original), + processed: brotli(minified), + }, + }; }; const tableize = (data) => { - return { - head: ["", "Uncompressed", "Gzip", "Brotli"], - rows: [ - {Original: [ - data.uncompressed.original, - data.gzip.original, - data.brotli.original ]}, - {Processed: [ - data.uncompressed.processed, - data.gzip.processed, - data.brotli.processed ]}, - {Difference: [ - data.uncompressed.difference, - data.gzip.difference, - data.brotli.difference ]}, - {Percent: [ - data.uncompressed.percent, - data.gzip.percent, - data.brotli.percent ]}, + return { + head: ['', 'Uncompressed', 'Gzip', 'Brotli'], + rows: [ + { + Original: [ + data.uncompressed.original, + data.gzip.original, + data.brotli.original, ], - }; + }, + { + Processed: [ + data.uncompressed.processed, + data.gzip.processed, + data.brotli.processed, + ], + }, + { + Difference: [ + data.uncompressed.difference, + data.gzip.difference, + data.brotli.difference, + ], + }, + { + Percent: [ + data.uncompressed.percent, + data.gzip.percent, + data.brotli.percent, + ], + }, + ], + }; }; -export function table (css, opts, processor) { - return cssSize(css, opts, processor).then(data => { - let result = tableize(data); - let output = new Table({head: result.head}); - output.push.apply(output, result.rows); - return output.toString(); - }); +export function table(css, opts, processor) { + return cssSize(css, opts, processor).then((data) => { + let result = tableize(data); + let output = new Table({ head: result.head }); + output.push.apply(output, result.rows); + return output.toString(); + }); } -export function numeric (css, opts, processor) { - processor = processor || nano.process.bind(nano); - css = css.toString(); - return processor(css, opts).then(result => { - let sizes = computeSizes(css, result.css); - deltasAsNumbers(sizes.uncompressed); - deltasAsNumbers(sizes.gzip); - deltasAsNumbers(sizes.brotli); - return sizes; - }); +export function numeric(css, opts, processor) { + processor = processor || nano.process.bind(nano); + css = css.toString(); + return processor(css, opts).then((result) => { + let sizes = computeSizes(css, result.css); + deltasAsNumbers(sizes.uncompressed); + deltasAsNumbers(sizes.gzip); + deltasAsNumbers(sizes.brotli); + return sizes; + }); } const deltasAsNumbers = (sizes) => { - sizes.difference = sizes.original - sizes.processed; - sizes.percent = round(sizes.processed / sizes.original, 4); + sizes.difference = sizes.original - sizes.processed; + sizes.percent = round(sizes.processed / sizes.original, 4); }; export default cssSize; diff --git a/packages/cssnano-preset-advanced/src/__tests__/autoprefixer.js b/packages/cssnano-preset-advanced/src/__tests__/autoprefixer.js index 18d612d5a..89087a8ce 100644 --- a/packages/cssnano-preset-advanced/src/__tests__/autoprefixer.js +++ b/packages/cssnano-preset-advanced/src/__tests__/autoprefixer.js @@ -1,32 +1,36 @@ import test from 'ava'; import preset from '..'; -import {processCSSWithPresetFactory} from '../../../../util/testHelpers'; +import { processCSSWithPresetFactory } from '../../../../util/testHelpers'; -const {processCSS} = processCSSWithPresetFactory(preset); +const { processCSS } = processCSSWithPresetFactory(preset); test( - 'should remove outdated vendor prefixes', - processCSS, - 'h1{-webkit-box-sizing:content-box;box-sizing:content-box}', - 'h1{box-sizing:content-box}' + 'should remove outdated vendor prefixes', + processCSS, + 'h1{-webkit-box-sizing:content-box;box-sizing:content-box}', + 'h1{box-sizing:content-box}' ); -const {passthroughCSS} = processCSSWithPresetFactory(preset({ - autoprefixer: {env: 'safari5'}, -})); +const { passthroughCSS } = processCSSWithPresetFactory( + preset({ + autoprefixer: { env: 'safari5' }, + }) +); test( - 'should not remove outdated vendor prefixes when minifying for older browsers', - passthroughCSS, - 'h1{-webkit-border-radius:5px;border-radius:5px}' + 'should not remove outdated vendor prefixes when minifying for older browsers', + passthroughCSS, + 'h1{-webkit-border-radius:5px;border-radius:5px}' ); -const {passthroughCSS: exclude} = processCSSWithPresetFactory(preset({ - autoprefixer: {exclude: true}, -})); +const { passthroughCSS: exclude } = processCSSWithPresetFactory( + preset({ + autoprefixer: { exclude: true }, + }) +); test( - 'should not remove outdated vendor prefixes if excluded', - exclude, - 'h1{-webkit-box-sizing:content-box;box-sizing:content-box}' + 'should not remove outdated vendor prefixes if excluded', + exclude, + 'h1{-webkit-box-sizing:content-box;box-sizing:content-box}' ); diff --git a/packages/cssnano-preset-advanced/src/__tests__/integrations.js b/packages/cssnano-preset-advanced/src/__tests__/integrations.js index 34f8d9a10..653da340b 100644 --- a/packages/cssnano-preset-advanced/src/__tests__/integrations.js +++ b/packages/cssnano-preset-advanced/src/__tests__/integrations.js @@ -1,27 +1,23 @@ import test from 'ava'; -import {integrationTests, loadPreset} from '../../../../util/testHelpers.js'; +import { integrationTests, loadPreset } from '../../../../util/testHelpers.js'; import preset from '..'; test( - 'should correctly handle the framework tests', - integrationTests, - preset, - `${__dirname}/integrations` + 'should correctly handle the framework tests', + integrationTests, + preset, + `${__dirname}/integrations` ); -function excludeProcessor (t, options) { - const input = `h1{z-index:10}`; - return loadPreset(preset(options)).process(input).then(({css}) => { - t.is(css, input); +function excludeProcessor(t, options) { + const input = `h1{z-index:10}`; + return loadPreset(preset(options)) + .process(input) + .then(({ css }) => { + t.is(css, input); }); } -test( - excludeProcessor, - {zindex: false} -); +test(excludeProcessor, { zindex: false }); -test( - excludeProcessor, - {zindex: {exclude: true}} -); +test(excludeProcessor, { zindex: { exclude: true } }); diff --git a/packages/cssnano-preset-advanced/src/index.js b/packages/cssnano-preset-advanced/src/index.js index a7f24f101..bc0087a65 100644 --- a/packages/cssnano-preset-advanced/src/index.js +++ b/packages/cssnano-preset-advanced/src/index.js @@ -6,22 +6,22 @@ import postcssZindex from 'lerna:postcss-zindex'; import autoprefixer from 'autoprefixer'; const defaultOpts = { - autoprefixer: { - add: false, - }, + autoprefixer: { + add: false, + }, }; -export default function advancedPreset (opts = {}) { - const options = Object.assign({}, defaultOpts, opts); +export default function advancedPreset(opts = {}) { + const options = Object.assign({}, defaultOpts, opts); - const plugins = [ - ...defaultPreset(options).plugins, - [autoprefixer, options.autoprefixer], - [postcssDiscardUnused, options.discardUnused], - [postcssMergeIdents, options.mergeIdents], - [postcssReduceIdents, options.reduceIdents], - [postcssZindex, options.zindex], - ]; + const plugins = [ + ...defaultPreset(options).plugins, + [autoprefixer, options.autoprefixer], + [postcssDiscardUnused, options.discardUnused], + [postcssMergeIdents, options.mergeIdents], + [postcssReduceIdents, options.reduceIdents], + [postcssZindex, options.zindex], + ]; - return {plugins}; + return { plugins }; } diff --git a/packages/cssnano-preset-default/src/__tests__/integrations.js b/packages/cssnano-preset-default/src/__tests__/integrations.js index 233572950..cb129146c 100644 --- a/packages/cssnano-preset-default/src/__tests__/integrations.js +++ b/packages/cssnano-preset-default/src/__tests__/integrations.js @@ -1,27 +1,23 @@ import test from 'ava'; -import {integrationTests, loadPreset} from '../../../../util/testHelpers.js'; +import { integrationTests, loadPreset } from '../../../../util/testHelpers.js'; import preset from '..'; test( - 'should correctly handle the framework tests', - integrationTests, - preset, - `${__dirname}/integrations` + 'should correctly handle the framework tests', + integrationTests, + preset, + `${__dirname}/integrations` ); -function excludeProcessor (t, options) { - const input = `h1{color:black}`; - return loadPreset(preset(options)).process(input).then(({css}) => { - t.is(css, input); +function excludeProcessor(t, options) { + const input = `h1{color:black}`; + return loadPreset(preset(options)) + .process(input) + .then(({ css }) => { + t.is(css, input); }); } -test( - excludeProcessor, - {colormin: false} -); +test(excludeProcessor, { colormin: false }); -test( - excludeProcessor, - {colormin: {exclude: true}} -); +test(excludeProcessor, { colormin: { exclude: true } }); diff --git a/packages/cssnano-preset-default/src/index.js b/packages/cssnano-preset-default/src/index.js index 0dc74456f..e1ccb345b 100644 --- a/packages/cssnano-preset-default/src/index.js +++ b/packages/cssnano-preset-default/src/index.js @@ -43,51 +43,51 @@ import postcssNormalizeTimingFunctions from 'lerna:postcss-normalize-timing-func import rawCache from 'lerna:cssnano-util-raw-cache'; const defaultOpts = { - convertValues: { - length: false, - }, - normalizeCharset: { - add: false, - }, - cssDeclarationSorter: { - exclude: true, - }, + convertValues: { + length: false, + }, + normalizeCharset: { + add: false, + }, + cssDeclarationSorter: { + exclude: true, + }, }; -export default function defaultPreset (opts = {}) { - const options = Object.assign({}, defaultOpts, opts); +export default function defaultPreset(opts = {}) { + const options = Object.assign({}, defaultOpts, opts); - const plugins = [ - [postcssDiscardComments, options.discardComments], - [postcssMinifyGradients, options.minifyGradients], - [postcssReduceInitial, options.reduceInitial], - [postcssSvgo, options.svgo], - [postcssNormalizeDisplayValues, options.normalizeDisplayValues], - [postcssReduceTransforms, options.reduceTransforms], - [postcssColormin, options.colormin], - [postcssNormalizeTimingFunctions, options.normalizeTimingFunctions], - [postcssCalc, options.calc], - [postcssConvertValues, options.convertValues], - [postcssOrderedValues, options.orderedValues], - [postcssMinifySelectors, options.minifySelectors], - [postcssMinifyParams, options.minifyParams], - [postcssNormalizeCharset, options.normalizeCharset], - [postcssDiscardOverridden, options.discardOverridden], - [postcssNormalizeString, options.normalizeString], - [postcssNormalizeUnicode, options.normalizeUnicode], - [postcssMinifyFontValues, options.minifyFontValues], - [postcssNormalizeUrl, options.normalizeUrl], - [postcssNormalizeRepeatStyle, options.normalizeRepeatStyle], - [postcssNormalizePositions, options.normalizePositions], - [postcssNormalizeWhitespace, options.normalizeWhitespace], - [postcssMergeLonghand, options.mergeLonghand], - [postcssDiscardDuplicates, options.discardDuplicates], - [postcssMergeRules, options.mergeRules], - [postcssDiscardEmpty, options.discardEmpty], - [postcssUniqueSelectors, options.uniqueSelectors], - [cssDeclarationSorter, options.cssDeclarationSorter], - [rawCache, options.rawCache], - ]; + const plugins = [ + [postcssDiscardComments, options.discardComments], + [postcssMinifyGradients, options.minifyGradients], + [postcssReduceInitial, options.reduceInitial], + [postcssSvgo, options.svgo], + [postcssNormalizeDisplayValues, options.normalizeDisplayValues], + [postcssReduceTransforms, options.reduceTransforms], + [postcssColormin, options.colormin], + [postcssNormalizeTimingFunctions, options.normalizeTimingFunctions], + [postcssCalc, options.calc], + [postcssConvertValues, options.convertValues], + [postcssOrderedValues, options.orderedValues], + [postcssMinifySelectors, options.minifySelectors], + [postcssMinifyParams, options.minifyParams], + [postcssNormalizeCharset, options.normalizeCharset], + [postcssDiscardOverridden, options.discardOverridden], + [postcssNormalizeString, options.normalizeString], + [postcssNormalizeUnicode, options.normalizeUnicode], + [postcssMinifyFontValues, options.minifyFontValues], + [postcssNormalizeUrl, options.normalizeUrl], + [postcssNormalizeRepeatStyle, options.normalizeRepeatStyle], + [postcssNormalizePositions, options.normalizePositions], + [postcssNormalizeWhitespace, options.normalizeWhitespace], + [postcssMergeLonghand, options.mergeLonghand], + [postcssDiscardDuplicates, options.discardDuplicates], + [postcssMergeRules, options.mergeRules], + [postcssDiscardEmpty, options.discardEmpty], + [postcssUniqueSelectors, options.uniqueSelectors], + [cssDeclarationSorter, options.cssDeclarationSorter], + [rawCache, options.rawCache], + ]; - return {plugins}; + return { plugins }; } diff --git a/packages/cssnano-util-get-arguments/src/__tests__/index.js b/packages/cssnano-util-get-arguments/src/__tests__/index.js index ae895e29a..84f1e8657 100644 --- a/packages/cssnano-util-get-arguments/src/__tests__/index.js +++ b/packages/cssnano-util-get-arguments/src/__tests__/index.js @@ -2,37 +2,49 @@ import test from 'ava'; import valueParser from 'postcss-value-parser'; import getArguments from '../'; -test('should get arguments', t => { - const parsed = valueParser('linear-gradient(to bottom left, red, blue)'); - t.deepEqual(getArguments(parsed.nodes[0]), [ - [{ - type: 'word', - sourceIndex: 16, - value: 'to', - }, { - type: 'space', - sourceIndex: 18, - value: ' ', - }, { - type: 'word', - sourceIndex: 19, - value: 'bottom', - }, { - type: 'space', - sourceIndex: 25, - value: ' ', - }, { - type: 'word', - sourceIndex: 26, - value: 'left', - }], [{ - type: 'word', - sourceIndex: 32, - value: 'red', - }], [{ - type: 'word', - sourceIndex: 37, - value: 'blue', - }], - ]); +test('should get arguments', (t) => { + const parsed = valueParser('linear-gradient(to bottom left, red, blue)'); + t.deepEqual(getArguments(parsed.nodes[0]), [ + [ + { + type: 'word', + sourceIndex: 16, + value: 'to', + }, + { + type: 'space', + sourceIndex: 18, + value: ' ', + }, + { + type: 'word', + sourceIndex: 19, + value: 'bottom', + }, + { + type: 'space', + sourceIndex: 25, + value: ' ', + }, + { + type: 'word', + sourceIndex: 26, + value: 'left', + }, + ], + [ + { + type: 'word', + sourceIndex: 32, + value: 'red', + }, + ], + [ + { + type: 'word', + sourceIndex: 37, + value: 'blue', + }, + ], + ]); }); diff --git a/packages/cssnano-util-get-arguments/src/index.js b/packages/cssnano-util-get-arguments/src/index.js index 62f167bc2..674063efa 100644 --- a/packages/cssnano-util-get-arguments/src/index.js +++ b/packages/cssnano-util-get-arguments/src/index.js @@ -1,10 +1,13 @@ -export default function getArguments (node) { - return node.nodes.reduce((list, child) => { - if (child.type !== 'div') { - list[list.length - 1].push(child); - } else { - list.push([]); - } - return list; - }, [[]]); +export default function getArguments(node) { + return node.nodes.reduce( + (list, child) => { + if (child.type !== 'div') { + list[list.length - 1].push(child); + } else { + list.push([]); + } + return list; + }, + [[]] + ); } diff --git a/packages/cssnano-util-get-match/src/__tests__/index.js b/packages/cssnano-util-get-match/src/__tests__/index.js index 6f84af7f0..e99bb9c80 100644 --- a/packages/cssnano-util-get-match/src/__tests__/index.js +++ b/packages/cssnano-util-get-match/src/__tests__/index.js @@ -1,15 +1,15 @@ import test from 'ava'; import getMatchFactory from '../'; -test('should get match', t => { - const matches = [ - ['foo', ['bar', 'baz']], - ['quux', ['bar', 'foo']], - ['baz', ['foo', 'bar']], - ]; +test('should get match', (t) => { + const matches = [ + ['foo', ['bar', 'baz']], + ['quux', ['bar', 'foo']], + ['baz', ['foo', 'bar']], + ]; - const getMatch = getMatchFactory(matches); + const getMatch = getMatchFactory(matches); - t.deepEqual(getMatch(['bar', 'foo']), 'quux'); - t.false(getMatch(['quux'])); + t.deepEqual(getMatch(['bar', 'foo']), 'quux'); + t.false(getMatch(['quux'])); }); diff --git a/packages/cssnano-util-get-match/src/index.js b/packages/cssnano-util-get-match/src/index.js index b4d675b6b..f4684967e 100644 --- a/packages/cssnano-util-get-match/src/index.js +++ b/packages/cssnano-util-get-match/src/index.js @@ -1,11 +1,13 @@ -export default function getMatchFactory (map) { - return function getMatch (args) { - const match = args.reduce((list, arg, i) => { - return list.filter(keyword => keyword[1][i] === arg); - }, map); - if (match.length) { - return match[0][0]; - } - return false; - }; +export default function getMatchFactory(map) { + return function getMatch(args) { + const match = args.reduce((list, arg, i) => { + return list.filter((keyword) => keyword[1][i] === arg); + }, map); + + if (match.length) { + return match[0][0]; + } + + return false; + }; } diff --git a/packages/cssnano-util-raw-cache/src/index.js b/packages/cssnano-util-raw-cache/src/index.js index b8b328d73..3a6a2e195 100644 --- a/packages/cssnano-util-raw-cache/src/index.js +++ b/packages/cssnano-util-raw-cache/src/index.js @@ -1,19 +1,19 @@ -import {plugin} from 'postcss'; +import { plugin } from 'postcss'; export default plugin('cssnano-util-raw-cache', () => { - return (css, result) => { - result.root.rawCache = { - colon: ':', - indent: '', - beforeDecl: '', - beforeRule: '', - beforeOpen: '', - beforeClose: '', - beforeComment: '', - after: '', - emptyBody: '', - commentLeft: '', - commentRight: '', - }; + return (css, result) => { + result.root.rawCache = { + colon: ':', + indent: '', + beforeDecl: '', + beforeRule: '', + beforeOpen: '', + beforeClose: '', + beforeComment: '', + after: '', + emptyBody: '', + commentLeft: '', + commentRight: '', }; + }; }); diff --git a/packages/cssnano-util-same-parent/src/__tests__/index.js b/packages/cssnano-util-same-parent/src/__tests__/index.js index 30b3ea07b..f75c71278 100644 --- a/packages/cssnano-util-same-parent/src/__tests__/index.js +++ b/packages/cssnano-util-same-parent/src/__tests__/index.js @@ -2,57 +2,68 @@ import test from 'ava'; import postcss from 'postcss'; import sameParent from '..'; -test('should calculate same parent', t => { - return postcss().process('h1 {} h2 {}').then(result => { - const h1 = result.root.nodes[0]; - const h2 = result.root.nodes[1]; - - t.true(sameParent(h1, h2)); +test('should calculate same parent', (t) => { + return postcss() + .process('h1 {} h2 {}') + .then((result) => { + const h1 = result.root.nodes[0]; + const h2 = result.root.nodes[1]; + + t.true(sameParent(h1, h2)); }); }); -test('should calculate same parent (detached nodes)', t => { - return postcss().process('h1 {} h2 {}').then(result => { - const h1 = result.root.nodes[0]; - const h2 = result.root.nodes[1]; +test('should calculate same parent (detached nodes)', (t) => { + return postcss() + .process('h1 {} h2 {}') + .then((result) => { + const h1 = result.root.nodes[0]; + const h2 = result.root.nodes[1]; - h1.remove(); - h2.remove(); + h1.remove(); + h2.remove(); - t.true(sameParent(h1, h2)); + t.true(sameParent(h1, h2)); }); }); -test('should calculate same parent (at rules)', t => { - return postcss().process('@media screen{h1 {} h2 {}}').then(result => { - const h1 = result.root.nodes[0].nodes[0]; - const h2 = result.root.nodes[0].nodes[1]; +test('should calculate same parent (at rules)', (t) => { + return postcss() + .process('@media screen{h1 {} h2 {}}') + .then((result) => { + const h1 = result.root.nodes[0].nodes[0]; + const h2 = result.root.nodes[0].nodes[1]; - t.true(sameParent(h1, h2)); + t.true(sameParent(h1, h2)); }); }); +test('should calculate same parent (multiple at rules)', (t) => { + return postcss() + .process('@media screen{h1 {}} @media screen{h2 {}}') + .then((result) => { + const h1 = result.root.nodes[0].nodes[0]; + const h2 = result.root.nodes[1].nodes[0]; -test('should calculate same parent (multiple at rules)', t => { - return postcss().process('@media screen{h1 {}} @media screen{h2 {}}').then(result => { - const h1 = result.root.nodes[0].nodes[0]; - const h2 = result.root.nodes[1].nodes[0]; - - t.true(sameParent(h1, h2)); + t.true(sameParent(h1, h2)); }); }); -test('should calculate same parent (multiple at rules (uppercase))', t => { - return postcss().process('@media screen{h1 {}} @MEDIA screen{h2 {}}').then(result => { - const h1 = result.root.nodes[0].nodes[0]; - const h2 = result.root.nodes[1].nodes[0]; +test('should calculate same parent (multiple at rules (uppercase))', (t) => { + return postcss() + .process('@media screen{h1 {}} @MEDIA screen{h2 {}}') + .then((result) => { + const h1 = result.root.nodes[0].nodes[0]; + const h2 = result.root.nodes[1].nodes[0]; - t.true(sameParent(h1, h2)); + t.true(sameParent(h1, h2)); }); }); -test('should calculate same parent (nested at rules)', t => { - return postcss().process(` +test('should calculate same parent (nested at rules)', (t) => { + return postcss() + .process( + ` @media screen { @supports(pointer: course) { h1 {} @@ -63,16 +74,19 @@ test('should calculate same parent (nested at rules)', t => { h2 {} } } - `).then(result => { - const h1 = result.root.nodes[0].nodes[0].nodes[0]; - const h2 = result.root.nodes[1].nodes[0].nodes[0]; - t.true(sameParent(h1, h2)); + ` + ) + .then((result) => { + const h1 = result.root.nodes[0].nodes[0].nodes[0]; + const h2 = result.root.nodes[1].nodes[0].nodes[0]; + t.true(sameParent(h1, h2)); }); }); - -test('should calculate not same parent (nested at rules)', t => { - return postcss().process(` +test('should calculate not same parent (nested at rules)', (t) => { + return postcss() + .process( + ` @media screen { @supports(pointer: fine) { h1 {} @@ -83,15 +97,19 @@ test('should calculate not same parent (nested at rules)', t => { h2 {} } } - `).then(result => { - const h1 = result.root.nodes[0].nodes[0].nodes[0]; - const h2 = result.root.nodes[1].nodes[0].nodes[0]; - t.false(sameParent(h1, h2)); + ` + ) + .then((result) => { + const h1 = result.root.nodes[0].nodes[0].nodes[0]; + const h2 = result.root.nodes[1].nodes[0].nodes[0]; + t.false(sameParent(h1, h2)); }); }); -test('should calculate not same parent (nested at rules) (2)', t => { - return postcss().process(` +test('should calculate not same parent (nested at rules) (2)', (t) => { + return postcss() + .process( + ` @media print { @supports(pointer: course) { h1 {} @@ -102,15 +120,19 @@ test('should calculate not same parent (nested at rules) (2)', t => { h2 {} } } - `).then(result => { - const h1 = result.root.nodes[0].nodes[0].nodes[0]; - const h2 = result.root.nodes[1].nodes[0].nodes[0]; - t.false(sameParent(h1, h2)); + ` + ) + .then((result) => { + const h1 = result.root.nodes[0].nodes[0].nodes[0]; + const h2 = result.root.nodes[1].nodes[0].nodes[0]; + t.false(sameParent(h1, h2)); }); }); -test('should calculate not same parent (nested at rules) (3)', t => { - return postcss().process(` +test('should calculate not same parent (nested at rules) (3)', (t) => { + return postcss() + .process( + ` @supports(pointer: course) { h1 {} } @@ -119,15 +141,19 @@ test('should calculate not same parent (nested at rules) (3)', t => { h2 {} } } - `).then(result => { - const h1 = result.root.nodes[0].nodes[0]; - const h2 = result.root.nodes[1].nodes[0].nodes[0]; - t.false(sameParent(h1, h2)); + ` + ) + .then((result) => { + const h1 = result.root.nodes[0].nodes[0]; + const h2 = result.root.nodes[1].nodes[0].nodes[0]; + t.false(sameParent(h1, h2)); }); }); -test('should calculate not same parent (nested at rules) (4)', t => { - return postcss().process(` +test('should calculate not same parent (nested at rules) (4)', (t) => { + return postcss() + .process( + ` @media screen { h1 {} } @@ -136,9 +162,11 @@ test('should calculate not same parent (nested at rules) (4)', t => { h2 {} } } - `).then(result => { - const h1 = result.root.nodes[0].nodes[0]; - const h2 = result.root.nodes[1].nodes[0].nodes[0]; - t.false(sameParent(h1, h2)); + ` + ) + .then((result) => { + const h1 = result.root.nodes[0].nodes[0]; + const h2 = result.root.nodes[1].nodes[0].nodes[0]; + t.false(sameParent(h1, h2)); }); }); diff --git a/packages/cssnano-util-same-parent/src/index.js b/packages/cssnano-util-same-parent/src/index.js index 6a2514bf3..946f876b0 100644 --- a/packages/cssnano-util-same-parent/src/index.js +++ b/packages/cssnano-util-same-parent/src/index.js @@ -3,14 +3,14 @@ * @param {postcss.ChildNode} nodeB * @return {boolean} */ -function checkMatch (nodeA, nodeB) { - if (nodeA.type === 'atrule' && nodeB.type === 'atrule') { - return ( - nodeA.params === nodeB.params && - nodeA.name.toLowerCase() === nodeB.name.toLowerCase() - ); - } - return nodeA.type === nodeB.type; +function checkMatch(nodeA, nodeB) { + if (nodeA.type === 'atrule' && nodeB.type === 'atrule') { + return ( + nodeA.params === nodeB.params && + nodeA.name.toLowerCase() === nodeB.name.toLowerCase() + ); + } + return nodeA.type === nodeB.type; } /** @@ -18,22 +18,22 @@ function checkMatch (nodeA, nodeB) { * @param {postcss.ChildNode} nodeB * @return {boolean} */ -export default function sameParent (nodeA, nodeB) { - if (!nodeA.parent) { - // A is orphaned, return if B is orphaned as well - return !nodeB.parent; - } +export default function sameParent(nodeA, nodeB) { + if (!nodeA.parent) { + // A is orphaned, return if B is orphaned as well + return !nodeB.parent; + } - if (!nodeB.parent) { - // B is orphaned and A is not - return false; - } + if (!nodeB.parent) { + // B is orphaned and A is not + return false; + } - // Check if parents match - if (!checkMatch(nodeA.parent, nodeB.parent)) { - return false; - } + // Check if parents match + if (!checkMatch(nodeA.parent, nodeB.parent)) { + return false; + } - // Check parents' parents - return sameParent(nodeA.parent, nodeB.parent); + // Check parents' parents + return sameParent(nodeA.parent, nodeB.parent); } diff --git a/packages/cssnano/quickstart.js b/packages/cssnano/quickstart.js index f6051128b..31e249368 100644 --- a/packages/cssnano/quickstart.js +++ b/packages/cssnano/quickstart.js @@ -23,9 +23,9 @@ h1 { */ const postcssOpts = { - // from: 'app.css', - // to: 'app.min.css', - // map: {inline: true}, + // from: 'app.css', + // to: 'app.min.css', + // map: {inline: true}, }; /* @@ -35,13 +35,14 @@ const postcssOpts = { */ const cssnanoOpts = { - preset: 'default', + preset: 'default', }; /* * Compress the CSS asynchronously and log it to the console. */ -cssnano.process(css, postcssOpts, cssnanoOpts).then(result => { - console.log(result.css); +cssnano.process(css, postcssOpts, cssnanoOpts).then((result) => { + // eslint-disable-next-line no-console + console.log(result.css); }); diff --git a/packages/cssnano/src/__tests__/_processCss.js b/packages/cssnano/src/__tests__/_processCss.js index 0f3e99b99..663735134 100644 --- a/packages/cssnano/src/__tests__/_processCss.js +++ b/packages/cssnano/src/__tests__/_processCss.js @@ -1,11 +1,11 @@ import cssnano from '..'; -export default function processCss (t, fixture, expected, options = {}) { - return cssnano.process(fixture, options).then(({css}) => { - t.deepEqual(css, expected); - }); +export default function processCss(t, fixture, expected, options = {}) { + return cssnano.process(fixture, options).then(({ css }) => { + t.deepEqual(css, expected); + }); } -export function passthrough (t, fixture, options = {}) { - return processCss(t, fixture, fixture, options); +export function passthrough(t, fixture, options = {}) { + return processCss(t, fixture, fixture, options); } diff --git a/packages/cssnano/src/__tests__/_webpack.config.js b/packages/cssnano/src/__tests__/_webpack.config.js index 4a943ad29..354d839ed 100644 --- a/packages/cssnano/src/__tests__/_webpack.config.js +++ b/packages/cssnano/src/__tests__/_webpack.config.js @@ -1,22 +1,24 @@ const path = require('path'); module.exports = { - entry: { - index: path.join(__dirname, '..'), - }, - output: { - path: path.resolve('./_webpackOutput/'), - filename: 'bundle.js', - }, - module: { - rules: [{ - test: /\.js$/, - loader: 'babel-loader', - exclude: /node_modules/, - }], - }, - // because client side doesn't have fs :) - node: { - fs: 'empty', - }, + entry: { + index: path.join(__dirname, '..'), + }, + output: { + path: path.resolve('./_webpackOutput/'), + filename: 'bundle.js', + }, + module: { + rules: [ + { + test: /\.js$/, + loader: 'babel-loader', + exclude: /node_modules/, + }, + ], + }, + // because client side doesn't have fs :) + node: { + fs: 'empty', + }, }; diff --git a/packages/cssnano/src/__tests__/api.js b/packages/cssnano/src/__tests__/api.js index 60e3fbc61..e39ce3a3f 100644 --- a/packages/cssnano/src/__tests__/api.js +++ b/packages/cssnano/src/__tests__/api.js @@ -1,30 +1,28 @@ import postcss from 'postcss'; import ava from 'ava'; import nano from '..'; -import {usePostCSSPlugin} from '../../../../util/testHelpers'; +import { usePostCSSPlugin } from '../../../../util/testHelpers'; -function pluginMacro (t, instance) { - const css = 'h1 { color: #ffffff }'; - const min = 'h1{color:#fff}'; +function pluginMacro(t, instance) { + const css = 'h1 { color: #ffffff }'; + const min = 'h1{color:#fff}'; - return instance.process(css).then((result) => { - t.deepEqual(result.css, min); - }); + return instance.process(css).then((result) => { + t.deepEqual(result.css, min); + }); } ava('can be used as a postcss plugin', pluginMacro, postcss().use(nano())); ava('can be used as a postcss plugin (2)', pluginMacro, postcss([nano()])); ava('can be used as a postcss plugin (3)', pluginMacro, postcss(nano)); -ava( - 'should use the postcss plugin api', - usePostCSSPlugin, - nano() -); +ava('should use the postcss plugin api', usePostCSSPlugin, nano()); -ava('should work with sourcemaps', t => { - return nano.process('h1{z-index:1}', {map: {inline: true}}).then(({css}) => { - const hasMap = /sourceMappingURL=data:application\/json;base64/.test(css); - t.truthy(hasMap); +ava('should work with sourcemaps', (t) => { + return nano + .process('h1{z-index:1}', { map: { inline: true } }) + .then(({ css }) => { + const hasMap = /sourceMappingURL=data:application\/json;base64/.test(css); + t.truthy(hasMap); }); }); diff --git a/packages/cssnano/src/__tests__/fixtures.js b/packages/cssnano/src/__tests__/fixtures.js index 389ae02aa..c359d4fe5 100644 --- a/packages/cssnano/src/__tests__/fixtures.js +++ b/packages/cssnano/src/__tests__/fixtures.js @@ -2,18 +2,18 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should keep spaces in background repeat', - processCss, - `h1 { + 'should keep spaces in background repeat', + processCss, + `h1 { background: url(image.png) no-repeat }`, - `h1{background:url(image.png) no-repeat}` + `h1{background:url(image.png) no-repeat}` ); test( - 'should handle css mixins', - processCss, - `paper-card { + 'should handle css mixins', + processCss, + `paper-card { --paper-card-content: { padding-top: 0; }; @@ -21,15 +21,15 @@ test( width: 768px; max-width: calc(100% - 32px); }`, - `paper-card{--paper-card-content:{padding-top:0};margin:0 auto 16px;width:768px;max-width:calc(100% - 32px)}` - // Switch back once css-declaration-sorter has been fixed - // `paper-card{margin:0 auto 16px;max-width:calc(100% - 32px);width:768px;--paper-card-content:{padding-top:0};}` + `paper-card{--paper-card-content:{padding-top:0};margin:0 auto 16px;width:768px;max-width:calc(100% - 32px)}` + // Switch back once css-declaration-sorter has been fixed + // `paper-card{margin:0 auto 16px;max-width:calc(100% - 32px);width:768px;--paper-card-content:{padding-top:0};}` ); test( - 'should handle css mixins (2)', - processCss, - `paper-card { + 'should handle css mixins (2)', + processCss, + `paper-card { --paper-card-header: { height: 128px; padding: 0 48px; @@ -48,16 +48,15 @@ test( }; width: 384px; }`, - `paper-card{--paper-card-header:{height:128px;padding:0 48px;background:var(--primary-color);@apply(--layout-vertical);@apply(--layout-end-justified)};--paper-card-header-color:#fff;--paper-card-content:{padding:64px};--paper-card-actions:{@apply(--layout-horizontal);@apply(--layout-end-justified)};width:384px}` - // Switch back once css-declaration-sorter has been fixed - // `paper-card{--paper-card-header-color:#fff;width:384px;--paper-card-header:{background:var(--primary-color);height:128px;padding:0 48px;@apply(--layout-vertical);@apply(--layout-end-justified)};--paper-card-content:{padding:64px};--paper-card-actions:{@apply(--layout-horizontal);@apply(--layout-end-justified)};}` - + `paper-card{--paper-card-header:{height:128px;padding:0 48px;background:var(--primary-color);@apply(--layout-vertical);@apply(--layout-end-justified)};--paper-card-header-color:#fff;--paper-card-content:{padding:64px};--paper-card-actions:{@apply(--layout-horizontal);@apply(--layout-end-justified)};width:384px}` + // Switch back once css-declaration-sorter has been fixed + // `paper-card{--paper-card-header-color:#fff;width:384px;--paper-card-header:{background:var(--primary-color);height:128px;padding:0 48px;@apply(--layout-vertical);@apply(--layout-end-justified)};--paper-card-content:{padding:64px};--paper-card-actions:{@apply(--layout-horizontal);@apply(--layout-end-justified)};}` ); test( - 'should dedupe charset definitions', - processCss, - `@charset "utf-8"; + 'should dedupe charset definitions', + processCss, + `@charset "utf-8"; a { display: block; @@ -68,38 +67,38 @@ test( div { font-family: €42; }`, - `@charset "utf-8";a{display:block}div{font-family:€42}` + `@charset "utf-8";a{display:block}div{font-family:€42}` ); test( - 'should dedupe selectors', - processCss, - `h1, h2 { + 'should dedupe selectors', + processCss, + `h1, h2 { color: red; } h2, h1 { font-weight: 400; }`, - `h1,h2{color:red;font-weight:400}` + `h1,h2{color:red;font-weight:400}` ); test( - 'should dedupe semicolons', - processCss, - `div { + 'should dedupe semicolons', + processCss, + `div { font-weight: 900;;;; color: red;; }`, - `div{font-weight:900;color:red}` - // Switch back once css-declaration-sorter has been fixed - // `div{color:red;font-weight:900}` + `div{font-weight:900;color:red}` + // Switch back once css-declaration-sorter has been fixed + // `div{color:red;font-weight:900}` ); test( - 'should discard duplicate keyframes', - processCss, - `@keyframes fadeOut { + 'should discard duplicate keyframes', + processCss, + `@keyframes fadeOut { 0% { opacity: 1; } @@ -120,22 +119,22 @@ test( .fadeOut { animation-name: fadeOut; }`, - `@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fadeOut{animation-name:fadeOut}` + `@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fadeOut{animation-name:fadeOut}` ); test( - 'should handle css variables', - processCss, - `.Button--action:hover:not(.is-disabled) { + 'should handle css variables', + processCss, + `.Button--action:hover:not(.is-disabled) { background-color: var(--wc-variant-background-light); }`, - `.Button--action:hover:not(.is-disabled){background-color:var(--wc-variant-background-light)}` + `.Button--action:hover:not(.is-disabled){background-color:var(--wc-variant-background-light)}` ); test( - 'should handle padding shorthand', - processCss, - `h1 { + 'should handle padding shorthand', + processCss, + `h1 { padding: 10px 20px 30px 40px; } @@ -150,65 +149,65 @@ test( h4 { padding: 10px; }`, - `h1{padding:10px 20px 30px 40px}h2{padding:10px 20px 30px}h3{padding:10px 20px}h4{padding:10px}` + `h1{padding:10px 20px 30px 40px}h2{padding:10px 20px 30px}h3{padding:10px 20px}h4{padding:10px}` ); test( - 'should normalize urls', - processCss, - `body { + 'should normalize urls', + processCss, + `body { background: url("http://somewebsite.com/assets/css/../images/test.jpg"); }`, - `body{background:url(http://somewebsite.com/assets/images/test.jpg)}` + `body{background:url(http://somewebsite.com/assets/images/test.jpg)}` ); test( - 'should optimise gradient colour stops', - processCss, - `div { + 'should optimise gradient colour stops', + processCss, + `div { background-image: -webkit-linear-gradient(black, green, yellow); }`, - `div{background-image:-webkit-linear-gradient(#000,green,#ff0)}` + `div{background-image:-webkit-linear-gradient(#000,green,#ff0)}` ); test( - 'should not mangle multiple gradients', - processCss, - `.two-gradients { + 'should not mangle multiple gradients', + processCss, + `.two-gradients { background: linear-gradient(#fff, #999) no-repeat border-box, linear-gradient(#eee, #777) no-repeat border-box; background-size: 98px 50px, 18px 50px; background-position: 0 0, 98px 0; background-origin: padding-box, padding-box; }`, - `.two-gradients{background:linear-gradient(#fff,#999) no-repeat border-box,linear-gradient(#eee,#777) no-repeat border-box;background-size:98px 50px,18px 50px;background-position:0 0,98px 0;background-origin:padding-box,padding-box}` - // Switch back once css-declaration-sorter has been fixed - // `.two-gradients{background:linear-gradient(#fff,#999) no-repeat border-box,linear-gradient(#eee,#777) no-repeat border-box;background-origin:padding-box,padding-box;background-position:0 0,98px 0;background-size:98px 50px,18px 50px}` + `.two-gradients{background:linear-gradient(#fff,#999) no-repeat border-box,linear-gradient(#eee,#777) no-repeat border-box;background-size:98px 50px,18px 50px;background-position:0 0,98px 0;background-origin:padding-box,padding-box}` + // Switch back once css-declaration-sorter has been fixed + // `.two-gradients{background:linear-gradient(#fff,#999) no-repeat border-box,linear-gradient(#eee,#777) no-repeat border-box;background-origin:padding-box,padding-box;background-position:0 0,98px 0;background-size:98px 50px,18px 50px}` ); test( - 'should optimise border longhand', - processCss, - `h1 { + 'should optimise border longhand', + processCss, + `h1 { border-width: 1px 1px 1px 1px; border-color: red #f00 red #f00; border-style: solid solid solid solid; }`, - `h1{border:1px solid red}` + `h1{border:1px solid red}` ); test( - 'should trim whitespace in border radius', - processCss, - `div { + 'should trim whitespace in border radius', + processCss, + `div { border-radius: 100% / 10%; }`, - `div{border-radius:100%/10%}` + `div{border-radius:100%/10%}` ); test( - 'should trim whitespace in selector combinators', - processCss, - `p + p { + 'should trim whitespace in selector combinators', + processCss, + `p + p { font-style: italic; } @@ -219,22 +218,22 @@ test( p > a { font-weight: 700; }`, - `p+p{font-style:italic}h1~p{font-size:2em}p>a{font-weight:700}` + `p+p{font-style:italic}h1~p{font-size:2em}p>a{font-weight:700}` ); test( - 'should trim whitespace in border radius', - processCss, - `div { + 'should trim whitespace in border radius', + processCss, + `div { border-radius: 100% / 10%; }`, - `div{border-radius:100%/10%}` + `div{border-radius:100%/10%}` ); test( - 'should optimise duration', - processCss, - `.short { + 'should optimise duration', + processCss, + `.short { animation-duration: 200ms; } @@ -245,13 +244,13 @@ test( .negative { animation-duration: -569ms; }`, - `.short{animation-duration:.2s}.long{animation-duration:2s}.negative{animation-duration:-569ms}` + `.short{animation-duration:.2s}.long{animation-duration:2s}.negative{animation-duration:-569ms}` ); test( - 'should optimise font face', - processCss, - `@font-face { + 'should optimise font face', + processCss, + `@font-face { font-family: Glyphicons Halflings; src: url(../fonts/bootstrap/glyphicons-halflings-regular.eot); src: url(../fonts/bootstrap/glyphicons-halflings-regular.eot?#iefix) format("embedded-opentype"), @@ -264,69 +263,69 @@ test( .icon { font-family: Glyphicons Halflings; }`, - `@font-face{font-family:Glyphicons Halflings;src:url(../fonts/bootstrap/glyphicons-halflings-regular.eot);src:url(../fonts/bootstrap/glyphicons-halflings-regular.eot?#iefix) format("embedded-opentype"),url(../fonts/bootstrap/glyphicons-halflings-regular.woff2) format("woff2"),url(../fonts/bootstrap/glyphicons-halflings-regular.woff) format("woff"),url(../fonts/bootstrap/glyphicons-halflings-regular.ttf) format("truetype"),url(../fonts/bootstrap/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format("svg")}.icon{font-family:Glyphicons Halflings}` + `@font-face{font-family:Glyphicons Halflings;src:url(../fonts/bootstrap/glyphicons-halflings-regular.eot);src:url(../fonts/bootstrap/glyphicons-halflings-regular.eot?#iefix) format("embedded-opentype"),url(../fonts/bootstrap/glyphicons-halflings-regular.woff2) format("woff2"),url(../fonts/bootstrap/glyphicons-halflings-regular.woff) format("woff"),url(../fonts/bootstrap/glyphicons-halflings-regular.ttf) format("truetype"),url(../fonts/bootstrap/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format("svg")}.icon{font-family:Glyphicons Halflings}` ); test( - 'should optimise ie9 hack', - processCss, - `h1 { + 'should optimise ie9 hack', + processCss, + `h1 { margin-top: 10px \\9; }`, - `h1{margin-top:10px\\9}` + `h1{margin-top:10px\\9}` ); test( - 'should optimise !important', - processCss, - `p { + 'should optimise !important', + processCss, + `p { margin: 100px !important; }`, - `p{margin:100px!important}` + `p{margin:100px!important}` ); test( - 'should optimise margin longhand', - processCss, - `h1 { + 'should optimise margin longhand', + processCss, + `h1 { margin-top: 9px; margin-right: 10px; margin-bottom: 11px; margin-left: 12px; }`, - `h1{margin:9px 10px 11px 12px}` + `h1{margin:9px 10px 11px 12px}` ); test( - 'should optimise margin longhand (2)', - processCss, - `h1 { + 'should optimise margin longhand (2)', + processCss, + `h1 { margin-top: 9px; margin-right: 10px; margin-bottom: 9px; margin-left: 10px; }`, - `h1{margin:9px 10px}` + `h1{margin:9px 10px}` ); test( - 'should optimise margin longhand (3)', - processCss, - `h1 { + 'should optimise margin longhand (3)', + processCss, + `h1 { margin-top: 8px; margin-right: 12px !important; margin-bottom: 14px; margin-left: 10px !important; }`, - `h1{margin-top:8px;margin-right:12px!important;margin-bottom:14px;margin-left:10px!important}` - // Switch back once css-declaration-sorter has been fixed - // `h1{margin-bottom:14px;margin-left:10px!important;margin-right:12px!important;margin-top:8px}` + `h1{margin-top:8px;margin-right:12px!important;margin-bottom:14px;margin-left:10px!important}` + // Switch back once css-declaration-sorter has been fixed + // `h1{margin-bottom:14px;margin-left:10px!important;margin-right:12px!important;margin-top:8px}` ); test( - 'should optimise margin shorthand', - processCss, - `h1 { + 'should optimise margin shorthand', + processCss, + `h1 { /* No expected optimisation */ margin: 10px 20px 30px 40px; } @@ -345,51 +344,51 @@ test( /* Expected to transform to 1 value */ margin: 10px 10px 10px 10px; }`, - `h1{margin:10px 20px 30px 40px}h2{margin:10px 20px 30px}h3{margin:10px 20px}h4{margin:10px}` + `h1{margin:10px 20px 30px 40px}h2{margin:10px 20px 30px}h3{margin:10px 20px}h4{margin:10px}` ); test( - 'should optimise padding longhand', - processCss, - `div { + 'should optimise padding longhand', + processCss, + `div { padding-left: 1px; padding-bottom: 2px; padding-top: 3px; padding-right: 4px; }`, - `div{padding:3px 4px 2px 1px}` + `div{padding:3px 4px 2px 1px}` ); test( - 'should preserve calc spaces', - processCss, - `div { + 'should preserve calc spaces', + processCss, + `div { width: calc(100vw / 2 - 6px + 0); }`, - `div{width:calc(50vw - 6px)}` + `div{width:calc(50vw - 6px)}` ); test( - 'should reduce calc', - processCss, - `h1 { + 'should reduce calc', + processCss, + `h1 { width: calc(3px * 2 - 1px); }`, - `h1{width:5px}` + `h1{width:5px}` ); test( - 'should optimise svg', - processCss, - `.box { + 'should optimise svg', + processCss, + `.box { background-image: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20viewBox%3D%220%200%201%201%22%20preserveAspectRatio%3D%22none%22%3E%3ClinearGradient%20id%3D%22gradient%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%25%22%20y1%3D%220%25%22%20x2%3D%22100%25%22%20y2%3D%220%25%22%3E%3Cstop%20offset%3D%220%25%22%20stop-color%3D%22%23414042%22%2F%3E%3Cstop%20offset%3D%22100%25%22%20stop-color%3D%22%23ffffff%22%20stop-opacity%3D%220%22%2F%3E%3C%2FlinearGradient%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22url(%23gradient)%22%20%2F%3E%3C%2Fsvg%3E'); }`, - `.box{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1' preserveAspectRatio='none'%3E%3ClinearGradient id='a' gradientUnits='userSpaceOnUse' x1='0%25' y1='0%25' x2='100%25' y2='0%25'%3E%3Cstop offset='0%25' stop-color='%23414042'/%3E%3Cstop offset='100%25' stop-color='%23fff' stop-opacity='0'/%3E%3C/linearGradient%3E%3Cpath fill='url(%23a)' d='M0 0h1v1H0z'/%3E%3C/svg%3E")}` + `.box{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1' preserveAspectRatio='none'%3E%3ClinearGradient id='a' gradientUnits='userSpaceOnUse' x1='0%25' y1='0%25' x2='100%25' y2='0%25'%3E%3Cstop offset='0%25' stop-color='%23414042'/%3E%3Cstop offset='100%25' stop-color='%23fff' stop-opacity='0'/%3E%3C/linearGradient%3E%3Cpath fill='url(%23a)' d='M0 0h1v1H0z'/%3E%3C/svg%3E")}` ); test( - 'should remove leading zeroes from reduced calc values', - processCss, - `.box { margin: calc(-.5 * 1rem); }`, - `.box{margin:-.5rem}` + 'should remove leading zeroes from reduced calc values', + processCss, + `.box { margin: calc(-.5 * 1rem); }`, + `.box{margin:-.5rem}` ); diff --git a/packages/cssnano/src/__tests__/issue26.js b/packages/cssnano/src/__tests__/issue26.js index c97fce6f1..9a59fa704 100644 --- a/packages/cssnano/src/__tests__/issue26.js +++ b/packages/cssnano/src/__tests__/issue26.js @@ -26,18 +26,18 @@ const expected = `@media print{.test{-webkit-box-shadow:none;box-shadow:none;-we // Switch back once css-declaration-sorter has been fixed // const expected = `@media print{.test{-webkit-border-radius:0;-webkit-box-shadow:none;border-radius:0;box-shadow:none}}.test{width:500px}`; -ava('it should compress whitespace after node.clone()', t => { - const processor = postcss([ - postcss.plugin('cloner', () => { - return css => { - css.walkAtRules(rule => { - css.prepend(rule.clone()); - rule.remove(); - }); - }; - }), - nano(), - ]); +ava('it should compress whitespace after node.clone()', (t) => { + const processor = postcss([ + postcss.plugin('cloner', () => { + return (css) => { + css.walkAtRules((rule) => { + css.prepend(rule.clone()); + rule.remove(); + }); + }; + }), + nano(), + ]); - return processor.process(fixture).then(r => t.deepEqual(r.css, expected)); + return processor.process(fixture).then((r) => t.deepEqual(r.css, expected)); }); diff --git a/packages/cssnano/src/__tests__/issue315.js b/packages/cssnano/src/__tests__/issue315.js index cf6bb378e..c4172145f 100644 --- a/packages/cssnano/src/__tests__/issue315.js +++ b/packages/cssnano/src/__tests__/issue315.js @@ -3,13 +3,18 @@ import postcss from 'postcss'; import fontMagician from 'postcss-font-magician'; import cssnano from '..'; -test('should work with postcss-font-magician', t => { - const css = ` +test('should work with postcss-font-magician', (t) => { + const css = ` body { - font-family: "Alice"; + font-family: "Alice"; } `; - return postcss([fontMagician, cssnano]).process(css).then(result => { - t.deepEqual(result.css, `@font-face{font-family:Alice;font-style:normal;font-weight:400;src:local("Alice Regular"),local(Alice-Regular),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgo.eot#) format("eot"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrg4.woff2) format("woff2"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgg.woff) format("woff")}body{font-family:Alice}`); + return postcss([fontMagician, cssnano]) + .process(css) + .then((result) => { + t.deepEqual( + result.css, + `@font-face{font-family:Alice;font-style:normal;font-weight:400;src:local("Alice Regular"),local(Alice-Regular),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgo.eot#) format("eot"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrg4.woff2) format("woff2"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgg.woff) format("woff")}body{font-family:Alice}` + ); }); }); diff --git a/packages/cssnano/src/__tests__/issue420.js b/packages/cssnano/src/__tests__/issue420.js index dbc726870..4b6d2915e 100644 --- a/packages/cssnano/src/__tests__/issue420.js +++ b/packages/cssnano/src/__tests__/issue420.js @@ -3,15 +3,21 @@ import postcss from 'postcss'; import fontMagician from 'postcss-font-magician'; import cssnano from '..'; -test('should work with postcss-font-magician with `display` parameter', t => { - const css = ` +test('should work with postcss-font-magician with `display` parameter', (t) => { + const css = ` body { - font-family: "Alice"; + font-family: "Alice"; } `; - return postcss([fontMagician({display: "optional"}), cssnano]).process(css).then(result => { - t.deepEqual(result.css, `@font-face{font-family:Alice;font-style:normal;font-weight:400;src:local("Alice Regular"),local(Alice-Regular),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgo.eot#) format("eot"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrg4.woff2) format("woff2"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgg.woff) format("woff");font-display:optional}body{font-family:Alice}`); - // Switch back once css-declaration-sorter has been fixed - // t.deepEqual(result.css, `@font-face{font-display:optional;font-family:Alice;font-style:normal;font-weight:400;src:local("Alice Regular"),local(Alice-Regular),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgo.eot#) format("eot"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrg4.woff2) format("woff2"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgg.woff) format("woff")}body{font-family:Alice}`); + + return postcss([fontMagician({ display: 'optional' }), cssnano]) + .process(css) + .then((result) => { + t.deepEqual( + result.css, + `@font-face{font-family:Alice;font-style:normal;font-weight:400;src:local("Alice Regular"),local(Alice-Regular),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgo.eot#) format("eot"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrg4.woff2) format("woff2"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgg.woff) format("woff");font-display:optional}body{font-family:Alice}` + ); + // Switch back once css-declaration-sorter has been fixed + // t.deepEqual(result.css, `@font-face{font-display:optional;font-family:Alice;font-style:normal;font-weight:400;src:local("Alice Regular"),local(Alice-Regular),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgo.eot#) format("eot"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrg4.woff2) format("woff2"),url(//fonts.gstatic.com/s/alice/v9/OpNCnoEEmtHa6GcOrgg.woff) format("woff")}body{font-family:Alice}`); }); }); diff --git a/packages/cssnano/src/__tests__/issue579.js b/packages/cssnano/src/__tests__/issue579.js index 99de62eaf..2fe3ec794 100644 --- a/packages/cssnano/src/__tests__/issue579.js +++ b/packages/cssnano/src/__tests__/issue579.js @@ -2,15 +2,21 @@ import test from 'ava'; import postcss from 'postcss'; import cssnano from '..'; -test('should support `env()` and `constant()` is an iPhone X-only feature', t => { - const css = ` +test('should support `env()` and `constant()` is an iPhone X-only feature', (t) => { + const css = ` @supports (height: env(safe-area-inset-bottom)) { .footer { padding-bottom: calc(env(safe-area-inset-bottom) * 3) !important; } } `; - return postcss([cssnano]).process(css).then(result => { - t.deepEqual(result.css, '@supports (height:env(safe-area-inset-bottom)){.footer{padding-bottom:calc(env(safe-area-inset-bottom)*3)!important}}'); + + return postcss([cssnano]) + .process(css) + .then((result) => { + t.deepEqual( + result.css, + '@supports (height:env(safe-area-inset-bottom)){.footer{padding-bottom:calc(env(safe-area-inset-bottom)*3)!important}}' + ); }); }); diff --git a/packages/cssnano/src/__tests__/knownIssues.js b/packages/cssnano/src/__tests__/knownIssues.js index ead751100..b77dbf9d0 100644 --- a/packages/cssnano/src/__tests__/knownIssues.js +++ b/packages/cssnano/src/__tests__/knownIssues.js @@ -10,10 +10,10 @@ import processCss from './_processCss'; */ test.failing( - 'should merge duplicate padding values', - processCss, - `body { padding: 50px; } body { padding: 0; }`, - `body{padding:0}` + 'should merge duplicate padding values', + processCss, + `body { padding: 50px; } body { padding: 0; }`, + `body{padding:0}` ); /* @@ -27,8 +27,8 @@ test.failing( */ test.failing( - 'should correctly handle escaped css', - processCss, - `\\64 \\69 \\76 { \\63 \\6f \\6c \\6f \\72 : \\72 \\67 \\62 \\61 \\28 \\32 \\35 \\35 \\2c \\30 \\2c \\30 \\2c \\2e \\37 \\35 \\29 }`, - `\\64\\69\\76{\\63\\6f\\6c\\6f\\72:\\72\\67\\62\\61\\28\\32\\35\\35\\2c\\30\\2c\\30\\2c\\2e\\37\\35\\29}`, + 'should correctly handle escaped css', + processCss, + `\\64 \\69 \\76 { \\63 \\6f \\6c \\6f \\72 : \\72 \\67 \\62 \\61 \\28 \\32 \\35 \\35 \\2c \\30 \\2c \\30 \\2c \\2e \\37 \\35 \\29 }`, + `\\64\\69\\76{\\63\\6f\\6c\\6f\\72:\\72\\67\\62\\61\\28\\32\\35\\35\\2c\\30\\2c\\30\\2c\\2e\\37\\35\\29}` ); diff --git a/packages/cssnano/src/__tests__/postcss-calc.js b/packages/cssnano/src/__tests__/postcss-calc.js index 0ffe0b9a1..c10f6126a 100644 --- a/packages/cssnano/src/__tests__/postcss-calc.js +++ b/packages/cssnano/src/__tests__/postcss-calc.js @@ -1,108 +1,107 @@ import test from 'ava'; import processCss from './_processCss'; - test( - 'should optimise inside calc', - processCss, - 'h1{width:calc(var(--h) * 1em)}', - 'h1{width:calc(var(--h)*1em)}', + 'should optimise inside calc', + processCss, + 'h1{width:calc(var(--h) * 1em)}', + 'h1{width:calc(var(--h)*1em)}' ); test( - 'should optimise inside calc (1)', - processCss, - 'h1{width:calc(1.5em / var(--h))}', - 'h1{width:calc(1.5em/var(--h))}', + 'should optimise inside calc (1)', + processCss, + 'h1{width:calc(1.5em / var(--h))}', + 'h1{width:calc(1.5em/var(--h))}' ); test( - 'should optimise inside calc (2)', - processCss, - 'h1{--a:calc(var(--x, 1) * 10vw)}', - 'h1{--a:calc(var(--x, 1)*10vw)}', + 'should optimise inside calc (2)', + processCss, + 'h1{--a:calc(var(--x, 1) * 10vw)}', + 'h1{--a:calc(var(--x, 1)*10vw)}' ); test( - 'should optimise inside calc (3)', - processCss, - 'h1{width:calc(calc(2.25rem + 2px) - 1px * 2)}', - 'h1{width:2.25rem}', + 'should optimise inside calc (3)', + processCss, + 'h1{width:calc(calc(2.25rem + 2px) - 1px * 2)}', + 'h1{width:2.25rem}' ); test( - 'should optimise inside calc (4)', - processCss, - 'h1{width:calc(env(safe-area-inset-bottom) * 3) !important}', - 'h1{width:calc(env(safe-area-inset-bottom)*3)!important}', + 'should optimise inside calc (4)', + processCss, + 'h1{width:calc(env(safe-area-inset-bottom) * 3) !important}', + 'h1{width:calc(env(safe-area-inset-bottom)*3)!important}' ); test( - 'should optimise inside calc (5)', - processCss, - 'h1{width:calc(14px + 6 * ((100vw - 320px) / 448))}', - 'h1{width:calc(14px + 6*(100vw - 320px)/448)}', + 'should optimise inside calc (5)', + processCss, + 'h1{width:calc(14px + 6 * ((100vw - 320px) / 448))}', + 'h1{width:calc(14px + 6*(100vw - 320px)/448)}' ); test( - 'should optimise inside calc (6)', - processCss, - 'h1{width:calc((100px - 1em) + (-50px + 1em))}', - 'h1{width:50px}', + 'should optimise inside calc (6)', + processCss, + 'h1{width:calc((100px - 1em) + (-50px + 1em))}', + 'h1{width:50px}' ); test( - 'should optimise inside calc (7)', - processCss, - 'h1{width:calc(50% + (5em + 5%))}', - 'h1{width:calc(55% + 5em)}', + 'should optimise inside calc (7)', + processCss, + 'h1{width:calc(50% + (5em + 5%))}', + 'h1{width:calc(55% + 5em)}' ); test( - 'should optimise inside calc (8)', - processCss, - 'h1{width:calc((99.99% * 1/1) - 0rem)}', - 'h1{width:99.99%}', + 'should optimise inside calc (8)', + processCss, + 'h1{width:calc((99.99% * 1/1) - 0rem)}', + 'h1{width:99.99%}' ); test( - 'should optimise inside calc (8)', - processCss, - 'h1{margin:1px 1px calc(0.5em + 1px)}', - 'h1{margin:1px 1px calc(.5em + 1px)}', + 'should optimise inside calc (8)', + processCss, + 'h1{margin:1px 1px calc(0.5em + 1px)}', + 'h1{margin:1px 1px calc(.5em + 1px)}' ); test( - 'should keep spaces in calc', - processCss, - 'h1{width:calc(100% * 120px)}', - 'h1{width:calc(100%*120px)}', + 'should keep spaces in calc', + processCss, + 'h1{width:calc(100% * 120px)}', + 'h1{width:calc(100%*120px)}' ); test( - 'should keep spaces in calc (2)', - processCss, - 'h1{width:calc(100% + 120px)}', - 'h1{width:calc(100% + 120px)}', + 'should keep spaces in calc (2)', + processCss, + 'h1{width:calc(100% + 120px)}', + 'h1{width:calc(100% + 120px)}' ); test( - 'should keep spaces in calc (3)', - processCss, - 'h1{width:calc(1.5em + var(--h))}', - 'h1{width:calc(1.5em + var(--h))}', + 'should keep spaces in calc (3)', + processCss, + 'h1{width:calc(1.5em + var(--h))}', + 'h1{width:calc(1.5em + var(--h))}' ); test( - 'should keep spaces in calc (4)', - processCss, - 'h1{width:calc(1.5em - var(--h))}', - 'h1{width:calc(1.5em - var(--h))}', + 'should keep spaces in calc (4)', + processCss, + 'h1{width:calc(1.5em - var(--h))}', + 'h1{width:calc(1.5em - var(--h))}' ); test( - 'should keep spaces in calc (5)', - processCss, - 'h1{width:calc(100% - var(--my-var))}', - 'h1{width:calc(100% - var(--my-var))}', + 'should keep spaces in calc (5)', + processCss, + 'h1{width:calc(100% - var(--my-var))}', + 'h1{width:calc(100% - var(--my-var))}' ); diff --git a/packages/cssnano/src/__tests__/postcss-convert-values.js b/packages/cssnano/src/__tests__/postcss-convert-values.js index 825a24429..6d369a878 100644 --- a/packages/cssnano/src/__tests__/postcss-convert-values.js +++ b/packages/cssnano/src/__tests__/postcss-convert-values.js @@ -2,141 +2,141 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should convert milliseconds to seconds', - processCss, - 'h1{transition-duration:500ms}', - 'h1{transition-duration:.5s}', + 'should convert milliseconds to seconds', + processCss, + 'h1{transition-duration:500ms}', + 'h1{transition-duration:.5s}' ); test( - 'should not convert negative milliseconds to seconds', - processCss, - 'h1{animation-duration:-569ms}', - 'h1{animation-duration:-569ms}', + 'should not convert negative milliseconds to seconds', + processCss, + 'h1{animation-duration:-569ms}', + 'h1{animation-duration:-569ms}' ); test( - 'should not remove the unit from zero values (duration)', - processCss, - 'h1{transition-duration:0s}', - 'h1{transition-duration:0s}', + 'should not remove the unit from zero values (duration)', + processCss, + 'h1{transition-duration:0s}', + 'h1{transition-duration:0s}' ); test( - 'should remove unnecessary plus signs', - processCss, - 'h1{width:+14px}', - 'h1{width:14px}', + 'should remove unnecessary plus signs', + processCss, + 'h1{width:+14px}', + 'h1{width:14px}' ); test( - 'should strip the units from length properties', - processCss, - 'h1{margin:0em 0% 0px 0pc}', - 'h1{margin:0}', + 'should strip the units from length properties', + processCss, + 'h1{margin:0em 0% 0px 0pc}', + 'h1{margin:0}' ); test( - 'should trim trailing zeros', - processCss, - 'h1{width:109.00000000000px}', - 'h1{width:109px}', + 'should trim trailing zeros', + processCss, + 'h1{width:109.00000000000px}', + 'h1{width:109px}' ); test( - 'should trim trailing zeros + unit', - processCss, - 'h1{width:0.00px}', - 'h1{width:0}', + 'should trim trailing zeros + unit', + processCss, + 'h1{width:0.00px}', + 'h1{width:0}' ); test( - 'should not mangle flex basis', - processCss, - 'h1{flex-basis:0%}', - 'h1{flex-basis:0%}', + 'should not mangle flex basis', + processCss, + 'h1{flex-basis:0%}', + 'h1{flex-basis:0%}' ); test( - 'should not mangle values without units', - processCss, - 'h1{z-index:1}', - 'h1{z-index:1}', + 'should not mangle values without units', + processCss, + 'h1{z-index:1}', + 'h1{z-index:1}' ); test( - 'should not mangle values outside of its domain', - processCss, - 'h1{background:url(a.png)}', - 'h1{background:url(a.png)}', + 'should not mangle values outside of its domain', + processCss, + 'h1{background:url(a.png)}', + 'h1{background:url(a.png)}' ); test( - 'should optimise fractions', - processCss, - 'h1{opacity:1.}h2{opacity:.0}', - 'h1{opacity:1}h2{opacity:0}', + 'should optimise fractions', + processCss, + 'h1{opacity:1.}h2{opacity:.0}', + 'h1{opacity:1}h2{opacity:0}' ); test( - 'should optimise fractions with units', - processCss, - 'h1{width:10.px}h2{width:.0px}', - 'h1{width:10px}h2{width:0}', + 'should optimise fractions with units', + processCss, + 'h1{width:10.px}h2{width:.0px}', + 'h1{width:10px}h2{width:0}' ); test( - 'should optimise fractions inside calc', - processCss, - 'h1{width:calc(10px + .0px)}', - 'h1{width:10px}', + 'should optimise fractions inside calc', + processCss, + 'h1{width:calc(10px + .0px)}', + 'h1{width:10px}' ); test( - 'should handle leading zero in rem values', - processCss, - '.one{top:0.25rem}', - '.one{top:.25rem}', + 'should handle leading zero in rem values', + processCss, + '.one{top:0.25rem}', + '.one{top:.25rem}' ); test( - 'should handle slash separated values', - processCss, - '.one{background:50% .0%/100.0% 100.0%}', - '.one{background:50% 0/100% 100%}', + 'should handle slash separated values', + processCss, + '.one{background:50% .0%/100.0% 100.0%}', + '.one{background:50% 0/100% 100%}' ); test( - 'should handle comma separated values', - processCss, - '.one{background:50% .0%,100.0% 100.0%}', - '.one{background:50% 0,100% 100%}', + 'should handle comma separated values', + processCss, + '.one{background:50% .0%,100.0% 100.0%}', + '.one{background:50% 0,100% 100%}' ); test( - 'should not mangle duration values', - processCss, - '.long{animation-duration:2s}', - '.long{animation-duration:2s}', + 'should not mangle duration values', + processCss, + '.long{animation-duration:2s}', + '.long{animation-duration:2s}' ); test( - 'should not mangle padding values', - processCss, - 'h1{padding:10px 20px 30px 40px}h2{padding:10px 20px 30px}h3{padding:10px 20px}h4{padding:10px}', - 'h1{padding:10px 20px 30px 40px}h2{padding:10px 20px 30px}h3{padding:10px 20px}h4{padding:10px}', + 'should not mangle padding values', + processCss, + 'h1{padding:10px 20px 30px 40px}h2{padding:10px 20px 30px}h3{padding:10px 20px}h4{padding:10px}', + 'h1{padding:10px 20px 30px 40px}h2{padding:10px 20px 30px}h3{padding:10px 20px}h4{padding:10px}' ); test( - 'should trim leading zeroes from negative values', - processCss, - 'h1,h2{letter-spacing:-0.1rem}', - 'h1,h2{letter-spacing:-.1rem}', + 'should trim leading zeroes from negative values', + processCss, + 'h1,h2{letter-spacing:-0.1rem}', + 'h1,h2{letter-spacing:-.1rem}' ); test( - 'should support viewports units', - processCss, - 'h1,h2{letter-spacing:-0.1vmin}', - 'h1,h2{letter-spacing:-.1vmin}', + 'should support viewports units', + processCss, + 'h1,h2{letter-spacing:-0.1vmin}', + 'h1,h2{letter-spacing:-.1vmin}' ); diff --git a/packages/cssnano/src/__tests__/postcss-discard-comments.js b/packages/cssnano/src/__tests__/postcss-discard-comments.js index 42b79f4c5..83185fc00 100644 --- a/packages/cssnano/src/__tests__/postcss-discard-comments.js +++ b/packages/cssnano/src/__tests__/postcss-discard-comments.js @@ -1,122 +1,122 @@ import test from 'ava'; -import processCss, {passthrough} from './_processCss'; +import processCss, { passthrough } from './_processCss'; test( - 'should remove non-special comments', - processCss, - 'h1{font-weight:700!important/*test comment*/}', - 'h1{font-weight:700!important}', + 'should remove non-special comments', + processCss, + 'h1{font-weight:700!important/*test comment*/}', + 'h1{font-weight:700!important}' ); test( - 'should remove non-special comments 2', - processCss, - 'h1{/*test comment*/font-weight:700}', - 'h1{font-weight:700}', + 'should remove non-special comments 2', + processCss, + 'h1{/*test comment*/font-weight:700}', + 'h1{font-weight:700}' ); test( - 'should remove non-special comments 3', - processCss, - '/*test comment*/h1{font-weight:700}/*test comment*/', - 'h1{font-weight:700}', + 'should remove non-special comments 3', + processCss, + '/*test comment*/h1{font-weight:700}/*test comment*/', + 'h1{font-weight:700}' ); test( - 'should remove non-special comments 4', - processCss, - 'h1{font-weight:/*test comment*/700}', - 'h1{font-weight:700}', + 'should remove non-special comments 4', + processCss, + 'h1{font-weight:/*test comment*/700}', + 'h1{font-weight:700}' ); test( - 'should remove non-special comments 5', - processCss, - 'h1{margin:10px/*test*/20px}', - 'h1{margin:10px 20px}', + 'should remove non-special comments 5', + processCss, + 'h1{margin:10px/*test*/20px}', + 'h1{margin:10px 20px}' ); test( - 'should remove non-special comments 6', - processCss, - 'h1{margin:10px /*test*/ 20px /*test*/ 10px /*test*/ 20px}', - 'h1{margin:10px 20px}', + 'should remove non-special comments 6', + processCss, + 'h1{margin:10px /*test*/ 20px /*test*/ 10px /*test*/ 20px}', + 'h1{margin:10px 20px}' ); test( - 'should remove non-special comments 7', - processCss, - '/*comment*/*/*comment*/{margin:10px}', - '*{margin:10px}', + 'should remove non-special comments 7', + processCss, + '/*comment*/*/*comment*/{margin:10px}', + '*{margin:10px}' ); test( - 'should remove non-special comments 8', - processCss, - 'h1,/*comment*/ h2, h3/*comment*/{margin:20px}', - 'h1,h2,h3{margin:20px}', + 'should remove non-special comments 8', + processCss, + 'h1,/*comment*/ h2, h3/*comment*/{margin:20px}', + 'h1,h2,h3{margin:20px}' ); test( - 'should remove non-special comments 9', - processCss, - '@keyframes /*test*/ fade{0%{opacity:0}100%{opacity:1}}a{animation:fade}', - '@keyframes fade{0%{opacity:0}to{opacity:1}}a{animation:fade}', + 'should remove non-special comments 9', + processCss, + '@keyframes /*test*/ fade{0%{opacity:0}100%{opacity:1}}a{animation:fade}', + '@keyframes fade{0%{opacity:0}to{opacity:1}}a{animation:fade}' ); test( - 'should remove non-special comments 10', - processCss, - '@media only screen /*desktop*/ and (min-width:900px){body{margin:0 auto}}', - '@media only screen and (min-width:900px){body{margin:0 auto}}', + 'should remove non-special comments 10', + processCss, + '@media only screen /*desktop*/ and (min-width:900px){body{margin:0 auto}}', + '@media only screen and (min-width:900px){body{margin:0 auto}}' ); test( - 'should remove non-special comments 11', - processCss, - '@media only screen and (min-width:900px)/*test*/{body{margin:0 auto}}', - '@media only screen and (min-width:900px){body{margin:0 auto}}', + 'should remove non-special comments 11', + processCss, + '@media only screen and (min-width:900px)/*test*/{body{margin:0 auto}}', + '@media only screen and (min-width:900px){body{margin:0 auto}}' ); test( - 'should remove non-special comments 12', - processCss, - 'h1{margin/*test*/:20px}', - 'h1{margin:20px}', + 'should remove non-special comments 12', + processCss, + 'h1{margin/*test*/:20px}', + 'h1{margin:20px}' ); test( - 'should remove non-special comments 13', - processCss, - 'h1{margin:20px! /* test */ important}', - 'h1{margin:20px!important}', + 'should remove non-special comments 13', + processCss, + 'h1{margin:20px! /* test */ important}', + 'h1{margin:20px!important}' ); test( - 'should keep special comments', - processCss, - 'h1{font-weight:700!important/*!test comment*/}', - 'h1{font-weight:700!important/*!test comment*/}', + 'should keep special comments', + processCss, + 'h1{font-weight:700!important/*!test comment*/}', + 'h1{font-weight:700!important/*!test comment*/}' ); test( - 'should keep special comments 2', - passthrough, - 'h1{/*!test comment*/font-weight:700}', - // Switch back once css-declaration-sorter has been fixed - // 'h1{font-weight:700/*!test comment*/}', + 'should keep special comments 2', + passthrough, + 'h1{/*!test comment*/font-weight:700}' + // Switch back once css-declaration-sorter has been fixed + // 'h1{font-weight:700/*!test comment*/}', ); test( - 'should keep special comments 3', - processCss, - '/*!test comment*/h1{font-weight:700}/*!test comment*/', - '/*!test comment*/h1{font-weight:700}/*!test comment*/', + 'should keep special comments 3', + processCss, + '/*!test comment*/h1{font-weight:700}/*!test comment*/', + '/*!test comment*/h1{font-weight:700}/*!test comment*/' ); test( - 'should pass through when it doesn\'t find a comment', - processCss, - 'h1{color:#000;font-weight:700}', - 'h1{color:#000;font-weight:700}', + "should pass through when it doesn't find a comment", + processCss, + 'h1{color:#000;font-weight:700}', + 'h1{color:#000;font-weight:700}' ); diff --git a/packages/cssnano/src/__tests__/postcss-discard-duplicates.js b/packages/cssnano/src/__tests__/postcss-discard-duplicates.js index 41d379e2d..d98122764 100644 --- a/packages/cssnano/src/__tests__/postcss-discard-duplicates.js +++ b/packages/cssnano/src/__tests__/postcss-discard-duplicates.js @@ -2,50 +2,50 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should remove duplicate rules', - processCss, - 'h1{font-weight:700}h1{font-weight:700}', - 'h1{font-weight:700}', + 'should remove duplicate rules', + processCss, + 'h1{font-weight:700}h1{font-weight:700}', + 'h1{font-weight:700}' ); test( - 'should remove duplicate declarations', - processCss, - 'h1{font-weight:700;font-weight:700}', - 'h1{font-weight:700}', + 'should remove duplicate declarations', + processCss, + 'h1{font-weight:700;font-weight:700}', + 'h1{font-weight:700}' ); test( - 'should remove duplicates inside @media queries', - processCss, - '@media print{h1{display:block}h1{display:block}}', - '@media print{h1{display:block}}', + 'should remove duplicates inside @media queries', + processCss, + '@media print{h1{display:block}h1{display:block}}', + '@media print{h1{display:block}}' ); test( - 'should remove duplicate @media queries', - processCss, - '@media print{h1{display:block}}@media print{h1{display:block}}', - '@media print{h1{display:block}}', + 'should remove duplicate @media queries', + processCss, + '@media print{h1{display:block}}@media print{h1{display:block}}', + '@media print{h1{display:block}}' ); test( - 'should remove declarations before rules', - processCss, - 'h1{font-weight:700;font-weight:700}h1{font-weight:700}', - 'h1{font-weight:700}', + 'should remove declarations before rules', + processCss, + 'h1{font-weight:700;font-weight:700}h1{font-weight:700}', + 'h1{font-weight:700}' ); test( - 'should not remove declarations when selectors are different', - processCss, - 'h1{font-weight:700}h2{font-weight:700}', - 'h1,h2{font-weight:700}', + 'should not remove declarations when selectors are different', + processCss, + 'h1{font-weight:700}h2{font-weight:700}', + 'h1,h2{font-weight:700}' ); test( - 'should not remove across contexts', - processCss, - 'h1{display:block}@media print{h1{display:block}}', - 'h1{display:block}@media print{h1{display:block}}', + 'should not remove across contexts', + processCss, + 'h1{display:block}@media print{h1{display:block}}', + 'h1{display:block}@media print{h1{display:block}}' ); diff --git a/packages/cssnano/src/__tests__/postcss-discard-overridden.js b/packages/cssnano/src/__tests__/postcss-discard-overridden.js index e758b616b..8daf4836d 100644 --- a/packages/cssnano/src/__tests__/postcss-discard-overridden.js +++ b/packages/cssnano/src/__tests__/postcss-discard-overridden.js @@ -2,15 +2,15 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should discard overridden rules', - processCss, - '@keyframes a{0%{opacity:1}to{opacity:0}}@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.box{animation-name:a}', - '@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.box{animation-name:a}', + 'should discard overridden rules', + processCss, + '@keyframes a{0%{opacity:1}to{opacity:0}}@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.box{animation-name:a}', + '@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.box{animation-name:a}' ); test( - 'should discard overridden rules in media queries', - processCss, - '@media screen and (max-width:500px){@keyframes a{0%{opacity:1}to{opacity:0}}@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(358deg)}}}@keyframes a{0%{opacity:1}to{opacity:0}}@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.box{animation-name:a}', - '@media screen and (max-width:500px){@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(358deg)}}}@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.box{animation-name:a}', + 'should discard overridden rules in media queries', + processCss, + '@media screen and (max-width:500px){@keyframes a{0%{opacity:1}to{opacity:0}}@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(358deg)}}}@keyframes a{0%{opacity:1}to{opacity:0}}@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.box{animation-name:a}', + '@media screen and (max-width:500px){@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(358deg)}}}@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.box{animation-name:a}' ); diff --git a/packages/cssnano/src/__tests__/postcss-merge-longhand.js b/packages/cssnano/src/__tests__/postcss-merge-longhand.js index 8ceb93b33..94f2022bf 100644 --- a/packages/cssnano/src/__tests__/postcss-merge-longhand.js +++ b/packages/cssnano/src/__tests__/postcss-merge-longhand.js @@ -1,228 +1,226 @@ import test from 'ava'; -import processCss, {passthrough} from './_processCss'; +import processCss, { passthrough } from './_processCss'; test( - 'should merge margin values', - processCss, - 'h1{margin-top:10px;margin-right:20px;margin-bottom:30px;margin-left:40px}', - 'h1{margin:10px 20px 30px 40px}', + 'should merge margin values', + processCss, + 'h1{margin-top:10px;margin-right:20px;margin-bottom:30px;margin-left:40px}', + 'h1{margin:10px 20px 30px 40px}' ); test( - 'should merge margin values with !important', - processCss, - 'h1{margin-top:10px!important;margin-right:20px!important;margin-bottom:30px!important;margin-left:40px!important}', - 'h1{margin:10px 20px 30px 40px!important}', + 'should merge margin values with !important', + processCss, + 'h1{margin-top:10px!important;margin-right:20px!important;margin-bottom:30px!important;margin-left:40px!important}', + 'h1{margin:10px 20px 30px 40px!important}' ); test( - 'should merge & then condense margin values', - processCss, - 'h1{margin-top:10px;margin-bottom:10px;margin-left:10px;margin-right:10px}', - 'h1{margin:10px}', + 'should merge & then condense margin values', + processCss, + 'h1{margin-top:10px;margin-bottom:10px;margin-left:10px;margin-right:10px}', + 'h1{margin:10px}' ); test( - 'should not merge margin values with mixed !important', - passthrough, - 'h1{margin-top:10px!important;margin-right:20px;margin-bottom:30px!important;margin-left:40px}', - // Switch back once css-declaration-sorter has been fixed - // 'h1{margin-bottom:30px!important;margin-left:40px;margin-right:20px;margin-top:10px!important}', + 'should not merge margin values with mixed !important', + passthrough, + 'h1{margin-top:10px!important;margin-right:20px;margin-bottom:30px!important;margin-left:40px}' + // Switch back once css-declaration-sorter has been fixed + // 'h1{margin-bottom:30px!important;margin-left:40px;margin-right:20px;margin-top:10px!important}', ); test( - 'should merge padding values', - processCss, - 'h1{padding-top:10px;padding-right:20px;padding-bottom:30px;padding-left:40px}', - 'h1{padding:10px 20px 30px 40px}', + 'should merge padding values', + processCss, + 'h1{padding-top:10px;padding-right:20px;padding-bottom:30px;padding-left:40px}', + 'h1{padding:10px 20px 30px 40px}' ); test( - 'should merge padding values with !important', - processCss, - 'h1{padding-top:10px!important;padding-right:20px!important;padding-bottom:30px!important;padding-left:40px!important}', - 'h1{padding:10px 20px 30px 40px!important}', + 'should merge padding values with !important', + processCss, + 'h1{padding-top:10px!important;padding-right:20px!important;padding-bottom:30px!important;padding-left:40px!important}', + 'h1{padding:10px 20px 30px 40px!important}' ); test( - 'should not merge padding values with mixed !important', - passthrough, - 'h1{padding-top:10px!important;padding-right:20px;padding-bottom:30px!important;padding-left:40px}', - // Switch back once css-declaration-sorter has been fixed - // 'h1{padding-bottom:30px!important;padding-left:40px;padding-right:20px;padding-top:10px!important}', + 'should not merge padding values with mixed !important', + passthrough, + 'h1{padding-top:10px!important;padding-right:20px;padding-bottom:30px!important;padding-left:40px}' + // Switch back once css-declaration-sorter has been fixed + // 'h1{padding-bottom:30px!important;padding-left:40px;padding-right:20px;padding-top:10px!important}', ); test( - 'should merge identical border values', - processCss, - 'h1{border-top:1px solid #000;border-bottom:1px solid #000;border-left:1px solid #000;border-right:1px solid #000}', - 'h1{border:1px solid #000}', + 'should merge identical border values', + processCss, + 'h1{border-top:1px solid #000;border-bottom:1px solid #000;border-left:1px solid #000;border-right:1px solid #000}', + 'h1{border:1px solid #000}' ); test( - 'should merge identical border values with !important', - processCss, - 'h1{border-top:1px solid #000!important;border-bottom:1px solid #000!important;border-left:1px solid #000!important;border-right:1px solid #000!important}', - 'h1{border:1px solid #000!important}', + 'should merge identical border values with !important', + processCss, + 'h1{border-top:1px solid #000!important;border-bottom:1px solid #000!important;border-left:1px solid #000!important;border-right:1px solid #000!important}', + 'h1{border:1px solid #000!important}' ); test( - 'should not merge identical border values with mixed !important', - passthrough, - 'h1{border-top:1px solid #000;border-bottom:1px solid #000;border-left:1px solid #000!important;border-right:1px solid #000!important}', - // Switch back once css-declaration-sorter has been fixed - // 'h1{border-bottom:1px solid #000;border-left:1px solid #000!important;border-right:1px solid #000!important;border-top:1px solid #000}', + 'should not merge identical border values with mixed !important', + passthrough, + 'h1{border-top:1px solid #000;border-bottom:1px solid #000;border-left:1px solid #000!important;border-right:1px solid #000!important}' + // Switch back once css-declaration-sorter has been fixed + // 'h1{border-bottom:1px solid #000;border-left:1px solid #000!important;border-right:1px solid #000!important;border-top:1px solid #000}', ); test( - 'should merge border values', - processCss, - 'h1{border-color:red;border-width:1px;border-style:dashed}', - 'h1{border:1px dashed red}', + 'should merge border values', + processCss, + 'h1{border-color:red;border-width:1px;border-style:dashed}', + 'h1{border:1px dashed red}' ); test( - 'should merge border values with !important', - processCss, - 'h1{border-color:red!important;border-width:1px!important;border-style:dashed!important}', - 'h1{border:1px dashed red!important}', + 'should merge border values with !important', + processCss, + 'h1{border-color:red!important;border-width:1px!important;border-style:dashed!important}', + 'h1{border:1px dashed red!important}' ); test( - 'should merge border values with identical values for all sides', - processCss, - 'h1{border-color:red red red red;border-width:1px 1px 1px 1px;border-style:solid solid solid solid}', - 'h1{border:1px solid red}', + 'should merge border values with identical values for all sides', + processCss, + 'h1{border-color:red red red red;border-width:1px 1px 1px 1px;border-style:solid solid solid solid}', + 'h1{border:1px solid red}' ); test( - 'should not merge border values with mixed !important', - passthrough, - 'h1{border-color:red;border-width:1px!important;border-style:dashed!important}', - // Switch back once css-declaration-sorter has been fixed - // 'h1{border-color:red;border-style:dashed!important;border-width:1px!important}', + 'should not merge border values with mixed !important', + passthrough, + 'h1{border-color:red;border-width:1px!important;border-style:dashed!important}' + // Switch back once css-declaration-sorter has been fixed + // 'h1{border-color:red;border-style:dashed!important;border-width:1px!important}', ); test( - 'should convert 4 values to 1', - processCss, - 'h1{margin:10px 10px 10px 10px}', - 'h1{margin:10px}', + 'should convert 4 values to 1', + processCss, + 'h1{margin:10px 10px 10px 10px}', + 'h1{margin:10px}' ); test( - 'should convert 3 values to 1', - processCss, - 'h1{margin:10px 10px 10px}', - 'h1{margin:10px}', + 'should convert 3 values to 1', + processCss, + 'h1{margin:10px 10px 10px}', + 'h1{margin:10px}' ); test( - 'should convert 3 values to 2', - processCss, - 'h1{margin:10px 20px 10px}', - 'h1{margin:10px 20px}', + 'should convert 3 values to 2', + processCss, + 'h1{margin:10px 20px 10px}', + 'h1{margin:10px 20px}' ); test( - 'should convert 2 values to 1', - processCss, - 'h1{margin:10px 10px}', - 'h1{margin:10px}', + 'should convert 2 values to 1', + processCss, + 'h1{margin:10px 10px}', + 'h1{margin:10px}' ); test( - 'should convert 1 value to 1', - processCss, - 'h1{margin:10px}', - 'h1{margin:10px}', + 'should convert 1 value to 1', + processCss, + 'h1{margin:10px}', + 'h1{margin:10px}' ); test( - 'should convert 4 values to 2', - processCss, - 'h1{margin:10px 20px 10px 20px}', - 'h1{margin:10px 20px}', + 'should convert 4 values to 2', + processCss, + 'h1{margin:10px 20px 10px 20px}', + 'h1{margin:10px 20px}' ); test( - 'should convert 4 values to 3', - processCss, - 'h1{margin:10px 20px 30px 20px}', - 'h1{margin:10px 20px 30px}', + 'should convert 4 values to 3', + processCss, + 'h1{margin:10px 20px 30px 20px}', + 'h1{margin:10px 20px 30px}' ); test( - 'should convert 4 values to 4', - processCss, - 'h1{margin:10px 20px 30px 40px}', - 'h1{margin:10px 20px 30px 40px}', + 'should convert 4 values to 4', + processCss, + 'h1{margin:10px 20px 30px 40px}', + 'h1{margin:10px 20px 30px 40px}' ); test( - 'save fallbacks if after them goes custom css props', - processCss, - 'h1{padding:1em;padding:var(--variable)}', - 'h1{padding:1em;padding:var(--variable)}' + 'save fallbacks if after them goes custom css props', + processCss, + 'h1{padding:1em;padding:var(--variable)}', + 'h1{padding:1em;padding:var(--variable)}' ); test( - 'overwrite custom css props if after them goes same props', - processCss, - 'h1{padding:var(--variable);padding:1em}', - 'h1{padding:1em}' + 'overwrite custom css props if after them goes same props', + processCss, + 'h1{padding:var(--variable);padding:1em}', + 'h1{padding:1em}' ); test( - 'overwrite custom css props if after them goes same props (2)', - processCss, - 'h1{padding:var(--first);padding:var(--second)}', - 'h1{padding:var(--second)}' + 'overwrite custom css props if after them goes same props (2)', + processCss, + 'h1{padding:var(--first);padding:var(--second)}', + 'h1{padding:var(--second)}' ); test( - 'should not break unmergeable rules with custom props', - processCss, - 'h1{padding:var(--variable)}', - 'h1{padding:var(--variable)}' + 'should not break unmergeable rules with custom props', + processCss, + 'h1{padding:var(--variable)}', + 'h1{padding:var(--variable)}' ); test( - 'should not merge rule if it includes mixed values', - passthrough, - 'h1{padding-top:10px;padding-right:15px;padding-bottom:20px;padding-left:var(--variable)}', - // Switch back once css-declaration-sorter has been fixed - // 'h1{padding-bottom:20px;padding-left:var(--variable);padding-right:15px;padding-top:10px}' + 'should not merge rule if it includes mixed values', + passthrough, + 'h1{padding-top:10px;padding-right:15px;padding-bottom:20px;padding-left:var(--variable)}' + // Switch back once css-declaration-sorter has been fixed + // 'h1{padding-bottom:20px;padding-left:var(--variable);padding-right:15px;padding-top:10px}' ); - test( - 'should merge rules with custom props', - processCss, - 'h1{padding-top:var(--variable);padding-right:var(--variable);padding-bottom:var(--variable);padding-left:var(--variable)}', - 'h1{padding:var(--variable)}' + 'should merge rules with custom props', + processCss, + 'h1{padding-top:var(--variable);padding-right:var(--variable);padding-bottom:var(--variable);padding-left:var(--variable)}', + 'h1{padding:var(--variable)}' ); -test( - 'should merge props and dont remove fallbacks', - processCss, - 'h1{padding-top:10px;padding-right:15px;padding-bottom:20px;padding-left:25px;padding-top:var(--variable);padding-right:var(--variable);padding-bottom:var(--variable);padding-left:var(--variable)}', - 'h1{padding:10px 15px 20px 25px;padding:var(--variable)}' +test( + 'should merge props and dont remove fallbacks', + processCss, + 'h1{padding-top:10px;padding-right:15px;padding-bottom:20px;padding-left:25px;padding-top:var(--variable);padding-right:var(--variable);padding-bottom:var(--variable);padding-left:var(--variable)}', + 'h1{padding:10px 15px 20px 25px;padding:var(--variable)}' ); - -test( - 'should merge props and overwrite', - processCss, - 'h1{padding-top:var(--variable);padding-right:var(--variable);padding-bottom:var(--variable);padding-left:var(--variable);padding-top:10px;padding-right:15px;padding-bottom:20px;padding-left:25px}', - 'h1{padding:10px 15px 20px 25px}' +test( + 'should merge props and overwrite', + processCss, + 'h1{padding-top:var(--variable);padding-right:var(--variable);padding-bottom:var(--variable);padding-left:var(--variable);padding-top:10px;padding-right:15px;padding-bottom:20px;padding-left:25px}', + 'h1{padding:10px 15px 20px 25px}' ); test( - 'should overwrite some props and save fallbacks', - processCss, - 'h1{padding-top:10px;padding-right:var(--variable);padding-right:15px;padding-bottom:var(--variable);padding-bottom:20px;padding-left:25px;padding-top:var(--variable);padding-left:var(--variable)}', - 'h1{padding:10px 15px 20px 25px;padding-top:var(--variable);padding-left:var(--variable)}' - // Switch back once css-declaration-sorter has been fixed - // 'h1{padding:10px 15px 20px 25px;padding-left:var(--variable);padding-top:var(--variable)}' + 'should overwrite some props and save fallbacks', + processCss, + 'h1{padding-top:10px;padding-right:var(--variable);padding-right:15px;padding-bottom:var(--variable);padding-bottom:20px;padding-left:25px;padding-top:var(--variable);padding-left:var(--variable)}', + 'h1{padding:10px 15px 20px 25px;padding-top:var(--variable);padding-left:var(--variable)}' + // Switch back once css-declaration-sorter has been fixed + // 'h1{padding:10px 15px 20px 25px;padding-left:var(--variable);padding-top:var(--variable)}' ); diff --git a/packages/cssnano/src/__tests__/postcss-merge-rules.js b/packages/cssnano/src/__tests__/postcss-merge-rules.js index 7356de0b7..b30d4ff50 100644 --- a/packages/cssnano/src/__tests__/postcss-merge-rules.js +++ b/packages/cssnano/src/__tests__/postcss-merge-rules.js @@ -1,251 +1,251 @@ import test from 'ava'; -import processCss, {passthrough} from './_processCss'; +import processCss, { passthrough } from './_processCss'; test( - 'should merge based on declarations', - processCss, - 'h1{display:block}h2{display:block}', - 'h1,h2{display:block}', + 'should merge based on declarations', + processCss, + 'h1{display:block}h2{display:block}', + 'h1,h2{display:block}' ); test( - 'should merge based on declarations (2)', - processCss, - 'h1{color:red;line-height:1.5;font-size:2em;}h2{color:red;line-height:1.5;font-size:2em;}', - 'h1,h2{color:red;line-height:1.5;font-size:2em}', - // Switch back once css-declaration-sorter has been fixed - // 'h1,h2{color:red;font-size:2em;line-height:1.5}', + 'should merge based on declarations (2)', + processCss, + 'h1{color:red;line-height:1.5;font-size:2em;}h2{color:red;line-height:1.5;font-size:2em;}', + 'h1,h2{color:red;line-height:1.5;font-size:2em}' + // Switch back once css-declaration-sorter has been fixed + // 'h1,h2{color:red;font-size:2em;line-height:1.5}', ); test( - 'should merge based on declarations, with a different property order', - processCss, - 'h1{color:red;line-height:1.5;font-size:2em;}h2{font-size:2em;color:red;line-height:1.5;}', - 'h1,h2{color:red;line-height:1.5;font-size:2em}', - // Switch back once css-declaration-sorter has been fixed - // 'h1,h2{color:red;font-size:2em;line-height:1.5}', + 'should merge based on declarations, with a different property order', + processCss, + 'h1{color:red;line-height:1.5;font-size:2em;}h2{font-size:2em;color:red;line-height:1.5;}', + 'h1,h2{color:red;line-height:1.5;font-size:2em}' + // Switch back once css-declaration-sorter has been fixed + // 'h1,h2{color:red;font-size:2em;line-height:1.5}', ); test( - 'should merge based on selectors', - processCss, - 'h1{display:block}h1{text-decoration:underline}', - 'h1{display:block;text-decoration:underline}', + 'should merge based on selectors', + processCss, + 'h1{display:block}h1{text-decoration:underline}', + 'h1{display:block;text-decoration:underline}' ); test( - 'should merge based on selectors (2)', - processCss, - 'h1{color:red;display:block}h1{text-decoration:underline}', - 'h1{color:red;display:block;text-decoration:underline}', + 'should merge based on selectors (2)', + processCss, + 'h1{color:red;display:block}h1{text-decoration:underline}', + 'h1{color:red;display:block;text-decoration:underline}' ); test( - 'should merge based on selectors (3)', - processCss, - 'h1{font-size:2em;color:#000}h1{background:#fff;line-height:1.5;}', - 'h1{font-size:2em;color:#000;background:#fff;line-height:1.5}', - // Switch back once css-declaration-sorter has been fixed - // 'h1{background:#fff;color:#000;font-size:2em;line-height:1.5}', + 'should merge based on selectors (3)', + processCss, + 'h1{font-size:2em;color:#000}h1{background:#fff;line-height:1.5;}', + 'h1{font-size:2em;color:#000;background:#fff;line-height:1.5}' + // Switch back once css-declaration-sorter has been fixed + // 'h1{background:#fff;color:#000;font-size:2em;line-height:1.5}', ); test( - 'should merge in media queries', - processCss, - '@media print{h1{display:block;}h1{color:red;}}', - '@media print{h1{display:block;color:red}}', - // Switch back once css-declaration-sorter has been fixed - // '@media print{h1{color:red;display:block}}', + 'should merge in media queries', + processCss, + '@media print{h1{display:block;}h1{color:red;}}', + '@media print{h1{display:block;color:red}}' + // Switch back once css-declaration-sorter has been fixed + // '@media print{h1{color:red;display:block}}', ); test( - 'should merge in media queries (2)', - processCss, - '@media print{h1{display:block}p{display:block}}', - '@media print{h1,p{display:block}}', + 'should merge in media queries (2)', + processCss, + '@media print{h1{display:block}p{display:block}}', + '@media print{h1,p{display:block}}' ); test( - 'should merge in media queries (3)', - processCss, - '@media print{h1{color:red;text-decoration:none}h2{text-decoration:none}}h3{text-decoration:none}', - '@media print{h1{color:red}h1,h2{text-decoration:none}}h3{text-decoration:none}', + 'should merge in media queries (3)', + processCss, + '@media print{h1{color:red;text-decoration:none}h2{text-decoration:none}}h3{text-decoration:none}', + '@media print{h1{color:red}h1,h2{text-decoration:none}}h3{text-decoration:none}' ); test( - 'should merge in media queries (4)', - processCss, - 'h3{text-decoration:none}@media print{h1{color:red;text-decoration:none}h2{text-decoration:none}}', - 'h3{text-decoration:none}@media print{h1{color:red}h1,h2{text-decoration:none}}', + 'should merge in media queries (4)', + processCss, + 'h3{text-decoration:none}@media print{h1{color:red;text-decoration:none}h2{text-decoration:none}}', + 'h3{text-decoration:none}@media print{h1{color:red}h1,h2{text-decoration:none}}' ); test( - 'should not merge across media queries', - processCss, - '@media screen and (max-width:480px){h1{display:block}}@media screen and (min-width:480px){h2{display:block}}', - '@media screen and (max-width:480px){h1{display:block}}@media screen and (min-width:480px){h2{display:block}}', + 'should not merge across media queries', + processCss, + '@media screen and (max-width:480px){h1{display:block}}@media screen and (min-width:480px){h2{display:block}}', + '@media screen and (max-width:480px){h1{display:block}}@media screen and (min-width:480px){h2{display:block}}' ); test( - 'should not merge across media queries (2)', - processCss, - '@media screen and (max-width:200px){h1{color:red}}@media screen and (min-width:480px){h1{display:block}}', - '@media screen and (max-width:200px){h1{color:red}}@media screen and (min-width:480px){h1{display:block}}', + 'should not merge across media queries (2)', + processCss, + '@media screen and (max-width:200px){h1{color:red}}@media screen and (min-width:480px){h1{display:block}}', + '@media screen and (max-width:200px){h1{color:red}}@media screen and (min-width:480px){h1{display:block}}' ); test( - 'should not merge in different contexts', - processCss, - 'h1{display:block}@media print{h1{color:red}}', - 'h1{display:block}@media print{h1{color:red}}', + 'should not merge in different contexts', + processCss, + 'h1{display:block}@media print{h1{color:red}}', + 'h1{display:block}@media print{h1{color:red}}' ); test( - 'should not merge in different contexts (2)', - processCss, - '@media print{h1{display:block}}h1{color:red}', - '@media print{h1{display:block}}h1{color:red}', + 'should not merge in different contexts (2)', + processCss, + '@media print{h1{display:block}}h1{color:red}', + '@media print{h1{display:block}}h1{color:red}' ); test( - 'should perform partial merging of selectors', - processCss, - 'h1{color:red}h2{color:red;text-decoration:underline}', - 'h1,h2{color:red}h2{text-decoration:underline}', + 'should perform partial merging of selectors', + processCss, + 'h1{color:red}h2{color:red;text-decoration:underline}', + 'h1,h2{color:red}h2{text-decoration:underline}' ); test( - 'should perform partial merging of selectors (2)', - processCss, - 'h1{color:red}h2{color:red;text-decoration:underline}h3{color:green;text-decoration:underline}', - 'h1,h2{color:red}h2,h3{text-decoration:underline}h3{color:green}', + 'should perform partial merging of selectors (2)', + processCss, + 'h1{color:red}h2{color:red;text-decoration:underline}h3{color:green;text-decoration:underline}', + 'h1,h2{color:red}h2,h3{text-decoration:underline}h3{color:green}' ); test( - 'should perform partial merging of selectors (3)', - processCss, - 'h1{color:red;text-decoration:underline}h2{text-decoration:underline;color:green}h3{font-weight:bold;color:green}', - 'h1{color:red}h1,h2{text-decoration:underline}h2,h3{color:green}h3{font-weight:700}', + 'should perform partial merging of selectors (3)', + processCss, + 'h1{color:red;text-decoration:underline}h2{text-decoration:underline;color:green}h3{font-weight:bold;color:green}', + 'h1{color:red}h1,h2{text-decoration:underline}h2,h3{color:green}h3{font-weight:700}' ); test( - 'should perform partial merging of selectors (4)', - processCss, - '.test0{color:red;border:none;margin:0}.test1{color:green;border:none;margin:0}', - '.test0{color:red}.test0,.test1{border:none;margin:0}.test1{color:green}', + 'should perform partial merging of selectors (4)', + processCss, + '.test0{color:red;border:none;margin:0}.test1{color:green;border:none;margin:0}', + '.test0{color:red}.test0,.test1{border:none;margin:0}.test1{color:green}' ); test( - 'should perform partial merging of selectors (5)', - processCss, - 'h1{color:red;font-weight:bold}h2{font-weight:bold}h3{text-decoration:none}', - 'h1{color:red}h1,h2{font-weight:700}h3{text-decoration:none}', + 'should perform partial merging of selectors (5)', + processCss, + 'h1{color:red;font-weight:bold}h2{font-weight:bold}h3{text-decoration:none}', + 'h1{color:red}h1,h2{font-weight:700}h3{text-decoration:none}' ); test( - 'should perform partial merging of selectors (6)', - processCss, - '.test-1,.test-2{margin-top:10px}.another-test{margin-top:10px;margin-bottom:30px}', - '.another-test,.test-1,.test-2{margin-top:10px}.another-test{margin-bottom:30px}', + 'should perform partial merging of selectors (6)', + processCss, + '.test-1,.test-2{margin-top:10px}.another-test{margin-top:10px;margin-bottom:30px}', + '.another-test,.test-1,.test-2{margin-top:10px}.another-test{margin-bottom:30px}' ); test( - 'should perform partial merging of selectors (7)', - processCss, - '.test-1{margin-top:10px;margin-bottom:20px}.test-2{margin-top:10px}.another-test{margin-top:10px;margin-bottom:30px}', - '.test-1{margin-bottom:20px}.another-test,.test-1,.test-2{margin-top:10px}.another-test{margin-bottom:30px}', + 'should perform partial merging of selectors (7)', + processCss, + '.test-1{margin-top:10px;margin-bottom:20px}.test-2{margin-top:10px}.another-test{margin-top:10px;margin-bottom:30px}', + '.test-1{margin-bottom:20px}.another-test,.test-1,.test-2{margin-top:10px}.another-test{margin-bottom:30px}' ); test( - 'should perform partial merging of selectors in the opposite direction', - processCss, - 'h1{color:black}h2{color:black;font-weight:bold}h3{color:black;font-weight:bold}', - 'h1{color:#000}h2,h3{color:#000;font-weight:700}', + 'should perform partial merging of selectors in the opposite direction', + processCss, + 'h1{color:black}h2{color:black;font-weight:bold}h3{color:black;font-weight:bold}', + 'h1{color:#000}h2,h3{color:#000;font-weight:700}' ); test( - 'should not perform partial merging of selectors if the output would be longer', - processCss, - '.test0{color:red;border:none;margin:0;}.longlonglonglong{color:green;border:none;margin:0;}', - '.test0{color:red;border:none;margin:0}.longlonglonglong{color:green;border:none;margin:0}', + 'should not perform partial merging of selectors if the output would be longer', + processCss, + '.test0{color:red;border:none;margin:0;}.longlonglonglong{color:green;border:none;margin:0;}', + '.test0{color:red;border:none;margin:0}.longlonglonglong{color:green;border:none;margin:0}' ); test( - 'should merge vendor prefixed selectors when vendors are the same', - processCss, - 'code ::-moz-selection{background:red}code::-moz-selection{background:red}', - 'code::-moz-selection,code ::-moz-selection{background:red}', + 'should merge vendor prefixed selectors when vendors are the same', + processCss, + 'code ::-moz-selection{background:red}code::-moz-selection{background:red}', + 'code::-moz-selection,code ::-moz-selection{background:red}' ); test( - 'should not merge mixed vendor prefixes', - processCss, - 'code ::-webkit-selection{background:red}code::-moz-selection{background:red}', - 'code ::-webkit-selection{background:red}code::-moz-selection{background:red}', + 'should not merge mixed vendor prefixes', + processCss, + 'code ::-webkit-selection{background:red}code::-moz-selection{background:red}', + 'code ::-webkit-selection{background:red}code::-moz-selection{background:red}' ); test( - 'should not merge mixed vendor prefixed and non-vendor prefixed', - processCss, - 'code ::selection{background:red}code ::-moz-selection{background:red}', - 'code ::selection{background:red}code ::-moz-selection{background:red}', + 'should not merge mixed vendor prefixed and non-vendor prefixed', + processCss, + 'code ::selection{background:red}code ::-moz-selection{background:red}', + 'code ::selection{background:red}code ::-moz-selection{background:red}' ); test( - 'should merge text-* properties', - processCss, - 'h1{color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline}', - 'h1{color:red}h1,h2{text-align:right;text-decoration:underline}', + 'should merge text-* properties', + processCss, + 'h1{color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline}', + 'h1{color:red}h1,h2{text-align:right;text-decoration:underline}' ); test( - 'should merge text-* properties (2)', - processCss, - 'h1{color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline;color:green}', - 'h1{color:red}h1,h2{text-align:right;text-decoration:underline}h2{color:green}', + 'should merge text-* properties (2)', + processCss, + 'h1{color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline;color:green}', + 'h1{color:red}h1,h2{text-align:right;text-decoration:underline}h2{color:green}' ); test( - 'should merge text-* properties (3)', - processCss, - 'h1{background:white;color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline;color:red}', - 'h1{background:#fff}h1,h2{color:red;text-align:right;text-decoration:underline}', + 'should merge text-* properties (3)', + processCss, + 'h1{background:white;color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline;color:red}', + 'h1{background:#fff}h1,h2{color:red;text-align:right;text-decoration:underline}' ); test( - 'should merge text-* properties (4)', - processCss, - 'h1{color:red;text-align:center;text-transform:small-caps}h2{text-align:center;color:red}', - 'h1{text-transform:small-caps}h1,h2{color:red;text-align:center}', + 'should merge text-* properties (4)', + processCss, + 'h1{color:red;text-align:center;text-transform:small-caps}h2{text-align:center;color:red}', + 'h1{text-transform:small-caps}h1,h2{color:red;text-align:center}' ); test( - 'should merge text-* properties (5)', - processCss, - 'h1{text-align:left;text-transform:small-caps}h2{text-align:right;text-transform:small-caps}', - 'h1{text-align:left}h1,h2{text-transform:small-caps}h2{text-align:right}', + 'should merge text-* properties (5)', + processCss, + 'h1{text-align:left;text-transform:small-caps}h2{text-align:right;text-transform:small-caps}', + 'h1{text-align:left}h1,h2{text-transform:small-caps}h2{text-align:right}' ); test( - 'should not incorrectly extract transform properties', - processCss, - '@keyframes a{0%{transform-origin:right bottom;transform:rotate(-90deg);opacity:0}100%{transform-origin:right bottom;transform:rotate(0);opacity:1}}a{animation:a}', - '@keyframes a{0%{transform-origin:right bottom;transform:rotate(-90deg);opacity:0}to{transform-origin:right bottom;transform:rotate(0);opacity:1}}a{animation:a}', - // Switch back once css-declaration-sorter has been fixed - // '@keyframes a{0%{opacity:0;transform:rotate(-90deg);transform-origin:right bottom}to{opacity:1;transform:rotate(0);transform-origin:right bottom}}a{animation:a}', + 'should not incorrectly extract transform properties', + processCss, + '@keyframes a{0%{transform-origin:right bottom;transform:rotate(-90deg);opacity:0}100%{transform-origin:right bottom;transform:rotate(0);opacity:1}}a{animation:a}', + '@keyframes a{0%{transform-origin:right bottom;transform:rotate(-90deg);opacity:0}to{transform-origin:right bottom;transform:rotate(0);opacity:1}}a{animation:a}' + // Switch back once css-declaration-sorter has been fixed + // '@keyframes a{0%{opacity:0;transform:rotate(-90deg);transform-origin:right bottom}to{opacity:1;transform:rotate(0);transform-origin:right bottom}}a{animation:a}', ); test( - 'should not incorrectly extract background properties', - passthrough, - '.iPhone{background:url(a.png);background-image:url(../../../sprites/c.png);background-repeat:no-repeat;background-position:-102px -74px}.logo{background:url(b.png);background-image:url(../../../sprites/c.png);background-repeat:no-repeat;background-position:-2px -146px}', - // Switch back once css-declaration-sorter has been fixed - // '.iPhone{background:url(a.png);background-image:url(../../../sprites/c.png);background-position:-102px -74px;background-repeat:no-repeat}.logo{background:url(b.png);background-image:url(../../../sprites/c.png);background-position:-2px -146px;background-repeat:no-repeat}', + 'should not incorrectly extract background properties', + passthrough, + '.iPhone{background:url(a.png);background-image:url(../../../sprites/c.png);background-repeat:no-repeat;background-position:-102px -74px}.logo{background:url(b.png);background-image:url(../../../sprites/c.png);background-repeat:no-repeat;background-position:-2px -146px}' + // Switch back once css-declaration-sorter has been fixed + // '.iPhone{background:url(a.png);background-image:url(../../../sprites/c.png);background-position:-102px -74px;background-repeat:no-repeat}.logo{background:url(b.png);background-image:url(../../../sprites/c.png);background-position:-2px -146px;background-repeat:no-repeat}', ); test( - 'should not incorrectly extract display properties', - processCss, - '.box1{display:inline-block;display:block}.box2{display:inline-block}', - '.box1{display:inline-block;display:block}.box2{display:inline-block}', + 'should not incorrectly extract display properties', + processCss, + '.box1{display:inline-block;display:block}.box2{display:inline-block}', + '.box1{display:inline-block;display:block}.box2{display:inline-block}' ); diff --git a/packages/cssnano/src/__tests__/postcss-minify-font-weight.js b/packages/cssnano/src/__tests__/postcss-minify-font-weight.js index 590d6dec3..b5ec8c2c9 100644 --- a/packages/cssnano/src/__tests__/postcss-minify-font-weight.js +++ b/packages/cssnano/src/__tests__/postcss-minify-font-weight.js @@ -2,22 +2,22 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should convert normal to 400', - processCss, - 'h1{font-weight: normal}', - 'h1{font-weight:400}', + 'should convert normal to 400', + processCss, + 'h1{font-weight: normal}', + 'h1{font-weight:400}' ); test( - 'should convert bold to 700', - processCss, - 'h1{font-weight: bold}', - 'h1{font-weight:700}', + 'should convert bold to 700', + processCss, + 'h1{font-weight: bold}', + 'h1{font-weight:700}' ); test( - 'should not update the font-style property', - processCss, - 'h1{font-style: normal}', - 'h1{font-style:normal}', + 'should not update the font-style property', + processCss, + 'h1{font-style: normal}', + 'h1{font-style:normal}' ); diff --git a/packages/cssnano/src/__tests__/postcss-minify-gradients.js b/packages/cssnano/src/__tests__/postcss-minify-gradients.js index e43bb2488..bd98467f8 100644 --- a/packages/cssnano/src/__tests__/postcss-minify-gradients.js +++ b/packages/cssnano/src/__tests__/postcss-minify-gradients.js @@ -2,106 +2,106 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'linear: should convert "to top" to 0deg', - processCss, - 'background:linear-gradient(to top,#ffe500,#121)', - 'background:linear-gradient(0deg,#ffe500,#121)', + 'linear: should convert "to top" to 0deg', + processCss, + 'background:linear-gradient(to top,#ffe500,#121)', + 'background:linear-gradient(0deg,#ffe500,#121)' ); test( - 'linear: should convert "to right" to 90deg', - processCss, - 'background:linear-gradient(to right,#ffe500,#121)', - 'background:linear-gradient(90deg,#ffe500,#121)', + 'linear: should convert "to right" to 90deg', + processCss, + 'background:linear-gradient(to right,#ffe500,#121)', + 'background:linear-gradient(90deg,#ffe500,#121)' ); test( - 'linear: should convert "to bottom" to 180deg', - processCss, - 'background:linear-gradient(to bottom,#ffe500,#121)', - 'background:linear-gradient(180deg,#ffe500,#121)', + 'linear: should convert "to bottom" to 180deg', + processCss, + 'background:linear-gradient(to bottom,#ffe500,#121)', + 'background:linear-gradient(180deg,#ffe500,#121)' ); test( - 'linear: should convert "to left" to 270deg', - processCss, - 'background:linear-gradient(to left,#ffe500,#121)', - 'background:linear-gradient(270deg,#ffe500,#121)', + 'linear: should convert "to left" to 270deg', + processCss, + 'background:linear-gradient(to left,#ffe500,#121)', + 'background:linear-gradient(270deg,#ffe500,#121)' ); test( - 'repeating-linear: should convert "to top" to 0deg', - processCss, - 'background:repeating-linear-gradient(to top,#ffe500,#121)', - 'background:repeating-linear-gradient(0deg,#ffe500,#121)', + 'repeating-linear: should convert "to top" to 0deg', + 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, - 'background:repeating-linear-gradient(to right,#ffe500,#121)', - 'background:repeating-linear-gradient(90deg,#ffe500,#121)', + 'repeating-linear: should convert "to right" to 90deg', + processCss, + 'background:repeating-linear-gradient(to right,#ffe500,#121)', + 'background:repeating-linear-gradient(90deg,#ffe500,#121)' ); test( - 'repeating-linear: should convert "to bottom" to 180deg', - processCss, - 'background:repeating-linear-gradient(to bottom,#ffe500,#121)', - 'background:repeating-linear-gradient(180deg,#ffe500,#121)', + 'repeating-linear: should convert "to bottom" to 180deg', + processCss, + 'background:repeating-linear-gradient(to bottom,#ffe500,#121)', + 'background:repeating-linear-gradient(180deg,#ffe500,#121)' ); test( - 'repeating-linear: should convert "to left" to 270deg', - processCss, - 'background:repeating-linear-gradient(to left,#ffe500,#121)', - 'background:repeating-linear-gradient(270deg,#ffe500,#121)', + 'repeating-linear: should convert "to left" to 270deg', + processCss, + 'background:repeating-linear-gradient(to left,#ffe500,#121)', + 'background:repeating-linear-gradient(270deg,#ffe500,#121)' ); test( - 'linear: should not convert "to top right" to an angle', - processCss, - 'background:linear-gradient(to top right,#ffe500,#121)', - 'background:linear-gradient(to top right,#ffe500,#121)', + 'linear: should not convert "to top right" to an angle', + processCss, + 'background:linear-gradient(to top right,#ffe500,#121)', + 'background:linear-gradient(to top right,#ffe500,#121)' ); test( - 'linear: should not convert "to bottom left" to an angle', - processCss, - 'background:linear-gradient(to bottom left,#ffe500,#121)', - 'background:linear-gradient(to bottom left,#ffe500,#121)', + 'linear: should not convert "to bottom left" to an angle', + processCss, + 'background:linear-gradient(to bottom left,#ffe500,#121)', + 'background:linear-gradient(to bottom left,#ffe500,#121)' ); test( - 'linear: should reduce length values if they are the same', - processCss, - 'background:linear-gradient(45deg,#ffe500 50%,#121 50%)', - 'background:linear-gradient(45deg,#ffe500 50%,#121 0)', + 'linear: should reduce length values if they are the same', + 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, - 'background:linear-gradient(45deg,#ffe500 50%,#121 25%)', - 'background:linear-gradient(45deg,#ffe500 50%,#121 0)', + 'linear: should reduce length values if they are less', + processCss, + 'background:linear-gradient(45deg,#ffe500 50%,#121 25%)', + 'background:linear-gradient(45deg,#ffe500 50%,#121 0)' ); test( - 'linear: should not reduce length values with different units', - processCss, - 'background:linear-gradient(45deg,#ffe500 25px,#121 20%)', - 'background:linear-gradient(45deg,#ffe500 25px,#121 20%)', + 'linear: should not reduce length values with different units', + processCss, + 'background:linear-gradient(45deg,#ffe500 25px,#121 20%)', + 'background:linear-gradient(45deg,#ffe500 25px,#121 20%)' ); test( - 'linear: should remove the (unnecessary) start/end length values', - processCss, - 'background:linear-gradient(#ffe500 0%,#121 100%)', - 'background:linear-gradient(#ffe500,#121)', + 'linear: should remove the (unnecessary) start/end length values', + processCss, + 'background:linear-gradient(#ffe500 0%,#121 100%)', + 'background:linear-gradient(#ffe500,#121)' ); test( - 'repeating-radial: should reduce length values if they are the same', - processCss, - 'background:repeating-radial-gradient(#121,#121 5px,#ffe500 5px,#ffe500 10px)', - 'background:repeating-radial-gradient(#121,#121 5px,#ffe500 0,#ffe500 10px)', + 'repeating-radial: should reduce length values if they are the same', + processCss, + 'background:repeating-radial-gradient(#121,#121 5px,#ffe500 5px,#ffe500 10px)', + 'background:repeating-radial-gradient(#121,#121 5px,#ffe500 0,#ffe500 10px)' ); diff --git a/packages/cssnano/src/__tests__/postcss-minify-params.js b/packages/cssnano/src/__tests__/postcss-minify-params.js index 3178c2021..c94a284fd 100644 --- a/packages/cssnano/src/__tests__/postcss-minify-params.js +++ b/packages/cssnano/src/__tests__/postcss-minify-params.js @@ -2,8 +2,8 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should normalise @media queries (2)', - processCss, - '@media only screen \n and ( min-width: 400px, min-height: 500px ){h1{color:#00f}}', - '@media only screen and (min-width:400px,min-height:500px){h1{color:#00f}}', + 'should normalise @media queries (2)', + processCss, + '@media only screen \n and ( min-width: 400px, min-height: 500px ){h1{color:#00f}}', + '@media only screen and (min-width:400px,min-height:500px){h1{color:#00f}}' ); diff --git a/packages/cssnano/src/__tests__/postcss-minify-selectors.js b/packages/cssnano/src/__tests__/postcss-minify-selectors.js index a1c15df6e..6e70e11a0 100644 --- a/packages/cssnano/src/__tests__/postcss-minify-selectors.js +++ b/packages/cssnano/src/__tests__/postcss-minify-selectors.js @@ -2,190 +2,190 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should trim spaces in simple selectors', - processCss, - 'h1, h2, h3{color:#00f}', - 'h1,h2,h3{color:#00f}', + 'should trim spaces in simple selectors', + processCss, + 'h1, h2, h3{color:#00f}', + 'h1,h2,h3{color:#00f}' ); test( - 'should trim spaces around combinators', - processCss, - 'h1 + p, h1 > p, h1 ~ p{color:#00f}', - 'h1+p,h1>p,h1~p{color:#00f}', + 'should trim spaces around combinators', + processCss, + 'h1 + p, h1 > p, h1 ~ p{color:#00f}', + 'h1+p,h1>p,h1~p{color:#00f}' ); test( - 'should not trim meaningful spaces', - processCss, - 'h1 p,h2 p{color:#00f}', - 'h1 p,h2 p{color:#00f}', + 'should not trim meaningful spaces', + processCss, + 'h1 p,h2 p{color:#00f}', + 'h1 p,h2 p{color:#00f}' ); test( - 'should reduce meaningful spaces', - processCss, - 'h1 p,h2 p{color:#00f}', - 'h1 p,h2 p{color:#00f}', + 'should reduce meaningful spaces', + processCss, + 'h1 p,h2 p{color:#00f}', + 'h1 p,h2 p{color:#00f}' ); test( - 'should remove qualified universal selectors', - processCss, - '*#id,*.test,*:not(.green),*[href]{color:#00f}', - '#id,.test,:not(.green),[href]{color:#00f}', + 'should remove qualified universal selectors', + processCss, + '*#id,*.test,*:not(.green),*[href]{color:#00f}', + '#id,.test,:not(.green),[href]{color:#00f}' ); test( - 'should remove complex qualified universal selectors', - processCss, - '[class] + *[href] *:not(*.green){color:#00f}', - '[class]+[href] :not(.green){color:#00f}', + 'should remove complex qualified universal selectors', + processCss, + '[class] + *[href] *:not(*.green){color:#00f}', + '[class]+[href] :not(.green){color:#00f}' ); test( - 'should remove complex qualified universal selectors (2)', - processCss, - '*:not(*.green) ~ *{color:#00f}', - ':not(.green)~*{color:#00f}', + 'should remove complex qualified universal selectors (2)', + processCss, + '*:not(*.green) ~ *{color:#00f}', + ':not(.green)~*{color:#00f}' ); test( - 'should not remove meaningful universal selectors', - processCss, - '* + *, * > *, * h1, * ~ *{color:#00f}', - '*+*,*>*,* h1,*~*{color:#00f}', + 'should not remove meaningful universal selectors', + processCss, + '* + *, * > *, * h1, * ~ *{color:#00f}', + '*+*,*>*,* h1,*~*{color:#00f}' ); test( - 'should preserve the universal selector in attribute selectors', - processCss, - 'h1[class=" *.js "] + *.js{color:#00f}', - 'h1[class=" *.js "]+.js{color:#00f}', + 'should preserve the universal selector in attribute selectors', + processCss, + 'h1[class=" *.js "] + *.js{color:#00f}', + 'h1[class=" *.js "]+.js{color:#00f}' ); test( - 'should preserve the universal selector in filenames', - processCss, - '[filename="*.js"]{color:#00f}', - '[filename="*.js"]{color:#00f}', + 'should preserve the universal selector in filenames', + processCss, + '[filename="*.js"]{color:#00f}', + '[filename="*.js"]{color:#00f}' ); test( - 'should preserve the universal selector in file globs', - processCss, - '[glob="/**/*.js"]{color:#00f}', - '[glob="/**/*.js"]{color:#00f}', + 'should preserve the universal selector in file globs', + processCss, + '[glob="/**/*.js"]{color:#00f}', + '[glob="/**/*.js"]{color:#00f}' ); test( - 'should preserve escaped zero plus sequences', - processCss, - '.\\31 0\\+,.\\31 5\\+,.\\32 0\\+{color:#00f}', - '.\\31 0\\+,.\\31 5\\+,.\\32 0\\+{color:#00f}', + 'should preserve escaped zero plus sequences', + processCss, + '.\\31 0\\+,.\\31 5\\+,.\\32 0\\+{color:#00f}', + '.\\31 0\\+,.\\31 5\\+,.\\32 0\\+{color:#00f}' ); test( - 'should handle deep combinators', - processCss, - 'body /deep/ .theme-element{color:#00f}', - 'body /deep/ .theme-element{color:#00f}', + 'should handle deep combinators', + processCss, + 'body /deep/ .theme-element{color:#00f}', + 'body /deep/ .theme-element{color:#00f}' ); test( - 'should sort using natural sort', - processCss, - '.item1, .item10, .item11, .item2{color:#00f}', - '.item1,.item2,.item10,.item11{color:#00f}', + 'should sort using natural sort', + processCss, + '.item1, .item10, .item11, .item2{color:#00f}', + '.item1,.item2,.item10,.item11{color:#00f}' ); test( - 'should dedupe selectors', - processCss, - 'h1,h2,h3,h4,h5,h5,h6{color:#00f}', - 'h1,h2,h3,h4,h5,h6{color:#00f}', + 'should dedupe selectors', + processCss, + 'h1,h2,h3,h4,h5,h5,h6{color:#00f}', + 'h1,h2,h3,h4,h5,h6{color:#00f}' ); test( - 'should trim spaces in :not()', - processCss, - 'h1:not(.article, .comments){color:#00f}', - 'h1:not(.article,.comments){color:#00f}', + 'should trim spaces in :not()', + processCss, + 'h1:not(.article, .comments){color:#00f}', + 'h1:not(.article,.comments){color:#00f}' ); test( - 'should trim spaces in :not() (2)', - processCss, - 'h1:not(.article, .comments), h2:not(.lead, .recommendation){color:#00f}', - 'h1:not(.article,.comments),h2:not(.lead,.recommendation){color:#00f}', + 'should trim spaces in :not() (2)', + processCss, + 'h1:not(.article, .comments), h2:not(.lead, .recommendation){color:#00f}', + 'h1:not(.article,.comments),h2:not(.lead,.recommendation){color:#00f}' ); test( - 'should dedupe simple selectors inside :not()', - processCss, - 'h1:not(h2, h3, h4, h5, h5, h6){color:#00f}', - 'h1:not(h2,h3,h4,h5,h6){color:#00f}', + 'should dedupe simple selectors inside :not()', + processCss, + 'h1:not(h2, h3, h4, h5, h5, h6){color:#00f}', + 'h1:not(h2,h3,h4,h5,h6){color:#00f}' ); test( - 'should normalise attribute selectors', - processCss, - 'a[ color= "blue" ]{color:#00f}', - 'a[color=blue]{color:#00f}', + 'should normalise attribute selectors', + processCss, + 'a[ color= "blue" ]{color:#00f}', + 'a[color=blue]{color:#00f}' ); test( - 'should normalise attribute selectors (2)', - processCss, - 'a[class^="options["]:after{color:#00f}', - 'a[class^="options["]:after{color:#00f}', + 'should normalise attribute selectors (2)', + processCss, + 'a[class^="options["]:after{color:#00f}', + 'a[class^="options["]:after{color:#00f}' ); test( - 'should normalise attribute selectors (3)', - processCss, - 'a[class="woop_woop_woop"]{color:#00f}', - 'a[class=woop_woop_woop]{color:#00f}', + 'should normalise attribute selectors (3)', + processCss, + 'a[class="woop_woop_woop"]{color:#00f}', + 'a[class=woop_woop_woop]{color:#00f}' ); test( - 'should normalise attribute selectors (4)', - processCss, - 'a[class="woop \\\nwoop woop"]{color:#00f}', - 'a[class="woop woop woop"]{color:#00f}', + 'should normalise attribute selectors (4)', + processCss, + 'a[class="woop \\\nwoop woop"]{color:#00f}', + 'a[class="woop woop woop"]{color:#00f}' ); test( - 'should not be responsible for normalising comments', - processCss, - 'h1 /*!test comment*/, h2{color:#00f}', - 'h1 /*!test comment*/,h2{color:#00f}', + 'should not be responsible for normalising comments', + processCss, + 'h1 /*!test comment*/, h2{color:#00f}', + 'h1 /*!test comment*/,h2{color:#00f}' ); test( - 'should not be responsible for normalising coments (2)', - processCss, - '/*!test comment*/h1, h2{color:#00f}', - '/*!test comment*/h1,h2{color:#00f}', + 'should not be responsible for normalising coments (2)', + processCss, + '/*!test comment*/h1, h2{color:#00f}', + '/*!test comment*/h1,h2{color:#00f}' ); test( - 'should not change strings', - processCss, - ':not([attr=" h1 a + b /* not a comment */ end of :not from 100% "]){color:#00f}', - ':not([attr=" h1 a + b /* not a comment */ end of :not from 100% "]){color:#00f}', + 'should not change strings', + processCss, + ':not([attr=" h1 a + b /* not a comment */ end of :not from 100% "]){color:#00f}', + ':not([attr=" h1 a + b /* not a comment */ end of :not from 100% "]){color:#00f}' ); test( - 'should not change strings (2)', - processCss, - ':not([attr=" h1 a + b /* not a comment */ not end of `:not`: ) from 100% "]){color:#00f}', - ':not([attr=" h1 a + b /* not a comment */ not end of `:not`: ) from 100% "]){color:#00f}', + 'should not change strings (2)', + processCss, + ':not([attr=" h1 a + b /* not a comment */ not end of `:not`: ) from 100% "]){color:#00f}', + ':not([attr=" h1 a + b /* not a comment */ not end of `:not`: ) from 100% "]){color:#00f}' ); test( - 'should not change strings (3)', - processCss, - '[a=":not( *.b, h1, h1 )"]{color:#00f}', - '[a=":not( *.b, h1, h1 )"]{color:#00f}', + 'should not change strings (3)', + processCss, + '[a=":not( *.b, h1, h1 )"]{color:#00f}', + '[a=":not( *.b, h1, h1 )"]{color:#00f}' ); diff --git a/packages/cssnano/src/__tests__/postcss-normalize-url.js b/packages/cssnano/src/__tests__/postcss-normalize-url.js index 5435bb58b..3a4b172ec 100644 --- a/packages/cssnano/src/__tests__/postcss-normalize-url.js +++ b/packages/cssnano/src/__tests__/postcss-normalize-url.js @@ -2,149 +2,149 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should strip double quotes', - processCss, - 'h1{background:url("cat.jpg")}', - 'h1{background:url(cat.jpg)}', + 'should strip double quotes', + processCss, + 'h1{background:url("cat.jpg")}', + 'h1{background:url(cat.jpg)}' ); test( - 'should strip single quotes', - processCss, - 'h1{background:url(\'cat.jpg\')}', - 'h1{background:url(cat.jpg)}', + 'should strip single quotes', + processCss, + "h1{background:url('cat.jpg')}", + 'h1{background:url(cat.jpg)}' ); test( - 'should escape special characters', - processCss, - 'h1{background:url("http://website.com/assets)_test.png")}', - 'h1{background:url(http://website.com/assets\\)_test.png)}', + 'should escape special characters', + processCss, + 'h1{background:url("http://website.com/assets)_test.png")}', + 'h1{background:url(http://website.com/assets\\)_test.png)}' ); test( - 'should not escape more than one special character', - processCss, - 'h1{background:url("http://website.com/assets_(test).png")}', - 'h1{background:url("http://website.com/assets_(test).png")}', + 'should not escape more than one special character', + processCss, + 'h1{background:url("http://website.com/assets_(test).png")}', + 'h1{background:url("http://website.com/assets_(test).png")}' ); test( - 'should remove the default port', - processCss, - 'h1{background:url(http://website.com:80/image.png)}', - 'h1{background:url(http://website.com/image.png)}', + 'should remove the default port', + processCss, + 'h1{background:url(http://website.com:80/image.png)}', + 'h1{background:url(http://website.com/image.png)}' ); test( - 'should not remove the fragment', - processCss, - 'h1{background:url(test.svg#icon)}', - 'h1{background:url(test.svg#icon)}', + 'should not remove the fragment', + processCss, + 'h1{background:url(test.svg#icon)}', + 'h1{background:url(test.svg#icon)}' ); test( - 'should not remove the fragment in absolute urls', - processCss, - 'h1{background:url(http://website.com/test.svg#icon)}', - 'h1{background:url(http://website.com/test.svg#icon)}', + 'should not remove the fragment in absolute urls', + processCss, + 'h1{background:url(http://website.com/test.svg#icon)}', + 'h1{background:url(http://website.com/test.svg#icon)}' ); test( - 'should normalize directory traversal', - processCss, - 'h1{background:url(http://website.com/assets/css/../font/t.eot)}', - 'h1{background:url(http://website.com/assets/font/t.eot)}', + 'should normalize directory traversal', + processCss, + 'h1{background:url(http://website.com/assets/css/../font/t.eot)}', + 'h1{background:url(http://website.com/assets/font/t.eot)}' ); test( - 'should normalize directory traversal in relative urls', - processCss, - 'h1{background:url(css/../font/t.eot)}', - 'h1{background:url(font/t.eot)}', + 'should normalize directory traversal in relative urls', + processCss, + 'h1{background:url(css/../font/t.eot)}', + 'h1{background:url(font/t.eot)}' ); test( - 'should trim current directory indicator in relative urls', - processCss, - 'h1{background:url(./images/cat.png)}', - 'h1{background:url(images/cat.png)}', + 'should trim current directory indicator in relative urls', + processCss, + 'h1{background:url(./images/cat.png)}', + 'h1{background:url(images/cat.png)}' ); test( - 'should do the above tests, stripping quotes', - processCss, - 'h1{background:url("./css/../font/t.eot")}', - 'h1{background:url(font/t.eot)}', + 'should do the above tests, stripping quotes', + processCss, + 'h1{background:url("./css/../font/t.eot")}', + 'h1{background:url(font/t.eot)}' ); test( - 'should normalize urls with special characters', - processCss, - 'h1{background:url("http://website.com/test/../(images)/1.png")}', - 'h1{background:url("http://website.com/(images)/1.png")}', + 'should normalize urls with special characters', + processCss, + 'h1{background:url("http://website.com/test/../(images)/1.png")}', + 'h1{background:url("http://website.com/(images)/1.png")}' ); test( - 'should normalize relative urls with special characters', - processCss, - 'h1{background:url("test/../(images)/1.png")}', - 'h1{background:url("(images)/1.png")}', + 'should normalize relative urls with special characters', + processCss, + 'h1{background:url("test/../(images)/1.png")}', + 'h1{background:url("(images)/1.png")}' ); test( - 'should minimise whitespace inside the url function', - processCss, - 'h1{background:url( test.png )}', - 'h1{background:url(test.png)}', + 'should minimise whitespace inside the url function', + processCss, + 'h1{background:url( test.png )}', + 'h1{background:url(test.png)}' ); test( - 'should minimise whitespace inside the url string', - processCss, - 'h1{background:url(" test.png ")}', - 'h1{background:url(test.png)}', + 'should minimise whitespace inside the url string', + processCss, + 'h1{background:url(" test.png ")}', + 'h1{background:url(test.png)}' ); test( - 'should minimise whitespace with special characters', - processCss, - 'h1{background:url(" test (2015).png ")}', - 'h1{background:url("test (2015).png")}', + 'should minimise whitespace with special characters', + processCss, + 'h1{background:url(" test (2015).png ")}', + 'h1{background:url("test (2015).png")}' ); test( - 'should join multiline url functions', - processCss, - 'h1{background:url("some really long string \\\nspanning multiple lines")}', - 'h1{background:url("some really long string spanning multiple lines")}', + 'should join multiline url functions', + processCss, + 'h1{background:url("some really long string \\\nspanning multiple lines")}', + 'h1{background:url("some really long string spanning multiple lines")}' ); test( - 'should process multiple backgrounds', - processCss, - 'h1{background:url( "./test/../foo/bar.jpg" ), url("http://website.com/img.jpg")}', - 'h1{background:url(foo/bar.jpg),url(http://website.com/img.jpg)}', + 'should process multiple backgrounds', + processCss, + 'h1{background:url( "./test/../foo/bar.jpg" ), url("http://website.com/img.jpg")}', + 'h1{background:url(foo/bar.jpg),url(http://website.com/img.jpg)}' ); test( - 'should not mangle data urls', - processCss, - '.has-svg:before{content:url("data:image/svg+xml;utf8,")}', - '.has-svg:before{content:url("data:image/svg+xml;utf8,")}', + 'should not mangle data urls', + processCss, + '.has-svg:before{content:url("data:image/svg+xml;utf8,")}', + '.has-svg:before{content:url("data:image/svg+xml;utf8,")}' ); test( - 'should optimise @namespace urls', - processCss, - '@namespace islands url("http://bar.yandex.ru/ui/islands");', - '@namespace islands "http://bar.yandex.ru/ui/islands";', - {discardUnused: {namespace: false}}, + 'should optimise @namespace urls', + processCss, + '@namespace islands url("http://bar.yandex.ru/ui/islands");', + '@namespace islands "http://bar.yandex.ru/ui/islands";', + { discardUnused: { namespace: false } } ); test( - 'should not normalize @document urls', - processCss, - '@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/){body{font-size:2em}}', - '@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/){body{font-size:2em}}', + 'should not normalize @document urls', + processCss, + '@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/){body{font-size:2em}}', + '@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/){body{font-size:2em}}' ); diff --git a/packages/cssnano/src/__tests__/postcss-reduce-transforms.js b/packages/cssnano/src/__tests__/postcss-reduce-transforms.js index 1ea160e7e..f2e5fc999 100644 --- a/packages/cssnano/src/__tests__/postcss-reduce-transforms.js +++ b/packages/cssnano/src/__tests__/postcss-reduce-transforms.js @@ -2,113 +2,113 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should shorten matrix3d(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1) to matrix(a, b, c, d, tx, ty)', - processCss, - 'h1{transform:matrix3d(20, 20, 0, 0, 40, 40, 0, 0, 0, 0, 1, 0, 80, 80, 0, 1)}', - 'h1{transform:matrix(20,20,40,40,80,80)}', + 'should shorten matrix3d(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1) to matrix(a, b, c, d, tx, ty)', + processCss, + 'h1{transform:matrix3d(20, 20, 0, 0, 40, 40, 0, 0, 0, 0, 1, 0, 80, 80, 0, 1)}', + 'h1{transform:matrix(20,20,40,40,80,80)}' ); test( - 'should shorten rotateZ to rotate', - processCss, - 'h1{transform:rotateZ(180deg)}', - 'h1{transform:rotate(180deg)}', + 'should shorten rotateZ to rotate', + processCss, + 'h1{transform:rotateZ(180deg)}', + 'h1{transform:rotate(180deg)}' ); test( - 'should shorten rotate3d(1, 0, 0, a) to rotateX(a)', - processCss, - 'h1{transform:rotate3d(1, 0, 0, 20deg)}', - 'h1{transform:rotateX(20deg)}', + 'should shorten rotate3d(1, 0, 0, a) to rotateX(a)', + processCss, + 'h1{transform:rotate3d(1, 0, 0, 20deg)}', + 'h1{transform:rotateX(20deg)}' ); test( - 'should shorten rotate3d(0, 1, 0, a) to rotateY(a)', - processCss, - 'h1{transform:rotate3d(0, 1, 0, 20deg)}', - 'h1{transform:rotateY(20deg)}', + 'should shorten rotate3d(0, 1, 0, a) to rotateY(a)', + processCss, + 'h1{transform:rotate3d(0, 1, 0, 20deg)}', + 'h1{transform:rotateY(20deg)}' ); test( - 'should shorten rotate3d(0, 0, 1, a) to rotate(a)', - processCss, - 'h1{transform:rotate3d(0, 0, 1, 20deg)}', - 'h1{transform:rotate(20deg)}', + 'should shorten rotate3d(0, 0, 1, a) to rotate(a)', + processCss, + 'h1{transform:rotate3d(0, 0, 1, 20deg)}', + 'h1{transform:rotate(20deg)}' ); test( - 'should shorten scale(sx, sy) to scale(sx)', - processCss, - 'h1{transform:scale(1.5, 1.5)}', - 'h1{transform:scale(1.5)}', + 'should shorten scale(sx, sy) to scale(sx)', + processCss, + 'h1{transform:scale(1.5, 1.5)}', + 'h1{transform:scale(1.5)}' ); test( - 'should shorten scale(sx, 1) to scaleX(sx)', - processCss, - 'h1{transform:scale(1.5, 1)}', - 'h1{transform:scaleX(1.5)}', + 'should shorten scale(sx, 1) to scaleX(sx)', + processCss, + 'h1{transform:scale(1.5, 1)}', + 'h1{transform:scaleX(1.5)}' ); test( - 'should shorten scale(1, sy) to scaleY(sy)', - processCss, - 'h1{transform:scale(1, 1.5)}', - 'h1{transform:scaleY(1.5)}', + 'should shorten scale(1, sy) to scaleY(sy)', + processCss, + 'h1{transform:scale(1, 1.5)}', + 'h1{transform:scaleY(1.5)}' ); test( - 'should shorten scale3d(sx, 1, 1) to scaleX(sx)', - processCss, - 'h1{transform:scale(1.5, 1, 1)}', - 'h1{transform:scaleX(1.5)}', + 'should shorten scale3d(sx, 1, 1) to scaleX(sx)', + processCss, + 'h1{transform:scale(1.5, 1, 1)}', + 'h1{transform:scaleX(1.5)}' ); test( - 'should shorten scale3d(1, sy, 1) to scaleY(sy)', - processCss, - 'h1{transform:scale(1, 1.5, 1)}', - 'h1{transform:scaleY(1.5)}', + 'should shorten scale3d(1, sy, 1) to scaleY(sy)', + processCss, + 'h1{transform:scale(1, 1.5, 1)}', + 'h1{transform:scaleY(1.5)}' ); test( - 'should shorten scale3d(1, 1, sz) to scaleZ(sz)', - processCss, - 'h1{transform:scale3d(1, 1, 1.5)}', - 'h1{transform:scaleZ(1.5)}', + 'should shorten scale3d(1, 1, sz) to scaleZ(sz)', + processCss, + 'h1{transform:scale3d(1, 1, 1.5)}', + 'h1{transform:scaleZ(1.5)}' ); test( - 'should not shorten translate(tx, ty) to translate(tx)', - processCss, - 'h1{transform:translate(5, 5)}', - 'h1{transform:translate(5,5)}', + 'should not shorten translate(tx, ty) to translate(tx)', + processCss, + 'h1{transform:translate(5, 5)}', + 'h1{transform:translate(5,5)}' ); test( - 'should shorten translate(tx, 0) to translate(tx)', - processCss, - 'h1{transform:translate(5, 0)}', - 'h1{transform:translate(5)}', + 'should shorten translate(tx, 0) to translate(tx)', + processCss, + 'h1{transform:translate(5, 0)}', + 'h1{transform:translate(5)}' ); test( - 'should shorten translate(0, ty) to translateY(ty)', - processCss, - 'h1{transform:translate(0, 5)}', - 'h1{transform:translateY(5)}', + 'should shorten translate(0, ty) to translateY(ty)', + processCss, + 'h1{transform:translate(0, 5)}', + 'h1{transform:translateY(5)}' ); test( - 'should shorten translate3d(0, 0, tz) to translateZ(tz)', - processCss, - 'h1{transform:translate3d(0, 0, 2)}', - 'h1{transform:translateZ(2)}', + 'should shorten translate3d(0, 0, tz) to translateZ(tz)', + processCss, + 'h1{transform:translate3d(0, 0, 2)}', + 'h1{transform:translateZ(2)}' ); test( - 'should work with vendor prefixes', - processCss, - 'h1{-webkit-transform:translate3d(0, 0, 0)}', - 'h1{-webkit-transform:translateZ(0)}', + 'should work with vendor prefixes', + processCss, + 'h1{-webkit-transform:translate3d(0, 0, 0)}', + 'h1{-webkit-transform:translateZ(0)}' ); diff --git a/packages/cssnano/src/__tests__/postcss-svgo.js b/packages/cssnano/src/__tests__/postcss-svgo.js index 5230adc00..558a819de 100644 --- a/packages/cssnano/src/__tests__/postcss-svgo.js +++ b/packages/cssnano/src/__tests__/postcss-svgo.js @@ -2,29 +2,29 @@ import test from 'ava'; import processCss from './_processCss'; test( - 'should optimise inline svg', - processCss, - 'h1{background:url(\'data:image/svg+xml;utf-8,\')}', - 'h1{background:url(\'data:image/svg+xml;charset=utf-8,\')}', + 'should optimise inline svg', + processCss, + 'h1{background:url(\'data:image/svg+xml;utf-8,\')}', + 'h1{background:url(\'data:image/svg+xml;charset=utf-8,\')}' ); test( - 'should optimise inline svg with standard charset definition', - processCss, - 'h1{background:url(\'data:image/svg+xml;charset=utf-8,\')}', - 'h1{background:url(\'data:image/svg+xml;charset=utf-8,\')}', + 'should optimise inline svg with standard charset definition', + processCss, + 'h1{background:url(\'data:image/svg+xml;charset=utf-8,\')}', + 'h1{background:url(\'data:image/svg+xml;charset=utf-8,\')}' ); test( - 'should optimise inline svg without charset definition', - processCss, - 'h1{background:url(\'data:image/svg+xml,\')}', - 'h1{background:url(\'data:image/svg+xml;charset=utf-8,\')}', + 'should optimise inline svg without charset definition', + processCss, + 'h1{background:url(\'data:image/svg+xml,\')}', + 'h1{background:url(\'data:image/svg+xml;charset=utf-8,\')}' ); test( - 'should optimise uri-encoded inline svg', - processCss, - 'h1{background:url(\'data:image/svg+xml;utf-8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20xml%3Aspace%3D%22preserve%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2240%22%20fill%3D%22yellow%22%20%2F%3E%3C!--test%20comment--%3E%3C%2Fsvg%3E\')}', - 'h1{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns=\'http://www.w3.org/2000/svg\'%3E%3Ccircle cx=\'50\' cy=\'50\' r=\'40\' fill=\'%23ff0\'/%3E%3C/svg%3E")}', + 'should optimise uri-encoded inline svg', + processCss, + "h1{background:url('data:image/svg+xml;utf-8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20xml%3Aspace%3D%22preserve%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2240%22%20fill%3D%22yellow%22%20%2F%3E%3C!--test%20comment--%3E%3C%2Fsvg%3E')}", + "h1{background:url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='50' cy='50' r='40' fill='%23ff0'/%3E%3C/svg%3E\")}" ); diff --git a/packages/cssnano/src/__tests__/presets.js b/packages/cssnano/src/__tests__/presets.js index ed59ece0c..983ae0d8f 100644 --- a/packages/cssnano/src/__tests__/presets.js +++ b/packages/cssnano/src/__tests__/presets.js @@ -3,107 +3,89 @@ import advancedPreset from 'cssnano-preset-advanced'; import defaultPreset from 'cssnano-preset-default'; import cssnano from '..'; -test('should accept an invoked preset', t => { - const preset = defaultPreset({normalizeCharset: {add: true}}); +test('should accept an invoked preset', (t) => { + const preset = defaultPreset({ normalizeCharset: { add: true } }); - return cssnano.process(`h1{content:"©"}`, {}, {preset}).then(result => { - t.is(result.css, `@charset "utf-8";h1{content:"©"}`); - }); + return cssnano.process(`h1{content:"©"}`, {}, { preset }).then((result) => { + t.is(result.css, `@charset "utf-8";h1{content:"©"}`); + }); }); -test('should accept a non-invoked preset', t => { - const preset = [ - defaultPreset, - {normalizeCharset: {add: true}}, - ]; +test('should accept a non-invoked preset', (t) => { + const preset = [defaultPreset, { normalizeCharset: { add: true } }]; - return cssnano.process(`h1{content:"©"}`, {}, {preset}).then(result => { - t.is(result.css, `@charset "utf-8";h1{content:"©"}`); - }); + return cssnano.process(`h1{content:"©"}`, {}, { preset }).then((result) => { + t.is(result.css, `@charset "utf-8";h1{content:"©"}`); + }); }); -test('should accept a default preset string', t => { - const preset = [ - 'default', - {normalizeCharset: {add: true}}, - ]; +test('should accept a default preset string', (t) => { + const preset = ['default', { normalizeCharset: { add: true } }]; - return cssnano.process(`h1{content:"©"}`, {}, {preset}).then(result => { - t.is(result.css, `@charset "utf-8";h1{content:"©"}`); - }); + return cssnano.process(`h1{content:"©"}`, {}, { preset }).then((result) => { + t.is(result.css, `@charset "utf-8";h1{content:"©"}`); + }); }); -test('should accept an invoked preset other than default', t => { - const preset = advancedPreset({zindex: {startIndex: 15}}); +test('should accept an invoked preset other than default', (t) => { + const preset = advancedPreset({ zindex: { startIndex: 15 } }); - return cssnano.process(`h1{z-index:10}`, {}, {preset}).then(result => { - t.is(result.css, `h1{z-index:15}`); - }); + return cssnano.process(`h1{z-index:10}`, {}, { preset }).then((result) => { + t.is(result.css, `h1{z-index:15}`); + }); }); -test('should accept a preset string other than default', t => { - const preset = 'cssnano-preset-advanced'; +test('should accept a preset string other than default', (t) => { + const preset = 'cssnano-preset-advanced'; - return cssnano.process(`h1{z-index:10}`, {}, {preset}).then(result => { - t.is(result.css, `h1{z-index:1}`); - }); + return cssnano.process(`h1{z-index:10}`, {}, { preset }).then((result) => { + t.is(result.css, `h1{z-index:1}`); + }); }); -test('should accept a preset string other than default, with options', t => { - const preset = [ - 'cssnano-preset-advanced', - {zindex: {startIndex: 15}}, - ]; +test('should accept a preset string other than default, with options', (t) => { + const preset = ['cssnano-preset-advanced', { zindex: { startIndex: 15 } }]; - return cssnano.process(`h1{z-index:10}`, {}, {preset}).then(result => { - t.is(result.css, `h1{z-index:15}`); - }); + return cssnano.process(`h1{z-index:10}`, {}, { preset }).then((result) => { + t.is(result.css, `h1{z-index:15}`); + }); }); -test('should accept a preset string other than default (sugar syntax)', t => { - const preset = [ - 'advanced', - {zindex: {startIndex: 15}}, - ]; +test('should accept a preset string other than default (sugar syntax)', (t) => { + const preset = ['advanced', { zindex: { startIndex: 15 } }]; - return cssnano.process(`h1{z-index:10}`, {}, {preset}).then(result => { - t.is(result.css, `h1{z-index:15}`); - }); + return cssnano.process(`h1{z-index:10}`, {}, { preset }).then((result) => { + t.is(result.css, `h1{z-index:15}`); + }); }); -test('should be able to exclude plugins', t => { - const preset = [ - 'advanced', - {zindex: false}, - ]; +test('should be able to exclude plugins', (t) => { + const preset = ['advanced', { zindex: false }]; - return cssnano.process(`h1{z-index:10}`, {}, {preset}).then(result => { - t.is(result.css, `h1{z-index:10}`); - }); + return cssnano.process(`h1{z-index:10}`, {}, { preset }).then((result) => { + t.is(result.css, `h1{z-index:10}`); + }); }); -test('should be able to include plugins', t => { - const preset = [ - 'advanced', - {zindex: true}, - ]; +test('should be able to include plugins', (t) => { + const preset = ['advanced', { zindex: true }]; - return cssnano.process(`h1{z-index:10}`, {}, {preset}).then(result => { - t.is(result.css, `h1{z-index:1}`); - }); + return cssnano.process(`h1{z-index:10}`, {}, { preset }).then((result) => { + t.is(result.css, `h1{z-index:1}`); + }); }); -test('should be able to exclude plugins (exclude syntax)', t => { - const preset = [ - 'advanced', - {zindex: {startIndex: 15, exclude: true}}, - ]; +test('should be able to exclude plugins (exclude syntax)', (t) => { + const preset = ['advanced', { zindex: { startIndex: 15, exclude: true } }]; - return cssnano.process(`h1{z-index:10}`, {}, {preset}).then(result => { - t.is(result.css, `h1{z-index:10}`); - }); + return cssnano.process(`h1{z-index:10}`, {}, { preset }).then((result) => { + t.is(result.css, `h1{z-index:10}`); + }); }); -test('should error on a bad preset', t => { - return t.throws(cssnano.process('h1{}', {}, {preset: 'avanced'}).then(() => {}), Error); +test('should error on a bad preset', (t) => { + return t.throws( + cssnano.process('h1{}', {}, { preset: 'avanced' }).then(() => {}), + Error + ); }); diff --git a/packages/cssnano/src/__tests__/webpack.js b/packages/cssnano/src/__tests__/webpack.js index 8205fbceb..d169ed694 100644 --- a/packages/cssnano/src/__tests__/webpack.js +++ b/packages/cssnano/src/__tests__/webpack.js @@ -4,15 +4,15 @@ import conf from './_webpack.config'; // test.cb -test.skip('cssnano should be consumed by webpack', t => { - webpack(conf, (err, stats) => { - if (err) { - t.fail(); - throw err; - } +test.skip('cssnano should be consumed by webpack', (t) => { + webpack(conf, (err, stats) => { + if (err) { + t.fail(); + throw err; + } - t.falsy(stats.hasErrors(), 'should not report any error'); - t.falsy(stats.hasWarnings(), 'should not report any warning'); - t.end(); - }); + t.falsy(stats.hasErrors(), 'should not report any error'); + t.falsy(stats.hasWarnings(), 'should not report any warning'); + t.end(); + }); }); diff --git a/packages/cssnano/src/index.js b/packages/cssnano/src/index.js index b5513c230..f4ac5c0d7 100644 --- a/packages/cssnano/src/index.js +++ b/packages/cssnano/src/index.js @@ -5,25 +5,23 @@ import isResolvable from 'is-resolvable'; const cssnano = 'cssnano'; -function initializePlugin (plugin, css, result) { - if (Array.isArray(plugin)) { - const [processor, opts] = plugin; - if ( - typeof opts === 'undefined' || - (typeof opts === 'object' && !opts.exclude) || - (typeof opts === 'boolean' && opts === true) - ) { - return Promise.resolve( - processor(opts)(css, result) - ); - } - } else { - return Promise.resolve( - plugin()(css, result) - ); +function initializePlugin(plugin, css, result) { + if (Array.isArray(plugin)) { + const [processor, opts] = plugin; + + if ( + typeof opts === 'undefined' || + (typeof opts === 'object' && !opts.exclude) || + (typeof opts === 'boolean' && opts === true) + ) { + return Promise.resolve(processor(opts)(css, result)); } - // Handle excluded plugins - return Promise.resolve(); + } else { + return Promise.resolve(plugin()(css, result)); + } + + // Handle excluded plugins + return Promise.resolve(); } /* @@ -34,38 +32,48 @@ function initializePlugin (plugin, css, result) { * preset = {plugins: []} <- already invoked function */ -function resolvePreset (preset) { - let fn, options; - if (Array.isArray(preset)) { - fn = preset[0]; - options = preset[1]; - } else { - fn = preset; - options = {}; - } - // For JS setups where we invoked the preset already - if (preset.plugins) { - return Promise.resolve(preset.plugins); - } - // Provide an alias for the default preset, as it is built-in. - if (fn === 'default') { - return Promise.resolve(require('cssnano-preset-default')(options).plugins); - } - // For non-JS setups; we'll need to invoke the preset ourselves. - if (typeof fn === 'function') { - return Promise.resolve(fn(options).plugins); - } - // Try loading a preset from node_modules - if (isResolvable(fn)) { - return Promise.resolve(require(fn)(options).plugins); - } - const sugar = `cssnano-preset-${fn}`; - // Try loading a preset from node_modules (sugar) - if (isResolvable(sugar)) { - return Promise.resolve(require(sugar)(options).plugins); - } - // If all else fails, we probably have a typo in the config somewhere - throw new Error(`Cannot load preset "${fn}". Please check your configuration for errors and try again.`); +function resolvePreset(preset) { + let fn, options; + + if (Array.isArray(preset)) { + fn = preset[0]; + options = preset[1]; + } else { + fn = preset; + options = {}; + } + + // For JS setups where we invoked the preset already + if (preset.plugins) { + return Promise.resolve(preset.plugins); + } + + // Provide an alias for the default preset, as it is built-in. + if (fn === 'default') { + return Promise.resolve(require('cssnano-preset-default')(options).plugins); + } + + // For non-JS setups; we'll need to invoke the preset ourselves. + if (typeof fn === 'function') { + return Promise.resolve(fn(options).plugins); + } + + // Try loading a preset from node_modules + if (isResolvable(fn)) { + return Promise.resolve(require(fn)(options).plugins); + } + + const sugar = `cssnano-preset-${fn}`; + + // Try loading a preset from node_modules (sugar) + if (isResolvable(sugar)) { + return Promise.resolve(require(sugar)(options).plugins); + } + + // If all else fails, we probably have a typo in the config somewhere + throw new Error( + `Cannot load preset "${fn}". Please check your configuration for errors and try again.` + ); } /* @@ -74,39 +82,40 @@ function resolvePreset (preset) { * load an external file. */ -function resolveConfig (css, result, options) { - if (options.preset) { - return resolvePreset(options.preset); - } +function resolveConfig(css, result, options) { + if (options.preset) { + return resolvePreset(options.preset); + } - const inputFile = css.source && css.source.input && css.source.input.file; - let searchPath = inputFile ? path.dirname(inputFile) : process.cwd(); - let configPath = null; + const inputFile = css.source && css.source.input && css.source.input.file; + let searchPath = inputFile ? path.dirname(inputFile) : process.cwd(); + let configPath = null; - if (options.configFile) { - searchPath = null; - configPath = path.resolve(process.cwd(), options.configFile); - } + if (options.configFile) { + searchPath = null; + configPath = path.resolve(process.cwd(), options.configFile); + } - const configExplorer = cosmiconfig(cssnano); - const searchForConfig = configPath - ? configExplorer.load(configPath) - : configExplorer.search(searchPath); + const configExplorer = cosmiconfig(cssnano); + const searchForConfig = configPath + ? configExplorer.load(configPath) + : configExplorer.search(searchPath); - return searchForConfig.then(config => { - if (config === null) { - return resolvePreset('default'); - } - return resolvePreset(config.config.preset || config.config); - }); + return searchForConfig.then((config) => { + if (config === null) { + return resolvePreset('default'); + } + + return resolvePreset(config.config.preset || config.config); + }); } export default postcss.plugin(cssnano, (options = {}) => { - return (css, result) => { - return resolveConfig(css, result, options).then((plugins) => { - return plugins.reduce((promise, plugin) => { - return promise.then(initializePlugin.bind(null, plugin, css, result)); - }, Promise.resolve()); - }); - }; + return (css, result) => { + return resolveConfig(css, result, options).then((plugins) => { + return plugins.reduce((promise, plugin) => { + return promise.then(initializePlugin.bind(null, plugin, css, result)); + }, Promise.resolve()); + }); + }; }); diff --git a/packages/example-cli-usage/postcss.config.js b/packages/example-cli-usage/postcss.config.js index a37877e08..897febe2e 100644 --- a/packages/example-cli-usage/postcss.config.js +++ b/packages/example-cli-usage/postcss.config.js @@ -1,5 +1,3 @@ module.exports = { - plugins: [ - require('cssnano')({preset: 'default'}), - ], + plugins: [require('cssnano')({ preset: 'default' })], }; diff --git a/packages/postcss-colormin/src/__tests__/colours.js b/packages/postcss-colormin/src/__tests__/colours.js index 2a6454e4f..84ab1fbd3 100644 --- a/packages/postcss-colormin/src/__tests__/colours.js +++ b/packages/postcss-colormin/src/__tests__/colours.js @@ -1,242 +1,182 @@ import test from 'ava'; import min from '../colours'; -function isEqual (t, input, output) { - t.deepEqual(min(input), output); +function isEqual(t, input, output) { + t.deepEqual(min(input), output); } -test( - 'should lowercase keywords', - isEqual, - 'RED', - 'red' -); +test('should lowercase keywords', isEqual, 'RED', 'red'); -test( - 'should convert shorthand hex to keyword', - isEqual, - '#f00', - 'red' -); +test('should convert shorthand hex to keyword', isEqual, '#f00', 'red'); -test( - 'should convert longhand hex to keyword', - isEqual, - '#ff0000', - 'red' -); +test('should convert longhand hex to keyword', isEqual, '#ff0000', 'red'); -test( - 'should convert rgb to keyword', - isEqual, - 'rgb(255,0,0)', - 'red' -); +test('should convert rgb to keyword', isEqual, 'rgb(255,0,0)', 'red'); test( - 'should convert fully opaque rgb to keyword', - isEqual, - 'rgba(255, 0, 0, 1)', - 'red' + 'should convert fully opaque rgb to keyword', + isEqual, + 'rgba(255, 0, 0, 1)', + 'red' ); -test( - 'should convert hsl to keyword', - isEqual, - 'hsl(0, 100%, 50%)', - 'red' -); +test('should convert hsl to keyword', isEqual, 'hsl(0, 100%, 50%)', 'red'); test( - 'should convert fully oqaque hsl to keyword', - isEqual, - 'hsla(0, 100%, 50%, 1)', - 'red' + 'should convert fully oqaque hsl to keyword', + isEqual, + 'hsla(0, 100%, 50%, 1)', + 'red' ); test( - 'should convert translucent hsla to rgba', - isEqual, - 'hsla(0, 100%, 50%, .5)', - 'rgba(255, 0, 0, 0.5)' + 'should convert translucent hsla to rgba', + isEqual, + 'hsla(0, 100%, 50%, .5)', + 'rgba(255, 0, 0, 0.5)' ); test( - 'should convert longhand hex to shorthand, case insensitive', - isEqual, - '#FFFFFF', - '#fff' + 'should convert longhand hex to shorthand, case insensitive', + isEqual, + '#FFFFFF', + '#fff' ); test( - 'should convert keyword to hex, case insensitive', - isEqual, - 'WHiTE', - '#fff' + 'should convert keyword to hex, case insensitive', + isEqual, + 'WHiTE', + '#fff' ); -test( - 'should convert keyword to hex', - isEqual, - 'yellow', - '#ff0' -); +test('should convert keyword to hex', isEqual, 'yellow', '#ff0'); -test( - 'should convert rgb to hex', - isEqual, - 'rgb(12, 134, 29)', - '#0c861d' -); +test('should convert rgb to hex', isEqual, 'rgb(12, 134, 29)', '#0c861d'); -test( - 'should convert hsl to hex', - isEqual, - 'hsl(230, 50%, 40%)', - '#349' -); +test('should convert hsl to hex', isEqual, 'hsl(230, 50%, 40%)', '#349'); test( - 'should convert another longhand hex to keyword', - isEqual, - '#000080', - 'navy' + 'should convert another longhand hex to keyword', + isEqual, + '#000080', + 'navy' ); test( - 'should convert rgba to hsla when shorter', - isEqual, - 'rgba(221, 221, 221, 0.5)', - 'hsla(0, 0%, 86.7%, 0.5)' + 'should convert rgba to hsla when shorter', + isEqual, + 'rgba(221, 221, 221, 0.5)', + 'hsla(0, 0%, 86.7%, 0.5)' ); test( - 'should convert this specific rgba value to "transparent"', - isEqual, - 'rgba(0,0,0,0)', - 'transparent' + 'should convert this specific rgba value to "transparent"', + isEqual, + 'rgba(0,0,0,0)', + 'transparent' ); test( - 'should convert this specific hsla value to "transparent"', - isEqual, - 'hsla(0,0%,0%,0)', - 'transparent' + 'should convert this specific hsla value to "transparent"', + isEqual, + 'hsla(0,0%,0%,0)', + 'transparent' ); test( - 'should convert hsla values with 0 saturation & 0 lightness to "transparent"', - isEqual, - 'hsla(200,0%,0%,0)', - 'transparent' + 'should convert hsla values with 0 saturation & 0 lightness to "transparent"', + isEqual, + 'hsla(200,0%,0%,0)', + 'transparent' ); test( - 'should leave transparent as it is', - isEqual, - 'transparent', - 'transparent' + 'should leave transparent as it is', + isEqual, + 'transparent', + 'transparent' ); test( - 'should prefer to output hex rather than keywords when they are the same length', - isEqual, - '#696969', - '#696969' + 'should prefer to output hex rather than keywords when they are the same length', + isEqual, + '#696969', + '#696969' ); -test( - 'should cap values at their maximum', - isEqual, - 'rgb(400,400,400)', - '#fff' -); +test('should cap values at their maximum', isEqual, 'rgb(400,400,400)', '#fff'); test( - 'should continue hsl value rotation', - isEqual, - 'hsl(400, 400%, 50%)', - '#fa0' + 'should continue hsl value rotation', + isEqual, + 'hsl(400, 400%, 50%)', + '#fa0' ); test( - 'should convert signed numbers', - isEqual, - 'rgba(-100,0,-100,.5)', - 'rgba(0, 0, 0, 0.5)' + 'should convert signed numbers', + isEqual, + 'rgba(-100,0,-100,.5)', + 'rgba(0, 0, 0, 0.5)' ); test( - 'should convert signed numbers (2)', - isEqual, - 'hsla(-400,50%,10%,.5)', - 'rgba(38, 13, 30, 0.5)' + 'should convert signed numbers (2)', + isEqual, + 'hsla(-400,50%,10%,.5)', + 'rgba(38, 13, 30, 0.5)' ); test( - 'should convert percentage based rgb values', - isEqual, - 'rgb(100%,100%,100%)', - '#fff' + 'should convert percentage based rgb values', + isEqual, + 'rgb(100%,100%,100%)', + '#fff' ); test( - 'should convert percentage based rgba values (2)', - isEqual, - 'rgba(50%,50%,50%,0.5)', - 'hsla(0, 0%, 49.8%, 0.5)' + 'should convert percentage based rgba values (2)', + isEqual, + 'rgba(50%,50%,50%,0.5)', + 'hsla(0, 0%, 49.8%, 0.5)' ); test( - 'should convert percentage based rgba values (3)', - isEqual, - 'rgb(100%,100%,100%)', - '#fff' + 'should convert percentage based rgba values (3)', + isEqual, + 'rgb(100%,100%,100%)', + '#fff' ); test( - 'should convert percentage based rgba values (4)', - isEqual, - 'rgba(100%,100%,100%,0.5)', - 'hsla(0, 0%, 100%, 0.5)' + 'should convert percentage based rgba values (4)', + isEqual, + 'rgba(100%,100%,100%,0.5)', + 'hsla(0, 0%, 100%, 0.5)' ); test( - 'should convert percentage based rgba values (5)', - isEqual, - 'rgba(100%,64.7%,0%,.5)', - 'rgba(255, 165, 0, 0.5)' + 'should convert percentage based rgba values (5)', + isEqual, + 'rgba(100%,64.7%,0%,.5)', + 'rgba(255, 165, 0, 0.5)' ); test( - 'should pass through on invalid rgb functions', - isEqual, - 'rgb(50%,23,54)', - 'rgb(50%,23,54)' + 'should pass through on invalid rgb functions', + isEqual, + 'rgb(50%,23,54)', + 'rgb(50%,23,54)' ); -test( - 'should convert darkgray to a hex', - isEqual, - 'darkgray', - '#a9a9a9' -); +test('should convert darkgray to a hex', isEqual, 'darkgray', '#a9a9a9'); -test( - 'should convert 8 character hex codes', - isEqual, - '#000000FF', - '#000' -); +test('should convert 8 character hex codes', isEqual, '#000000FF', '#000'); -test( - 'should convert 4 character hex codes', - isEqual, - '#000F', - '#000' -); +test('should convert 4 character hex codes', isEqual, '#000F', '#000'); -test('should pass through if not recognised', t => { - t.deepEqual(min('Unrecognised'), 'Unrecognised'); - t.deepEqual(min('inherit'), 'inherit'); +test('should pass through if not recognised', (t) => { + t.deepEqual(min('Unrecognised'), 'Unrecognised'); + t.deepEqual(min('inherit'), 'inherit'); }); diff --git a/packages/postcss-colormin/src/__tests__/index.js b/packages/postcss-colormin/src/__tests__/index.js index 7cbf1695e..5e0f69baa 100644 --- a/packages/postcss-colormin/src/__tests__/index.js +++ b/packages/postcss-colormin/src/__tests__/index.js @@ -1,302 +1,294 @@ import test from 'ava'; import plugin from '..'; import { - usePostCSSPlugin, - processCSSFactory, - processCSSWithPresetFactory, + usePostCSSPlugin, + processCSSFactory, + processCSSWithPresetFactory, } from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); const { - processCSS: withDefaultPreset, - passthroughCSS: passthroughDefault, + processCSS: withDefaultPreset, + passthroughCSS: passthroughDefault, } = processCSSWithPresetFactory('default'); test( - 'should minify lowercase color values', - withDefaultPreset, - 'h1{color:yellow}', - 'h1{color:#ff0}' + 'should minify lowercase color values', + withDefaultPreset, + 'h1{color:yellow}', + 'h1{color:#ff0}' ); test( - 'should minify uppercase color values', - withDefaultPreset, - 'h1{COLOR:YELLOW}', - 'h1{COLOR:#ff0}' + 'should minify uppercase color values', + withDefaultPreset, + 'h1{COLOR:YELLOW}', + 'h1{COLOR:#ff0}' ); test( - 'should minify color values (2)', - withDefaultPreset, - 'h1{box-shadow:0 1px 3px rgba(255, 230, 220, 0.5)}', - 'h1{box-shadow:0 1px 3px rgba(255,230,220,.5)}', + 'should minify color values (2)', + withDefaultPreset, + 'h1{box-shadow:0 1px 3px rgba(255, 230, 220, 0.5)}', + 'h1{box-shadow:0 1px 3px rgba(255,230,220,.5)}' ); test( - 'should minify color values (3)', - withDefaultPreset, - 'h1{background:hsla(134, 50%, 50%, 1)}', - 'h1{background:#40bf5e}' + 'should minify color values (3)', + withDefaultPreset, + 'h1{background:hsla(134, 50%, 50%, 1)}', + 'h1{background:#40bf5e}' ); test( - 'should minify color values (4)', - withDefaultPreset, - 'h1{text-shadow:1px 1px 2px #000000}', - 'h1{text-shadow:1px 1px 2px #000}' + 'should minify color values (4)', + withDefaultPreset, + 'h1{text-shadow:1px 1px 2px #000000}', + 'h1{text-shadow:1px 1px 2px #000}' ); test( - 'should minify color values (5)', - withDefaultPreset, - 'h1{text-shadow:1px 1px 2px rgb(255, 255, 255)}', - 'h1{text-shadow:1px 1px 2px #fff}' + 'should minify color values (5)', + withDefaultPreset, + 'h1{text-shadow:1px 1px 2px rgb(255, 255, 255)}', + 'h1{text-shadow:1px 1px 2px #fff}' ); test( - 'should minify color values (6)', - withDefaultPreset, - 'h1{text-shadow:1px 1px 2px hsl(0,0%,100%)}', - 'h1{text-shadow:1px 1px 2px #fff}' + 'should minify color values (6)', + withDefaultPreset, + 'h1{text-shadow:1px 1px 2px hsl(0,0%,100%)}', + 'h1{text-shadow:1px 1px 2px #fff}' ); test( - 'should minify color values (7)', - withDefaultPreset, - 'h1{background:HSLA(134, 50%, 50%, 1)}', - 'h1{background:#40bf5e}' + 'should minify color values (7)', + withDefaultPreset, + 'h1{background:HSLA(134, 50%, 50%, 1)}', + 'h1{background:#40bf5e}' ); test( - 'should minify color values (8)', - withDefaultPreset, - 'h1{background:#FFFFFF}', - 'h1{background:#fff}' + 'should minify color values (8)', + withDefaultPreset, + 'h1{background:#FFFFFF}', + 'h1{background:#fff}' ); test( - 'should minify color values (9)', - withDefaultPreset, - 'h1{background:#F0FFFF}', - 'h1{background:azure}' + 'should minify color values (9)', + withDefaultPreset, + 'h1{background:#F0FFFF}', + 'h1{background:azure}' ); test( - 'should minify color values in background gradients', - processCSS, - 'h1{background:linear-gradient( #ff0000,yellow )}', - 'h1{background:linear-gradient( red,#ff0 )}' + 'should minify color values in background gradients', + processCSS, + 'h1{background:linear-gradient( #ff0000,yellow )}', + 'h1{background:linear-gradient( red,#ff0 )}' ); test( - 'should minify color values in background gradients (preset)', - withDefaultPreset, - 'h1{background:linear-gradient( #ff0000,yellow )}', - 'h1{background:linear-gradient(red,#ff0)}' + 'should minify color values in background gradients (preset)', + withDefaultPreset, + 'h1{background:linear-gradient( #ff0000,yellow )}', + 'h1{background:linear-gradient(red,#ff0)}' ); test( - 'should minify color values in background gradients (2)', - processCSS, - 'h1{background:linear-gradient(yellow, orange), linear-gradient(black, rgba(255, 255, 255, 0))}', - 'h1{background:linear-gradient(#ff0, orange), linear-gradient(#000, hsla(0, 0%, 100%, 0))}' + 'should minify color values in background gradients (2)', + processCSS, + 'h1{background:linear-gradient(yellow, orange), linear-gradient(black, rgba(255, 255, 255, 0))}', + 'h1{background:linear-gradient(#ff0, orange), linear-gradient(#000, hsla(0, 0%, 100%, 0))}' ); test( - 'should minify color values in background gradients (2) (preset)', - withDefaultPreset, - 'h1{background:linear-gradient(yellow, orange), linear-gradient(black, rgba(255, 255, 255, 0))}', - 'h1{background:linear-gradient(#ff0,orange),linear-gradient(#000,hsla(0,0%,100%,0))}' + 'should minify color values in background gradients (2) (preset)', + withDefaultPreset, + 'h1{background:linear-gradient(yellow, orange), linear-gradient(black, rgba(255, 255, 255, 0))}', + 'h1{background:linear-gradient(#ff0,orange),linear-gradient(#000,hsla(0,0%,100%,0))}' ); test( - 'should minify color values in background gradients (3)', - processCSS, - 'h1{background:linear-gradient(0deg, yellow, black 40%, red)}', - 'h1{background:linear-gradient(0deg, #ff0, #000 40%, red)}' + 'should minify color values in background gradients (3)', + processCSS, + 'h1{background:linear-gradient(0deg, yellow, black 40%, red)}', + 'h1{background:linear-gradient(0deg, #ff0, #000 40%, red)}' ); test( - 'should minify color values in background gradients (3) (preset)', - withDefaultPreset, - 'h1{background:linear-gradient(0deg, yellow, black 40%, red)}', - 'h1{background:linear-gradient(0deg,#ff0,#000 40%,red)}' + 'should minify color values in background gradients (3) (preset)', + withDefaultPreset, + 'h1{background:linear-gradient(0deg, yellow, black 40%, red)}', + 'h1{background:linear-gradient(0deg,#ff0,#000 40%,red)}' ); test( - 'should not minify in font properties', - passthroughCSS, - 'h1{font-family:black}' + 'should not minify in font properties', + passthroughCSS, + 'h1{font-family:black}' ); test( - 'should make an exception for webkit tap highlight color (issue 1)', - passthroughCSS, - 'h1{-webkit-tap-highlight-color:rgba(0,0,0,0)}' + 'should make an exception for webkit tap highlight color (issue 1)', + passthroughCSS, + 'h1{-webkit-tap-highlight-color:rgba(0,0,0,0)}' ); test( - 'should not crash on transparent in webkit tap highlight color', - passthroughCSS, - 'h1{-webkit-tap-highlight-color:transparent}' + 'should not crash on transparent in webkit tap highlight color', + passthroughCSS, + 'h1{-webkit-tap-highlight-color:transparent}' ); test( - 'should not crash on inherit in webkit tap highlight color', - passthroughCSS, - 'h1{-webkit-tap-highlight-color:inherit}' + 'should not crash on inherit in webkit tap highlight color', + passthroughCSS, + 'h1{-webkit-tap-highlight-color:inherit}' ); test( - 'should not minify in lowercase filter properties', - passthroughDefault, - 'h1{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr= #000000,endColorstr= #ffffff)}' + 'should not minify in lowercase filter properties', + passthroughDefault, + 'h1{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr= #000000,endColorstr= #ffffff)}' ); test( - 'should not minify in uppercase filter properties', - passthroughDefault, - 'h1{FILTER:progid:DXImageTransform.Microsoft.gradient(startColorstr= #000000,endColorstr= #ffffff)}' + 'should not minify in uppercase filter properties', + passthroughDefault, + 'h1{FILTER:progid:DXImageTransform.Microsoft.gradient(startColorstr= #000000,endColorstr= #ffffff)}' ); test( - 'should minify color stops', - processCSS, - 'h1{background-image:-webkit-gradient(linear,50% 0%,50% 100%,color-stop(1px, #fbfbfb),color-stop(1px, #ffffff),color-stop(2px, #ffffff),color-stop(2px, #fbfbfb),color-stop(100%, #ececec))}', - 'h1{background-image:-webkit-gradient(linear,50% 0%,50% 100%,color-stop(1px, #fbfbfb),color-stop(1px, #fff),color-stop(2px, #fff),color-stop(2px, #fbfbfb),color-stop(100%, #ececec))}' + 'should minify color stops', + processCSS, + 'h1{background-image:-webkit-gradient(linear,50% 0%,50% 100%,color-stop(1px, #fbfbfb),color-stop(1px, #ffffff),color-stop(2px, #ffffff),color-stop(2px, #fbfbfb),color-stop(100%, #ececec))}', + 'h1{background-image:-webkit-gradient(linear,50% 0%,50% 100%,color-stop(1px, #fbfbfb),color-stop(1px, #fff),color-stop(2px, #fff),color-stop(2px, #fbfbfb),color-stop(100%, #ececec))}' ); test( - 'should not minify in lowercase calc values', - passthroughCSS, - 'h1{width:calc(100vw / 2 - 6px + 0)}' + 'should not minify in lowercase calc values', + passthroughCSS, + 'h1{width:calc(100vw / 2 - 6px + 0)}' ); test( - 'should not minify in uppercase calc values', - passthroughCSS, - 'h1{width:CALC(100vw / 2 - 6px + 0)}' + 'should not minify in uppercase calc values', + passthroughCSS, + 'h1{width:CALC(100vw / 2 - 6px + 0)}' ); test( - 'should minify hex colors without keywords', - processCSS, - 'h1{background:linear-gradient(#ffffff,#999999) no-repeat;}', - 'h1{background:linear-gradient(#fff,#999) no-repeat;}' + 'should minify hex colors without keywords', + processCSS, + 'h1{background:linear-gradient(#ffffff,#999999) no-repeat;}', + 'h1{background:linear-gradient(#fff,#999) no-repeat;}' ); test( - 'should not mangle percentage based rgba values', - processCSS, - 'h1{color:rgba(50%,50%,50%,0.5)}', - 'h1{color:hsla(0, 0%, 49.8%, 0.5)}' + 'should not mangle percentage based rgba values', + processCSS, + 'h1{color:rgba(50%,50%,50%,0.5)}', + 'h1{color:hsla(0, 0%, 49.8%, 0.5)}' ); test( - 'should convert percentage based rgba values', - processCSS, - 'h1{color:rgb(100%,100%,100%)}', - 'h1{color:#fff}' + 'should convert percentage based rgba values', + processCSS, + 'h1{color:rgb(100%,100%,100%)}', + 'h1{color:#fff}' ); test( - 'should handle errored cases', - passthroughCSS, - 'h1{color:rgb(50%, 23, 54)}' + 'should handle errored cases', + passthroughCSS, + 'h1{color:rgb(50%, 23, 54)}' ); test( - 'should add extra spaces when converting rgb', - processCSS, - 'h1{background:linear-gradient(rgb(50, 50, 50)0%,blue 100%)}', - 'h1{background:linear-gradient(#323232 0%,#00f 100%)}' + 'should add extra spaces when converting rgb', + processCSS, + 'h1{background:linear-gradient(rgb(50, 50, 50)0%,blue 100%)}', + 'h1{background:linear-gradient(#323232 0%,#00f 100%)}' ); test( - 'should add extra spaces when converting rgb (2)', - processCSS, - 'h1{background:linear-gradient(rgba(0,0,0,0)0%, blue 100%)}', - 'h1{background:linear-gradient(transparent 0%, #00f 100%)}' + 'should add extra spaces when converting rgb (2)', + processCSS, + 'h1{background:linear-gradient(rgba(0,0,0,0)0%, blue 100%)}', + 'h1{background:linear-gradient(transparent 0%, #00f 100%)}' ); test( - 'should add extra spaces when converting rgb (3)', - processCSS, - 'h1{background:rgb(1,2,3)url(bar.png)}', - 'h1{background:#010203 url(bar.png)}' + 'should add extra spaces when converting rgb (3)', + processCSS, + 'h1{background:rgb(1,2,3)url(bar.png)}', + 'h1{background:#010203 url(bar.png)}' ); test( - 'should save extra spaces when converting hex', - withDefaultPreset, - 'h1{background:#F0FFFF url(bar.png)}', - 'h1{background:azure url(bar.png)}' + 'should save extra spaces when converting hex', + withDefaultPreset, + 'h1{background:#F0FFFF url(bar.png)}', + 'h1{background:azure url(bar.png)}' ); test( - 'should bail on the "composes" property', - passthroughDefault, - 'h1{composes:black from "styles"}' + 'should bail on the "composes" property', + passthroughDefault, + 'h1{composes:black from "styles"}' ); -test( - 'should not mangle empty strings', - passthroughDefault, - 'h1{content:""}' -); +test('should not mangle empty strings', passthroughDefault, 'h1{content:""}'); test( - 'should passthrough css variables', - passthroughDefault, - 'h1{color:var(--foo)}' + 'should passthrough css variables', + passthroughDefault, + 'h1{color:var(--foo)}' ); test( - 'should passthrough css variables #2', - passthroughDefault, - 'h1{color:var(--foo) var(--bar)}' + 'should passthrough css variables #2', + passthroughDefault, + 'h1{color:var(--foo) var(--bar)}' ); test( - 'should passthrough css variables #3', - passthroughDefault, - 'h1{color:rgb(var(--foo),255,255)}' + 'should passthrough css variables #3', + passthroughDefault, + 'h1{color:rgb(var(--foo),255,255)}' ); test( - 'should passthrough css variables #4', - passthroughDefault, - 'h1{color:rgb(255,var(--bar),255)}' + 'should passthrough css variables #4', + passthroughDefault, + 'h1{color:rgb(255,var(--bar),255)}' ); test( - 'should passthrough css variables #5', - passthroughDefault, - 'h1{color:rgb(255,255,var(--baz))}' + 'should passthrough css variables #5', + passthroughDefault, + 'h1{color:rgb(255,255,var(--baz))}' ); test( - 'should passthrough css variables #6', - passthroughDefault, - 'h1{color:rgb(var(--foo))}' + 'should passthrough css variables #6', + passthroughDefault, + 'h1{color:rgb(var(--foo))}' ); test( - 'should not convert this specific rgba value to "transparent" (old IE)', - passthroughCSS, - 'h1{color:rgba(0, 0, 0, 0)}', - {env: 'ie8'} + 'should not convert this specific rgba value to "transparent" (old IE)', + passthroughCSS, + 'h1{color:rgba(0, 0, 0, 0)}', + { env: 'ie8' } ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); test( - 'should not mangle colours in the content property', - passthroughCSS, - 'h2:before{content:"black"}' + 'should not mangle colours in the content property', + passthroughCSS, + 'h2:before{content:"black"}' ); diff --git a/packages/postcss-colormin/src/colours.js b/packages/postcss-colormin/src/colours.js index c6b278e9b..123913bd3 100644 --- a/packages/postcss-colormin/src/colours.js +++ b/packages/postcss-colormin/src/colours.js @@ -5,62 +5,62 @@ import toShorthand from './lib/toShorthand'; const shorter = (a, b) => (a && a.length < b.length ? a : b).toLowerCase(); export default (colour, isLegacy = false, cache = false) => { - const key = colour + "|" + isLegacy; + const key = colour + '|' + isLegacy; - if (cache && cache[key]) { - return cache[key]; - } - - try { - const parsed = color(colour.toLowerCase()); - const alpha = parsed.alpha(); + if (cache && cache[key]) { + return cache[key]; + } - if (alpha === 1) { - const toHex = toShorthand(parsed.hex().toLowerCase()); - const result = shorter(keywords[toHex], toHex); + try { + const parsed = color(colour.toLowerCase()); + const alpha = parsed.alpha(); - if (cache) { - cache[key] = result; - } + if (alpha === 1) { + const toHex = toShorthand(parsed.hex().toLowerCase()); + const result = shorter(keywords[toHex], toHex); - return result; - } else { - const rgb = parsed.rgb(); + if (cache) { + cache[key] = result; + } - if ( - !isLegacy && - !rgb.color[0] && - !rgb.color[1] && - !rgb.color[2] && - !alpha - ) { - const result = 'transparent'; + return result; + } else { + const rgb = parsed.rgb(); - if (cache) { - cache[key] = result; - } + if ( + !isLegacy && + !rgb.color[0] && + !rgb.color[1] && + !rgb.color[2] && + !alpha + ) { + const result = 'transparent'; - return result; - } + if (cache) { + cache[key] = result; + } - let hsla = parsed.hsl().string(); - let rgba = rgb.string(); - let result = hsla.length < rgba.length ? hsla : rgba; + return result; + } - if (cache) { - cache[key] = result; - } + let hsla = parsed.hsl().string(); + let rgba = rgb.string(); + let result = hsla.length < rgba.length ? hsla : rgba; - return result; - } - } catch (e) { - // Possibly malformed, so pass through - const result = colour; + if (cache) { + cache[key] = result; + } - if (cache) { - cache[key] = result; - } + return result; + } + } catch (e) { + // Possibly malformed, so pass through + const result = colour; - return result; + if (cache) { + cache[key] = result; } + + return result; + } }; diff --git a/packages/postcss-colormin/src/generate.js b/packages/postcss-colormin/src/generate.js index 26dfe3bb3..7c62a38f6 100644 --- a/packages/postcss-colormin/src/generate.js +++ b/packages/postcss-colormin/src/generate.js @@ -6,33 +6,29 @@ import toShorthand from './lib/toShorthand'; const keywords = {}; const hexes = {}; -Object.keys(colorNames).forEach(keyword => { - const hex = toShorthand(colorNames[keyword]); - if (keyword.length < hex.length) { - keywords[hex] = keyword; - return; - } - hexes[keyword] = hex; +Object.keys(colorNames).forEach((keyword) => { + const hex = toShorthand(colorNames[keyword]); + if (keyword.length < hex.length) { + keywords[hex] = keyword; + return; + } + hexes[keyword] = hex; }); -function stringify (map) { - return JSON.stringify(map, null, 2) + '\n'; +function stringify(map) { + return JSON.stringify(map, null, 2) + '\n'; } -function callback (err) { - if (err) { - throw err; - } +function callback(err) { + if (err) { + throw err; + } } -writeFile( - path.join(__dirname, 'keywords.json'), - stringify(keywords), - callback -); +writeFile(path.join(__dirname, 'keywords.json'), stringify(keywords), callback); writeFile( - path.join(__dirname, '../dist/keywords.json'), - stringify(keywords), - callback + path.join(__dirname, '../dist/keywords.json'), + stringify(keywords), + callback ); diff --git a/packages/postcss-colormin/src/index.js b/packages/postcss-colormin/src/index.js index fc866d669..24bfb46e7 100644 --- a/packages/postcss-colormin/src/index.js +++ b/packages/postcss-colormin/src/index.js @@ -1,16 +1,16 @@ -import browserslist from "browserslist"; -import postcss from "postcss"; -import valueParser, {stringify} from "postcss-value-parser"; -import colormin from "./colours"; +import browserslist from 'browserslist'; +import postcss from 'postcss'; +import valueParser, { stringify } from 'postcss-value-parser'; +import colormin from './colours'; -function walk (parent, callback) { - parent.nodes.forEach((node, index) => { - const bubble = callback(node, index, parent); +function walk(parent, callback) { + parent.nodes.forEach((node, index) => { + const bubble = callback(node, index, parent); - if (node.nodes && bubble !== false) { - walk(node, callback); - } - }); + if (node.nodes && bubble !== false) { + walk(node, callback); + } + }); } /* @@ -20,75 +20,69 @@ function walk (parent, callback) { * https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer */ -function hasTransparentBug (browser) { - return ~["ie 8", "ie 9"].indexOf(browser); +function hasTransparentBug(browser) { + return ~['ie 8', 'ie 9'].indexOf(browser); } -export default postcss.plugin("postcss-colormin", () => { - return (css, result) => { - const resultOpts = result.opts || {}; - const browsers = browserslist(null, { - stats: resultOpts.stats, - path: __dirname, - env: resultOpts.env, - }); - const isLegacy = browsers.some(hasTransparentBug); - const colorminCache = {}; - const cache = {}; +export default postcss.plugin('postcss-colormin', () => { + return (css, result) => { + const resultOpts = result.opts || {}; + const browsers = browserslist(null, { + stats: resultOpts.stats, + path: __dirname, + env: resultOpts.env, + }); + const isLegacy = browsers.some(hasTransparentBug); + const colorminCache = {}; + const cache = {}; - css.walkDecls(decl => { - if ( - /^(composes|font|filter|-webkit-tap-highlight-color)/i.test( - decl.prop - ) - ) { - return; - } + css.walkDecls((decl) => { + if ( + /^(composes|font|filter|-webkit-tap-highlight-color)/i.test(decl.prop) + ) { + return; + } - if (cache[decl.value]) { - decl.value = cache[decl.value]; + if (cache[decl.value]) { + decl.value = cache[decl.value]; - return; - } + return; + } - const parsed = valueParser(decl.value); + const parsed = valueParser(decl.value); - walk(parsed, (node, index, parent) => { - if (node.type === "function") { - if (/^(rgb|hsl)a?$/i.test(node.value)) { - const {value} = node; + walk(parsed, (node, index, parent) => { + if (node.type === 'function') { + if (/^(rgb|hsl)a?$/i.test(node.value)) { + const { value } = node; - node.value = colormin( - stringify(node), - isLegacy, - colorminCache - ); - node.type = "word"; + node.value = colormin(stringify(node), isLegacy, colorminCache); + node.type = 'word'; - const next = parent.nodes[index + 1]; + const next = parent.nodes[index + 1]; - if ( - node.value !== value && - next && - (next.type === "word" || next.type === "function") - ) { - parent.nodes.splice(index + 1, 0, { - type: "space", - value: " ", - }); - } - } else if (node.value.toLowerCase() === "calc") { - return false; - } - } else if (node.type === "word") { - node.value = colormin(node.value, isLegacy, colorminCache); - } - }); + if ( + node.value !== value && + next && + (next.type === 'word' || next.type === 'function') + ) { + parent.nodes.splice(index + 1, 0, { + type: 'space', + value: ' ', + }); + } + } else if (node.value.toLowerCase() === 'calc') { + return false; + } + } else if (node.type === 'word') { + node.value = colormin(node.value, isLegacy, colorminCache); + } + }); - const optimizedValue = parsed.toString(); + const optimizedValue = parsed.toString(); - decl.value = optimizedValue; - cache[decl.value] = optimizedValue; - }); - }; + decl.value = optimizedValue; + cache[decl.value] = optimizedValue; + }); + }; }); diff --git a/packages/postcss-colormin/src/lib/toShorthand.js b/packages/postcss-colormin/src/lib/toShorthand.js index 294a1c881..60f152814 100644 --- a/packages/postcss-colormin/src/lib/toShorthand.js +++ b/packages/postcss-colormin/src/lib/toShorthand.js @@ -1,11 +1,7 @@ -export default hex => { - if ( - hex[1] === hex[2] && - hex[3] === hex[4] && - hex[5] === hex[6] - ) { - return '#' + hex[2] + hex[4] + hex[6]; - } +export default (hex) => { + if (hex[1] === hex[2] && hex[3] === hex[4] && hex[5] === hex[6]) { + return '#' + hex[2] + hex[4] + hex[6]; + } - return hex; + return hex; }; diff --git a/packages/postcss-convert-values/src/__tests__/index.js b/packages/postcss-convert-values/src/__tests__/index.js index 662c3adf1..b4716e3c0 100644 --- a/packages/postcss-convert-values/src/__tests__/index.js +++ b/packages/postcss-convert-values/src/__tests__/index.js @@ -1,442 +1,415 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); test( - 'should convert milliseconds to seconds', - processCSS, - 'h1{transition-duration:500ms}', - 'h1{transition-duration:.5s}' + 'should convert milliseconds to seconds', + processCSS, + 'h1{transition-duration:500ms}', + 'h1{transition-duration:.5s}' ); test( - 'should convert seconds to milliseconds', - processCSS, - 'h1{transition-duration:.005s}', - 'h1{transition-duration:5ms}' + 'should convert seconds to milliseconds', + processCSS, + 'h1{transition-duration:.005s}', + 'h1{transition-duration:5ms}' ); test( - 'should not convert negative milliseconds to seconds', - passthroughCSS, - 'h1{animation-duration:-569ms}' + 'should not convert negative milliseconds to seconds', + passthroughCSS, + 'h1{animation-duration:-569ms}' ); test( - 'should not remove the unit from zero values (duration)', - passthroughCSS, - 'h1{transition-duration:0s}' + 'should not remove the unit from zero values (duration)', + passthroughCSS, + 'h1{transition-duration:0s}' ); test( - 'should not remove the unit from zero values (custom properties)', - passthroughCSS, - 'h1{--my-variable:0px}' + 'should not remove the unit from zero values (custom properties)', + passthroughCSS, + 'h1{--my-variable:0px}' ); test( - 'should remove unnecessary plus signs', - processCSS, - 'h1{width:+14px}', - 'h1{width:14px}' + 'should remove unnecessary plus signs', + processCSS, + 'h1{width:+14px}', + 'h1{width:14px}' ); -test( - 'should convert px to pc', - processCSS, - 'h1{width:16px}', - 'h1{width:1pc}' -); +test('should convert px to pc', processCSS, 'h1{width:16px}', 'h1{width:1pc}'); test( - 'should convert px to pt', - processCSS, - 'h1{width:120px}', - 'h1{width:90pt}' + 'should convert px to pt', + processCSS, + 'h1{width:120px}', + 'h1{width:90pt}' ); -test( - 'should convert px to in', - processCSS, - 'h1{width:192px}', - 'h1{width:2in}' -); +test('should convert px to in', processCSS, 'h1{width:192px}', 'h1{width:2in}'); -test( - 'should not convert in to px', - passthroughCSS, - 'h1{width:192in}' -); +test('should not convert in to px', passthroughCSS, 'h1{width:192in}'); test( - 'should strip the units from length properties', - processCSS, - 'h1{margin: 0em 0% 0px 0pc}', - 'h1{margin: 0 0 0 0}' + 'should strip the units from length properties', + processCSS, + 'h1{margin: 0em 0% 0px 0pc}', + 'h1{margin: 0 0 0 0}' ); test( - 'should trim trailing zeros', - processCSS, - 'h1{width:109.00000000000px}', - 'h1{width:109px}' + 'should trim trailing zeros', + processCSS, + 'h1{width:109.00000000000px}', + 'h1{width:109px}' ); test( - 'should trim trailing zeros + unit', - processCSS, - 'h1{width:0.00px}', - 'h1{width:0}' + 'should trim trailing zeros + unit', + processCSS, + 'h1{width:0.00px}', + 'h1{width:0}' ); test( - 'should trim trailing zeros without unit', - processCSS, - 'h1{width:100.00%}', - 'h1{width:100%}' + 'should trim trailing zeros without unit', + processCSS, + 'h1{width:100.00%}', + 'h1{width:100%}' ); -test( - 'should not mangle flex basis', - passthroughCSS, - 'h1{flex-basis:0%}' -); +test('should not mangle flex basis', passthroughCSS, 'h1{flex-basis:0%}'); -test( - 'should not mangle flex basis (2)', - passthroughCSS, - 'h1{FLEX-BASIC:0%}' -); +test('should not mangle flex basis (2)', passthroughCSS, 'h1{FLEX-BASIC:0%}'); -test( - 'should not mangle values without units', - passthroughCSS, - 'h1{z-index:5}' -); +test('should not mangle values without units', passthroughCSS, 'h1{z-index:5}'); test( - 'should operate in calc values', - processCSS, - 'h1{width:calc(192px + 2em - (0px * 4))}', - 'h1{width:calc(2in + 2em - (0px * 4))}' + 'should operate in calc values', + processCSS, + 'h1{width:calc(192px + 2em - (0px * 4))}', + 'h1{width:calc(2in + 2em - (0px * 4))}' ); test( - 'should operate in calc values (2)', - processCSS, - 'h1{width:CALC(192px + 2em - (0px * 4))}', - 'h1{width:CALC(2in + 2em - (0px * 4))}' + 'should operate in calc values (2)', + processCSS, + 'h1{width:CALC(192px + 2em - (0px * 4))}', + 'h1{width:CALC(2in + 2em - (0px * 4))}' ); test( - 'should not convert zero values in calc', - passthroughCSS, - 'h1{width:calc(0em)}' + 'should not convert zero values in calc', + passthroughCSS, + 'h1{width:calc(0em)}' ); test( - 'should not mangle values outside of its domain', - passthroughCSS, - 'h1{background:url(a.png)}' + 'should not mangle values outside of its domain', + passthroughCSS, + 'h1{background:url(a.png)}' ); test( - 'should not mangle values outside of its domain (2)', - passthroughCSS, - 'h1{background:URL(a.png)}' + 'should not mangle values outside of its domain (2)', + passthroughCSS, + 'h1{background:URL(a.png)}' ); test( - 'should optimise fractions', - processCSS, - 'h1{opacity:1.}h2{opacity:.0}', - 'h1{opacity:1}h2{opacity:0}' + 'should optimise fractions', + processCSS, + 'h1{opacity:1.}h2{opacity:.0}', + 'h1{opacity:1}h2{opacity:0}' ); test( - 'should optimise fractions with units', - processCSS, - 'h1{width:10.px}h2{width:.0px}', - 'h1{width:10px}h2{width:0}' + 'should optimise fractions with units', + processCSS, + 'h1{width:10.px}h2{width:.0px}', + 'h1{width:10px}h2{width:0}' ); test( - 'should optimise fractions inside calc', - processCSS, - 'h1{width:calc(10.px + .0px)}', - 'h1{width:calc(10px + 0px)}' + 'should optimise fractions inside calc', + processCSS, + 'h1{width:calc(10.px + .0px)}', + 'h1{width:calc(10px + 0px)}' ); test( - 'should handle leading zero in rem values', - processCSS, - '.one{top:0.25rem}', - '.one{top:.25rem}' + 'should handle leading zero in rem values', + processCSS, + '.one{top:0.25rem}', + '.one{top:.25rem}' ); test( - 'should handle slash separated values', - processCSS, - '.one{background: 50% .0%/100.0% 100.0%}', - '.one{background: 50% 0/100% 100%}' + 'should handle slash separated values', + processCSS, + '.one{background: 50% .0%/100.0% 100.0%}', + '.one{background: 50% 0/100% 100%}' ); test( - 'should handle comma separated values', - processCSS, - '.one{background: 50% .0% ,100.0% 100.0%}', - '.one{background: 50% 0 ,100% 100%}' + 'should handle comma separated values', + processCSS, + '.one{background: 50% .0% ,100.0% 100.0%}', + '.one{background: 50% 0 ,100% 100%}' ); test( - 'should not mangle duration values', - passthroughCSS, - '.long{animation-duration:2s}' + 'should not mangle duration values', + passthroughCSS, + '.long{animation-duration:2s}' ); test( - 'should not mangle padding values', - passthroughCSS, - 'h1{padding:10px 20px 30px 40px}h2{padding:10px 20px 30px}h3{padding:10px 20px}h4{padding:10px}' + 'should not mangle padding values', + passthroughCSS, + 'h1{padding:10px 20px 30px 40px}h2{padding:10px 20px 30px}h3{padding:10px 20px}h4{padding:10px}' ); test( - 'should trim leading zeroes from negative values', - processCSS, - 'h1,h2{letter-spacing:-0.1rem}', - 'h1,h2{letter-spacing:-.1rem}' + 'should trim leading zeroes from negative values', + processCSS, + 'h1,h2{letter-spacing:-0.1rem}', + 'h1,h2{letter-spacing:-.1rem}' ); test( - 'should support viewports units', - processCSS, - 'h1,h2{letter-spacing:-0.1vmin}', - 'h1,h2{letter-spacing:-.1vmin}' + 'should support viewports units', + processCSS, + 'h1,h2{letter-spacing:-0.1vmin}', + 'h1,h2{letter-spacing:-.1vmin}' ); -test( - 'should support ch units', - passthroughCSS, - 'a{line-height:1.1ch}' -); +test('should support ch units', passthroughCSS, 'a{line-height:1.1ch}'); test( - 'should support PX units', - processCSS, - 'h1{font-size:20PX}', - 'h1{font-size:20PX}' + 'should support PX units', + processCSS, + 'h1{font-size:20PX}', + 'h1{font-size:20PX}' ); test( - 'should not mangle data urls', - passthroughCSS, - '.has-svg:before{content:url("data:image/svg+xml;utf8,")}' + 'should not mangle data urls', + passthroughCSS, + '.has-svg:before{content:url("data:image/svg+xml;utf8,")}' ); test( - 'should not mangle data urls (2)', - passthroughCSS, - '.has-svg:before{content:URL("data:image/svg+xml;utf8,")}' + 'should not mangle data urls (2)', + passthroughCSS, + '.has-svg:before{content:URL("data:image/svg+xml;utf8,")}' ); test( - 'should convert angle units', - processCSS, - 'h1{transform: rotate(0.25turn);transform: rotate(0.25TURN)}', - 'h1{transform: rotate(90deg);transform: rotate(90deg)}' + 'should convert angle units', + processCSS, + 'h1{transform: rotate(0.25turn);transform: rotate(0.25TURN)}', + 'h1{transform: rotate(90deg);transform: rotate(90deg)}' ); test( - 'should not convert length units', - processCSS, - 'h1{transition-duration:500ms; width:calc(192px + 2em); width:+14px; letter-spacing:-0.1VMIN}', - 'h1{transition-duration:.5s; width:calc(192px + 2em); width:14px; letter-spacing:-.1VMIN}', - {length: false} + 'should not convert length units', + processCSS, + 'h1{transition-duration:500ms; width:calc(192px + 2em); width:+14px; letter-spacing:-0.1VMIN}', + 'h1{transition-duration:.5s; width:calc(192px + 2em); width:14px; letter-spacing:-.1VMIN}', + { length: false } ); test( - 'should not convert time units', - processCSS, - 'h1{transition-duration:500ms; width:calc(192px + 2em); width:+14px; letter-spacing:-0.1VMIN}', - 'h1{transition-duration:500ms; width:calc(2in + 2em); width:14px; letter-spacing:-.1VMIN}', - {time: false} + 'should not convert time units', + processCSS, + 'h1{transition-duration:500ms; width:calc(192px + 2em); width:+14px; letter-spacing:-0.1VMIN}', + 'h1{transition-duration:500ms; width:calc(2in + 2em); width:14px; letter-spacing:-.1VMIN}', + { time: false } ); test( - 'should not convert angle units', - processCSS, - 'h1{transform: rotate(0.25turn);transform: rotate(0.25TURN)}', - 'h1{transform: rotate(.25turn);transform: rotate(.25TURN)}', - {angle: false} + 'should not convert angle units', + processCSS, + 'h1{transform: rotate(0.25turn);transform: rotate(0.25TURN)}', + 'h1{transform: rotate(.25turn);transform: rotate(.25TURN)}', + { angle: false } ); test( - 'should not remove units from angle values', - passthroughCSS, - 'h1{transform:rotate(0deg)}' + 'should not remove units from angle values', + passthroughCSS, + 'h1{transform:rotate(0deg)}' ); test( - 'should not remove units from angle values (2)', - passthroughCSS, - 'h1{transform:rotate(0turn)}' + 'should not remove units from angle values (2)', + passthroughCSS, + 'h1{transform:rotate(0turn)}' ); test( - 'should not remove unit with zero value in hsl and hsla functions', - passthroughCSS, - 'h1{color:hsl(0, 0%, 244%); background:hsl(0, 0%, 0%)}' + 'should not remove unit with zero value in hsl and hsla functions', + passthroughCSS, + 'h1{color:hsl(0, 0%, 244%); background:hsl(0, 0%, 0%)}' ); test( - 'should strip trailing zeroes from percentage heights', - processCSS, - 'h1{height:12.500%}', - 'h1{height:12.5%}' + 'should strip trailing zeroes from percentage heights', + processCSS, + 'h1{height:12.500%}', + 'h1{height:12.5%}' ); test( - 'should not strip the percentage from 0 in max-height & height props', - passthroughCSS, - 'h1{height:0%;max-height:0%}' + 'should not strip the percentage from 0 in max-height & height props', + passthroughCSS, + 'h1{height:0%;max-height:0%}' ); test( - 'should not crash when analysing a declaration with one parent', - passthroughCSS, - 'width:0' + 'should not crash when analysing a declaration with one parent', + passthroughCSS, + 'width:0' ); test( - 'should strip the unit from 0 in max-height & height props', - processCSS, - 'h1{height:0em;max-height:0em}', - 'h1{height:0;max-height:0}' + 'should strip the unit from 0 in max-height & height props', + processCSS, + 'h1{height:0em;max-height:0em}', + 'h1{height:0;max-height:0}' ); test( - 'should strip the unit from 0 in max-height & height props (2)', - processCSS, - 'h1{height:0em;MAX-HEIGHT:0em}', - 'h1{height:0;MAX-HEIGHT:0}' + 'should strip the unit from 0 in max-height & height props (2)', + processCSS, + 'h1{height:0em;MAX-HEIGHT:0em}', + 'h1{height:0;MAX-HEIGHT:0}' ); test( - 'should round pixel values to two decimal places', - processCSS, - 'h1{right:6.66667px}', - 'h1{right:6.67px}', - {precision: 2} + 'should round pixel values to two decimal places', + processCSS, + 'h1{right:6.66667px}', + 'h1{right:6.67px}', + { precision: 2 } ); test( - 'should round pixel values with customisable precision', - processCSS, - 'h1{right:6.66667px}', - 'h1{right:7px}', - {precision: 0} + 'should round pixel values with customisable precision', + processCSS, + 'h1{right:6.66667px}', + 'h1{right:7px}', + { precision: 0 } ); test( - 'should not round pixel values to two decimal places by default', - passthroughCSS, - 'h1{right:6.66667px}' + 'should not round pixel values to two decimal places by default', + passthroughCSS, + 'h1{right:6.66667px}' ); test( - 'should clamp opacity to 1 maximum', - processCSS, - 'h1{opacity:150;opacity:15;opacity:1.5}', - 'h1{opacity:1;opacity:1;opacity:1}' + 'should clamp opacity to 1 maximum', + processCSS, + 'h1{opacity:150;opacity:15;opacity:1.5}', + 'h1{opacity:1;opacity:1;opacity:1}' ); test( - 'should clamp opacity to 0 minimum', - processCSS, - 'h1{opacity:-0.5;opacity:-5;opacity:-50}', - 'h1{opacity:0;opacity:0;opacity:0}' + 'should clamp opacity to 0 minimum', + processCSS, + 'h1{opacity:-0.5;opacity:-5;opacity:-50}', + 'h1{opacity:0;opacity:0;opacity:0}' ); test( - 'should keep stripping zeroes from opacity', - processCSS, - 'h1{opacity:0.0625}', - 'h1{opacity:.0625}' + 'should keep stripping zeroes from opacity', + processCSS, + 'h1{opacity:0.0625}', + 'h1{opacity:.0625}' ); test( - 'should keep stripping zeroes from opacity (2)', - processCSS, - 'h1{OPACITY:0.0625}', - 'h1{OPACITY:.0625}' + 'should keep stripping zeroes from opacity (2)', + processCSS, + 'h1{OPACITY:0.0625}', + 'h1{OPACITY:.0625}' ); test( - 'should handle global values for opacity', - passthroughCSS, - 'h1{opacity:initial}' + 'should handle global values for opacity', + passthroughCSS, + 'h1{opacity:initial}' ); test( - 'should clamp shape-image-threshold to 1 maximum', - processCSS, - 'h1{shape-image-threshold:150;shape-image-threshold:15;shape-image-threshold:1.5}', - 'h1{shape-image-threshold:1;shape-image-threshold:1;shape-image-threshold:1}' + 'should clamp shape-image-threshold to 1 maximum', + processCSS, + 'h1{shape-image-threshold:150;shape-image-threshold:15;shape-image-threshold:1.5}', + 'h1{shape-image-threshold:1;shape-image-threshold:1;shape-image-threshold:1}' ); test( - 'should clamp shape-image-threshold to 1 maximum (2)', - processCSS, - 'h1{SHAPE-IMAGE-THRESHOLD:150;SHAPE-IMAGE-THRESHOLD:15;SHAPE-IMAGE-THRESHOLD:1.5}', - 'h1{SHAPE-IMAGE-THRESHOLD:1;SHAPE-IMAGE-THRESHOLD:1;SHAPE-IMAGE-THRESHOLD:1}' + 'should clamp shape-image-threshold to 1 maximum (2)', + processCSS, + 'h1{SHAPE-IMAGE-THRESHOLD:150;SHAPE-IMAGE-THRESHOLD:15;SHAPE-IMAGE-THRESHOLD:1.5}', + 'h1{SHAPE-IMAGE-THRESHOLD:1;SHAPE-IMAGE-THRESHOLD:1;SHAPE-IMAGE-THRESHOLD:1}' ); test( - 'should clamp shape-image-threshold to 0 minimum', - processCSS, - 'h1{shape-image-threshold:-0.5;shape-image-threshold:-5;shape-image-threshold:-50}', - 'h1{shape-image-threshold:0;shape-image-threshold:0;shape-image-threshold:0}' + 'should clamp shape-image-threshold to 0 minimum', + processCSS, + 'h1{shape-image-threshold:-0.5;shape-image-threshold:-5;shape-image-threshold:-50}', + 'h1{shape-image-threshold:0;shape-image-threshold:0;shape-image-threshold:0}' ); test( - 'should handle global values for shape-image-threshold', - passthroughCSS, - 'h1{shape-image-threshold:initial}' + 'should handle global values for shape-image-threshold', + passthroughCSS, + 'h1{shape-image-threshold:initial}' ); test( - 'should keep stripping zeroes from shape-image-threshold', - processCSS, - 'h1{shape-image-threshold:0.0625}', - 'h1{shape-image-threshold:.0625}' + 'should keep stripping zeroes from shape-image-threshold', + processCSS, + 'h1{shape-image-threshold:0.0625}', + 'h1{shape-image-threshold:.0625}' ); test( - 'should keep unknown units or hacks', - passthroughCSS, - 'h1{top:0\\9\\0;left:0lightyear}' + 'should keep unknown units or hacks', + passthroughCSS, + 'h1{top:0\\9\\0;left:0lightyear}' ); -['stroke-dasharray', 'stroke-dashoffset', 'stroke-width'].forEach(property => { +['stroke-dasharray', 'stroke-dashoffset', 'stroke-width'].forEach( + (property) => { test( - `should not strip the percentage from 0 in SVG animation, for IE (${property})`, - passthroughCSS, - `@keyframes a{0%{${property}:200%}to{${property}:0%}}` + `should not strip the percentage from 0 in SVG animation, for IE (${property})`, + passthroughCSS, + `@keyframes a{0%{${property}:200%}to{${property}:0%}}` ); -}); + } +); -['STROKE-DASHARRAY', 'STROKE-DASHOFFSET', 'STROKE-WIDTH'].forEach(property => { +['STROKE-DASHARRAY', 'STROKE-DASHOFFSET', 'STROKE-WIDTH'].forEach( + (property) => { test( - `should not strip the percentage from 0 in SVG animation, for IE (${property}) (2)`, - passthroughCSS, - `@KEYFRAMES a{0%{${property}:200%}to{${property}:0%}}` + `should not strip the percentage from 0 in SVG animation, for IE (${property}) (2)`, + passthroughCSS, + `@KEYFRAMES a{0%{${property}:200%}to{${property}:0%}}` ); -}); - -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() + } ); + +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-convert-values/src/index.js b/packages/postcss-convert-values/src/index.js index a443eb212..af0dd5d95 100644 --- a/packages/postcss-convert-values/src/index.js +++ b/packages/postcss-convert-values/src/index.js @@ -1,97 +1,120 @@ import postcss from 'postcss'; -import valueParser, {unit, walk} from 'postcss-value-parser'; +import valueParser, { unit, walk } from 'postcss-value-parser'; import convert from './lib/convert'; const LENGTH_UNITS = [ - 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', - 'cm', 'mm', 'q', 'in', 'pt', 'pc', 'px', + 'em', + 'ex', + 'ch', + 'rem', + 'vw', + 'vh', + 'vmin', + 'vmax', + 'cm', + 'mm', + 'q', + 'in', + 'pt', + 'pc', + 'px', ]; -function parseWord (node, opts, keepZeroUnit) { - const pair = unit(node.value); - if (pair) { - const num = Number(pair.number); - const u = pair.unit; - if (num === 0) { - node.value = ( - keepZeroUnit || - !~LENGTH_UNITS.indexOf(u.toLowerCase()) && u !== '%' - ) ? 0 + u : 0; - } else { - node.value = convert(num, u, opts); +function parseWord(node, opts, keepZeroUnit) { + const pair = unit(node.value); + if (pair) { + const num = Number(pair.number); + const u = pair.unit; + if (num === 0) { + node.value = + keepZeroUnit || (!~LENGTH_UNITS.indexOf(u.toLowerCase()) && u !== '%') + ? 0 + u + : 0; + } else { + node.value = convert(num, u, opts); - if ( - typeof opts.precision === 'number' && - u.toLowerCase() === 'px' && - ~pair.number.indexOf('.') - ) { - const precision = Math.pow(10, opts.precision); - node.value = Math.round(parseFloat(node.value) * precision) / precision + u; - } - } + if ( + typeof opts.precision === 'number' && + u.toLowerCase() === 'px' && + ~pair.number.indexOf('.') + ) { + const precision = Math.pow(10, opts.precision); + node.value = + Math.round(parseFloat(node.value) * precision) / precision + u; + } } + } } -function clampOpacity (node) { - const pair = unit(node.value); - if (!pair) { - return; - } - let num = Number(pair.number); - if (num > 1) { - node.value = 1 + pair.unit; - } else if (num < 0) { - node.value = 0 + pair.unit; - } +function clampOpacity(node) { + const pair = unit(node.value); + if (!pair) { + return; + } + let num = Number(pair.number); + if (num > 1) { + node.value = 1 + pair.unit; + } else if (num < 0) { + node.value = 0 + pair.unit; + } } -function shouldStripPercent (decl) { - const {parent} = decl; - const lowerCasedProp = decl.prop.toLowerCase(); - return ~decl.value.indexOf('%') && - (lowerCasedProp === 'max-height' || lowerCasedProp === 'height') || - parent.parent && - parent.parent.name && - parent.parent.name.toLowerCase() === 'keyframes' && - lowerCasedProp === 'stroke-dasharray' || - lowerCasedProp === 'stroke-dashoffset' || - lowerCasedProp === 'stroke-width'; +function shouldStripPercent(decl) { + const { parent } = decl; + const lowerCasedProp = decl.prop.toLowerCase(); + return ( + (~decl.value.indexOf('%') && + (lowerCasedProp === 'max-height' || lowerCasedProp === 'height')) || + (parent.parent && + parent.parent.name && + parent.parent.name.toLowerCase() === 'keyframes' && + lowerCasedProp === 'stroke-dasharray') || + lowerCasedProp === 'stroke-dashoffset' || + lowerCasedProp === 'stroke-width' + ); } -function transform (opts, decl) { - const lowerCasedProp = decl.prop.toLowerCase(); - if (~lowerCasedProp.indexOf('flex') || lowerCasedProp.indexOf('--') === 0) { - return; - } +function transform(opts, decl) { + const lowerCasedProp = decl.prop.toLowerCase(); + if (~lowerCasedProp.indexOf('flex') || lowerCasedProp.indexOf('--') === 0) { + return; + } - decl.value = valueParser(decl.value).walk(node => { - const lowerCasedValue = node.value.toLowerCase(); + decl.value = valueParser(decl.value) + .walk((node) => { + const lowerCasedValue = node.value.toLowerCase(); - if (node.type === 'word') { - parseWord(node, opts, shouldStripPercent(decl)); - if (lowerCasedProp === 'opacity' || lowerCasedProp === 'shape-image-threshold') { - clampOpacity(node); - } - } else if (node.type === 'function') { - if (lowerCasedValue === 'calc' || - lowerCasedValue === 'hsl' || - lowerCasedValue === 'hsla') { - walk(node.nodes, n => { - if (n.type === 'word') { - parseWord(n, opts, true); - } - }); - return false; - } - if (lowerCasedValue === 'url') { - return false; + if (node.type === 'word') { + parseWord(node, opts, shouldStripPercent(decl)); + if ( + lowerCasedProp === 'opacity' || + lowerCasedProp === 'shape-image-threshold' + ) { + clampOpacity(node); + } + } else if (node.type === 'function') { + if ( + lowerCasedValue === 'calc' || + lowerCasedValue === 'hsl' || + lowerCasedValue === 'hsla' + ) { + walk(node.nodes, (n) => { + if (n.type === 'word') { + parseWord(n, opts, true); } + }); + return false; + } + if (lowerCasedValue === 'url') { + return false; } - }).toString(); + } + }) + .toString(); } const plugin = 'postcss-convert-values'; -export default postcss.plugin(plugin, (opts = {precision: false}) => { - return css => css.walkDecls(transform.bind(null, opts)); +export default postcss.plugin(plugin, (opts = { precision: false }) => { + return (css) => css.walkDecls(transform.bind(null, opts)); }); diff --git a/packages/postcss-convert-values/src/lib/convert.js b/packages/postcss-convert-values/src/lib/convert.js index 1fe128f69..44bf0ac9a 100644 --- a/packages/postcss-convert-values/src/lib/convert.js +++ b/packages/postcss-convert-values/src/lib/convert.js @@ -1,76 +1,76 @@ const lengthConv = { - in: 96, - px: 1, - pt: 4 / 3, - pc: 16, + in: 96, + px: 1, + pt: 4 / 3, + pc: 16, }; const timeConv = { - s: 1000, - ms: 1, + s: 1000, + ms: 1, }; const angleConv = { - turn: 360, - deg: 1, + turn: 360, + deg: 1, }; -function dropLeadingZero (number) { - const value = String(number); +function dropLeadingZero(number) { + const value = String(number); - if (number % 1) { - if (value[0] === '0') { - return value.slice(1); - } + if (number % 1) { + if (value[0] === '0') { + return value.slice(1); + } - if (value[0] === '-' && value[1] === '0') { - return '-' + value.slice(2); - } + if (value[0] === '-' && value[1] === '0') { + return '-' + value.slice(2); } + } - return value; + return value; } -function transform (number, unit, conversion) { - const lowerCasedUnit = unit.toLowerCase(); - let one, base; - let convertionUnits = Object.keys(conversion).filter(u => { - if (conversion[u] === 1) { - one = u; - } - return lowerCasedUnit !== u; - }); - - if (lowerCasedUnit === one) { - base = number / conversion[lowerCasedUnit]; - } else { - base = number * conversion[lowerCasedUnit]; +function transform(number, unit, conversion) { + const lowerCasedUnit = unit.toLowerCase(); + let one, base; + let convertionUnits = Object.keys(conversion).filter((u) => { + if (conversion[u] === 1) { + one = u; } + return lowerCasedUnit !== u; + }); - return convertionUnits - .map(u => dropLeadingZero(base / conversion[u]) + u) - .reduce((a, b) => a.length < b.length ? a : b); + if (lowerCasedUnit === one) { + base = number / conversion[lowerCasedUnit]; + } else { + base = number * conversion[lowerCasedUnit]; + } + + return convertionUnits + .map((u) => dropLeadingZero(base / conversion[u]) + u) + .reduce((a, b) => (a.length < b.length ? a : b)); } -export default function (number, unit, {time, length, angle}) { - let value = dropLeadingZero(number) + (unit ? unit : ''); - let converted; +export default function(number, unit, { time, length, angle }) { + let value = dropLeadingZero(number) + (unit ? unit : ''); + let converted; - if (length !== false && unit.toLowerCase() in lengthConv) { - converted = transform(number, unit, lengthConv); - } + if (length !== false && unit.toLowerCase() in lengthConv) { + converted = transform(number, unit, lengthConv); + } - if (time !== false && unit.toLowerCase() in timeConv) { - converted = transform(number, unit, timeConv); - } + if (time !== false && unit.toLowerCase() in timeConv) { + converted = transform(number, unit, timeConv); + } - if (angle !== false && unit.toLowerCase() in angleConv) { - converted = transform(number, unit, angleConv); - } + if (angle !== false && unit.toLowerCase() in angleConv) { + converted = transform(number, unit, angleConv); + } - if (converted && converted.length < value.length) { - value = converted; - } + if (converted && converted.length < value.length) { + value = converted; + } - return value; + return value; } diff --git a/packages/postcss-discard-comments/src/__tests__/index.js b/packages/postcss-discard-comments/src/__tests__/index.js index 4a3f95a46..538184bca 100644 --- a/packages/postcss-discard-comments/src/__tests__/index.js +++ b/packages/postcss-discard-comments/src/__tests__/index.js @@ -1,266 +1,265 @@ import vars from 'postcss-simple-vars'; import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); test( - 'should remove non-special comments', - processCSS, - 'h1{font-weight:700!important/*test comment*/}', - 'h1{font-weight:700!important}' + 'should remove non-special comments', + processCSS, + 'h1{font-weight:700!important/*test comment*/}', + 'h1{font-weight:700!important}' ); test( - 'should remove non-special comments 2', - processCSS, - 'h1{/*test comment*/font-weight:700}', - 'h1{font-weight:700}' + 'should remove non-special comments 2', + processCSS, + 'h1{/*test comment*/font-weight:700}', + 'h1{font-weight:700}' ); test( - 'should remove non-special comments 3', - processCSS, - '/*test comment*/h1{font-weight:700}/*test comment*/', - 'h1{font-weight:700}' + 'should remove non-special comments 3', + processCSS, + '/*test comment*/h1{font-weight:700}/*test comment*/', + 'h1{font-weight:700}' ); test( - 'should remove non-special comments 4', - processCSS, - 'h1{font-weight:/*test comment*/700}', - 'h1{font-weight:700}' + 'should remove non-special comments 4', + processCSS, + 'h1{font-weight:/*test comment*/700}', + 'h1{font-weight:700}' ); test( - 'should remove non-special comments 5', - processCSS, - 'h1{margin:10px/*test*/20px}', - 'h1{margin:10px 20px}' + 'should remove non-special comments 5', + processCSS, + 'h1{margin:10px/*test*/20px}', + 'h1{margin:10px 20px}' ); test( - 'should remove non-special comments 6', - processCSS, - 'h1{margin:10px /*test*/ 20px /*test*/ 30px /*test*/ 40px}', - 'h1{margin:10px 20px 30px 40px}' + 'should remove non-special comments 6', + processCSS, + 'h1{margin:10px /*test*/ 20px /*test*/ 30px /*test*/ 40px}', + 'h1{margin:10px 20px 30px 40px}' ); test( - 'should remove non-special comments 7', - processCSS, - '/*comment*/*/*comment*/{margin:10px}', - '*{margin:10px}' + 'should remove non-special comments 7', + processCSS, + '/*comment*/*/*comment*/{margin:10px}', + '*{margin:10px}' ); test( - 'should remove non-special comments 8', - processCSS, - 'h1,/*comment*/ h2, h3/*comment*/{margin:20px}', - 'h1, h2, h3{margin:20px}' + 'should remove non-special comments 8', + processCSS, + 'h1,/*comment*/ h2, h3/*comment*/{margin:20px}', + 'h1, h2, h3{margin:20px}' ); test( - 'should remove non-special comments 9', - processCSS, - '@keyframes /*test*/ fade{0%{opacity:0}to{opacity:1}}', - '@keyframes fade{0%{opacity:0}to{opacity:1}}' + 'should remove non-special comments 9', + processCSS, + '@keyframes /*test*/ fade{0%{opacity:0}to{opacity:1}}', + '@keyframes fade{0%{opacity:0}to{opacity:1}}' ); test( - 'should remove non-special comments 10', - processCSS, - '@media only screen /*desktop*/ and (min-width:900px){body{margin:0 auto}}', - '@media only screen and (min-width:900px){body{margin:0 auto}}' + 'should remove non-special comments 10', + processCSS, + '@media only screen /*desktop*/ and (min-width:900px){body{margin:0 auto}}', + '@media only screen and (min-width:900px){body{margin:0 auto}}' ); test( - 'should remove non-special comments 11', - processCSS, - '@media only screen and (min-width:900px)/*test*/{body{margin:0 auto}}', - '@media only screen and (min-width:900px){body{margin:0 auto}}' + 'should remove non-special comments 11', + processCSS, + '@media only screen and (min-width:900px)/*test*/{body{margin:0 auto}}', + '@media only screen and (min-width:900px){body{margin:0 auto}}' ); test( - 'should remove non-special comments 12', - processCSS, - 'h1{margin/*test*/:20px}', - 'h1{margin:20px}' + 'should remove non-special comments 12', + processCSS, + 'h1{margin/*test*/:20px}', + 'h1{margin:20px}' ); test( - 'should remove non-special comments 13', - processCSS, - 'h1{margin:20px! /* test */ important}', - 'h1{margin:20px!important}' + 'should remove non-special comments 13', + processCSS, + 'h1{margin:20px! /* test */ important}', + 'h1{margin:20px!important}' ); test( - 'should keep special comments', - passthroughCSS, - 'h1{font-weight:700!important/*!test comment*/}' + 'should keep special comments', + passthroughCSS, + 'h1{font-weight:700!important/*!test comment*/}' ); test( - 'should keep special comments 2', - passthroughCSS, - 'h1{/*!test comment*/font-weight:700}' + 'should keep special comments 2', + passthroughCSS, + 'h1{/*!test comment*/font-weight:700}' ); test( - 'should keep special comments 3', - passthroughCSS, - '/*!test comment*/h1{font-weight:700}/*!test comment*/' + 'should keep special comments 3', + passthroughCSS, + '/*!test comment*/h1{font-weight:700}/*!test comment*/' ); test( - 'should keep special comments 4', - passthroughCSS, - 'h1{font-weight:/*!test comment*/700}' + 'should keep special comments 4', + passthroughCSS, + 'h1{font-weight:/*!test comment*/700}' ); test( - 'should keep special comments 5', - passthroughCSS, - 'h1{margin:10px/*!test*/20px}' + 'should keep special comments 5', + passthroughCSS, + 'h1{margin:10px/*!test*/20px}' ); test( - 'should keep special comments 6', - passthroughCSS, - 'h1{margin:10px /*!test*/ 20px /*!test*/ 30px /*!test*/ 40px}' + 'should keep special comments 6', + passthroughCSS, + 'h1{margin:10px /*!test*/ 20px /*!test*/ 30px /*!test*/ 40px}' ); test( - 'should keep special comments 7', - passthroughCSS, - '/*!comment*/*/*!comment*/{margin:10px}' + 'should keep special comments 7', + passthroughCSS, + '/*!comment*/*/*!comment*/{margin:10px}' ); test( - 'should keep special comments 8', - passthroughCSS, - 'h1,/*!comment*/h2,h3/*!comment*/{margin:20px}' + 'should keep special comments 8', + passthroughCSS, + 'h1,/*!comment*/h2,h3/*!comment*/{margin:20px}' ); test( - 'should keep special comments 9', - passthroughCSS, - '@keyframes /*!test*/ fade{0%{opacity:0}to{opacity:1}}' + 'should keep special comments 9', + passthroughCSS, + '@keyframes /*!test*/ fade{0%{opacity:0}to{opacity:1}}' ); test( - 'should keep special comments 10', - passthroughCSS, - '@media only screen /*!desktop*/ and (min-width:900px){body{margin:0 auto}}' + 'should keep special comments 10', + passthroughCSS, + '@media only screen /*!desktop*/ and (min-width:900px){body{margin:0 auto}}' ); test( - 'should keep special comments 11', - passthroughCSS, - '@media only screen and (min-width:900px)/*!test*/{body{margin:0 auto}}' + 'should keep special comments 11', + passthroughCSS, + '@media only screen and (min-width:900px)/*!test*/{body{margin:0 auto}}' ); test( - 'should keep special comments 12', - passthroughCSS, - 'h1{margin/*!test*/:20px}' + 'should keep special comments 12', + passthroughCSS, + 'h1{margin/*!test*/:20px}' ); test( - 'should keep special comments 13', - passthroughCSS, - 'h1{margin:20px! /*! test */ important}' + 'should keep special comments 13', + passthroughCSS, + 'h1{margin:20px! /*! test */ important}' ); test( - 'should remove comments marked as @ but keep other', - processCSS, - '/* keep *//*@ remove */h1{color:#000;/*@ remove */font-weight:700}', - '/* keep */h1{color:#000;font-weight:700}', - {remove: comment => comment[0] === "@"} + 'should remove comments marked as @ but keep other', + processCSS, + '/* keep *//*@ remove */h1{color:#000;/*@ remove */font-weight:700}', + '/* keep */h1{color:#000;font-weight:700}', + { remove: (comment) => comment[0] === '@' } ); test( - 'should remove all important comments, with a flag', - processCSS, - '/*!license*/h1{font-weight:700}/*!license 2*/h2{color:#000}', - 'h1{font-weight:700}h2{color:#000}', - {removeAll: true} + 'should remove all important comments, with a flag', + processCSS, + '/*!license*/h1{font-weight:700}/*!license 2*/h2{color:#000}', + 'h1{font-weight:700}h2{color:#000}', + { removeAll: true } ); test( - 'should remove all important comments but the first, with a flag', - processCSS, - '/*!license*/h1{font-weight:700}/*!license 2*/h2{color:#000}', - '/*!license*/h1{font-weight:700}h2{color:#000}', - {removeAllButFirst: true} + 'should remove all important comments but the first, with a flag', + processCSS, + '/*!license*/h1{font-weight:700}/*!license 2*/h2{color:#000}', + '/*!license*/h1{font-weight:700}h2{color:#000}', + { removeAllButFirst: true } ); test( - 'should remove non-special comments that have exclamation marks', - processCSS, - '/* This makes a heading black! Wow! */h1{color:#000}', - 'h1{color:#000}' + 'should remove non-special comments that have exclamation marks', + processCSS, + '/* This makes a heading black! Wow! */h1{color:#000}', + 'h1{color:#000}' ); // Looks we should remove this tests, because it is invalid syntax test.skip( - 'should handle space appropriately in selectors', - processCSS, - '.h/* ... */1{color:#000}', - '.h1{color:#000}' + 'should handle space appropriately in selectors', + processCSS, + '.h/* ... */1{color:#000}', + '.h1{color:#000}' ); // Looks we should remove this tests, because it is invalid syntax test.skip( - 'should handle space appropriately in properties', - processCSS, - 'h1{co/* ... */lor:#000}', - 'h1{color:#000}' + 'should handle space appropriately in properties', + processCSS, + 'h1{co/* ... */lor:#000}', + 'h1{color:#000}' ); test( - 'should remove block comments', - processCSS, - '/*\n\n# Pagination\n\n...\n\n*/.pagination{color:#000}', - '.pagination{color:#000}' + 'should remove block comments', + processCSS, + '/*\n\n# Pagination\n\n...\n\n*/.pagination{color:#000}', + '.pagination{color:#000}' ); test( - 'should pass through when it doesn\'t find a comment', - passthroughCSS, - 'h1{color:#000;font-weight:700}' + "should pass through when it doesn't find a comment", + passthroughCSS, + 'h1{color:#000;font-weight:700}' ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); test( - 'should pass through at rules without comments', - passthroughCSS, - '@page{body{font-size:1em}}' + 'should pass through at rules without comments', + passthroughCSS, + '@page{body{font-size:1em}}' ); -const {processCSS: singleLine} = processCSSFactory(plugin); +const { processCSS: singleLine } = processCSSFactory(plugin); test( - 'should work with single line comments', - singleLine, - '//!wow\n//wow\nh1{//color:red\n}', - '//!wow\nh1{\n}', - {syntax: require('postcss-scss')} + 'should work with single line comments', + singleLine, + '//!wow\n//wow\nh1{//color:red\n}', + '//!wow\nh1{\n}', + { syntax: require('postcss-scss') } ); -const {processCSS: otherPlugins} = processCSSFactory([vars(), plugin]); +const { processCSS: otherPlugins } = processCSSFactory([vars(), plugin]); test( - 'should handle comments from other plugins', - otherPlugins, - '$color: red; :root { box-shadow: inset 0 -10px 12px 0 $color, /* some comment */ inset 0 0 5px 0 $color; }', - ':root{ box-shadow:inset 0 -10px 12px 0 red, inset 0 0 5px 0 red; }' + 'should handle comments from other plugins', + otherPlugins, + '$color: red; :root { box-shadow: inset 0 -10px 12px 0 $color, /* some comment */ inset 0 0 5px 0 $color; }', + ':root{ box-shadow:inset 0 -10px 12px 0 red, inset 0 0 5px 0 red; }' ); diff --git a/packages/postcss-discard-comments/src/index.js b/packages/postcss-discard-comments/src/index.js index 0b2f01a52..d17fbc4cd 100644 --- a/packages/postcss-discard-comments/src/index.js +++ b/packages/postcss-discard-comments/src/index.js @@ -1,125 +1,113 @@ -import {plugin, list} from "postcss"; -import CommentRemover from "./lib/commentRemover"; -import commentParser from "./lib/commentParser"; +import { plugin, list } from 'postcss'; +import CommentRemover from './lib/commentRemover'; +import commentParser from './lib/commentParser'; -const {space} = list; +const { space } = list; -export default plugin("postcss-discard-comments", (opts = {}) => { - const remover = new CommentRemover(opts); - const matcherCache = {}; - const replacerCache = {}; +export default plugin('postcss-discard-comments', (opts = {}) => { + const remover = new CommentRemover(opts); + const matcherCache = {}; + const replacerCache = {}; - function matchesComments (source) { - if (matcherCache[source]) { - return matcherCache[source]; - } + function matchesComments(source) { + if (matcherCache[source]) { + return matcherCache[source]; + } + + const result = commentParser(source).filter(([type]) => type); - const result = commentParser(source).filter(([type]) => type); + matcherCache[source] = result; - matcherCache[source] = result; + return result; + } - return result; + function replaceComments(source, separator = ' ') { + const key = source + '@|@' + separator; + + if (replacerCache[key]) { + return replacerCache[key]; } - function replaceComments (source, separator = " ") { - const key = source + "@|@" + separator; + const parsed = commentParser(source).reduce((value, [type, start, end]) => { + const contents = source.slice(start, end); + + if (!type) { + return value + contents; + } + + if (remover.canRemove(contents)) { + return value + separator; + } + + return `${value}/*${contents}*/`; + }, ''); + + const result = space(parsed).join(' '); + + replacerCache[key] = result; + + return result; + } - if (replacerCache[key]) { - return replacerCache[key]; + return (css) => { + css.walk((node) => { + if (node.type === 'comment' && remover.canRemove(node.text)) { + node.remove(); + + return; + } + + if (node.raws.between) { + node.raws.between = replaceComments(node.raws.between); + } + + if (node.type === 'decl') { + if (node.raws.value && node.raws.value.raw) { + if (node.raws.value.value === node.value) { + node.value = replaceComments(node.raws.value.raw); + } else { + node.value = replaceComments(node.value); + } + + node.raws.value = null; } - const parsed = commentParser(source).reduce( - (value, [type, start, end]) => { - const contents = source.slice(start, end); + if (node.raws.important) { + node.raws.important = replaceComments(node.raws.important); - if (!type) { - return value + contents; - } + const b = matchesComments(node.raws.important); - if (remover.canRemove(contents)) { - return value + separator; - } + node.raws.important = b.length ? node.raws.important : '!important'; + } - return `${value}/*${contents}*/`; - }, - "" - ); + return; + } - const result = space(parsed).join(" "); + if ( + node.type === 'rule' && + node.raws.selector && + node.raws.selector.raw + ) { + node.raws.selector.raw = replaceComments(node.raws.selector.raw, ''); - replacerCache[key] = result; + return; + } - return result; - } + if (node.type === 'atrule') { + if (node.raws.afterName) { + const commentsReplaced = replaceComments(node.raws.afterName); + + if (!commentsReplaced.length) { + node.raws.afterName = commentsReplaced + ' '; + } else { + node.raws.afterName = ' ' + commentsReplaced + ' '; + } + } - return css => { - css.walk(node => { - if (node.type === "comment" && remover.canRemove(node.text)) { - node.remove(); - - return; - } - - if (node.raws.between) { - node.raws.between = replaceComments(node.raws.between); - } - - if (node.type === "decl") { - if (node.raws.value && node.raws.value.raw) { - if (node.raws.value.value === node.value) { - node.value = replaceComments(node.raws.value.raw); - } else { - node.value = replaceComments(node.value); - } - - node.raws.value = null; - } - - if (node.raws.important) { - node.raws.important = replaceComments(node.raws.important); - - const b = matchesComments(node.raws.important); - - node.raws.important = b.length - ? node.raws.important - : "!important"; - } - - return; - } - - if ( - node.type === "rule" && - node.raws.selector && - node.raws.selector.raw - ) { - node.raws.selector.raw = replaceComments( - node.raws.selector.raw, - "" - ); - - return; - } - - if (node.type === "atrule") { - if (node.raws.afterName) { - const commentsReplaced = replaceComments( - node.raws.afterName - ); - - if (!commentsReplaced.length) { - node.raws.afterName = commentsReplaced + " "; - } else { - node.raws.afterName = " " + commentsReplaced + " "; - } - } - - if (node.raws.params && node.raws.params.raw) { - node.raws.params.raw = replaceComments( - node.raws.params.raw - ); - } - } - }); - }; + if (node.raws.params && node.raws.params.raw) { + node.raws.params.raw = replaceComments(node.raws.params.raw); + } + } + }); + }; }); diff --git a/packages/postcss-discard-comments/src/lib/commentParser.js b/packages/postcss-discard-comments/src/lib/commentParser.js index 5b2a53259..510e4fcb0 100644 --- a/packages/postcss-discard-comments/src/lib/commentParser.js +++ b/packages/postcss-discard-comments/src/lib/commentParser.js @@ -1,24 +1,24 @@ -export default function commentParser (input) { - const tokens = []; - const length = input.length; - let pos = 0; - let next; +export default function commentParser(input) { + const tokens = []; + const length = input.length; + let pos = 0; + let next; - while (pos < length) { - next = input.indexOf('/*', pos); + while (pos < length) { + next = input.indexOf('/*', pos); - if (~next) { - tokens.push([0, pos, next]); - pos = next; + if (~next) { + tokens.push([0, pos, next]); + pos = next; - next = input.indexOf('*/', pos + 2); - tokens.push([1, pos + 2, next]); - pos = next + 2; - } else { - tokens.push([0, pos, length]); - pos = length; - } + next = input.indexOf('*/', pos + 2); + tokens.push([1, pos + 2, next]); + pos = next + 2; + } else { + tokens.push([0, pos, length]); + pos = length; } + } - return tokens; -}; + return tokens; +} diff --git a/packages/postcss-discard-comments/src/lib/commentRemover.js b/packages/postcss-discard-comments/src/lib/commentRemover.js index de0c546c4..003638569 100644 --- a/packages/postcss-discard-comments/src/lib/commentRemover.js +++ b/packages/postcss-discard-comments/src/lib/commentRemover.js @@ -1,26 +1,26 @@ -function CommentRemover (options) { - this.options = options; +function CommentRemover(options) { + this.options = options; } -CommentRemover.prototype.canRemove = function (comment) { - const remove = this.options.remove; +CommentRemover.prototype.canRemove = function(comment) { + const remove = this.options.remove; - if (remove) { - return remove(comment); - } else { - const isImportant = comment.indexOf('!') === 0; + if (remove) { + return remove(comment); + } else { + const isImportant = comment.indexOf('!') === 0; - if (!isImportant) { - return true; - } + if (!isImportant) { + return true; + } - if (this.options.removeAll || this._hasFirst) { - return true; - } else if (this.options.removeAllButFirst && !this._hasFirst) { - this._hasFirst = true; - return false; - } + if (this.options.removeAll || this._hasFirst) { + return true; + } else if (this.options.removeAllButFirst && !this._hasFirst) { + this._hasFirst = true; + return false; } + } }; export default CommentRemover; diff --git a/packages/postcss-discard-duplicates/src/__tests__/index.js b/packages/postcss-discard-duplicates/src/__tests__/index.js index 20ea62f74..a741f5304 100644 --- a/packages/postcss-discard-duplicates/src/__tests__/index.js +++ b/packages/postcss-discard-duplicates/src/__tests__/index.js @@ -1,162 +1,161 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); test( - 'should remove duplicate rules', - processCSS, - 'h1{font-weight:bold}h1{font-weight:bold}', - 'h1{font-weight:bold}' + 'should remove duplicate rules', + processCSS, + 'h1{font-weight:bold}h1{font-weight:bold}', + 'h1{font-weight:bold}' ); test( - 'should remove duplicate rules (2)', - processCSS, - 'h1{color:#000}h2{color:#fff}h1{color:#000}', - 'h2{color:#fff}h1{color:#000}' + 'should remove duplicate rules (2)', + processCSS, + 'h1{color:#000}h2{color:#fff}h1{color:#000}', + 'h2{color:#fff}h1{color:#000}' ); test( - 'should remove duplicate rules (3)', - processCSS, - 'h1 { font-weight: bold }\nh1{font-weight:bold}', - 'h1{font-weight:bold}' + 'should remove duplicate rules (3)', + processCSS, + 'h1 { font-weight: bold }\nh1{font-weight:bold}', + 'h1{font-weight:bold}' ); test( - 'should remove duplicate declarations', - processCSS, - 'h1{font-weight:bold;font-weight:bold}', - 'h1{font-weight:bold}' + 'should remove duplicate declarations', + processCSS, + 'h1{font-weight:bold;font-weight:bold}', + 'h1{font-weight:bold}' ); test( - 'should remove duplicate declarations, with comments', - processCSS, - 'h1{/*test*/font-weight:bold}h1{/*test*/font-weight:bold}', - 'h1{/*test*/font-weight:bold}' + 'should remove duplicate declarations, with comments', + processCSS, + 'h1{/*test*/font-weight:bold}h1{/*test*/font-weight:bold}', + 'h1{/*test*/font-weight:bold}' ); test( - 'should remove duplicate @rules', - processCSS, - '@charset "utf-8";@charset "utf-8";', - '@charset "utf-8";' + 'should remove duplicate @rules', + processCSS, + '@charset "utf-8";@charset "utf-8";', + '@charset "utf-8";' ); test( - 'should remove duplicate @rules (2)', - processCSS, - '@charset "utf-8";@charset "hello!";@charset "utf-8";', - '@charset "hello!";@charset "utf-8";' + 'should remove duplicate @rules (2)', + processCSS, + '@charset "utf-8";@charset "hello!";@charset "utf-8";', + '@charset "hello!";@charset "utf-8";' ); test( - 'should remove duplicates inside @media queries', - processCSS, - '@media print{h1{display:block}h1{display:block}}', - '@media print{h1{display:block}}' + 'should remove duplicates inside @media queries', + processCSS, + '@media print{h1{display:block}h1{display:block}}', + '@media print{h1{display:block}}' ); test( - 'should remove duplicate @media queries', - processCSS, - '@media print{h1{display:block}}@media print{h1{display:block}}', - '@media print{h1{display:block}}' + 'should remove duplicate @media queries', + processCSS, + '@media print{h1{display:block}}@media print{h1{display:block}}', + '@media print{h1{display:block}}' ); test( - 'should not mangle same keyframe rules but with different vendors', - passthroughCSS, - '@-webkit-keyframes flash{0%,50%,100%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,50%,100%{opacity:1}25%,75%{opacity:0}}' + 'should not mangle same keyframe rules but with different vendors', + passthroughCSS, + '@-webkit-keyframes flash{0%,50%,100%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,50%,100%{opacity:1}25%,75%{opacity:0}}' ); test( - 'should not merge across keyframes', - passthroughCSS, - '@-webkit-keyframes test{0%{color:#000}to{color:#fff}}@keyframes test{0%{color:#000}to{color:#fff}}' + 'should not merge across keyframes', + passthroughCSS, + '@-webkit-keyframes test{0%{color:#000}to{color:#fff}}@keyframes test{0%{color:#000}to{color:#fff}}' ); test( - 'should not merge across keyframes (2)', - passthroughCSS, - '@-webkit-keyframes slideInDown{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes slideInDown{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}to{-webkit-transform:translateY(0);transform:translateY(0)}}' + 'should not merge across keyframes (2)', + passthroughCSS, + '@-webkit-keyframes slideInDown{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes slideInDown{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}to{-webkit-transform:translateY(0);transform:translateY(0)}}' ); test( - 'should remove declarations before rules', - processCSS, - 'h1{font-weight:bold;font-weight:bold}h1{font-weight:bold}', - 'h1{font-weight:bold}' + 'should remove declarations before rules', + processCSS, + 'h1{font-weight:bold;font-weight:bold}h1{font-weight:bold}', + 'h1{font-weight:bold}' ); test( - 'should not deduplicate comments', - passthroughCSS, - 'h1{color:#000}/*test*/h2{color:#fff}/*test*/' + 'should not deduplicate comments', + passthroughCSS, + 'h1{color:#000}/*test*/h2{color:#fff}/*test*/' ); test( - 'should not remove declarations when selectors are different', - passthroughCSS, - 'h1{font-weight:bold}h2{font-weight:bold}' + 'should not remove declarations when selectors are different', + passthroughCSS, + 'h1{font-weight:bold}h2{font-weight:bold}' ); test( - 'should not remove across contexts', - passthroughCSS, - 'h1{display:block}@media print{h1{display:block}}' + 'should not remove across contexts', + passthroughCSS, + 'h1{display:block}@media print{h1{display:block}}' ); test( - 'should not be responsible for normalising selectors', - passthroughCSS, - 'h1,h2{font-weight:bold}h2,h1{font-weight:bold}' + 'should not be responsible for normalising selectors', + passthroughCSS, + 'h1,h2{font-weight:bold}h2,h1{font-weight:bold}' ); test( - 'should not be responsible for normalising declarations', - passthroughCSS, - 'h1{margin:10px 0 10px 0;margin:10px 0}' + 'should not be responsible for normalising declarations', + passthroughCSS, + 'h1{margin:10px 0 10px 0;margin:10px 0}' ); test( - 'should remove duplicate rules and declarations', - processCSS, - 'h1{color:#000}h2{color:#fff}h1{color:#000;color:#000}', - 'h2{color:#fff}h1{color:#000}' + 'should remove duplicate rules and declarations', + processCSS, + 'h1{color:#000}h2{color:#fff}h1{color:#000;color:#000}', + 'h2{color:#fff}h1{color:#000}' ); test( - 'should remove differently ordered duplicates', - processCSS, - 'h1{color:black;font-size:12px}h1{font-size:12px;color:black}', - 'h1{font-size:12px;color:black}' + 'should remove differently ordered duplicates', + processCSS, + 'h1{color:black;font-size:12px}h1{font-size:12px;color:black}', + 'h1{font-size:12px;color:black}' ); test( - 'should remove partial duplicates', - processCSS, - 'h1{color:red;background:blue}h1{color:red}', - 'h1{background:blue}h1{color:red}' + 'should remove partial duplicates', + processCSS, + 'h1{color:red;background:blue}h1{color:red}', + 'h1{background:blue}h1{color:red}' ); test( - 'should preserve browser hacks (1)', - passthroughCSS, - 'h1{_color:white;color:white}' + 'should preserve browser hacks (1)', + passthroughCSS, + 'h1{_color:white;color:white}' ); test( - 'should preserve browser hacks (2)', - passthroughCSS, - '@media \0 all {}@media all {}' + 'should preserve browser hacks (2)', + passthroughCSS, + '@media \0 all {}@media all {}' ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-discard-duplicates/src/index.js b/packages/postcss-discard-duplicates/src/index.js index 6d7e5c31f..ecaa00b8d 100644 --- a/packages/postcss-discard-duplicates/src/index.js +++ b/packages/postcss-discard-duplicates/src/index.js @@ -1,132 +1,127 @@ -import {plugin} from 'postcss'; +import { plugin } from 'postcss'; -function noop () {} +function noop() {} -function trimValue (value) { - return value ? value.trim() : value; +function trimValue(value) { + return value ? value.trim() : value; } -function empty (node) { - return !node.nodes - .filter(child => child.type !== 'comment') - .length; +function empty(node) { + return !node.nodes.filter((child) => child.type !== 'comment').length; } -function equals (a, b) { - if (a.type !== b.type) { - return false; - } +function equals(a, b) { + if (a.type !== b.type) { + return false; + } - if (a.important !== b.important) { - return false; - } + if (a.important !== b.important) { + return false; + } - if ((a.raws && !b.raws) || (!a.raws && b.raws)) { - return false; - } + if ((a.raws && !b.raws) || (!a.raws && b.raws)) { + return false; + } - switch (a.type) { + switch (a.type) { case 'rule': - if (a.selector !== b.selector) { - return false; - } - break; + if (a.selector !== b.selector) { + return false; + } + break; case 'atrule': - if (a.name !== b.name || a.params !== b.params) { - return false; - } + if (a.name !== b.name || a.params !== b.params) { + return false; + } - if (a.raws && trimValue(a.raws.before) !== trimValue(b.raws.before)) { - return false; - } + if (a.raws && trimValue(a.raws.before) !== trimValue(b.raws.before)) { + return false; + } - if (a.raws && trimValue(a.raws.afterName) !== trimValue(b.raws.afterName)) { - return false; - } - break; + if ( + a.raws && + trimValue(a.raws.afterName) !== trimValue(b.raws.afterName) + ) { + return false; + } + break; case 'decl': - if (a.prop !== b.prop || a.value !== b.value) { - return false; - } + if (a.prop !== b.prop || a.value !== b.value) { + return false; + } - if (a.raws && trimValue(a.raws.before) !== trimValue(b.raws.before)) { - return false; - } - break; - } + if (a.raws && trimValue(a.raws.before) !== trimValue(b.raws.before)) { + return false; + } + break; + } - if (a.nodes) { - if (a.nodes.length !== b.nodes.length) { - return false; - } + if (a.nodes) { + if (a.nodes.length !== b.nodes.length) { + return false; + } - for (let i = 0; i < a.nodes.length; i++) { - if (!equals(a.nodes[i], b.nodes[i])) { - return false; - } - } + for (let i = 0; i < a.nodes.length; i++) { + if (!equals(a.nodes[i], b.nodes[i])) { + return false; + } } - return true; + } + return true; } -function dedupeRule (last, nodes) { - let index = nodes.indexOf(last) -1; - while (index >= 0) { - const node = nodes[index--]; - if ( - node && - node.type === 'rule' && - node.selector === last.selector - ) { - last.each(child => { - if (child.type === 'decl') { - dedupeNode(child, node.nodes); - } - }); - - if (empty(node)) { - node.remove(); - } +function dedupeRule(last, nodes) { + let index = nodes.indexOf(last) - 1; + while (index >= 0) { + const node = nodes[index--]; + if (node && node.type === 'rule' && node.selector === last.selector) { + last.each((child) => { + if (child.type === 'decl') { + dedupeNode(child, node.nodes); } + }); + + if (empty(node)) { + node.remove(); + } } + } } -function dedupeNode (last, nodes) { - let index = !!~nodes.indexOf(last) - ? nodes.indexOf(last) - 1 - : nodes.length - 1; +function dedupeNode(last, nodes) { + let index = ~nodes.indexOf(last) ? nodes.indexOf(last) - 1 : nodes.length - 1; - while (index >= 0) { - const node = nodes[index--]; - if (node && equals(node, last)) { - node.remove(); - } + while (index >= 0) { + const node = nodes[index--]; + if (node && equals(node, last)) { + node.remove(); } + } } const handlers = { - rule: dedupeRule, - atrule: dedupeNode, - decl: dedupeNode, - comment: noop, + rule: dedupeRule, + atrule: dedupeNode, + decl: dedupeNode, + comment: noop, }; -function dedupe (root) { - const {nodes} = root; +function dedupe(root) { + const { nodes } = root; - if (!nodes) { - return; - } + if (!nodes) { + return; + } - let index = nodes.length - 1; - while (index >= 0) { - let last = nodes[index--]; - if (!last || !last.parent) { - continue; - } - dedupe(last); - handlers[last.type](last, nodes); + let index = nodes.length - 1; + while (index >= 0) { + let last = nodes[index--]; + if (!last || !last.parent) { + continue; } + dedupe(last); + handlers[last.type](last, nodes); + } } export default plugin('postcss-discard-duplicates', () => dedupe); diff --git a/packages/postcss-discard-empty/src/__tests__/index.js b/packages/postcss-discard-empty/src/__tests__/index.js index 102e2cdc2..1890ca454 100644 --- a/packages/postcss-discard-empty/src/__tests__/index.js +++ b/packages/postcss-discard-empty/src/__tests__/index.js @@ -1,120 +1,95 @@ import test from 'ava'; import plugin from '..'; import { - usePostCSSPlugin, - processCSSFactory, - processCSSWithPresetFactory, + usePostCSSPlugin, + processCSSFactory, + processCSSWithPresetFactory, } from '../../../../util/testHelpers'; -const {passthroughCSS, processor} = processCSSFactory(plugin); -const {processCSS: withDefaultPreset} = processCSSWithPresetFactory('default'); - -function testRemovals (t, fixture, expected, removedSelectors) { - return processor(fixture).then(result => { - removedSelectors.forEach((removedSelector) => { - const message = result.messages.some((m) => { - return m.plugin === 'postcss-discard-empty' && - m.type === 'removal' && - m.node.selector === removedSelector; - }); - - if (!message) { - t.fail('expected selector `' + removedSelector + '` was not removed'); - } - }); - - result.messages.forEach((m) => { - if ( - m.plugin !== 'postcss-discard-empty' || - m.type !== 'removal' || - m.selector !== undefined || - ~removedSelectors.indexOf(m.selector) - ) { - t.fail('unexpected selector `' + m.selector + '` was removed'); - } - }); - - t.deepEqual(result.css, expected); +const { passthroughCSS, processor } = processCSSFactory(plugin); +const { processCSS: withDefaultPreset } = processCSSWithPresetFactory( + 'default' +); + +function testRemovals(t, fixture, expected, removedSelectors) { + return processor(fixture).then((result) => { + removedSelectors.forEach((removedSelector) => { + const message = result.messages.some((m) => { + return ( + m.plugin === 'postcss-discard-empty' && + m.type === 'removal' && + m.node.selector === removedSelector + ); + }); + + if (!message) { + t.fail('expected selector `' + removedSelector + '` was not removed'); + } }); + + result.messages.forEach((m) => { + if ( + m.plugin !== 'postcss-discard-empty' || + m.type !== 'removal' || + m.selector !== undefined || + ~removedSelectors.indexOf(m.selector) + ) { + t.fail('unexpected selector `' + m.selector + '` was removed'); + } + }); + + t.deepEqual(result.css, expected); + }); } -test( - 'should remove empty @ rules', - withDefaultPreset, - '@font-face;', - '' -); +test('should remove empty @ rules', withDefaultPreset, '@font-face;', ''); -test( - 'should remove empty @ rules (2)', - withDefaultPreset, - '@font-face {}', - '' -); +test('should remove empty @ rules (2)', withDefaultPreset, '@font-face {}', ''); test( - 'should not mangle @ rules with decls', - passthroughCSS, - '@font-face {font-family: Helvetica}' + 'should not mangle @ rules with decls', + passthroughCSS, + '@font-face {font-family: Helvetica}' ); test( - 'should not mangle @ rules with parameters', - passthroughCSS, - '@charset "utf-8";' + 'should not mangle @ rules with parameters', + passthroughCSS, + '@charset "utf-8";' ); -test( - 'should remove empty rules', - withDefaultPreset, - 'h1{}h2{}h4{}h5,h6{}', - '' -); +test('should remove empty rules', withDefaultPreset, 'h1{}h2{}h4{}h5,h6{}', ''); -test( - 'should remove empty declarations', - withDefaultPreset, - 'h1{color:}', - '' -); +test('should remove empty declarations', withDefaultPreset, 'h1{color:}', ''); -test( - 'should remove null selectors', - withDefaultPreset, - '{color:blue}', - '' -); +test('should remove null selectors', withDefaultPreset, '{color:blue}', ''); test( - 'should remove null selectors in media queries', - withDefaultPreset, - '@media screen, print {{}}', - '' + 'should remove null selectors in media queries', + withDefaultPreset, + '@media screen, print {{}}', + '' ); test( - 'should remove empty media queries', - withDefaultPreset, - '@media screen, print {h1,h2{}}', - '' + 'should remove empty media queries', + withDefaultPreset, + '@media screen, print {h1,h2{}}', + '' ); test( - 'should not be responsible for removing comments', - passthroughCSS, - 'h1{/*comment*/}' + 'should not be responsible for removing comments', + passthroughCSS, + 'h1{/*comment*/}' ); test( - 'should report removed selectors', - testRemovals, - 'h1{}.hot{}.a.b{}{}@media screen, print{h1,h2{}}', - '', - ['h1', '.hot', '.a.b', '', 'h1,h2'] + 'should report removed selectors', + testRemovals, + 'h1{}.hot{}.a.b{}{}@media screen, print{h1,h2{}}', + '', + ['h1', '.hot', '.a.b', '', 'h1,h2'] ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-discard-empty/src/index.js b/packages/postcss-discard-empty/src/index.js index dc85b4e2e..5a9e26376 100644 --- a/packages/postcss-discard-empty/src/index.js +++ b/packages/postcss-discard-empty/src/index.js @@ -2,30 +2,30 @@ import postcss from 'postcss'; const plugin = 'postcss-discard-empty'; -function discardAndReport (css, result) { - function discardEmpty (node) { - const {type, nodes: sub, params} = node; +function discardAndReport(css, result) { + function discardEmpty(node) { + const { type, nodes: sub, params } = node; - if (sub) { - node.each(discardEmpty); - } + if (sub) { + node.each(discardEmpty); + } - if ( - (type === 'decl' && !node.value) || - (type === 'rule' && !node.selector || sub && !sub.length) || - (type === 'atrule' && (!sub && !params || !params && !sub.length)) - ) { - node.remove(); + if ( + (type === 'decl' && !node.value) || + ((type === 'rule' && !node.selector) || (sub && !sub.length)) || + (type === 'atrule' && ((!sub && !params) || (!params && !sub.length))) + ) { + node.remove(); - result.messages.push({ - type: 'removal', - plugin, - node, - }); - } + result.messages.push({ + type: 'removal', + plugin, + node, + }); } + } - css.each(discardEmpty); + css.each(discardEmpty); } export default postcss.plugin(plugin, () => discardAndReport); diff --git a/packages/postcss-discard-overridden/src/__tests__/index.js b/packages/postcss-discard-overridden/src/__tests__/index.js index 7e123ae33..1752ac305 100644 --- a/packages/postcss-discard-overridden/src/__tests__/index.js +++ b/packages/postcss-discard-overridden/src/__tests__/index.js @@ -1,63 +1,62 @@ import fs from 'fs'; import postcss from 'postcss'; import test from 'ava'; -import {diffLines} from 'diff'; +import { diffLines } from 'diff'; import chalk from 'chalk'; import plugin from '../'; -function getDiff (left, right) { - let msg = ['\n']; - diffLines(left, right).forEach(item => { - if (item.added || item.removed) { - let text = item.value - .replace('\n', '\u00b6\n') - .replace('\ufeff', '[[BOM]]'); - msg.push(chalk[item.added ? 'green' : 'red'](text)); - } else { - let value = item.value.replace('\ufeff', '[[BOM]]'); - let lines = value.split('\n'); +function getDiff(left, right) { + let msg = ['\n']; + diffLines(left, right).forEach((item) => { + if (item.added || item.removed) { + let text = item.value + .replace('\n', '\u00b6\n') + .replace('\ufeff', '[[BOM]]'); + msg.push(chalk[item.added ? 'green' : 'red'](text)); + } else { + let value = item.value.replace('\ufeff', '[[BOM]]'); + let lines = value.split('\n'); - // max line count for each item - let keepLines = 6; - // lines to be omitted - let omitLines = lines.length - keepLines; - if (lines.length > keepLines) { - lines.splice( - Math.floor(keepLines / 2), - omitLines, - chalk.gray('(...' + omitLines + ' lines omitted...)') - ); - } - msg.concat(lines); - } - }); - msg.push('\n'); - return msg.map(line => ' ' + line).join(''); + // max line count for each item + let keepLines = 6; + // lines to be omitted + let omitLines = lines.length - keepLines; + if (lines.length > keepLines) { + lines.splice( + Math.floor(keepLines / 2), + omitLines, + chalk.gray('(...' + omitLines + ' lines omitted...)') + ); + } + msg.concat(lines); + } + }); + msg.push('\n'); + return msg.map((line) => ' ' + line).join(''); } -function read (file) { - return fs.readFileSync(__dirname + `/fixtures/${file}.css`, {encoding: 'utf-8'}); +function read(file) { + return fs.readFileSync(__dirname + `/fixtures/${file}.css`, { + encoding: 'utf-8', + }); } -function exec (t, input) { - let output = read(`${input}.post`); - return postcss([ plugin() ]).process(read(input)) - .then( result => { - if (result.css !== output) { - t.fail(getDiff(result.css, output)); - } - t.deepEqual(result.warnings().length, 0); - }); +function exec(t, input) { + let output = read(`${input}.post`); + return postcss([plugin()]) + .process(read(input)) + .then((result) => { + if (result.css !== output) { + t.fail(getDiff(result.css, output)); + } + t.deepEqual(result.warnings().length, 0); + }); } -test( - 'Overridden @keyframes should be discarded correctly.', - exec, - 'keyframes' -); +test('Overridden @keyframes should be discarded correctly.', exec, 'keyframes'); test( - 'Overridden @counter-style should be discarded correctly.', - exec, - 'counter-style' + 'Overridden @counter-style should be discarded correctly.', + exec, + 'counter-style' ); diff --git a/packages/postcss-discard-overridden/src/index.js b/packages/postcss-discard-overridden/src/index.js index bf5857fc1..0f40e97b5 100644 --- a/packages/postcss-discard-overridden/src/index.js +++ b/packages/postcss-discard-overridden/src/index.js @@ -3,44 +3,52 @@ import postcss from 'postcss'; const OVERRIDABLE_RULES = ['keyframes', 'counter-style']; const SCOPE_RULES = ['media', 'supports']; -function isOverridable (name) { - return ~OVERRIDABLE_RULES.indexOf(postcss.vendor.unprefixed(name.toLowerCase())); +function isOverridable(name) { + return ~OVERRIDABLE_RULES.indexOf( + postcss.vendor.unprefixed(name.toLowerCase()) + ); } -function isScope (name) { - return ~SCOPE_RULES.indexOf(postcss.vendor.unprefixed(name.toLowerCase())); +function isScope(name) { + return ~SCOPE_RULES.indexOf(postcss.vendor.unprefixed(name.toLowerCase())); } -function getScope (node) { - let current = node.parent; - const chain = [node.name.toLowerCase(), node.params]; - do { - if (current.type === 'atrule' && isScope(current.name)) { - chain.unshift(current.name + ' ' + current.params); - } - current = current.parent; - } while (current); - return chain.join('|'); +function getScope(node) { + let current = node.parent; + + const chain = [node.name.toLowerCase(), node.params]; + + do { + if (current.type === 'atrule' && isScope(current.name)) { + chain.unshift(current.name + ' ' + current.params); + } + current = current.parent; + } while (current); + + return chain.join('|'); } export default postcss.plugin('postcss-discard-overridden', () => { - return css => { - const cache = {}; - const rules = []; - css.walkAtRules(node => { - if (isOverridable(node.name)) { - const scope = getScope(node); - cache[scope] = node; - rules.push({ - node, - scope, - }); - } - }); - rules.forEach(rule => { - if (cache[rule.scope] !== rule.node) { - rule.node.remove(); - } + return (css) => { + const cache = {}; + const rules = []; + + css.walkAtRules((node) => { + if (isOverridable(node.name)) { + const scope = getScope(node); + + cache[scope] = node; + rules.push({ + node, + scope, }); - }; + } + }); + + rules.forEach((rule) => { + if (cache[rule.scope] !== rule.node) { + rule.node.remove(); + } + }); + }; }); diff --git a/packages/postcss-discard-unused/src/__tests__/index.js b/packages/postcss-discard-unused/src/__tests__/index.js index 56bb24be5..dc7dffcc8 100644 --- a/packages/postcss-discard-unused/src/__tests__/index.js +++ b/packages/postcss-discard-unused/src/__tests__/index.js @@ -1,148 +1,147 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); test( - 'should remove unused counter styles', - processCSS, - '@counter-style custom{system:extends decimal;suffix:"> "}', - '' + 'should remove unused counter styles', + processCSS, + '@counter-style custom{system:extends decimal;suffix:"> "}', + '' ); test( - 'should be aware of extensions', - passthroughCSS, - '@counter-style custom{system:extends decimal;suffix:"> "}@counter-style custom2{system:extends custom;suffix:"| "}a{list-style: custom2}' + 'should be aware of extensions', + passthroughCSS, + '@counter-style custom{system:extends decimal;suffix:"> "}@counter-style custom2{system:extends custom;suffix:"| "}a{list-style: custom2}' ); test( - 'should remove unused counters & keep used counters', - processCSS, - '@counter-style custom{system:extends decimal;suffix:"> "}@counter-style custom2{system:extends decimal;suffix:"| "}a{list-style: custom2}', - '@counter-style custom2{system:extends decimal;suffix:"| "}a{list-style: custom2}' + 'should remove unused counters & keep used counters', + processCSS, + '@counter-style custom{system:extends decimal;suffix:"> "}@counter-style custom2{system:extends decimal;suffix:"| "}a{list-style: custom2}', + '@counter-style custom2{system:extends decimal;suffix:"| "}a{list-style: custom2}' ); test( - 'should remove counter styles if they have no identifier', - processCSS, - '@counter-style {system:extends decimal;suffix:"> "}', - '' + 'should remove counter styles if they have no identifier', + processCSS, + '@counter-style {system:extends decimal;suffix:"> "}', + '' ); test( - 'should remove unused keyframes', - processCSS, - '@keyframes fadeOut{0%{opacity:1}to{opacity:0}}', - '' + 'should remove unused keyframes', + processCSS, + '@keyframes fadeOut{0%{opacity:1}to{opacity:0}}', + '' ); test( - 'should remove unused keyframes & keep used keyframes', - processCSS, - '@keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}a{animation-name:fadeIn}', - '@keyframes fadeIn{0%{opacity:0}to{opacity:1}}a{animation-name:fadeIn}' + 'should remove unused keyframes & keep used keyframes', + processCSS, + '@keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}a{animation-name:fadeIn}', + '@keyframes fadeIn{0%{opacity:0}to{opacity:1}}a{animation-name:fadeIn}' ); test( - 'should remove keyframes if they have no identifier', - processCSS, - '@keyframes {0%{opacity:0}to{opacity:1}}', - '' + 'should remove keyframes if they have no identifier', + processCSS, + '@keyframes {0%{opacity:0}to{opacity:1}}', + '' ); test( - 'should support multiple animations', - passthroughCSS, - '@keyframes one{0%{transform:rotate(0deg)}to{transform:rotate(360deg)}}@keyframes two{0%{border-width:0;opacity:0}}.loader{animation:one 1250ms infinite linear,two .3s ease-out both}' + 'should support multiple animations', + passthroughCSS, + '@keyframes one{0%{transform:rotate(0deg)}to{transform:rotate(360deg)}}@keyframes two{0%{border-width:0;opacity:0}}.loader{animation:one 1250ms infinite linear,two .3s ease-out both}' ); test( - 'should remove unused fonts', - processCSS, - '@font-face {font-family:"Does Not Exist";src:url("fonts/does-not-exist.ttf") format("truetype")}', - '' + 'should remove unused fonts', + processCSS, + '@font-face {font-family:"Does Not Exist";src:url("fonts/does-not-exist.ttf") format("truetype")}', + '' ); test( - 'should remove unused fonts (2)', - processCSS, - '@font-face {font-family:"Does Not Exist";src:url("fonts/does-not-exist.ttf") format("truetype")}@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}', - '' + 'should remove unused fonts (2)', + processCSS, + '@font-face {font-family:"Does Not Exist";src:url("fonts/does-not-exist.ttf") format("truetype")}@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}', + '' ); test( - 'should remove unused fonts & keep used fonts', - processCSS, - '@font-face {font-family:"Does Not Exist";src:url("fonts/does-not-exist.ttf") format("truetype")}@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font-family:"Does Exist",Helvetica,Arial,sans-serif}', - '@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font-family:"Does Exist",Helvetica,Arial,sans-serif}' + 'should remove unused fonts & keep used fonts', + processCSS, + '@font-face {font-family:"Does Not Exist";src:url("fonts/does-not-exist.ttf") format("truetype")}@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font-family:"Does Exist",Helvetica,Arial,sans-serif}', + '@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font-family:"Does Exist",Helvetica,Arial,sans-serif}' ); test( - 'should work with the font shorthand', - passthroughCSS, - '@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font: 10px/1.5 "Does Exist",Helvetica,Arial,sans-serif}' + 'should work with the font shorthand', + passthroughCSS, + '@font-face {font-family:"Does Exist";src: url("fonts/does-exist.ttf") format("truetype")}body{font: 10px/1.5 "Does Exist",Helvetica,Arial,sans-serif}' ); test( - 'should not be responsible for normalising fonts', - processCSS, - '@font-face {font-family:"Does Exist";src:url("fonts/does-exist.ttf") format("truetype")}body{font-family:Does Exist}', - 'body{font-family:Does Exist}' + 'should not be responsible for normalising fonts', + processCSS, + '@font-face {font-family:"Does Exist";src:url("fonts/does-exist.ttf") format("truetype")}body{font-family:Does Exist}', + 'body{font-family:Does Exist}' ); test( - 'should remove font faces if they have no font-family property', - processCSS, - '@font-face {src:url("fonts/does-not-exist.ttf") format("truetype")}', - '' + 'should remove font faces if they have no font-family property', + processCSS, + '@font-face {src:url("fonts/does-not-exist.ttf") format("truetype")}', + '' ); test( - 'should not remove fonts used with a different casing', - passthroughCSS, - '@font-face {font-family:"DoEs ExIst";src: url("fonts/does-exist.ttf") format("truetype")}body{font: 10px/1.5 "does exisT",Helvetica,Arial,sans-serif}' + 'should not remove fonts used with a different casing', + passthroughCSS, + '@font-face {font-family:"DoEs ExIst";src: url("fonts/does-exist.ttf") format("truetype")}body{font: 10px/1.5 "does exisT",Helvetica,Arial,sans-serif}' ); test( - 'shouldn\'t remove font fames', - processCSS, - [ - '@font-face {src:url("fonts/does-not-exist.ttf") format("truetype")}', - '@keyframes {0%{opacity:0}to{opacity:1}}', - '@counter-style custom{system:extends decimal;suffix:"> "}', - ].join(''), + "shouldn't remove font fames", + processCSS, + [ '@font-face {src:url("fonts/does-not-exist.ttf") format("truetype")}', - {fontFace: false} + '@keyframes {0%{opacity:0}to{opacity:1}}', + '@counter-style custom{system:extends decimal;suffix:"> "}', + ].join(''), + '@font-face {src:url("fonts/does-not-exist.ttf") format("truetype")}', + { fontFace: false } ); test( - 'shouldn\'t remove keyframes if they have no identifier', - processCSS, - [ - '@keyframes {0%{opacity:0}to{opacity:1}}', - '@font-face {src:url("fonts/does-not-exist.ttf") format("truetype")}', - '@counter-style custom{system:extends decimal;suffix:"> "}', - ].join(''), + "shouldn't remove keyframes if they have no identifier", + processCSS, + [ '@keyframes {0%{opacity:0}to{opacity:1}}', - {keyframes: false} + '@font-face {src:url("fonts/does-not-exist.ttf") format("truetype")}', + '@counter-style custom{system:extends decimal;suffix:"> "}', + ].join(''), + '@keyframes {0%{opacity:0}to{opacity:1}}', + { keyframes: false } ); test( - 'shouldn\'t remove unused counter styles', - processCSS, - [ - '@counter-style custom{system:extends decimal;suffix:"> "}', - '@font-face {src:url("fonts/does-not-exist.ttf") format("truetype")}', - '@keyframes {0%{opacity:0}to{opacity:1}}', - ].join(''), + "shouldn't remove unused counter styles", + processCSS, + [ '@counter-style custom{system:extends decimal;suffix:"> "}', - {counterStyle: false} + '@font-face {src:url("fonts/does-not-exist.ttf") format("truetype")}', + '@keyframes {0%{opacity:0}to{opacity:1}}', + ].join(''), + '@counter-style custom{system:extends decimal;suffix:"> "}', + { counterStyle: false } ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-discard-unused/src/__tests__/namespace.js b/packages/postcss-discard-unused/src/__tests__/namespace.js index 82f029668..ee136cfa9 100644 --- a/packages/postcss-discard-unused/src/__tests__/namespace.js +++ b/packages/postcss-discard-unused/src/__tests__/namespace.js @@ -1,50 +1,45 @@ import test from 'ava'; import plugin from '..'; -import {processCSSFactory} from '../../../../util/testHelpers'; +import { processCSSFactory } from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); test( - 'should remove unused prefixed namespace', - processCSS, - '@namespace svg url(http://www.w3.org/2000/svg);a{color:blue}', - 'a{color:blue}' + 'should remove unused prefixed namespace', + processCSS, + '@namespace svg url(http://www.w3.org/2000/svg);a{color:blue}', + 'a{color:blue}' ); -test( - 'should remove invalid namespace', - processCSS, - '@namespace', - '' -); +test('should remove invalid namespace', processCSS, '@namespace', ''); test( - 'shouldn\'t remove default namespace', - passthroughCSS, - '@namespace url(http://www.w3.org/2000/svg)' + "shouldn't remove default namespace", + passthroughCSS, + '@namespace url(http://www.w3.org/2000/svg)' ); test( - 'shouldn\'t remove used prefixed namespace', - passthroughCSS, - '@namespace svg url(http://www.w3.org/2000/svg);svg|a{color:blue}' + "shouldn't remove used prefixed namespace", + passthroughCSS, + '@namespace svg url(http://www.w3.org/2000/svg);svg|a{color:blue}' ); test( - 'shouldn\'t remove prefixed namespace in case of universal selector', - passthroughCSS, - '@namespace svg url(http://www.w3.org/2000/svg);*|a{color:blue}' + "shouldn't remove prefixed namespace in case of universal selector", + passthroughCSS, + '@namespace svg url(http://www.w3.org/2000/svg);*|a{color:blue}' ); test( - `shouldn't remove when namespace is used in attribute selector`, - passthroughCSS, - `@namespace xlink url('http://www.w3.org/1999/xlink');svg:hover use[xlink|href*=facebook]{fill:blue}` + `shouldn't remove when namespace is used in attribute selector`, + passthroughCSS, + `@namespace xlink url('http://www.w3.org/1999/xlink');svg:hover use[xlink|href*=facebook]{fill:blue}` ); test( - 'shouldn\'t remove unused prefixed namespace', - passthroughCSS, - '@namespace svg url(http://www.w3.org/2000/svg)', - {namespace: false} + "shouldn't remove unused prefixed namespace", + passthroughCSS, + '@namespace svg url(http://www.w3.org/2000/svg)', + { namespace: false } ); diff --git a/packages/postcss-discard-unused/src/index.js b/packages/postcss-discard-unused/src/index.js index 7bc03b6ee..48f2dd424 100644 --- a/packages/postcss-discard-unused/src/index.js +++ b/packages/postcss-discard-unused/src/index.js @@ -1,125 +1,151 @@ import uniqs from 'uniqs'; -import {list, plugin} from 'postcss'; +import { list, plugin } from 'postcss'; import selectorParser from 'postcss-selector-parser'; -const {comma, space} = list; +const { comma, space } = list; const atrule = 'atrule'; const decl = 'decl'; const rule = 'rule'; -function addValues (cache, {value}) { - return comma(value).reduce((memo, val) => [...memo, ...space(val)], cache); +function addValues(cache, { value }) { + return comma(value).reduce((memo, val) => [...memo, ...space(val)], cache); } -function filterAtRule ({atRules, values}) { - values = uniqs(values); - atRules.forEach(node => { - const hasAtRule = values.some(value => value === node.params); - if (!hasAtRule) { - node.remove(); - } - }); +function filterAtRule({ atRules, values }) { + values = uniqs(values); + atRules.forEach((node) => { + const hasAtRule = values.some((value) => value === node.params); + + if (!hasAtRule) { + node.remove(); + } + }); } -function filterNamespace ({atRules, rules}) { - rules = uniqs(rules); - atRules.forEach(atRule => { - const {0: param, length: len} = atRule.params.split(' ').filter(Boolean); - if (len === 1) { - return; - } - const hasRule = rules.some(r => r === param || r === '*'); - if (!hasRule) { - atRule.remove(); - } - }); +function filterNamespace({ atRules, rules }) { + rules = uniqs(rules); + atRules.forEach((atRule) => { + const { 0: param, length: len } = atRule.params.split(' ').filter(Boolean); + + if (len === 1) { + return; + } + + const hasRule = rules.some((r) => r === param || r === '*'); + + if (!hasRule) { + atRule.remove(); + } + }); } -function hasFont (fontFamily, cache) { - return comma(fontFamily).some(font => cache.some(c => ~c.indexOf(font))); +function hasFont(fontFamily, cache) { + return comma(fontFamily).some((font) => cache.some((c) => ~c.indexOf(font))); } // fonts have slightly different logic -function filterFont ({atRules, values}) { - values = uniqs(values); - atRules.forEach(r => { - const families = r.nodes.filter(({prop}) => prop === 'font-family'); - // Discard the @font-face if it has no font-family - if (!families.length) { - return r.remove(); - } - families.forEach(family => { - if (!hasFont(family.value.toLowerCase(), values)) { - r.remove(); - } - }); +function filterFont({ atRules, values }) { + values = uniqs(values); + atRules.forEach((r) => { + const families = r.nodes.filter(({ prop }) => prop === 'font-family'); + + // Discard the @font-face if it has no font-family + if (!families.length) { + return r.remove(); + } + + families.forEach((family) => { + if (!hasFont(family.value.toLowerCase(), values)) { + r.remove(); + } }); + }); } -export default plugin('postcss-discard-unused', opts => { - const {fontFace, counterStyle, keyframes, namespace} = Object.assign({}, { - fontFace: true, - counterStyle: true, - keyframes: true, - namespace: true, - }, opts); - return css => { - const counterStyleCache = {atRules: [], values: []}; - const keyframesCache = {atRules: [], values: []}; - const namespaceCache = {atRules: [], rules: []}; - const fontCache = {atRules: [], values: []}; - css.walk(node => { - const {type, prop, selector, name} = node; - if (type === rule && namespace && ~selector.indexOf('|')) { - if (~selector.indexOf('[')) { - // Attribute selector, so we should parse further. - selectorParser(ast => { - ast.walkAttributes(({namespace: ns}) => { - namespaceCache.rules = namespaceCache.rules.concat( - ns - ); - }); - }).process(selector); - } else { - // Use a simple split function for the namespace - namespaceCache.rules = namespaceCache.rules.concat( - selector.split('|')[0] - ); - } - return; - } - if (type === decl) { - if (counterStyle && /list-style|system/.test(prop)) { - counterStyleCache.values = addValues(counterStyleCache.values, node); - } - if (fontFace && node.parent.type === rule && /font(|-family)/.test(prop)) { - fontCache.values = fontCache.values.concat(comma(node.value.toLowerCase())); - } - if (keyframes && /animation/.test(prop)) { - keyframesCache.values = addValues(keyframesCache.values, node); - } - return; - } - if (type === atrule) { - if (counterStyle && /counter-style/.test(name)) { - counterStyleCache.atRules.push(node); - } - if (fontFace && name === 'font-face' && node.nodes) { - fontCache.atRules.push(node); - } - if (keyframes && /keyframes/.test(name)) { - keyframesCache.atRules.push(node); - } - if (namespace && name === 'namespace') { - namespaceCache.atRules.push(node); - } - return; - } - }); - counterStyle && filterAtRule(counterStyleCache); - fontFace && filterFont(fontCache); - keyframes && filterAtRule(keyframesCache); - namespace && filterNamespace(namespaceCache); - }; +export default plugin('postcss-discard-unused', (opts) => { + const { fontFace, counterStyle, keyframes, namespace } = Object.assign( + {}, + { + fontFace: true, + counterStyle: true, + keyframes: true, + namespace: true, + }, + opts + ); + return (css) => { + const counterStyleCache = { atRules: [], values: [] }; + const keyframesCache = { atRules: [], values: [] }; + const namespaceCache = { atRules: [], rules: [] }; + const fontCache = { atRules: [], values: [] }; + + css.walk((node) => { + const { type, prop, selector, name } = node; + + if (type === rule && namespace && ~selector.indexOf('|')) { + if (~selector.indexOf('[')) { + // Attribute selector, so we should parse further. + selectorParser((ast) => { + ast.walkAttributes(({ namespace: ns }) => { + namespaceCache.rules = namespaceCache.rules.concat(ns); + }); + }).process(selector); + } else { + // Use a simple split function for the namespace + namespaceCache.rules = namespaceCache.rules.concat( + selector.split('|')[0] + ); + } + return; + } + + if (type === decl) { + if (counterStyle && /list-style|system/.test(prop)) { + counterStyleCache.values = addValues(counterStyleCache.values, node); + } + + if ( + fontFace && + node.parent.type === rule && + /font(|-family)/.test(prop) + ) { + fontCache.values = fontCache.values.concat( + comma(node.value.toLowerCase()) + ); + } + + if (keyframes && /animation/.test(prop)) { + keyframesCache.values = addValues(keyframesCache.values, node); + } + + return; + } + + if (type === atrule) { + if (counterStyle && /counter-style/.test(name)) { + counterStyleCache.atRules.push(node); + } + + if (fontFace && name === 'font-face' && node.nodes) { + fontCache.atRules.push(node); + } + + if (keyframes && /keyframes/.test(name)) { + keyframesCache.atRules.push(node); + } + + if (namespace && name === 'namespace') { + namespaceCache.atRules.push(node); + } + + return; + } + }); + + counterStyle && filterAtRule(counterStyleCache); + fontFace && filterFont(fontCache); + keyframes && filterAtRule(keyframesCache); + namespace && filterNamespace(namespaceCache); + }; }); diff --git a/packages/postcss-merge-idents/src/__tests__/index.js b/packages/postcss-merge-idents/src/__tests__/index.js index 1d042becc..e0d290034 100644 --- a/packages/postcss-merge-idents/src/__tests__/index.js +++ b/packages/postcss-merge-idents/src/__tests__/index.js @@ -1,202 +1,201 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); test( - 'should merge keyframe identifiers', - processCSS, - '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}', - '@keyframes b{0%{color:#fff}to{color:#000}}' + 'should merge keyframe identifiers', + processCSS, + '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}', + '@keyframes b{0%{color:#fff}to{color:#000}}' ); test( - 'should merge keyframe identifiers (2)', - processCSS, - '@keyframes a{0%{color:#fff}to{color:#000}}@KEYFRAMES b{0%{color:#fff}to{color:#000}}', - '@KEYFRAMES b{0%{color:#fff}to{color:#000}}' + 'should merge keyframe identifiers (2)', + processCSS, + '@keyframes a{0%{color:#fff}to{color:#000}}@KEYFRAMES b{0%{color:#fff}to{color:#000}}', + '@KEYFRAMES b{0%{color:#fff}to{color:#000}}' ); test( - 'should merge multiple keyframe identifiers', - processCSS, - '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}@keyframes c{0%{color:#fff}to{color:#000}}', - '@keyframes c{0%{color:#fff}to{color:#000}}' + 'should merge multiple keyframe identifiers', + processCSS, + '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}@keyframes c{0%{color:#fff}to{color:#000}}', + '@keyframes c{0%{color:#fff}to{color:#000}}' ); test( - 'should update relevant animation declarations', - processCSS, - '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}div{animation:a .2s ease}', - '@keyframes b{0%{color:#fff}to{color:#000}}div{animation:b .2s ease}' + 'should update relevant animation declarations', + processCSS, + '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}div{animation:a .2s ease}', + '@keyframes b{0%{color:#fff}to{color:#000}}div{animation:b .2s ease}' ); test( - 'should update relevant animation declarations (2)', - processCSS, - '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}div{ANIMATION:a .2s ease}', - '@keyframes b{0%{color:#fff}to{color:#000}}div{ANIMATION:b .2s ease}' + 'should update relevant animation declarations (2)', + processCSS, + '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}div{ANIMATION:a .2s ease}', + '@keyframes b{0%{color:#fff}to{color:#000}}div{ANIMATION:b .2s ease}' ); test( - 'should update relevant animation declarations (2)', - processCSS, - '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}@keyframes c{0%{color:#fff}to{color:#000}}div{animation:a .2s ease}', - '@keyframes c{0%{color:#fff}to{color:#000}}div{animation:c .2s ease}' + 'should update relevant animation declarations (2)', + processCSS, + '@keyframes a{0%{color:#fff}to{color:#000}}@keyframes b{0%{color:#fff}to{color:#000}}@keyframes c{0%{color:#fff}to{color:#000}}div{animation:a .2s ease}', + '@keyframes c{0%{color:#fff}to{color:#000}}div{animation:c .2s ease}' ); test( - 'should not merge vendor prefixed keyframes', - passthroughCSS, - '@-webkit-keyframes a{0%{color:#fff}to{color:#000}}@keyframes a{0%{color:#fff}to{color:#000}}' + 'should not merge vendor prefixed keyframes', + passthroughCSS, + '@-webkit-keyframes a{0%{color:#fff}to{color:#000}}@keyframes a{0%{color:#fff}to{color:#000}}' ); test( - 'should merge duplicated keyframes with the same name', - processCSS, - '@keyframes a{0%{opacity:1}to{opacity:0}}@keyframes a{0%{opacity:1}to{opacity:0}}', - '@keyframes a{0%{opacity:1}to{opacity:0}}' + 'should merge duplicated keyframes with the same name', + processCSS, + '@keyframes a{0%{opacity:1}to{opacity:0}}@keyframes a{0%{opacity:1}to{opacity:0}}', + '@keyframes a{0%{opacity:1}to{opacity:0}}' ); test( - 'should merge duplicated counter styles with the same name', - processCSS, - '@counter-style a{system:extends decimal;suffix:"> "}@counter-style a{system:extends decimal;suffix:"> "}', - '@counter-style a{system:extends decimal;suffix:"> "}' + 'should merge duplicated counter styles with the same name', + processCSS, + '@counter-style a{system:extends decimal;suffix:"> "}@counter-style a{system:extends decimal;suffix:"> "}', + '@counter-style a{system:extends decimal;suffix:"> "}' ); test( - 'should merge duplicated counter styles with the same name (2)', - processCSS, - '@counter-style a{system:extends decimal;suffix:"> "}@COUNTER-STYLE a{system:extends decimal;suffix:"> "}', - '@COUNTER-STYLE a{system:extends decimal;suffix:"> "}' + 'should merge duplicated counter styles with the same name (2)', + processCSS, + '@counter-style a{system:extends decimal;suffix:"> "}@COUNTER-STYLE a{system:extends decimal;suffix:"> "}', + '@COUNTER-STYLE a{system:extends decimal;suffix:"> "}' ); test( - 'should merge counter style identifiers', - processCSS, - '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}', - '@counter-style b{system:extends decimal;suffix:"> "}' + 'should merge counter style identifiers', + processCSS, + '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}', + '@counter-style b{system:extends decimal;suffix:"> "}' ); test( - 'should merge multiple counter style identifiers', - processCSS, - '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}@counter-style c{system:extends decimal;suffix:"> "}', - '@counter-style c{system:extends decimal;suffix:"> "}' + 'should merge multiple counter style identifiers', + processCSS, + '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}@counter-style c{system:extends decimal;suffix:"> "}', + '@counter-style c{system:extends decimal;suffix:"> "}' ); test( - 'should update relevant list style declarations', - processCSS, - '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}ol{list-style:a}', - '@counter-style b{system:extends decimal;suffix:"> "}ol{list-style:b}' + 'should update relevant list style declarations', + processCSS, + '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}ol{list-style:a}', + '@counter-style b{system:extends decimal;suffix:"> "}ol{list-style:b}' ); test( - 'should update relevant list style declarations (2)', - processCSS, - '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}@counter-style c{system:extends decimal;suffix:"> "}ol{list-style:a}', - '@counter-style c{system:extends decimal;suffix:"> "}ol{list-style:c}' + 'should update relevant list style declarations (2)', + processCSS, + '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}@counter-style c{system:extends decimal;suffix:"> "}ol{list-style:a}', + '@counter-style c{system:extends decimal;suffix:"> "}ol{list-style:c}' ); test( - 'should update relevant list style declarations (3)', - processCSS, - '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}ol{LIST-STYLE:a}', - '@counter-style b{system:extends decimal;suffix:"> "}ol{LIST-STYLE:b}' + 'should update relevant list style declarations (3)', + processCSS, + '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends decimal;suffix:"> "}ol{LIST-STYLE:a}', + '@counter-style b{system:extends decimal;suffix:"> "}ol{LIST-STYLE:b}' ); test( - 'should update relevant system declarations', - processCSS, - '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends a;suffix:"> "}@counter-style c{system:extends a;suffix:"> "}ol{list-style:c}', - '@counter-style a{system:extends decimal;suffix:"> "}@counter-style c{system:extends a;suffix:"> "}ol{list-style:c}' + 'should update relevant system declarations', + processCSS, + '@counter-style a{system:extends decimal;suffix:"> "}@counter-style b{system:extends a;suffix:"> "}@counter-style c{system:extends a;suffix:"> "}ol{list-style:c}', + '@counter-style a{system:extends decimal;suffix:"> "}@counter-style c{system:extends a;suffix:"> "}ol{list-style:c}' ); test( - 'should not output JS functions', - passthroughCSS, - '.ui.indeterminate.loader:after{-webkit-animation-direction:reverse;animation-direction:reverse;-webkit-animation-duration:1.2s;animation-duration:1.2s}' + 'should not output JS functions', + passthroughCSS, + '.ui.indeterminate.loader:after{-webkit-animation-direction:reverse;animation-direction:reverse;-webkit-animation-duration:1.2s;animation-duration:1.2s}' ); test( - 'should handle duplicated definitions', - processCSS, - [ - '.checkbox input[type=checkbox]:checked + .checkbox-material:before{-webkit-animation:rippleOn 500ms;-o-animation:rippleOn 500ms;animation:rippleOn 500ms}', - '.checkbox input[type=checkbox]:checked + .checkbox-material .check:after{-webkit-animation:rippleOn 500ms forwards;-o-animation:rippleOn 500ms forwards;animation:rippleOn 500ms forwards}', - '@-webkit-keyframes rippleOn{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - '@-o-keyframes rippleOn{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - '@keyframes rippleOn{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - '@-webkit-keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - '@-o-keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - '@keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - '@keyframes rippleOn{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - '@keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - ].join(''), - [ - '.checkbox input[type=checkbox]:checked + .checkbox-material:before{-webkit-animation:rippleOff 500ms;-o-animation:rippleOff 500ms;animation:rippleOff 500ms}', - '.checkbox input[type=checkbox]:checked + .checkbox-material .check:after{-webkit-animation:rippleOff 500ms forwards;-o-animation:rippleOff 500ms forwards;animation:rippleOff 500ms forwards}', - '@-webkit-keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - '@-o-keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - '@keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', - ].join('') + 'should handle duplicated definitions', + processCSS, + [ + '.checkbox input[type=checkbox]:checked + .checkbox-material:before{-webkit-animation:rippleOn 500ms;-o-animation:rippleOn 500ms;animation:rippleOn 500ms}', + '.checkbox input[type=checkbox]:checked + .checkbox-material .check:after{-webkit-animation:rippleOn 500ms forwards;-o-animation:rippleOn 500ms forwards;animation:rippleOn 500ms forwards}', + '@-webkit-keyframes rippleOn{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + '@-o-keyframes rippleOn{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + '@keyframes rippleOn{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + '@-webkit-keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + '@-o-keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + '@keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + '@keyframes rippleOn{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + '@keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + ].join(''), + [ + '.checkbox input[type=checkbox]:checked + .checkbox-material:before{-webkit-animation:rippleOff 500ms;-o-animation:rippleOff 500ms;animation:rippleOff 500ms}', + '.checkbox input[type=checkbox]:checked + .checkbox-material .check:after{-webkit-animation:rippleOff 500ms forwards;-o-animation:rippleOff 500ms forwards;animation:rippleOff 500ms forwards}', + '@-webkit-keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + '@-o-keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + '@keyframes rippleOff{0%{opacity:0}50%{opacity:0.2}100%{opacity:0}}', + ].join('') ); test( - 'should handle duplication within media queries', - passthroughCSS, - [ - '@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}', - '@media (max-width:400px){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', - '.spin{animation:1s spin infinite linear}', - ].join('') + 'should handle duplication within media queries', + passthroughCSS, + [ + '@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}', + '@media (max-width:400px){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', + '.spin{animation:1s spin infinite linear}', + ].join('') ); test( - 'should handle duplication within supports rules', - passthroughCSS, - [ - '@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}', - '@supports (transform:rotate(0deg)){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', - '.spin{animation:1s spin infinite linear}', - ].join('') + 'should handle duplication within supports rules', + passthroughCSS, + [ + '@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}', + '@supports (transform:rotate(0deg)){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', + '.spin{animation:1s spin infinite linear}', + ].join('') ); test( - 'should handle duplication within supports rules & media queries', - passthroughCSS, - [ - '@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}', - '@media (max-width:400px){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', - '@supports (transform:rotate(0deg)){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', - '.spin{animation:1s spin infinite linear}', - ].join('') + 'should handle duplication within supports rules & media queries', + passthroughCSS, + [ + '@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}', + '@media (max-width:400px){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', + '@supports (transform:rotate(0deg)){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', + '.spin{animation:1s spin infinite linear}', + ].join('') ); test( - 'should handle duplication within nested at-rules', - passthroughCSS, - [ - '@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}', - '@media (max-width:400px){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', - '@supports (transform:rotate(0deg)){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', - '@media (max-width: 400px){@supports (transform:rotate(0deg)){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}}', - '.spin{animation:1s spin infinite linear}', - ].join('') + 'should handle duplication within nested at-rules', + passthroughCSS, + [ + '@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}', + '@media (max-width:400px){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', + '@supports (transform:rotate(0deg)){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}', + '@media (max-width: 400px){@supports (transform:rotate(0deg)){@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}}}', + '.spin{animation:1s spin infinite linear}', + ].join('') ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); test( - 'should not crash on potential circular references', - processCSS, - `.hi{animation:hi 2s infinite linear}@-webkit-keyframes hi{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.ho{animation:ho 2s infinite linear}@-webkit-keyframes ho{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}@keyframes ho{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}@keyframes hi{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}`, - `.hi{animation:hi 2s infinite linear}.ho{animation:ho 2s infinite linear}@-webkit-keyframes ho{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}@keyframes hi{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}` + 'should not crash on potential circular references', + processCSS, + `.hi{animation:hi 2s infinite linear}@-webkit-keyframes hi{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.ho{animation:ho 2s infinite linear}@-webkit-keyframes ho{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}@keyframes ho{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}@keyframes hi{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}`, + `.hi{animation:hi 2s infinite linear}.ho{animation:ho 2s infinite linear}@-webkit-keyframes ho{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}@keyframes hi{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}` ); diff --git a/packages/postcss-merge-idents/src/index.js b/packages/postcss-merge-idents/src/index.js index 80c61f19e..73637dd6c 100644 --- a/packages/postcss-merge-idents/src/index.js +++ b/packages/postcss-merge-idents/src/index.js @@ -1,86 +1,108 @@ import has from 'has'; -import {plugin} from 'postcss'; +import { plugin } from 'postcss'; import valueParser from 'postcss-value-parser'; import sameParent from 'lerna:cssnano-util-same-parent'; -function canonical (obj) { - // Prevent potential infinite loops - let stack = 50; - return function recurse (key) { - if (has(obj, key) && obj[key] !== key && stack) { - stack --; - return recurse(obj[key]); - } - stack = 50; - return key; - }; +function canonical(obj) { + // Prevent potential infinite loops + let stack = 50; + + return function recurse(key) { + if (has(obj, key) && obj[key] !== key && stack) { + stack--; + + return recurse(obj[key]); + } + + stack = 50; + + return key; + }; } -function mergeAtRules (css, pairs) { - pairs.forEach(pair => { - pair.cache = []; - pair.replacements = []; - pair.decls = []; - pair.removals = []; - }); +function mergeAtRules(css, pairs) { + pairs.forEach((pair) => { + pair.cache = []; + pair.replacements = []; + pair.decls = []; + pair.removals = []; + }); - let relevant; - - css.walk(node => { - if (node.type === 'atrule') { - relevant = pairs.filter(pair => pair.atrule.test(node.name.toLowerCase()))[0]; - if (!relevant) { - return; - } - if (relevant.cache.length < 1) { - relevant.cache.push(node); - return; - } else { - let toString = node.nodes.toString(); - relevant.cache.forEach(cached => { - if ( - cached.name.toLowerCase() === node.name.toLowerCase() && - sameParent(cached, node) && - cached.nodes.toString() === toString - ) { - relevant.removals.push(cached); - relevant.replacements[cached.params] = node.params; - } - }); - relevant.cache.push(node); - return; - } - } - if (node.type === 'decl') { - relevant = pairs.filter(pair => pair.decl.test(node.prop.toLowerCase()))[0]; - if (!relevant) { - return; - } - relevant.decls.push(node); - } - }); + let relevant; + + css.walk((node) => { + if (node.type === 'atrule') { + relevant = pairs.filter((pair) => + pair.atrule.test(node.name.toLowerCase()) + )[0]; + + if (!relevant) { + return; + } - pairs.forEach(pair => { - let canon = canonical(pair.replacements); - pair.decls.forEach(decl => { - decl.value = valueParser(decl.value).walk(node => { - if (node.type === 'word') { - node.value = canon(node.value); - } - }).toString(); + if (relevant.cache.length < 1) { + relevant.cache.push(node); + return; + } else { + let toString = node.nodes.toString(); + + relevant.cache.forEach((cached) => { + if ( + cached.name.toLowerCase() === node.name.toLowerCase() && + sameParent(cached, node) && + cached.nodes.toString() === toString + ) { + relevant.removals.push(cached); + relevant.replacements[cached.params] = node.params; + } }); - pair.removals.forEach(cached => cached.remove()); + + relevant.cache.push(node); + + return; + } + } + + if (node.type === 'decl') { + relevant = pairs.filter((pair) => + pair.decl.test(node.prop.toLowerCase()) + )[0]; + + if (!relevant) { + return; + } + + relevant.decls.push(node); + } + }); + + pairs.forEach((pair) => { + let canon = canonical(pair.replacements); + + pair.decls.forEach((decl) => { + decl.value = valueParser(decl.value) + .walk((node) => { + if (node.type === 'word') { + node.value = canon(node.value); + } + }) + .toString(); }); + pair.removals.forEach((cached) => cached.remove()); + }); } export default plugin('postcss-merge-idents', () => { - return css => { - mergeAtRules(css, [{ - atrule: /keyframes/i, - decl: /animation/i, - }, { - atrule: /counter-style/i, - decl: /(list-style|system)/i, - }]); - }; + return (css) => { + mergeAtRules(css, [ + { + atrule: /keyframes/i, + decl: /animation/i, + }, + { + atrule: /counter-style/i, + decl: /(list-style|system)/i, + }, + ]); + }; }); diff --git a/packages/postcss-merge-longhand/src/__tests__/api.js b/packages/postcss-merge-longhand/src/__tests__/api.js index cba459fbe..2ae404e46 100644 --- a/packages/postcss-merge-longhand/src/__tests__/api.js +++ b/packages/postcss-merge-longhand/src/__tests__/api.js @@ -1,8 +1,8 @@ import test from 'ava'; import plugin from '..'; -import {name} from '../../package.json'; +import { name } from '../../package.json'; -test('should use the postcss plugin api', t => { - t.truthy(plugin().postcssVersion, 'should be able to access version'); - t.deepEqual(plugin().postcssPlugin, name, 'should be able to access name'); +test('should use the postcss plugin api', (t) => { + t.truthy(plugin().postcssVersion, 'should be able to access version'); + t.deepEqual(plugin().postcssPlugin, name, 'should be able to access name'); }); diff --git a/packages/postcss-merge-longhand/src/__tests__/borders.js b/packages/postcss-merge-longhand/src/__tests__/borders.js index 304252483..dc8247f31 100644 --- a/packages/postcss-merge-longhand/src/__tests__/borders.js +++ b/packages/postcss-merge-longhand/src/__tests__/borders.js @@ -1,1121 +1,1123 @@ import test from 'ava'; import trbl from '../lib/trbl'; import plugin from '..'; -import {processCSSFactory} from '../../../../util/testHelpers'; +import { processCSSFactory } from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); -const wsc = [{ +const wsc = [ + { property: 'width', fixture: '1px', -}, { + }, + { property: 'style', fixture: 'solid', -}, { + }, + { property: 'color', fixture: 'red', -}]; - -wsc.forEach(({property, fixture}) => { - test( - `should merge to form a border-trbl-${property} definition`, - processCSS, - [ - `h1{`, - `border-${trbl[0]}-${property}:${fixture};`, - `border-${trbl[1]}-${property}:${fixture};`, - `border-${trbl[2]}-${property}:${fixture};`, - `border-${trbl[3]}-${property}:${fixture}`, - `}`, - ].join(''), - `h1{border-${property}:${fixture}}` - ); - - test( - `should merge to form a BORDER-TRBL-${property.toUpperCase()} definition`, - processCSS, - [ - `h1{`, - `BORDER-${trbl[0].toUpperCase()}-${property.toUpperCase()}:${fixture.toUpperCase()};`, - `BORDER-${trbl[1].toUpperCase()}-${property.toUpperCase()}:${fixture.toUpperCase()};`, - `BORDER-${trbl[2].toUpperCase()}-${property.toUpperCase()}:${fixture.toUpperCase()};`, - `BORDER-${trbl[3].toUpperCase()}-${property.toUpperCase()}:${fixture.toUpperCase()}`, - `}`, - ].join(''), - `h1{border-${property}:${fixture.toUpperCase()}}` - ); + }, +]; + +wsc.forEach(({ property, fixture }) => { + test( + `should merge to form a border-trbl-${property} definition`, + processCSS, + [ + `h1{`, + `border-${trbl[0]}-${property}:${fixture};`, + `border-${trbl[1]}-${property}:${fixture};`, + `border-${trbl[2]}-${property}:${fixture};`, + `border-${trbl[3]}-${property}:${fixture}`, + `}`, + ].join(''), + `h1{border-${property}:${fixture}}` + ); + + test( + `should merge to form a BORDER-TRBL-${property.toUpperCase()} definition`, + processCSS, + [ + `h1{`, + `BORDER-${trbl[0].toUpperCase()}-${property.toUpperCase()}:${fixture.toUpperCase()};`, + `BORDER-${trbl[1].toUpperCase()}-${property.toUpperCase()}:${fixture.toUpperCase()};`, + `BORDER-${trbl[2].toUpperCase()}-${property.toUpperCase()}:${fixture.toUpperCase()};`, + `BORDER-${trbl[3].toUpperCase()}-${property.toUpperCase()}:${fixture.toUpperCase()}`, + `}`, + ].join(''), + `h1{border-${property}:${fixture.toUpperCase()}}` + ); }); -trbl.forEach(direction => { - const value = wsc.reduce((list, {fixture}) => [...list, fixture], []); - - test( - `should merge to form a border-${direction} definition`, - processCSS, - [ - `h1{`, - `border-${direction}-width:${value[0]};`, - `border-${direction}-style:${value[1]};`, - `border-${direction}-color:${value[2]}`, - `}`, - ].join(''), - `h1{border-${direction}:${value[0]} ${value[1]} ${value[2]}}` - ); - - test( - `should merge to form a border-${direction.toUpperCase()} definition`, - processCSS, - [ - `h1{`, - `BORDER-${direction.toUpperCase()}-WIDTH:${value[0].toUpperCase()};`, - `BORDER-${direction.toUpperCase()}-STYLE:${value[1].toUpperCase()};`, - `BORDER-${direction.toUpperCase()}-COLOR:${value[2].toUpperCase()}`, - `}`, - ].join(''), - `h1{border-${direction}:${value[0]} ${value[1]} ${value[2]}}` - ); +trbl.forEach((direction) => { + const value = wsc.reduce((list, { fixture }) => [...list, fixture], []); + + test( + `should merge to form a border-${direction} definition`, + processCSS, + [ + `h1{`, + `border-${direction}-width:${value[0]};`, + `border-${direction}-style:${value[1]};`, + `border-${direction}-color:${value[2]}`, + `}`, + ].join(''), + `h1{border-${direction}:${value[0]} ${value[1]} ${value[2]}}` + ); + + test( + `should merge to form a border-${direction.toUpperCase()} definition`, + processCSS, + [ + `h1{`, + `BORDER-${direction.toUpperCase()}-WIDTH:${value[0].toUpperCase()};`, + `BORDER-${direction.toUpperCase()}-STYLE:${value[1].toUpperCase()};`, + `BORDER-${direction.toUpperCase()}-COLOR:${value[2].toUpperCase()}`, + `}`, + ].join(''), + `h1{border-${direction}:${value[0]} ${value[1]} ${value[2]}}` + ); }); test( - 'should merge identical border values', - processCSS, - 'h1{border-top:1px solid black;border-bottom:1px solid black;border-left:1px solid black;border-right:1px solid black}', - 'h1{border:1px solid black}', + 'should merge identical border values', + processCSS, + 'h1{border-top:1px solid black;border-bottom:1px solid black;border-left:1px solid black;border-right:1px solid black}', + 'h1{border:1px solid black}' ); test( - 'should merge identical border values (uppercase)', - processCSS, - 'h1{BORDER-TOP:1px solid black;BORDER-BOTTOM:1px solid black;BORDER-LEFT:1px solid black;BORDER-RIGHT:1px solid black}', - 'h1{border:1px solid black}', + 'should merge identical border values (uppercase)', + processCSS, + 'h1{BORDER-TOP:1px solid black;BORDER-BOTTOM:1px solid black;BORDER-LEFT:1px solid black;BORDER-RIGHT:1px solid black}', + 'h1{border:1px solid black}' ); test( - 'should merge identical border values with !important', - processCSS, - 'h1{border-top:1px solid black!important;border-bottom:1px solid black!important;border-left:1px solid black!important;border-right:1px solid black!important}', - 'h1{border:1px solid black!important}' + 'should merge identical border values with !important', + processCSS, + 'h1{border-top:1px solid black!important;border-bottom:1px solid black!important;border-left:1px solid black!important;border-right:1px solid black!important}', + 'h1{border:1px solid black!important}' ); test( - 'should merge identical border values with !important (uppercase)', - processCSS, - 'h1{BORDER-TOP:1px solid black!important;BORDER-BOTTOM:1px solid black!important;BORDER-LEFT:1px solid black!important;BORDER-RIGHT:1px solid black!important}', - 'h1{border:1px solid black!important}' + 'should merge identical border values with !important (uppercase)', + processCSS, + 'h1{BORDER-TOP:1px solid black!important;BORDER-BOTTOM:1px solid black!important;BORDER-LEFT:1px solid black!important;BORDER-RIGHT:1px solid black!important}', + 'h1{border:1px solid black!important}' ); test( - 'should merge identical border values with !important 1 (uppercase)', - processCSS, - 'h1{border-top:1px solid black!IMPORTANT;border-bottom:1px solid black!IMPORTANT;border-left:1px solid black!IMPORTANT;border-right:1px solid black!IMPORTANT}', - 'h1{border:1px solid black!IMPORTANT}' + 'should merge identical border values with !important 1 (uppercase)', + processCSS, + 'h1{border-top:1px solid black!IMPORTANT;border-bottom:1px solid black!IMPORTANT;border-left:1px solid black!IMPORTANT;border-right:1px solid black!IMPORTANT}', + 'h1{border:1px solid black!IMPORTANT}' ); test( - 'should not merge identical border values with mixed !important', - passthroughCSS, - 'h1{border-top:1px solid black;border-bottom:1px solid black;border-left:1px solid black!important;border-right:1px solid black!important}' + 'should not merge identical border values with mixed !important', + passthroughCSS, + 'h1{border-top:1px solid black;border-bottom:1px solid black;border-left:1px solid black!important;border-right:1px solid black!important}' ); test( - 'should merge border values', - processCSS, - 'h1{border-color:red;border-width:1px;border-style:dashed}', - 'h1{border:1px dashed red}' + 'should merge border values', + processCSS, + 'h1{border-color:red;border-width:1px;border-style:dashed}', + 'h1{border:1px dashed red}' ); test( - 'should merge border values (uppercase)', - processCSS, - 'h1{BORDER-COLOR:RED;BORDER-WIDTH:1PX;BORDER-STYLE:DASHED}', - 'h1{border:1px dashed red}' + 'should merge border values (uppercase)', + processCSS, + 'h1{BORDER-COLOR:RED;BORDER-WIDTH:1PX;BORDER-STYLE:DASHED}', + 'h1{border:1px dashed red}' ); test( - 'should merge border values with !important', - processCSS, - 'h1{border-color:red!important;border-width:1px!important;border-style:dashed!important}', - 'h1{border:1px dashed red!important}' + 'should merge border values with !important', + processCSS, + 'h1{border-color:red!important;border-width:1px!important;border-style:dashed!important}', + 'h1{border:1px dashed red!important}' ); test( - 'should merge border values with !important (uppercase)', - processCSS, - 'h1{BORDER-COLOR:RED!IMPORTANT;BORDER-WIDTH:1PX!IMPORTANT;BORDER-STYLE:DASHED!IMPORTANT}', - 'h1{border:1px dashed red!IMPORTANT}' + 'should merge border values with !important (uppercase)', + processCSS, + 'h1{BORDER-COLOR:RED!IMPORTANT;BORDER-WIDTH:1PX!IMPORTANT;BORDER-STYLE:DASHED!IMPORTANT}', + 'h1{border:1px dashed red!IMPORTANT}' ); test( - 'should merge border values with identical values for all sides', - processCSS, - 'h1{border-color:red red red red;border-width:1px 1px 1px 1px;border-style:solid solid solid solid}', - 'h1{border:1px solid red}' + 'should merge border values with identical values for all sides', + processCSS, + 'h1{border-color:red red red red;border-width:1px 1px 1px 1px;border-style:solid solid solid solid}', + 'h1{border:1px solid red}' ); test( - 'should merge border values with identical values for all sides (uppercase)', - processCSS, - 'h1{BORDER-COLOR:RED RED RED RED;BORDER-WIDTH:1PX 1PX 1PX 1PX;BORDER-STYLE:SOLID SOLID SOLID SOLID}', - 'h1{border:1px solid red}' + 'should merge border values with identical values for all sides (uppercase)', + processCSS, + 'h1{BORDER-COLOR:RED RED RED RED;BORDER-WIDTH:1PX 1PX 1PX 1PX;BORDER-STYLE:SOLID SOLID SOLID SOLID}', + 'h1{border:1px solid red}' ); test( - 'should merge border value shorthands', - processCSS, - 'h1{border-color:red blue red blue;border-style:solid;border-width:10px 20px 10px 20px}', - 'h1{border-color:red blue;border-style:solid;border-width:10px 20px}' + 'should merge border value shorthands', + processCSS, + 'h1{border-color:red blue red blue;border-style:solid;border-width:10px 20px 10px 20px}', + 'h1{border-color:red blue;border-style:solid;border-width:10px 20px}' ); test( - 'should merge border value shorthands (uppercase)', - processCSS, - 'h1{BORDER-COLOR:RED BLUE RED BLUE;BORDER-STYLE:SOLID;BORDER-WIDTH:10PX 20PX 10PX 20PX}', - 'h1{border-color:red blue;border-style:solid;border-width:10px 20px}' + 'should merge border value shorthands (uppercase)', + processCSS, + 'h1{BORDER-COLOR:RED BLUE RED BLUE;BORDER-STYLE:SOLID;BORDER-WIDTH:10PX 20PX 10PX 20PX}', + 'h1{border-color:red blue;border-style:solid;border-width:10px 20px}' ); test( - 'should not merge border values with mixed !important', - passthroughCSS, - 'h1{border-color:red;border-width:1px!important;border-style:dashed!important}' + 'should not merge border values with mixed !important', + passthroughCSS, + 'h1{border-color:red;border-width:1px!important;border-style:dashed!important}' ); test( - 'should not merge border values with mixed !important (uppercase)', - processCSS, - 'h1{BORDER-COLOR:RED;BORDER-WIDTH:1PX!IMPORTANT;BORDER-STYLE:DASHED!IMPORTANT}', - 'h1{border-color:RED;border-width:1PX!IMPORTANT;border-style:DASHED!IMPORTANT}', + 'should not merge border values with mixed !important (uppercase)', + processCSS, + 'h1{BORDER-COLOR:RED;BORDER-WIDTH:1PX!IMPORTANT;BORDER-STYLE:DASHED!IMPORTANT}', + 'h1{border-color:RED;border-width:1PX!IMPORTANT;border-style:DASHED!IMPORTANT}' ); test( - 'should not merge border values with more than 3 values', - passthroughCSS, - 'h1{border-color:red;border-style:dashed;border-width:1px 5px}' + 'should not merge border values with more than 3 values', + passthroughCSS, + 'h1{border-color:red;border-style:dashed;border-width:1px 5px}' ); test( - 'should not merge border values with more than 3 values (uppercase)', - processCSS, - 'h1{BORDER-COLOR:RED;BORDER-STYLE:DASHED;BORDER-WIDTH:1PX 5PX}', - 'h1{border-color:red;border-style:dashed;border-width:1px 5px}' + 'should not merge border values with more than 3 values (uppercase)', + processCSS, + 'h1{BORDER-COLOR:RED;BORDER-STYLE:DASHED;BORDER-WIDTH:1PX 5PX}', + 'h1{border-color:red;border-style:dashed;border-width:1px 5px}' ); test( - 'should not merge rules with the inherit keyword', - processCSS, - 'h1{border-width:3px;border-style:solid;border-color:inherit}', - 'h1{border:3px solid;border-color:inherit}' + 'should not merge rules with the inherit keyword', + processCSS, + 'h1{border-width:3px;border-style:solid;border-color:inherit}', + 'h1{border:3px solid;border-color:inherit}' ); test( - 'should not merge rules with the inherit keyword (uppercase)', - processCSS, - 'h1{BORDER-WIDTH:3PX;BORDER-STYLE:SOLID;BORDER-COLOR:INHERIT}', - 'h1{border:3px solid;BORDER-COLOR:INHERIT}' + 'should not merge rules with the inherit keyword (uppercase)', + processCSS, + 'h1{BORDER-WIDTH:3PX;BORDER-STYLE:SOLID;BORDER-COLOR:INHERIT}', + 'h1{border:3px solid;BORDER-COLOR:INHERIT}' ); test( - 'should not crash on comments', - processCSS, - 'h1{\n border-width:3px;/* 1 */\n border-style:solid;/* 2 */\n border-color:red;/* 3 */}', - 'h1{/* 1 *//* 2 */\n border:3px solid red;/* 3 */}' + 'should not crash on comments', + processCSS, + 'h1{\n border-width:3px;/* 1 */\n border-style:solid;/* 2 */\n border-color:red;/* 3 */}', + 'h1{/* 1 *//* 2 */\n border:3px solid red;/* 3 */}' ); test( - 'should not convert border: 0 to border-width: 0', - passthroughCSS, - 'h1{border:none}' + 'should not convert border: 0 to border-width: 0', + passthroughCSS, + 'h1{border:none}' ); test( - 'should not convert border: 0 to border-width: 0 (uppercase)', - processCSS, - 'h1{BORDER:none}', - 'h1{border:none}', + 'should not convert border: 0 to border-width: 0 (uppercase)', + processCSS, + 'h1{BORDER:none}', + 'h1{border:none}' ); test( - 'should not merge border-left values with mixed !important', - passthroughCSS, - 'h1{border-left-color:red;border-left-width:1px!important;border-left-style:dashed!important}' + 'should not merge border-left values with mixed !important', + passthroughCSS, + 'h1{border-left-color:red;border-left-width:1px!important;border-left-style:dashed!important}' ); test( - 'should not merge border-left values with mixed !important (uppercase)', - passthroughCSS, - 'h1{BORDER-LEFT-COLOR:RED;BORDER-LEFT-WIDTH:1PX!IMPORTANT;BORDER-LEFT-STYLE:DASHED!IMPORTANT}' + 'should not merge border-left values with mixed !important (uppercase)', + passthroughCSS, + 'h1{BORDER-LEFT-COLOR:RED;BORDER-LEFT-WIDTH:1PX!IMPORTANT;BORDER-LEFT-STYLE:DASHED!IMPORTANT}' ); test( - 'should minimize default border values', - processCSS, - 'h1{border:medium none currentColor}', - 'h1{border:none}' + 'should minimize default border values', + processCSS, + 'h1{border:medium none currentColor}', + 'h1{border:none}' ); test( - 'should minimize default border values (uppercase)', - processCSS, - 'h1{BORDER:medium none currentColor}', - 'h1{border:none}' + 'should minimize default border values (uppercase)', + processCSS, + 'h1{BORDER:medium none currentColor}', + 'h1{border:none}' ); test( - 'should optimize border merging for length', - processCSS, - 'h1{border:1px solid #ddd;border-bottom:1px solid #fff}', - 'h1{border:1px solid;border-color:#ddd #ddd #fff}' + 'should optimize border merging for length', + processCSS, + 'h1{border:1px solid #ddd;border-bottom:1px solid #fff}', + 'h1{border:1px solid;border-color:#ddd #ddd #fff}' ); test( - 'should optimize border merging for length (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID #DDD;BORDER-BOTTOM:1PX SOLID #FFF}', - 'h1{border:1px solid;border-color:#ddd #ddd #fff}' + 'should optimize border merging for length (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID #DDD;BORDER-BOTTOM:1PX SOLID #FFF}', + 'h1{border:1px solid;border-color:#ddd #ddd #fff}' ); test( - 'should not mangle borders', - passthroughCSS, - 'hr{display:block;height:1px;border:none;border-top:1px solid #ddd}' + 'should not mangle borders', + passthroughCSS, + 'hr{display:block;height:1px;border:none;border-top:1px solid #ddd}' ); test( - 'should not mangle borders (uppercase)', - processCSS, - 'hr{DISPLAY:BLOCK;HEIGHT:1PX;BORDER:NONE;BORDER-TOP:1PX SOLID #DDD}', - 'hr{DISPLAY:BLOCK;HEIGHT:1PX;border:none;border-top:1px solid #ddd}' + 'should not mangle borders (uppercase)', + processCSS, + 'hr{DISPLAY:BLOCK;HEIGHT:1PX;BORDER:NONE;BORDER-TOP:1PX SOLID #DDD}', + 'hr{DISPLAY:BLOCK;HEIGHT:1PX;border:none;border-top:1px solid #ddd}' ); test( - 'Should not mangle borders (#579) (1)', - processCSS, - 'h1{border-bottom:none;border-color:red}', - 'h1{border-bottom:none;border-color:red}', + 'Should not mangle borders (#579) (1)', + processCSS, + 'h1{border-bottom:none;border-color:red}', + 'h1{border-bottom:none;border-color:red}' ); test( - 'Should not mangle borders (#579) (1) (uppercase)', - processCSS, - 'h1{BORDER-BOTTOM:NONE;BORDER-COLOR:RED}', - 'h1{border-bottom:none;border-color:red}', + 'Should not mangle borders (#579) (1) (uppercase)', + processCSS, + 'h1{BORDER-BOTTOM:NONE;BORDER-COLOR:RED}', + 'h1{border-bottom:none;border-color:red}' ); - test( - 'Should not mangle borders (#579) (2)', - processCSS, - 'h1{border:none;border-color:red}', - 'h1{border:red}', + 'Should not mangle borders (#579) (2)', + processCSS, + 'h1{border:none;border-color:red}', + 'h1{border:red}' ); test( - 'Should not mangle borders (#579) (2) (uppercase)', - processCSS, - 'h1{BORDER:NONE;BORDER-COLOR:RED}', - 'h1{border:red}', + 'Should not mangle borders (#579) (2) (uppercase)', + processCSS, + 'h1{BORDER:NONE;BORDER-COLOR:RED}', + 'h1{border:red}' ); test( - 'should use shorter equivalent rules', - processCSS, - 'h1{border:5px solid;border-color:#222 transparent transparent}', - 'h1{border:5px solid transparent;border-top-color:#222}', + 'should use shorter equivalent rules', + processCSS, + 'h1{border:5px solid;border-color:#222 transparent transparent}', + 'h1{border:5px solid transparent;border-top-color:#222}' ); test( - 'should use shorter equivalent rules (uppercase)', - processCSS, - 'h1{BORDER:5PX SOLID;BORDER-COLOR:#222 TRANSPARENT TRANSPARENT}', - 'h1{border:5px solid transparent;border-top-color:#222}', + 'should use shorter equivalent rules (uppercase)', + processCSS, + 'h1{BORDER:5PX SOLID;BORDER-COLOR:#222 TRANSPARENT TRANSPARENT}', + 'h1{border:5px solid transparent;border-top-color:#222}' ); test( - 'should merge redundant values', - processCSS, - 'h1{border-width:5px 5px 0;border-bottom-width:0}', - 'h1{border-width:5px 5px 0}' + 'should merge redundant values', + processCSS, + 'h1{border-width:5px 5px 0;border-bottom-width:0}', + 'h1{border-width:5px 5px 0}' ); test( - 'should merge redundant values (uppercase)', - processCSS, - 'h1{BORDER-WIDTH:5PX 5PX 0;BORDER-BOTTOM-WIDTH:0}', - 'h1{border-width:5PX 5PX 0}' + 'should merge redundant values (uppercase)', + processCSS, + 'h1{BORDER-WIDTH:5PX 5PX 0;BORDER-BOTTOM-WIDTH:0}', + 'h1{border-width:5PX 5PX 0}' ); test( - 'should merge redundant values (2)', - processCSS, - 'h1{border-width:5px 5px 0;border-bottom-width:10px}', - 'h1{border-width:5px 5px 10px}' + 'should merge redundant values (2)', + processCSS, + 'h1{border-width:5px 5px 0;border-bottom-width:10px}', + 'h1{border-width:5px 5px 10px}' ); test( - 'should merge redundant values (2) (uppercase)', - processCSS, - 'h1{BORDER-WIDTH:5PX 5PX 0;BORDER-BOTTOM-WIDTH:10PX}', - 'h1{border-width:5PX 5PX 10PX}' + 'should merge redundant values (2) (uppercase)', + processCSS, + 'h1{BORDER-WIDTH:5PX 5PX 0;BORDER-BOTTOM-WIDTH:10PX}', + 'h1{border-width:5PX 5PX 10PX}' ); test( - 'should merge redundant values (3)', - processCSS, - 'h1{border:1px solid #ddd;border-bottom-color:transparent}', - 'h1{border:1px solid;border-color:#ddd #ddd transparent}' + 'should merge redundant values (3)', + processCSS, + 'h1{border:1px solid #ddd;border-bottom-color:transparent}', + 'h1{border:1px solid;border-color:#ddd #ddd transparent}' ); test( - 'should merge redundant values (3) (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID #DDD;BORDER-BOTTOM-COLOR:TRANSPARENT}', - 'h1{border:1px solid;border-color:#ddd #ddd transparent}' + 'should merge redundant values (3) (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID #DDD;BORDER-BOTTOM-COLOR:TRANSPARENT}', + 'h1{border:1px solid;border-color:#ddd #ddd transparent}' ); test( - 'should merge redundant values (4)', - processCSS, - 'h1{border:1px solid #ddd;border-bottom-style:dotted}', - 'h1{border:1px #ddd;border-style:solid solid dotted}' + 'should merge redundant values (4)', + processCSS, + 'h1{border:1px solid #ddd;border-bottom-style:dotted}', + 'h1{border:1px #ddd;border-style:solid solid dotted}' ); test( - 'should merge redundant values (4) (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID #DDD;BORDER-BOTTOM-STYLE:DOTTED}', - 'h1{border:1px #ddd;border-style:solid solid dotted}' + 'should merge redundant values (4) (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID #DDD;BORDER-BOTTOM-STYLE:DOTTED}', + 'h1{border:1px #ddd;border-style:solid solid dotted}' ); test( - 'should merge redundant values (5)', - processCSS, - 'h1{border:1px solid #ddd;border-bottom-width:5px}', - 'h1{border:solid #ddd;border-width:1px 1px 5px}' + 'should merge redundant values (5)', + processCSS, + 'h1{border:1px solid #ddd;border-bottom-width:5px}', + 'h1{border:solid #ddd;border-width:1px 1px 5px}' ); test( - 'should merge redundant values (5) (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID #DDD;BORDER-BOTTOM-WIDTH:5PX}', - 'h1{border:solid #ddd;border-width:1px 1px 5px}' + 'should merge redundant values (5) (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID #DDD;BORDER-BOTTOM-WIDTH:5PX}', + 'h1{border:solid #ddd;border-width:1px 1px 5px}' ); test( - 'should merge redundant values (6)', - processCSS, - 'h1{border-width:1px;border-top-width:none;border-left-width:none;border-style:solid;border-color:#000;}', - 'h1{border-color:#000;border-style:solid;border-width:0 1px 1px 0;}', + 'should merge redundant values (6)', + processCSS, + 'h1{border-width:1px;border-top-width:none;border-left-width:none;border-style:solid;border-color:#000;}', + 'h1{border-color:#000;border-style:solid;border-width:0 1px 1px 0;}' ); test( - 'should merge redundant values (6) (uppercase)', - processCSS, - 'h1{BORDER-WIDTH:1PX;BORDER-TOP-WIDTH:NONE;BORDER-LEFT-WIDTH:NONE;BORDER-STYLE:SOLID;BORDER-COLOR:#000;}', - 'h1{border-color:#000;border-style:solid;border-width:0 1px 1px 0;}', + 'should merge redundant values (6) (uppercase)', + processCSS, + 'h1{BORDER-WIDTH:1PX;BORDER-TOP-WIDTH:NONE;BORDER-LEFT-WIDTH:NONE;BORDER-STYLE:SOLID;BORDER-COLOR:#000;}', + 'h1{border-color:#000;border-style:solid;border-width:0 1px 1px 0;}' ); test( - 'Should not merge redundant values if declarations are of different importance (#618)', - passthroughCSS, - 'h1{border:1px solid #3060b1;border-bottom:1px solid #3060b1 !important}', + 'Should not merge redundant values if declarations are of different importance (#618)', + passthroughCSS, + 'h1{border:1px solid #3060b1;border-bottom:1px solid #3060b1 !important}' ); test( - 'Should not merge redundant values if declarations are of different importance (#618) (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID #3060B1;BORDER-BOTTOM:1PX SOLID #3060B1 !IMPORTANT}', - 'h1{border:1px solid #3060b1;border-bottom:1px solid #3060b1 !IMPORTANT}' + 'Should not merge redundant values if declarations are of different importance (#618) (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID #3060B1;BORDER-BOTTOM:1PX SOLID #3060B1 !IMPORTANT}', + 'h1{border:1px solid #3060b1;border-bottom:1px solid #3060b1 !IMPORTANT}' ); test( - 'should merge redundant border-spacing values', - processCSS, - 'h1{border-spacing:10px 10px;}', - 'h1{border-spacing:10px;}' + 'should merge redundant border-spacing values', + processCSS, + 'h1{border-spacing:10px 10px;}', + 'h1{border-spacing:10px;}' ); test( - 'should merge redundant border-spacing values (uppercase)', - processCSS, - 'h1{BORDER-SPACING:10px 10px;}', - 'h1{BORDER-SPACING:10px;}' + 'should merge redundant border-spacing values (uppercase)', + processCSS, + 'h1{BORDER-SPACING:10px 10px;}', + 'h1{BORDER-SPACING:10px;}' ); test( - 'should not merge different border-spacing values', - passthroughCSS, - 'h1{border-spacing:10px 50px;}' + 'should not merge different border-spacing values', + passthroughCSS, + 'h1{border-spacing:10px 50px;}' ); test( - 'should not merge different border-spacing values (uppercase)', - passthroughCSS, - 'h1{BORDER-SPACING:10px 50px;}' + 'should not merge different border-spacing values (uppercase)', + passthroughCSS, + 'h1{BORDER-SPACING:10px 50px;}' ); test( - 'should merge border and border-width values', - processCSS, - 'h1{border:0 solid rgba(0, 0, 0, 0.2);border-width:1px;}', - 'h1{border:1px solid rgba(0, 0, 0, 0.2);}' + 'should merge border and border-width values', + processCSS, + 'h1{border:0 solid rgba(0, 0, 0, 0.2);border-width:1px;}', + 'h1{border:1px solid rgba(0, 0, 0, 0.2);}' ); test( - 'should merge border and border-width values (uppercase)', - processCSS, - 'h1{BORDER:0 SOLID RGBA(0, 0, 0, 0.2);BORDER-WIDTH:1PX;}', - 'h1{border:1px solid rgba(0, 0, 0, 0.2);}' + 'should merge border and border-width values (uppercase)', + processCSS, + 'h1{BORDER:0 SOLID RGBA(0, 0, 0, 0.2);BORDER-WIDTH:1PX;}', + 'h1{border:1px solid rgba(0, 0, 0, 0.2);}' ); test( - 'should merge border and multiple border-*-width values', - processCSS, - 'h1{border:0 solid rgba(0, 0, 0, 0.2);border-top-width:1px;border-right-width:1px;border-bottom-width:1px;border-left-width:1px;}', - 'h1{border:1px solid rgba(0, 0, 0, 0.2);}' + 'should merge border and multiple border-*-width values', + processCSS, + 'h1{border:0 solid rgba(0, 0, 0, 0.2);border-top-width:1px;border-right-width:1px;border-bottom-width:1px;border-left-width:1px;}', + 'h1{border:1px solid rgba(0, 0, 0, 0.2);}' ); test( - 'should merge border and multiple border-*-width values (uppercase)', - processCSS, - 'h1{BORDER:0 SOLID RGBA(0, 0, 0, 0.2);BORDER-TOP-WIDTH:1PX;BORDER-RIGHT-WIDTH:1PX;BORDER-BOTTOM-WIDTH:1PX;BORDER-LEFT-WIDTH:1PX;}', - 'h1{border:1px solid rgba(0, 0, 0, 0.2);}' + 'should merge border and multiple border-*-width values (uppercase)', + processCSS, + 'h1{BORDER:0 SOLID RGBA(0, 0, 0, 0.2);BORDER-TOP-WIDTH:1PX;BORDER-RIGHT-WIDTH:1PX;BORDER-BOTTOM-WIDTH:1PX;BORDER-LEFT-WIDTH:1PX;}', + 'h1{border:1px solid rgba(0, 0, 0, 0.2);}' ); test( - 'should produce the minimum css necessary', - passthroughCSS, - 'h1{border-width:none;border-top:1px solid #e1e1e1}' + 'should produce the minimum css necessary', + passthroughCSS, + 'h1{border-width:none;border-top:1px solid #e1e1e1}' ); test( - 'should produce the minimum css necessary (uppercase)', - processCSS, - 'h1{BORDER-WIDTH:NONE;BORDER-TOP:1PX SOLID #E1E1E1}', - 'h1{border-width:NONE;border-top:1px solid #e1e1e1}' + 'should produce the minimum css necessary (uppercase)', + processCSS, + 'h1{BORDER-WIDTH:NONE;BORDER-TOP:1PX SOLID #E1E1E1}', + 'h1{border-width:NONE;border-top:1px solid #e1e1e1}' ); test( - 'should produce the minimum css necessary (2)', - processCSS, - 'h1{border-color:rgba(0,0,0,.2);border-right-style:solid;border-right-width:1px}', - 'h1{border-right:1px solid;border-color:rgba(0,0,0,.2)}' + 'should produce the minimum css necessary (2)', + processCSS, + 'h1{border-color:rgba(0,0,0,.2);border-right-style:solid;border-right-width:1px}', + 'h1{border-right:1px solid;border-color:rgba(0,0,0,.2)}' ); test( - 'should produce the minimum css necessary (2) (uppercase)', - processCSS, - 'h1{BORDER-COLOR:RGBA(0,0,0,.2);BORDER-RIGHT-STYLE:SOLID;BORDER-RIGHT-WIDTH:1PX}', - 'h1{border-right:1px solid;border-color:RGBA(0,0,0,.2)}' + 'should produce the minimum css necessary (2) (uppercase)', + processCSS, + 'h1{BORDER-COLOR:RGBA(0,0,0,.2);BORDER-RIGHT-STYLE:SOLID;BORDER-RIGHT-WIDTH:1PX}', + 'h1{border-right:1px solid;border-color:RGBA(0,0,0,.2)}' ); test( - 'should produce the minimum css necessary (3)', - processCSS, - 'h1{border-top:0 solid transparent;border-right:4em solid transparent;border-bottom:4em solid transparent;border-left:0 solid transparent;border-right-color:inherit}', - 'h1{border-color:transparent;border-style:solid;border-width:0 4em 4em 0;border-right-color:inherit}' + 'should produce the minimum css necessary (3)', + processCSS, + 'h1{border-top:0 solid transparent;border-right:4em solid transparent;border-bottom:4em solid transparent;border-left:0 solid transparent;border-right-color:inherit}', + 'h1{border-color:transparent;border-style:solid;border-width:0 4em 4em 0;border-right-color:inherit}' ); test( - 'should produce the minimum css necessary (3) (uppercase)', - processCSS, - 'h1{BORDER-TOP:0 SOLID TRANSPARENT;BORDER-RIGHT:4EM SOLID TRANSPARENT;BORDER-BOTTOM:4EM SOLID TRANSPARENT;BORDER-LEFT:0 SOLID TRANSPARENT;BORDER-RIGHT-COLOR:INHERIT}', - 'h1{border-color:transparent;border-style:solid;border-width:0 4em 4em 0;BORDER-RIGHT-COLOR:INHERIT}' + 'should produce the minimum css necessary (3) (uppercase)', + processCSS, + 'h1{BORDER-TOP:0 SOLID TRANSPARENT;BORDER-RIGHT:4EM SOLID TRANSPARENT;BORDER-BOTTOM:4EM SOLID TRANSPARENT;BORDER-LEFT:0 SOLID TRANSPARENT;BORDER-RIGHT-COLOR:INHERIT}', + 'h1{border-color:transparent;border-style:solid;border-width:0 4em 4em 0;BORDER-RIGHT-COLOR:INHERIT}' ); test( - 'should produce the minimum css necessary (4)', - processCSS, - 'h1{border:none;border-top:1px solid #d4d4d5;border-right:1px solid #d4d4d5}', - 'h1{border:1px solid #d4d4d5;border-bottom:none;border-left:none}' + 'should produce the minimum css necessary (4)', + processCSS, + 'h1{border:none;border-top:1px solid #d4d4d5;border-right:1px solid #d4d4d5}', + 'h1{border:1px solid #d4d4d5;border-bottom:none;border-left:none}' ); test( - 'should produce the minimum css necessary (4) (uppercase)', - processCSS, - 'h1{BORDER:NONE;BORDER-TOP:1PX SOLID #D4D4D5;BORDER-RIGHT:1PX SOLID #D4D4D5}', - 'h1{border:1px solid #d4d4d5;border-bottom:none;border-left:none}' + 'should produce the minimum css necessary (4) (uppercase)', + processCSS, + 'h1{BORDER:NONE;BORDER-TOP:1PX SOLID #D4D4D5;BORDER-RIGHT:1PX SOLID #D4D4D5}', + 'h1{border:1px solid #d4d4d5;border-bottom:none;border-left:none}' ); test( - 'should produce the minimum css necessary (5)', - processCSS, - 'h1{border-spacing:50px 50px;border-top:0 solid transparent;border-right:4em solid transparent;border-bottom:4em solid transparent;border-left:0 solid transparent;border-right-color:inherit}', - 'h1{border-spacing:50px;border-color:transparent;border-style:solid;border-width:0 4em 4em 0;border-right-color:inherit}' + 'should produce the minimum css necessary (5)', + processCSS, + 'h1{border-spacing:50px 50px;border-top:0 solid transparent;border-right:4em solid transparent;border-bottom:4em solid transparent;border-left:0 solid transparent;border-right-color:inherit}', + 'h1{border-spacing:50px;border-color:transparent;border-style:solid;border-width:0 4em 4em 0;border-right-color:inherit}' ); test( - 'should produce the minimum css necessary (5) (uppercase)', - processCSS, - 'h1{BORDER-SPACING:50PX 50PX;BORDER-TOP:0 SOLID TRANSPARENT;BORDER-RIGHT:4EM SOLID TRANSPARENT;BORDER-BOTTOM:4EM SOLID TRANSPARENT;BORDER-LEFT:0 SOLID TRANSPARENT;BORDER-RIGHT-COLOR:INHERIT}', - 'h1{BORDER-SPACING:50PX;border-color:transparent;border-style:solid;border-width:0 4em 4em 0;BORDER-RIGHT-COLOR:INHERIT}' + 'should produce the minimum css necessary (5) (uppercase)', + processCSS, + 'h1{BORDER-SPACING:50PX 50PX;BORDER-TOP:0 SOLID TRANSPARENT;BORDER-RIGHT:4EM SOLID TRANSPARENT;BORDER-BOTTOM:4EM SOLID TRANSPARENT;BORDER-LEFT:0 SOLID TRANSPARENT;BORDER-RIGHT-COLOR:INHERIT}', + 'h1{BORDER-SPACING:50PX;border-color:transparent;border-style:solid;border-width:0 4em 4em 0;BORDER-RIGHT-COLOR:INHERIT}' ); test( - 'should produce the minimum css necessary (6)', - processCSS, - 'h1{border:1px solid #00d1b2;border-right:none;border-top:none}', - 'h1{border:1px solid #00d1b2;border-top:none;border-right:none}', + 'should produce the minimum css necessary (6)', + processCSS, + 'h1{border:1px solid #00d1b2;border-right:none;border-top:none}', + 'h1{border:1px solid #00d1b2;border-top:none;border-right:none}' ); test( - 'should produce the minimum css necessary (6) (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID #00D1B2;BORDER-RIGHT:NONE;BORDER-TOP:NONE}', - 'h1{border:1px solid #00d1b2;border-top:none;border-right:none}', + 'should produce the minimum css necessary (6) (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID #00D1B2;BORDER-RIGHT:NONE;BORDER-TOP:NONE}', + 'h1{border:1px solid #00d1b2;border-top:none;border-right:none}' ); test( - 'should produce the minimum css necessary (7)', - processCSS, - 'h1{border-top:none;border-right:none;border-bottom:1px solid #cacaca;border-left:none}', - 'h1{border:none;border-bottom:1px solid #cacaca}', + 'should produce the minimum css necessary (7)', + processCSS, + 'h1{border-top:none;border-right:none;border-bottom:1px solid #cacaca;border-left:none}', + 'h1{border:none;border-bottom:1px solid #cacaca}' ); test( - 'should produce the minimum css necessary (7) (uppercase)', - processCSS, - 'h1{BORDER-TOP:NONE;BORDER-RIGHT:NONE;BORDER-BOTTOM:1PX SOLID #CACACA;BORDER-LEFT:NONE}', - 'h1{border:none;border-bottom:1px solid #cacaca}', + 'should produce the minimum css necessary (7) (uppercase)', + processCSS, + 'h1{BORDER-TOP:NONE;BORDER-RIGHT:NONE;BORDER-BOTTOM:1PX SOLID #CACACA;BORDER-LEFT:NONE}', + 'h1{border:none;border-bottom:1px solid #cacaca}' ); test( - 'should produce the minimum css necessary (8)', - processCSS, - 'h1{border-top:none;border-right:none;border-bottom:none;border-left:5px}', - 'h1{border:none;border-left:5px}', + 'should produce the minimum css necessary (8)', + processCSS, + 'h1{border-top:none;border-right:none;border-bottom:none;border-left:5px}', + 'h1{border:none;border-left:5px}' ); test( - 'should produce the minimum css necessary (8) (uppercase)', - processCSS, - 'h1{BORDER-TOP:NONE;BORDER-RIGHT:NONE;BORDER-BOTTOM:NONE;BORDER-LEFT:5PX}', - 'h1{border:none;border-left:5PX}', + 'should produce the minimum css necessary (8) (uppercase)', + processCSS, + 'h1{BORDER-TOP:NONE;BORDER-RIGHT:NONE;BORDER-BOTTOM:NONE;BORDER-LEFT:5PX}', + 'h1{border:none;border-left:5PX}' ); test( - 'should produce the minimum css necessary (9)', - processCSS, - 'h1{border:medium none;border-style:solid;border-color:rgba(34, 36, 38, 0.15);border-width:0px 1px 1px 0px}', - 'h1{border:solid rgba(34, 36, 38, 0.15);border-width:0px 1px 1px 0px}' + 'should produce the minimum css necessary (9)', + processCSS, + 'h1{border:medium none;border-style:solid;border-color:rgba(34, 36, 38, 0.15);border-width:0px 1px 1px 0px}', + 'h1{border:solid rgba(34, 36, 38, 0.15);border-width:0px 1px 1px 0px}' ); test( - 'should produce the minimum css necessary (9) (uppercase)', - processCSS, - 'h1{BORDER:MEDIUM NONE;BORDER-STYLE:SOLID;BORDER-COLOR:RGBA(34, 36, 38, 0.15);BORDER-WIDTH:0PX 1PX 1PX 0PX}', - 'h1{border:solid rgba(34, 36, 38, 0.15);border-width:0px 1px 1px 0px}' + 'should produce the minimum css necessary (9) (uppercase)', + processCSS, + 'h1{BORDER:MEDIUM NONE;BORDER-STYLE:SOLID;BORDER-COLOR:RGBA(34, 36, 38, 0.15);BORDER-WIDTH:0PX 1PX 1PX 0PX}', + 'h1{border:solid rgba(34, 36, 38, 0.15);border-width:0px 1px 1px 0px}' ); test( - 'should produce the minimum css necessary (10)', - processCSS, - 'h1{border-bottom:none;border-left:1px solid transparent;border-right:1px solid transparent;border-top:2px solid transparent}', - 'h1{border:1px solid transparent;border-top:2px solid transparent;border-bottom:none}', + 'should produce the minimum css necessary (10)', + processCSS, + 'h1{border-bottom:none;border-left:1px solid transparent;border-right:1px solid transparent;border-top:2px solid transparent}', + 'h1{border:1px solid transparent;border-top:2px solid transparent;border-bottom:none}' ); test( - 'should produce the minimum css necessary (10) (uppercase)', - processCSS, - 'h1{BORDER-BOTTOM:NONE;BORDER-LEFT:1PX SOLID TRANSPARENT;BORDER-RIGHT:1PX SOLID TRANSPARENT;BORDER-TOP:2PX SOLID TRANSPARENT}', - 'h1{border:1px solid transparent;border-top:2px solid transparent;border-bottom:none}', + 'should produce the minimum css necessary (10) (uppercase)', + processCSS, + 'h1{BORDER-BOTTOM:NONE;BORDER-LEFT:1PX SOLID TRANSPARENT;BORDER-RIGHT:1PX SOLID TRANSPARENT;BORDER-TOP:2PX SOLID TRANSPARENT}', + 'h1{border:1px solid transparent;border-top:2px solid transparent;border-bottom:none}' ); test( - 'should not merge declarations with hacks', - processCSS, - 'h1{border-color:red red red red;_border-width:1px 1px 1px 1px;border-style:solid solid solid solid}', - 'h1{border-color:red;_border-width:1px 1px 1px 1px;border-style:solid}' + 'should not merge declarations with hacks', + processCSS, + 'h1{border-color:red red red red;_border-width:1px 1px 1px 1px;border-style:solid solid solid solid}', + 'h1{border-color:red;_border-width:1px 1px 1px 1px;border-style:solid}' ); test( - 'should not merge declarations with hacks (uppercase)', - processCSS, - 'h1{BORDER-COLOR:RED RED RED RED;_BORDER-WIDTH:1PX 1PX 1PX 1PX;BORDER-STYLE:SOLID SOLID SOLID SOLID}', - 'h1{border-color:RED;_BORDER-WIDTH:1PX 1PX 1PX 1PX;border-style:SOLID}' + 'should not merge declarations with hacks (uppercase)', + processCSS, + 'h1{BORDER-COLOR:RED RED RED RED;_BORDER-WIDTH:1PX 1PX 1PX 1PX;BORDER-STYLE:SOLID SOLID SOLID SOLID}', + 'h1{border-color:RED;_BORDER-WIDTH:1PX 1PX 1PX 1PX;border-style:SOLID}' ); test( - 'should not merge fallback colours', - passthroughCSS, - 'h1{border-color:#ddd;border-color:rgba(0,0,0,.15)}' + 'should not merge fallback colours', + passthroughCSS, + 'h1{border-color:#ddd;border-color:rgba(0,0,0,.15)}' ); test( - 'should not merge fallback colours (uppercase)', - processCSS, - 'h1{BORDER-COLOR:#DDD;BORDER-COLOR:RGBA(0,0,0,.15)}', - 'h1{border-color:#DDD;border-color:RGBA(0,0,0,.15)}' + 'should not merge fallback colours (uppercase)', + processCSS, + 'h1{BORDER-COLOR:#DDD;BORDER-COLOR:RGBA(0,0,0,.15)}', + 'h1{border-color:#DDD;border-color:RGBA(0,0,0,.15)}' ); test( - 'should not merge fallback colours with shorthand property', - processCSS, - 'h1{border:1px solid #ccc;border:1px solid rgba(0,0,0,.2)}', - 'h1{border:1px solid #ccc;border:1px solid rgba(0,0,0,.2)}' + 'should not merge fallback colours with shorthand property', + processCSS, + 'h1{border:1px solid #ccc;border:1px solid rgba(0,0,0,.2)}', + 'h1{border:1px solid #ccc;border:1px solid rgba(0,0,0,.2)}' ); test( - 'should not merge fallback colours with shorthand property (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID #CCC;BORDER:1PX SOLID RGBA(0,0,0,.2)}', - 'h1{border:1px solid #ccc;border:1px solid rgba(0,0,0,.2)}' + 'should not merge fallback colours with shorthand property (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID #CCC;BORDER:1PX SOLID RGBA(0,0,0,.2)}', + 'h1{border:1px solid #ccc;border:1px solid rgba(0,0,0,.2)}' ); test( - 'should merge together all initial values', - processCSS, - 'h1{border-color:initial;border-width:initial;border-style:initial}', - 'h1{border:initial}' + 'should merge together all initial values', + processCSS, + 'h1{border-color:initial;border-width:initial;border-style:initial}', + 'h1{border:initial}' ); test( - 'should merge together all initial values (uppercase)', - processCSS, - 'h1{BORDER-COLOR:initial;BORDER-WIDTH:initial;BORDER-STYLE:initial}', - 'h1{border:initial}' + 'should merge together all initial values (uppercase)', + processCSS, + 'h1{BORDER-COLOR:initial;BORDER-WIDTH:initial;BORDER-STYLE:initial}', + 'h1{border:initial}' ); test( - 'should merge together all initial values 1 (uppercase)', - processCSS, - 'h1{border-color:INITIAL;border-width:INITIAL;border-style:INITIAL}', - 'h1{border:INITIAL}' + 'should merge together all initial values 1 (uppercase)', + processCSS, + 'h1{border-color:INITIAL;border-width:INITIAL;border-style:INITIAL}', + 'h1{border:INITIAL}' ); test( - 'should merge together all inherit values', - processCSS, - 'h1{border-color:inherit;border-width:inherit;border-style:inherit}', - 'h1{border:inherit}' + 'should merge together all inherit values', + processCSS, + 'h1{border-color:inherit;border-width:inherit;border-style:inherit}', + 'h1{border:inherit}' ); test( - 'should merge together all inherit values (uppercase)', - processCSS, - 'h1{BORDER-COLOR:INHERIT;BORDER-WIDTH:INHERIT;BORDER-STYLE:INHERIT}', - 'h1{border:INHERIT}' + 'should merge together all inherit values (uppercase)', + processCSS, + 'h1{BORDER-COLOR:INHERIT;BORDER-WIDTH:INHERIT;BORDER-STYLE:INHERIT}', + 'h1{border:INHERIT}' ); test( - 'should preserve nesting level', - processCSS, - 'section{h1{border-color:red;border-width:1px;border-style:solid}}', - 'section{h1{border:1px solid red}}' + 'should preserve nesting level', + processCSS, + 'section{h1{border-color:red;border-width:1px;border-style:solid}}', + 'section{h1{border:1px solid red}}' ); test( - 'should preserve nesting level (uppercase)', - processCSS, - 'section{h1{BORDER-COLOR:RED;BORDER-WIDTH:1PX;BORDER-STYLE:SOLID}}', - 'section{h1{border:1px solid red}}' + 'should preserve nesting level (uppercase)', + processCSS, + 'section{h1{BORDER-COLOR:RED;BORDER-WIDTH:1PX;BORDER-STYLE:SOLID}}', + 'section{h1{border:1px solid red}}' ); test( - 'should not merge custom properties', - passthroughCSS, - ':root{--my-border-width:2px;--my-border-style:solid;--my-border-color:#fff;}' + 'should not merge custom properties', + passthroughCSS, + ':root{--my-border-width:2px;--my-border-style:solid;--my-border-color:#fff;}' ); test( - 'should not merge custom properties (uppercase)', - passthroughCSS, - ':root{--MY-BORDER-WIDTH:2PX;--MY-BORDER-STYLE:SOLID;--MY-BORDER-COLOR:#FFF;}' + 'should not merge custom properties (uppercase)', + passthroughCSS, + ':root{--MY-BORDER-WIDTH:2PX;--MY-BORDER-STYLE:SOLID;--MY-BORDER-COLOR:#FFF;}' ); test( - 'should not merge custom properties with variables', - passthroughCSS, - ':root{--my-border-width:var(--my-border-width);--my-border-style:var(--my-border-style);--my-border-color:var(--my-border-color);}' + 'should not merge custom properties with variables', + passthroughCSS, + ':root{--my-border-width:var(--my-border-width);--my-border-style:var(--my-border-style);--my-border-color:var(--my-border-color);}' ); test( - 'should not merge custom properties with variables (uppercase)', - passthroughCSS, - ':root{--MY-BORDER-WIDTH:VAR(--MY-BORDER-WIDTH);--MY-BORDER-STYLE:VAR(--MY-BORDER-STYLE);--MY-BORDER-COLOR:VAR(--MY-BORDER-COLOR);}' + 'should not merge custom properties with variables (uppercase)', + passthroughCSS, + ':root{--MY-BORDER-WIDTH:VAR(--MY-BORDER-WIDTH);--MY-BORDER-STYLE:VAR(--MY-BORDER-STYLE);--MY-BORDER-COLOR:VAR(--MY-BORDER-COLOR);}' ); test( - 'should overwrite some border-width props and save fallbacks', - processCSS, - 'h1{border-top-width:10px;border-right-width:var(--variable);border-right-width:15px;border-bottom-width:var(--variable);border-bottom-width:20px;border-left-width:25px;border-top-width:var(--variable);border-left-width:var(--variable)}', - 'h1{border-width:10px 15px 20px 25px;border-top-width:var(--variable);border-left-width:var(--variable)}' + 'should overwrite some border-width props and save fallbacks', + processCSS, + 'h1{border-top-width:10px;border-right-width:var(--variable);border-right-width:15px;border-bottom-width:var(--variable);border-bottom-width:20px;border-left-width:25px;border-top-width:var(--variable);border-left-width:var(--variable)}', + 'h1{border-width:10px 15px 20px 25px;border-top-width:var(--variable);border-left-width:var(--variable)}' ); test( - 'should overwrite some border-width props and save fallbacks (uppercase)', - processCSS, - 'h1{BORDER-TOP-WIDTH:10PX;BORDER-RIGHT-WIDTH:VAR(--VARIABLE);BORDER-RIGHT-WIDTH:15PX;BORDER-BOTTOM-WIDTH:VAR(--VARIABLE);BORDER-BOTTOM-WIDTH:20PX;BORDER-LEFT-WIDTH:25PX;BORDER-TOP-WIDTH:VAR(--VARIABLE);BORDER-LEFT-WIDTH:VAR(--VARIABLE)}', - 'h1{border-width:10PX 15PX 20PX 25PX;BORDER-TOP-WIDTH:VAR(--VARIABLE);BORDER-LEFT-WIDTH:VAR(--VARIABLE)}' + 'should overwrite some border-width props and save fallbacks (uppercase)', + processCSS, + 'h1{BORDER-TOP-WIDTH:10PX;BORDER-RIGHT-WIDTH:VAR(--VARIABLE);BORDER-RIGHT-WIDTH:15PX;BORDER-BOTTOM-WIDTH:VAR(--VARIABLE);BORDER-BOTTOM-WIDTH:20PX;BORDER-LEFT-WIDTH:25PX;BORDER-TOP-WIDTH:VAR(--VARIABLE);BORDER-LEFT-WIDTH:VAR(--VARIABLE)}', + 'h1{border-width:10PX 15PX 20PX 25PX;BORDER-TOP-WIDTH:VAR(--VARIABLE);BORDER-LEFT-WIDTH:VAR(--VARIABLE)}' ); test( - 'save fallbacks should border-style', - processCSS, - 'h1{border-style:dotted;border-style:var(--variable)}', - 'h1{border-style:dotted;border-style:var(--variable)}' + 'save fallbacks should border-style', + processCSS, + 'h1{border-style:dotted;border-style:var(--variable)}', + 'h1{border-style:dotted;border-style:var(--variable)}' ); test( - 'save fallbacks should border-color (uppercase)', - processCSS, - 'h1{BORDER-COLOR:DOTTED;BORDER-COLOR:VAR(--VARIABLE)}', - 'h1{border-color:DOTTED;border-color:VAR(--VARIABLE)}' + 'save fallbacks should border-color (uppercase)', + processCSS, + 'h1{BORDER-COLOR:DOTTED;BORDER-COLOR:VAR(--VARIABLE)}', + 'h1{border-color:DOTTED;border-color:VAR(--VARIABLE)}' ); test( - 'should not explode border with custom properties', - passthroughCSS, - 'h1{border:var(--variable)}', + 'should not explode border with custom properties', + passthroughCSS, + 'h1{border:var(--variable)}' ); test( - 'should not explode border with custom properties (uppercase)', - passthroughCSS, - 'h1{border:VAR(--VARIABLE)}', + 'should not explode border with custom properties (uppercase)', + passthroughCSS, + 'h1{border:VAR(--VARIABLE)}' ); test( - 'should not explode border with initial properties', - passthroughCSS, - 'h1{border:initial}', + 'should not explode border with initial properties', + passthroughCSS, + 'h1{border:initial}' ); test( - 'should not explode border with initial properties (uppercase)', - passthroughCSS, - 'h1{BORDER:initial}', + 'should not explode border with initial properties (uppercase)', + passthroughCSS, + 'h1{BORDER:initial}' ); test( - 'should not explode border with initial properties 1 (uppercase)', - passthroughCSS, - 'h1{border:INITIAL}', + 'should not explode border with initial properties 1 (uppercase)', + passthroughCSS, + 'h1{border:INITIAL}' ); test( - 'should not explode border with inherit properties', - passthroughCSS, - 'h1{border:inherit}', + 'should not explode border with inherit properties', + passthroughCSS, + 'h1{border:inherit}' ); test( - 'should not explode border with inherit properties (uppercase)', - passthroughCSS, - 'h1{BORDER:inherit}', + 'should not explode border with inherit properties (uppercase)', + passthroughCSS, + 'h1{BORDER:inherit}' ); test( - 'should not explode border with inherit properties 1 (uppercase)', - passthroughCSS, - 'h1{border:INHERIT}', + 'should not explode border with inherit properties 1 (uppercase)', + passthroughCSS, + 'h1{border:INHERIT}' ); test( - 'should not explode border with unset properties', - passthroughCSS, - 'h1{border:unset}', + 'should not explode border with unset properties', + passthroughCSS, + 'h1{border:unset}' ); test( - 'should not explode border with unset properties (uppercase)', - passthroughCSS, - 'h1{BORDER:unset}', + 'should not explode border with unset properties (uppercase)', + passthroughCSS, + 'h1{BORDER:unset}' ); test( - 'should not explode border with unset properties 1 (uppercase)', - passthroughCSS, - 'h1{border:UNSET}', + 'should not explode border with unset properties 1 (uppercase)', + passthroughCSS, + 'h1{border:UNSET}' ); -trbl.forEach(direction => { - test( - `should not explode border-${direction} with custom properties`, - passthroughCSS, - `h1{border-${direction}:var(--variable)}`, - ); +trbl.forEach((direction) => { + test( + `should not explode border-${direction} with custom properties`, + passthroughCSS, + `h1{border-${direction}:var(--variable)}` + ); - test( - `should not explode border-${direction.toUpperCase()} with custom properties`, - passthroughCSS, - `h1{BORDER-${direction.toUpperCase()}:VAR(--variable)}`, - ); + test( + `should not explode border-${direction.toUpperCase()} with custom properties`, + passthroughCSS, + `h1{BORDER-${direction.toUpperCase()}:VAR(--variable)}` + ); }); test( - 'should not explode custom properties with less than two concrete sides (1)', - passthroughCSS, - 'h1{border:var(--border-width) var(--border-style) transparent}', + 'should not explode custom properties with less than two concrete sides (1)', + passthroughCSS, + 'h1{border:var(--border-width) var(--border-style) transparent}' ); test( - 'should not explode custom properties with less than two concrete sides (1) (uppercase)', - passthroughCSS, - 'h1{BORDER:VAR(--BORDER-WIDTH) VAR(--BORDER-STYLE) TRANSPARENT}', + 'should not explode custom properties with less than two concrete sides (1) (uppercase)', + passthroughCSS, + 'h1{BORDER:VAR(--BORDER-WIDTH) VAR(--BORDER-STYLE) TRANSPARENT}' ); test( - 'should not explode custom properties with less than two concrete sides (2)', - passthroughCSS, - 'h1{border:var(--border-width) solid var(--border-color)}', + 'should not explode custom properties with less than two concrete sides (2)', + passthroughCSS, + 'h1{border:var(--border-width) solid var(--border-color)}' ); test( - 'should not explode custom properties with less than two concrete sides (2) (uppercase)', - passthroughCSS, - 'h1{BORDER:VAR(--BORDER-WIDTH) SOLID VAR(--BORDER-COLOR)}', + 'should not explode custom properties with less than two concrete sides (2) (uppercase)', + passthroughCSS, + 'h1{BORDER:VAR(--BORDER-WIDTH) SOLID VAR(--BORDER-COLOR)}' ); test( - 'should not explode custom properties with less than two concrete sides (3)', - passthroughCSS, - 'h1{border:1px var(--border-style) var(--border-color)}', + 'should not explode custom properties with less than two concrete sides (3)', + passthroughCSS, + 'h1{border:1px var(--border-style) var(--border-color)}' ); test( - 'should not explode custom properties with less than two concrete sides (3) (uppercase)', - passthroughCSS, - 'h1{BORDER:1PX VAR(--BORDER-STYLE) VAR(--BORDER-COLOR)}', + 'should not explode custom properties with less than two concrete sides (3) (uppercase)', + passthroughCSS, + 'h1{BORDER:1PX VAR(--BORDER-STYLE) VAR(--BORDER-COLOR)}' ); test( - 'Should correctly merge border declarations (#551) (1)', - processCSS, - 'h1{border:1px solid black;border-top-width:2px;border-right-width:2px;border-bottom-width:2px}', - 'h1{border:2px solid black;border-left-width:1px}', + 'Should correctly merge border declarations (#551) (1)', + processCSS, + 'h1{border:1px solid black;border-top-width:2px;border-right-width:2px;border-bottom-width:2px}', + 'h1{border:2px solid black;border-left-width:1px}' ); test( - 'Should correctly merge border declarations (#551) (1) (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID BLACK;BORDER-TOP-WIDTH:2PX;BORDER-RIGHT-WIDTH:2PX;BORDER-BOTTOM-WIDTH:2PX}', - 'h1{border:2px solid black;border-left-width:1px}', + 'Should correctly merge border declarations (#551) (1) (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID BLACK;BORDER-TOP-WIDTH:2PX;BORDER-RIGHT-WIDTH:2PX;BORDER-BOTTOM-WIDTH:2PX}', + 'h1{border:2px solid black;border-left-width:1px}' ); test( - 'Should correctly merge border declarations (#551) (2)', - processCSS, - 'h1{border:none;border-top:6px solid #000;border-bottom:1px solid #fff}', - 'h1{border:none;border-top:6px solid #000;border-bottom:1px solid #fff}', + 'Should correctly merge border declarations (#551) (2)', + processCSS, + 'h1{border:none;border-top:6px solid #000;border-bottom:1px solid #fff}', + 'h1{border:none;border-top:6px solid #000;border-bottom:1px solid #fff}' ); test( - 'Should correctly merge border declarations (#551) (2) (uppercase)', - processCSS, - 'h1{BORDER:NONE;BORDER-TOP:6PX SOLID #000;BORDER-BOTTOM:1PX SOLID #FFF}', - 'h1{border:none;border-top:6px solid #000;border-bottom:1px solid #fff}', + 'Should correctly merge border declarations (#551) (2) (uppercase)', + processCSS, + 'h1{BORDER:NONE;BORDER-TOP:6PX SOLID #000;BORDER-BOTTOM:1PX SOLID #FFF}', + 'h1{border:none;border-top:6px solid #000;border-bottom:1px solid #fff}' ); - test( - 'should not break border-color (#553)', - processCSS, - 'h1{border:solid transparent;border-width:0 8px 16px;border-bottom-color:#eee}', - 'h1{border:solid transparent;border-bottom:solid #eee;border-width:0 8px 16px}' + 'should not break border-color (#553)', + processCSS, + 'h1{border:solid transparent;border-width:0 8px 16px;border-bottom-color:#eee}', + 'h1{border:solid transparent;border-bottom:solid #eee;border-width:0 8px 16px}' ); test( - 'should not break border-color (#553) (uppercase)', - processCSS, - 'h1{BORDER:SOLID TRANSPARENT;BORDER-WIDTH:0 8PX 16PX;BORDER-BOTTOM-COLOR:#EEE}', - 'h1{border:solid transparent;border-bottom:solid #eee;border-width:0 8px 16px}' + 'should not break border-color (#553) (uppercase)', + processCSS, + 'h1{BORDER:SOLID TRANSPARENT;BORDER-WIDTH:0 8PX 16PX;BORDER-BOTTOM-COLOR:#EEE}', + 'h1{border:solid transparent;border-bottom:solid #eee;border-width:0 8px 16px}' ); test( - 'should not remove border-top-color (#554)', - passthroughCSS, - 'h1{border-top-color: rgba(85, 85, 85, 0.95);border-bottom: 0}', + 'should not remove border-top-color (#554)', + passthroughCSS, + 'h1{border-top-color: rgba(85, 85, 85, 0.95);border-bottom: 0}' ); test( - 'should not remove border-top-color (#554) (uppercase)', - passthroughCSS, - 'h1{BORDER-TOP-COLOR: RGBA(85, 85, 85, 0.95);BORDER-BOTTOM: 0}', + 'should not remove border-top-color (#554) (uppercase)', + passthroughCSS, + 'h1{BORDER-TOP-COLOR: RGBA(85, 85, 85, 0.95);BORDER-BOTTOM: 0}' ); test( - 'Should not merge if there is a shorthand property between them (#557) (1)', - passthroughCSS, - 'h1{border:1px solid #d3d6db;border:1px solid var(--gray-lighter);border-left-width:0;}', + 'Should not merge if there is a shorthand property between them (#557) (1)', + passthroughCSS, + 'h1{border:1px solid #d3d6db;border:1px solid var(--gray-lighter);border-left-width:0;}' ); test( - 'Should not merge if there is a shorthand property between them (#557) (1) (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID #D3D6DB;BORDER:1PX SOLID VAR(--GRAY-LIGHTER);BORDER-LEFT-WIDTH:0;}', - 'h1{border:1px solid #d3d6db;border:1px solid VAR(--GRAY-LIGHTER);border-left-width:0;}', + 'Should not merge if there is a shorthand property between them (#557) (1) (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID #D3D6DB;BORDER:1PX SOLID VAR(--GRAY-LIGHTER);BORDER-LEFT-WIDTH:0;}', + 'h1{border:1px solid #d3d6db;border:1px solid VAR(--GRAY-LIGHTER);border-left-width:0;}' ); test( - 'Should not merge if there is a shorthand property between them (#557) (2)', - processCSS, - 'h1{border-left-style:solid;border-left-color:#d3d6db;border:1px solid var(--gray-lighter);border-left-width:0;}', - 'h1{border-left:1px solid #d3d6db;border:1px solid var(--gray-lighter);border-left-width:0;}', + 'Should not merge if there is a shorthand property between them (#557) (2)', + processCSS, + 'h1{border-left-style:solid;border-left-color:#d3d6db;border:1px solid var(--gray-lighter);border-left-width:0;}', + 'h1{border-left:1px solid #d3d6db;border:1px solid var(--gray-lighter);border-left-width:0;}' ); test( - 'Should not merge if there is a shorthand property between them (#557) (2) (uppercase)', - processCSS, - 'h1{BORDER-LEFT-STYLE:SOLID;BORDER-LEFT-COLOR:#D3D6DB;BORDER:1PX SOLID VAR(--GRAY-LIGHTER);BORDER-LEFT-WIDTH:0;}', - 'h1{border-left:1px solid #d3d6db;border:1px solid VAR(--GRAY-LIGHTER);border-left-width:0;}', + 'Should not merge if there is a shorthand property between them (#557) (2) (uppercase)', + processCSS, + 'h1{BORDER-LEFT-STYLE:SOLID;BORDER-LEFT-COLOR:#D3D6DB;BORDER:1PX SOLID VAR(--GRAY-LIGHTER);BORDER-LEFT-WIDTH:0;}', + 'h1{border-left:1px solid #d3d6db;border:1px solid VAR(--GRAY-LIGHTER);border-left-width:0;}' ); test( - 'Should not convert currentcolor (#559)', - passthroughCSS, - 'h1{border:2px solid transparent;border-top-color:currentcolor;}', + 'Should not convert currentcolor (#559)', + passthroughCSS, + 'h1{border:2px solid transparent;border-top-color:currentcolor;}' ); test( - 'Should not convert currentcolor (#559) (uppercase)', - processCSS, - 'h1{BORDER:2PX SOLID TRANSPARENT;BORDER-TOP-COLOR:CURRENTCOLOR;}', - 'h1{border:2px solid transparent;border-top-color:currentcolor;}', + 'Should not convert currentcolor (#559) (uppercase)', + processCSS, + 'h1{BORDER:2PX SOLID TRANSPARENT;BORDER-TOP-COLOR:CURRENTCOLOR;}', + 'h1{border:2px solid transparent;border-top-color:currentcolor;}' ); test( - 'Should not convert currentcolor (#559) (2)', - processCSS, - 'h1{border:2px solid transparent;border-top-color:currentColor;}', - 'h1{border:2px solid transparent;border-top-color:currentcolor;}', + 'Should not convert currentcolor (#559) (2)', + processCSS, + 'h1{border:2px solid transparent;border-top-color:currentColor;}', + 'h1{border:2px solid transparent;border-top-color:currentcolor;}' ); test( - 'Should not convert currentcolor (#559) (2) (uppercase)', - processCSS, - 'h1{BORDER:2PX SOLID TRANSPARENT;BORDER-TOP-COLOR:CURRENTCOLOR;}', - 'h1{border:2px solid transparent;border-top-color:currentcolor;}', + 'Should not convert currentcolor (#559) (2) (uppercase)', + processCSS, + 'h1{BORDER:2PX SOLID TRANSPARENT;BORDER-TOP-COLOR:CURRENTCOLOR;}', + 'h1{border:2px solid transparent;border-top-color:currentcolor;}' ); test( - 'should not drop border-width with custom property from border shorthand (#561)', - passthroughCSS, - 'h1{border:var(--border-width) solid grey}', + 'should not drop border-width with custom property from border shorthand (#561)', + passthroughCSS, + 'h1{border:var(--border-width) solid grey}' ); test( - 'should not drop border-width with custom property from border shorthand (#561) (uppercase)', - processCSS, - 'h1{BORDER:VAR(--border-width) SOLID GREY}', - 'h1{border:VAR(--border-width) solid grey}', + 'should not drop border-width with custom property from border shorthand (#561) (uppercase)', + processCSS, + 'h1{BORDER:VAR(--border-width) SOLID GREY}', + 'h1{border:VAR(--border-width) solid grey}' ); test( - 'Should not throw error (#570)', - processCSS, - 'h1{border:1px none;border-bottom-style:solid}', - 'h1{border:1px;border-bottom:1px solid}', + 'Should not throw error (#570)', + processCSS, + 'h1{border:1px none;border-bottom-style:solid}', + 'h1{border:1px;border-bottom:1px solid}' ); test( - 'Should not throw error (#570) (uppercase)', - processCSS, - 'h1{BORDER:1PX NONE;BORDER-BOTTOM-STYLE:SOLID}', - 'h1{border:1px;border-bottom:1px solid}', + 'Should not throw error (#570) (uppercase)', + processCSS, + 'h1{BORDER:1PX NONE;BORDER-BOTTOM-STYLE:SOLID}', + 'h1{border:1px;border-bottom:1px solid}' ); test( - 'Should correctly merge borders with custom properties (#572)', - passthroughCSS, - 'h1{border:6px solid red;border-top:6px solid var(--mycolor);}', + 'Should correctly merge borders with custom properties (#572)', + passthroughCSS, + 'h1{border:6px solid red;border-top:6px solid var(--mycolor);}' ); test( - 'Should correctly merge borders with custom properties (#572) (uppercase)', - processCSS, - 'h1{BORDER:6PX SOLID RED;BORDER-TOP:6PX SOLID VAR(--mycolor);}', - 'h1{border:6px solid red;border-top:6px solid VAR(--mycolor);}' + 'Should correctly merge borders with custom properties (#572) (uppercase)', + processCSS, + 'h1{BORDER:6PX SOLID RED;BORDER-TOP:6PX SOLID VAR(--mycolor);}', + 'h1{border:6px solid red;border-top:6px solid VAR(--mycolor);}' ); test( - 'Should correctly merge borders with custom properties (#619) (1)', - passthroughCSS, - 'h1{border:1px solid;border-color:var(--color-var)}', + 'Should correctly merge borders with custom properties (#619) (1)', + passthroughCSS, + 'h1{border:1px solid;border-color:var(--color-var)}' ); test( - 'Should correctly merge borders with custom properties (#619) (1) (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID;BORDER-COLOR:VAR(--COLOR-VAR)}', - 'h1{border:1px solid;border-color:VAR(--COLOR-VAR)}', + 'Should correctly merge borders with custom properties (#619) (1) (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID;BORDER-COLOR:VAR(--COLOR-VAR)}', + 'h1{border:1px solid;border-color:VAR(--COLOR-VAR)}' ); test( - 'Should correctly merge borders with custom properties (#619) (2)', - passthroughCSS, - 'h1{border-left:1px solid;border-left-color:var(--color-var)}', + 'Should correctly merge borders with custom properties (#619) (2)', + passthroughCSS, + 'h1{border-left:1px solid;border-left-color:var(--color-var)}' ); test( - 'Should correctly merge borders with custom properties (#619) (2) (uppercase)', - processCSS, - 'h1{BORDER-LEFT:1PX SOLID;BORDER-LEFT-COLOR:VAR(--COLOR-VAR)}', - 'h1{border-left:1px solid;border-left-color:VAR(--COLOR-VAR)}', + 'Should correctly merge borders with custom properties (#619) (2) (uppercase)', + processCSS, + 'h1{BORDER-LEFT:1PX SOLID;BORDER-LEFT-COLOR:VAR(--COLOR-VAR)}', + 'h1{border-left:1px solid;border-left-color:VAR(--COLOR-VAR)}' ); test( - 'Should correctly merge borders with custom properties (#619) (3)', - passthroughCSS, - 'h1{border-color:red green blue magenta;border-top-color:var(--color-var)}', + 'Should correctly merge borders with custom properties (#619) (3)', + passthroughCSS, + 'h1{border-color:red green blue magenta;border-top-color:var(--color-var)}' ); test( - 'Should correctly merge borders with custom properties (#619) (3) (uppercase)', - processCSS, - 'h1{BORDER-COLOR:RED GREEN BLUE MAGENTA;BORDER-TOP-COLOR:VAR(--COLOR-VAR)}', - 'h1{border-color:RED GREEN BLUE MAGENTA;BORDER-TOP-COLOR:VAR(--COLOR-VAR)}', + 'Should correctly merge borders with custom properties (#619) (3) (uppercase)', + processCSS, + 'h1{BORDER-COLOR:RED GREEN BLUE MAGENTA;BORDER-TOP-COLOR:VAR(--COLOR-VAR)}', + 'h1{border-color:RED GREEN BLUE MAGENTA;BORDER-TOP-COLOR:VAR(--COLOR-VAR)}' ); test( - 'Should correctly merge borders with custom properties (#652)', - processCSS, - 'h1{border-width:var(--a);border-style:var(--b);border-color:var(--c)}', - 'h1{border:var(--a) var(--b) var(--c)}' + 'Should correctly merge borders with custom properties (#652)', + processCSS, + 'h1{border-width:var(--a);border-style:var(--b);border-color:var(--c)}', + 'h1{border:var(--a) var(--b) var(--c)}' ); test( - 'Should correctly merge borders with custom properties (#652) (uppercase)', - processCSS, - 'h1{BORDER-WIDTH:VAR(--A);BORDER-STYLE:VAR(--B);BORDER-COLOR:VAR(--C)}', - 'h1{border:VAR(--A) VAR(--B) VAR(--C)}' + 'Should correctly merge borders with custom properties (#652) (uppercase)', + processCSS, + 'h1{BORDER-WIDTH:VAR(--A);BORDER-STYLE:VAR(--B);BORDER-COLOR:VAR(--C)}', + 'h1{border:VAR(--A) VAR(--B) VAR(--C)}' ); test( - 'Should not throw error when a border property value is undefined (#639)', - processCSS, - 'h1{border:2px solid #fff;border-color:inherit}', - 'h1{border:2px solid;border-color:inherit}', + 'Should not throw error when a border property value is undefined (#639)', + processCSS, + 'h1{border:2px solid #fff;border-color:inherit}', + 'h1{border:2px solid;border-color:inherit}' ); test( - 'Should not throw error when a border property value is undefined (#639) (uppercase)', - processCSS, - 'h1{BORDER:2PX SOLID #FFF;BORDER-COLOR:INHERIT}', - 'h1{border:2px solid;BORDER-COLOR:INHERIT}', + 'Should not throw error when a border property value is undefined (#639) (uppercase)', + processCSS, + 'h1{BORDER:2PX SOLID #FFF;BORDER-COLOR:INHERIT}', + 'h1{border:2px solid;BORDER-COLOR:INHERIT}' ); test( - 'Should preserve case of css custom properties #648', - passthroughCSS, - 'h1{border:1px solid rgba(var(--fooBar));}', + 'Should preserve case of css custom properties #648', + passthroughCSS, + 'h1{border:1px solid rgba(var(--fooBar));}' ); test( - 'Should preserve case of css custom properties #648 (uppercase)', - processCSS, - 'h1{BORDER:1PX SOLID RGBA(VAR(--fooBar));}', - 'h1{border:1px solid rgba(var(--fooBar));}' + 'Should preserve case of css custom properties #648 (uppercase)', + processCSS, + 'h1{BORDER:1PX SOLID RGBA(VAR(--fooBar));}', + 'h1{border:1px solid rgba(var(--fooBar));}' ); test( - 'should overwrite some border-width props and save fallbacks and preserve case #648 2', - processCSS, - 'h1{border-top-width:10px;border-right-width:var(--fooBar);border-right-width:15px;border-bottom-width:var(--fooBar);border-bottom-width:20px;border-left-width:25px;border-top-width:var(--fooBar);border-left-width:var(--fooBar)}', - 'h1{border-width:10px 15px 20px 25px;border-top-width:var(--fooBar);border-left-width:var(--fooBar)}' + 'should overwrite some border-width props and save fallbacks and preserve case #648 2', + processCSS, + 'h1{border-top-width:10px;border-right-width:var(--fooBar);border-right-width:15px;border-bottom-width:var(--fooBar);border-bottom-width:20px;border-left-width:25px;border-top-width:var(--fooBar);border-left-width:var(--fooBar)}', + 'h1{border-width:10px 15px 20px 25px;border-top-width:var(--fooBar);border-left-width:var(--fooBar)}' ); test( - 'should overwrite some border-width props and save fallbacks and preserve case #648 2 (uppercase)', - processCSS, - 'h1{BORDER-TOP-WIDTH:10PX;BORDER-RIGHT-WIDTH:VAR(--fooBar);BORDER-RIGHT-WIDTH:15PX;BORDER-BOTTOM-WIDTH:VAR(--fooBar);BORDER-BOTTOM-WIDTH:20PX;BORDER-LEFT-WIDTH:25PX;BORDER-TOP-WIDTH:VAR(--fooBar);BORDER-LEFT-WIDTH:VAR(--fooBar)}', - 'h1{border-width:10PX 15PX 20PX 25PX;BORDER-TOP-WIDTH:VAR(--fooBar);BORDER-LEFT-WIDTH:VAR(--fooBar)}' + 'should overwrite some border-width props and save fallbacks and preserve case #648 2 (uppercase)', + processCSS, + 'h1{BORDER-TOP-WIDTH:10PX;BORDER-RIGHT-WIDTH:VAR(--fooBar);BORDER-RIGHT-WIDTH:15PX;BORDER-BOTTOM-WIDTH:VAR(--fooBar);BORDER-BOTTOM-WIDTH:20PX;BORDER-LEFT-WIDTH:25PX;BORDER-TOP-WIDTH:VAR(--fooBar);BORDER-LEFT-WIDTH:VAR(--fooBar)}', + 'h1{border-width:10PX 15PX 20PX 25PX;BORDER-TOP-WIDTH:VAR(--fooBar);BORDER-LEFT-WIDTH:VAR(--fooBar)}' ); diff --git a/packages/postcss-merge-longhand/src/__tests__/boxBase.js b/packages/postcss-merge-longhand/src/__tests__/boxBase.js index 751fb96db..d2877d94f 100644 --- a/packages/postcss-merge-longhand/src/__tests__/boxBase.js +++ b/packages/postcss-merge-longhand/src/__tests__/boxBase.js @@ -1,222 +1,317 @@ import test from 'ava'; import plugin from '..'; -import {processCSSFactory} from '../../../../util/testHelpers'; +import { processCSSFactory } from '../../../../util/testHelpers'; -const {processCSS} = processCSSFactory(plugin); +const { processCSS } = processCSSFactory(plugin); -function addTests (...tests) { - tests.forEach(({message, fixture, expected}) => { - const isExpectedFunc = typeof expected === "function"; +function addTests(...tests) { + tests.forEach(({ message, fixture, expected }) => { + const isExpectedFunc = typeof expected === 'function'; - test( - message.replace(/box/gi, 'margin'), - processCSS, - fixture.replace(/box/gi, 'margin'), - isExpectedFunc ? expected('margin') : expected.replace(/box/gi, 'margin') - ); - test( - message.replace(/box/gi, 'MARGIN'), - processCSS, - fixture.replace(/box/gi, 'MARGIN'), - isExpectedFunc ? expected('MARGIN') : expected.replace(/box/gi, 'margin') - ); - test( - message.replace(/box/gi, 'padding'), - processCSS, - fixture.replace(/box/gi, 'padding'), - isExpectedFunc ? expected('padding') : expected.replace(/box/gi, 'padding') - ); - test( - message.replace(/box/gi, 'PADDING'), - processCSS, - fixture.replace(/box/gi, 'PADDING'), - isExpectedFunc ? expected('PADDING') : expected.replace(/box/gi, 'padding') - ); - }); + test( + message.replace(/box/gi, 'margin'), + processCSS, + fixture.replace(/box/gi, 'margin'), + isExpectedFunc ? expected('margin') : expected.replace(/box/gi, 'margin') + ); + test( + message.replace(/box/gi, 'MARGIN'), + processCSS, + fixture.replace(/box/gi, 'MARGIN'), + isExpectedFunc ? expected('MARGIN') : expected.replace(/box/gi, 'margin') + ); + test( + message.replace(/box/gi, 'padding'), + processCSS, + fixture.replace(/box/gi, 'padding'), + isExpectedFunc + ? expected('padding') + : expected.replace(/box/gi, 'padding') + ); + test( + message.replace(/box/gi, 'PADDING'), + processCSS, + fixture.replace(/box/gi, 'PADDING'), + isExpectedFunc + ? expected('PADDING') + : expected.replace(/box/gi, 'padding') + ); + }); } -addTests({ +addTests( + { message: 'should merge box values', fixture: 'h1{box-top:10px;box-right:20px;box-bottom:30px;box-left:40px}', expected: 'h1{box:10px 20px 30px 40px}', -}, { + }, + { message: 'should merge box values with !important', - fixture: 'h1{box-top:10px!important;box-right:20px!important;box-bottom:30px!important;box-left:40px!important}', + fixture: + 'h1{box-top:10px!important;box-right:20px!important;box-bottom:30px!important;box-left:40px!important}', expected: 'h1{box:10px 20px 30px 40px!important}', -}, { + }, + { message: 'should merge & then condense box values', fixture: 'h1{box-top:10px;box-bottom:10px;box-left:10px;box-right:10px}', expected: 'h1{box:10px}', -}, { + }, + { message: 'should not merge box values with mixed !important', - fixture: 'h1{box-top:10px!important;box-right:20px;box-bottom:30px!important;box-left:40px}', - expected: (prop) => `h1{${prop}-top:10px!important;${prop}-right:20px;${prop}-bottom:30px!important;${prop}-left:40px}`, -}, { + fixture: + 'h1{box-top:10px!important;box-right:20px;box-bottom:30px!important;box-left:40px}', + expected: (prop) => + `h1{${prop}-top:10px!important;${prop}-right:20px;${prop}-bottom:30px!important;${prop}-left:40px}`, + }, + { message: 'should convert 4 values to 1 (box)', fixture: 'h1{box:10px 10px 10px 10px}', expected: 'h1{box:10px}', -}, { + }, + { message: 'should convert 3 values to 1 (box)', fixture: 'h1{box:10px 10px 10px}', expected: 'h1{box:10px}', -}, { + }, + { message: 'should convert 3 values to 2 (box)', fixture: 'h1{box:10px 20px 10px}', expected: 'h1{box:10px 20px}', -}, { + }, + { message: 'should convert 2 values to 1 (box)', fixture: 'h1{box:10px 10px}', expected: 'h1{box:10px}', -}, { + }, + { message: 'should convert 1 value to 1 (box)', fixture: 'h1{box:10px}', expected: 'h1{box:10px}', -}, { + }, + { message: 'should convert 4 values to 2 (box)', fixture: 'h1{box:10px 20px 10px 20px}', expected: 'h1{box:10px 20px}', -}, { + }, + { message: 'should convert 4 values to 3 (box)', fixture: 'h1{box:10px 20px 30px 20px}', expected: 'h1{box:10px 20px 30px}', -}, { + }, + { message: 'should convert 4 values to 4 (box)', fixture: 'h1{box:10px 20px 30px 40px}', expected: 'h1{box:10px 20px 30px 40px}', -}, { + }, + { message: 'should not mangle calc values (box)', fixture: 'h1{box:1px 1px calc(0.5em + 1px)}', expected: 'h1{box:1px 1px calc(0.5em + 1px)}', -}, { + }, + { message: 'should merge box-left with box', fixture: 'h1{box:10px 20px;box-left:10px}', expected: 'h1{box:10px 20px 10px 10px}', -}, { + }, + { message: 'should merge !important and normal box values', - fixture: 'h1{box-left:10px;box-left:20px!important;box-right:10px;box-right:20px!important;box-top:10px;box-top:20px!important;box-bottom:10px;box-bottom:20px!important}', + fixture: + 'h1{box-left:10px;box-left:20px!important;box-right:10px;box-right:20px!important;box-top:10px;box-top:20px!important;box-bottom:10px;box-bottom:20px!important}', expected: 'h1{box:10px;box:20px!important}', -}, { + }, + { message: 'should not merge declarations with hacks (box)', fixture: 'h1{box:4px 0;_box-top:1px}', expected: (prop) => `h1{${prop.toLowerCase()}:4px 0;_${prop}-top:1px}`, -}, { + }, + { message: 'should not merge declarations with hacks (box) (2)', fixture: 'h1{box:4px 0;box-top:1px\\9}', expected: (prop) => `h1{${prop.toLowerCase()}:4px 0;${prop}-top:1px\\9}`, -}, { + }, + { message: 'should convert 2 values to 1 with an unrelated inherit (box)', fixture: '.ui.table td{box:0.71428571em 0.71428571em;text-align:inherit}', expected: '.ui.table td{box:0.71428571em;text-align:inherit}', -}, { + }, + { message: 'should not explode box: inherit', fixture: 'h1{box:inherit}', expected: (prop) => `h1{${prop}:inherit}`, -}, { + }, + { message: 'should not explode box: inherit (uppercase)', fixture: 'h1{box:INHERIT}', expected: (prop) => `h1{${prop}:INHERIT}`, -}, { + }, + { message: 'should not merge declarations with hacks', fixture: 'h1{box:4px 0 0 0;box-top:1px\\9}', expected: (prop) => `h1{${prop.toLowerCase()}:4px 0 0;${prop}-top:1px\\9}`, -}, { + }, + { message: 'should preserve nesting level', fixture: 'section{h1{box:0 48px}}', expected: 'section{h1{box:0 48px}}', -}, { + }, + { message: 'should override shorthand property', fixture: 'h1{box:10px;box-left:5px}', expected: 'h1{box:10px 10px 10px 5px}', -}, { + }, + { message: 'should overwrite some box props and save fallbacks', - fixture: 'h1{box-top:10px;box-right:var(--variable);box-right:15px;box-bottom:var(--variable);box-bottom:20px;box-left:25px;box-top:var(--variable);box-left:var(--variable)}', - expected: (prop) => `h1{${prop.toLowerCase()}:10px 15px 20px 25px;${prop}-top:var(--variable);${prop}-left:var(--variable)}`, -}, { + fixture: + 'h1{box-top:10px;box-right:var(--variable);box-right:15px;box-bottom:var(--variable);box-bottom:20px;box-left:25px;box-top:var(--variable);box-left:var(--variable)}', + expected: (prop) => + `h1{${prop.toLowerCase()}:10px 15px 20px 25px;${prop}-top:var(--variable);${prop}-left:var(--variable)}`, + }, + { message: 'should not explode box props with custom properties', fixture: 'h1{box-bottom:var(--variable)}', expected: (prop) => `h1{${prop}-bottom:var(--variable)}`, -}, { + }, + { message: 'should preserve case of custom properties', - fixture: 'h1{box-top:10px;box-right:var(--fooBar);box-right:15px;box-bottom:var(--fooBar);box-bottom:20px;box-left:25px;box-top:var(--fooBar);box-left:var(--fooBar)}', - expected: (prop) => `h1{${prop.toLowerCase()}:10px 15px 20px 25px;${prop}-top:var(--fooBar);${prop}-left:var(--fooBar)}`, -}, { - message: 'should not merge incomplete box props where one has an unset property', + fixture: + 'h1{box-top:10px;box-right:var(--fooBar);box-right:15px;box-bottom:var(--fooBar);box-bottom:20px;box-left:25px;box-top:var(--fooBar);box-left:var(--fooBar)}', + expected: (prop) => + `h1{${prop.toLowerCase()}:10px 15px 20px 25px;${prop}-top:var(--fooBar);${prop}-left:var(--fooBar)}`, + }, + { + message: + 'should not merge incomplete box props where one has an unset property', fixture: 'h1{box-bottom:10px;box-top:unset;box-left:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:unset;${prop}-left:20px}`, -}, { - message: 'should not merge incomplete box props where one has an unset property (uppercase)', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:unset;${prop}-left:20px}`, + }, + { + message: + 'should not merge incomplete box props where one has an unset property (uppercase)', fixture: 'h1{box-bottom:10px;box-top:UNSET;box-left:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:UNSET;${prop}-left:20px}`, -}, { - message: 'should not merge incomplete box props where one has an initial property', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:UNSET;${prop}-left:20px}`, + }, + { + message: + 'should not merge incomplete box props where one has an initial property', fixture: 'h1{box-bottom:10px;box-top:initial;box-left:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:initial;${prop}-left:20px}`, -}, { - message: 'should not merge incomplete box props where one has an initial property (uppercase)', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:initial;${prop}-left:20px}`, + }, + { + message: + 'should not merge incomplete box props where one has an initial property (uppercase)', fixture: 'h1{box-bottom:10px;box-top:INITIAL;box-left:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:INITIAL;${prop}-left:20px}`, -}, { - message: 'should not merge incomplete box props where one has an inherit property', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:INITIAL;${prop}-left:20px}`, + }, + { + message: + 'should not merge incomplete box props where one has an inherit property', fixture: 'h1{box-bottom:10px;box-top:inherit;box-left:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:inherit;${prop}-left:20px}`, -}, { - message: 'should not merge incomplete box props where one has an inherit property (uppercase)', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:inherit;${prop}-left:20px}`, + }, + { + message: + 'should not merge incomplete box props where one has an inherit property (uppercase)', fixture: 'h1{box-bottom:10px;box-top:INHERIT;box-left:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:INHERIT;${prop}-left:20px}`, -}, { - message: 'should not merge complete box props where one has an unset property', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:INHERIT;${prop}-left:20px}`, + }, + { + message: + 'should not merge complete box props where one has an unset property', fixture: 'h1{box-bottom:10px;box-top:unset;box-left:20px;box-right:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:unset;${prop}-left:20px;${prop}-right:20px}`, -}, { - message: 'should not merge complete box props where one has an unset property (uppercase)', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:unset;${prop}-left:20px;${prop}-right:20px}`, + }, + { + message: + 'should not merge complete box props where one has an unset property (uppercase)', fixture: 'h1{box-bottom:10px;box-top:UNSET;box-left:20px;box-right:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:UNSET;${prop}-left:20px;${prop}-right:20px}`, -}, { - message: 'should not merge complete box props where one has an initial property', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:UNSET;${prop}-left:20px;${prop}-right:20px}`, + }, + { + message: + 'should not merge complete box props where one has an initial property', fixture: 'h1{box-bottom:10px;box-top:initial;box-left:20px;box-right:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:initial;${prop}-left:20px;${prop}-right:20px}`, -}, { - message: 'should not merge complete box props where one has an initial property (uppercase)', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:initial;${prop}-left:20px;${prop}-right:20px}`, + }, + { + message: + 'should not merge complete box props where one has an initial property (uppercase)', fixture: 'h1{box-bottom:10px;box-top:INITIAL;box-left:20px;box-right:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:INITIAL;${prop}-left:20px;${prop}-right:20px}`, -}, { - message: 'should not merge complete box props where one has an inherit property', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:INITIAL;${prop}-left:20px;${prop}-right:20px}`, + }, + { + message: + 'should not merge complete box props where one has an inherit property', fixture: 'h1{box-bottom:10px;box-top:inherit;box-left:20px;box-right:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:inherit;${prop}-left:20px;${prop}-right:20px}`, -}, { - message: 'should not merge complete box props where one has an inherit property (uppercase)', + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:inherit;${prop}-left:20px;${prop}-right:20px}`, + }, + { + message: + 'should not merge complete box props where one has an inherit property (uppercase)', fixture: 'h1{box-bottom:10px;box-top:INHERIT;box-left:20px;box-right:20px}', - expected: (prop) => `h1{${prop}-bottom:10px;${prop}-top:INHERIT;${prop}-left:20px;${prop}-right:20px}`, -}, { - message: 'should not merge box props where there is a mix of reserved properties', - fixture: 'h1{box-bottom:unset;box-top:initial;box-left:inherit;box-right:initial}', - expected: (prop) => `h1{${prop}-bottom:unset;${prop}-top:initial;${prop}-left:inherit;${prop}-right:initial}`, -}, { - message: 'should not merge box props where there is a mix of reserved properties (uppercase)', - fixture: 'h1{box-bottom:UNSET;box-top:INITIAL;box-left:INHERIT;box-right:INITIAL}', - expected: (prop) => `h1{${prop}-bottom:UNSET;${prop}-top:INITIAL;${prop}-left:INHERIT;${prop}-right:INITIAL}`, -}, { + expected: (prop) => + `h1{${prop}-bottom:10px;${prop}-top:INHERIT;${prop}-left:20px;${prop}-right:20px}`, + }, + { + message: + 'should not merge box props where there is a mix of reserved properties', + fixture: + 'h1{box-bottom:unset;box-top:initial;box-left:inherit;box-right:initial}', + expected: (prop) => + `h1{${prop}-bottom:unset;${prop}-top:initial;${prop}-left:inherit;${prop}-right:initial}`, + }, + { + message: + 'should not merge box props where there is a mix of reserved properties (uppercase)', + fixture: + 'h1{box-bottom:UNSET;box-top:INITIAL;box-left:INHERIT;box-right:INITIAL}', + expected: (prop) => + `h1{${prop}-bottom:UNSET;${prop}-top:INITIAL;${prop}-left:INHERIT;${prop}-right:INITIAL}`, + }, + { message: 'should merge box props when they are all unset', - fixture: 'h1{box-bottom:unset;box-top:unset;box-left:unset;box-right:unset}', + fixture: + 'h1{box-bottom:unset;box-top:unset;box-left:unset;box-right:unset}', expected: 'h1{box:unset}', -}, { + }, + { message: 'should merge box props when they are all unset (uppercase)', - fixture: 'h1{box-bottom:UNSET;box-top:UNSET;box-left:UNSET;box-right:UNSET}', + fixture: + 'h1{box-bottom:UNSET;box-top:UNSET;box-left:UNSET;box-right:UNSET}', expected: 'h1{box:UNSET}', -}, { + }, + { message: 'should merge box props when they are all initial', - fixture: 'h1{box-bottom:initial;box-top:initial;box-left:initial;box-right:initial}', + fixture: + 'h1{box-bottom:initial;box-top:initial;box-left:initial;box-right:initial}', expected: 'h1{box:initial}', -}, { + }, + { message: 'should merge box props when they are all initial (uppercase)', - fixture: 'h1{box-bottom:INITIAL;box-top:INITIAL;box-left:INITIAL;box-right:INITIAL}', + fixture: + 'h1{box-bottom:INITIAL;box-top:INITIAL;box-left:INITIAL;box-right:INITIAL}', expected: 'h1{box:INITIAL}', -}, { + }, + { message: 'should merge box props when they are all inherit', - fixture: 'h1{box-bottom:inherit;box-top:inherit;box-left:inherit;box-right:inherit}', + fixture: + 'h1{box-bottom:inherit;box-top:inherit;box-left:inherit;box-right:inherit}', expected: 'h1{box:inherit}', -}, { + }, + { message: 'should merge box props when they are all inherit (uppercase)', - fixture: 'h1{box-bottom:INHERIT;box-top:INHERIT;box-left:INHERIT;box-right:INHERIT}', + fixture: + 'h1{box-bottom:INHERIT;box-top:INHERIT;box-left:INHERIT;box-right:INHERIT}', expected: 'h1{box:INHERIT}', -}); + } +); diff --git a/packages/postcss-merge-longhand/src/__tests__/columns.js b/packages/postcss-merge-longhand/src/__tests__/columns.js index a40fe987c..891a9fe42 100644 --- a/packages/postcss-merge-longhand/src/__tests__/columns.js +++ b/packages/postcss-merge-longhand/src/__tests__/columns.js @@ -1,225 +1,217 @@ import test from 'ava'; import plugin from '..'; -import {processCSSFactory} from '../../../../util/testHelpers'; +import { processCSSFactory } from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); test( - 'should merge column values', - processCSS, - 'h1{column-width:12em;column-count:auto}', - 'h1{columns:12em}' + 'should merge column values', + processCSS, + 'h1{column-width:12em;column-count:auto}', + 'h1{columns:12em}' ); test( - 'should merge column values (uppercase)', - processCSS, - 'h1{COLUMN-WIDTH:12em;COLUMN-COUNT:auto}', - 'h1{columns:12em}' + 'should merge column values (uppercase)', + processCSS, + 'h1{COLUMN-WIDTH:12em;COLUMN-COUNT:auto}', + 'h1{columns:12em}' ); test( - 'should minify column values', - processCSS, - 'h1{column-width:auto;column-count:auto}', - 'h1{columns:auto}' + 'should minify column values', + processCSS, + 'h1{column-width:auto;column-count:auto}', + 'h1{columns:auto}' ); test( - 'should minify column values (uppercase)', - processCSS, - 'h1{COLUMN-WIDTH:auto;COLUMN-COUNT:auto}', - 'h1{columns:auto}' + 'should minify column values (uppercase)', + processCSS, + 'h1{COLUMN-WIDTH:auto;COLUMN-COUNT:auto}', + 'h1{columns:auto}' ); test( - 'should minify column values 1 (uppercase)', - processCSS, - 'h1{column-width:AUTO;column-count:AUTO}', - 'h1{columns:AUTO}' + 'should minify column values 1 (uppercase)', + processCSS, + 'h1{column-width:AUTO;column-count:AUTO}', + 'h1{columns:AUTO}' ); test( - 'should merge column-width with columns', - processCSS, - 'h1{columns:12em auto;column-width:11em}', - 'h1{columns:11em}' + 'should merge column-width with columns', + processCSS, + 'h1{columns:12em auto;column-width:11em}', + 'h1{columns:11em}' ); test( - 'should merge column-width with columns (uppercase)', - processCSS, - 'h1{COLUMNS:12em auto;COLUMN-WIDTH:11em}', - 'h1{columns:11em}' + 'should merge column-width with columns (uppercase)', + processCSS, + 'h1{COLUMNS:12em auto;COLUMN-WIDTH:11em}', + 'h1{columns:11em}' ); test( - 'should merge column-width with columns', - processCSS, - 'h1{columns:12em AUTO;column-width:11em}', - 'h1{columns:11em}' + 'should merge column-width with columns', + processCSS, + 'h1{columns:12em AUTO;column-width:11em}', + 'h1{columns:11em}' ); test( - 'should merge column width and column count', - processCSS, - 'h1{column-width:6em;column-count:3}', - 'h1{columns:6em 3}' + 'should merge column width and column count', + processCSS, + 'h1{column-width:6em;column-count:3}', + 'h1{columns:6em 3}' ); test( - 'should merge column width and column count (uppercase)', - processCSS, - 'h1{COLUMN-WIDTH:6em;COLUMN-COUNT:3}', - 'h1{columns:6em 3}' + 'should merge column width and column count (uppercase)', + processCSS, + 'h1{COLUMN-WIDTH:6em;COLUMN-COUNT:3}', + 'h1{columns:6em 3}' ); test( - 'should pass through column width', - passthroughCSS, - 'h1{column-width:6em}', + 'should pass through column width', + passthroughCSS, + 'h1{column-width:6em}' ); test( - 'should pass through column width (uppercase)', - passthroughCSS, - 'h1{COLUMN-WIDTH:6em}', + 'should pass through column width (uppercase)', + passthroughCSS, + 'h1{COLUMN-WIDTH:6em}' ); -test( - 'should pass through column count', - passthroughCSS, - 'h1{column-count:3}' -); +test('should pass through column count', passthroughCSS, 'h1{column-count:3}'); test( - 'should pass through column count (uppercase)', - passthroughCSS, - 'h1{COLUMN-COUNT:3}' + 'should pass through column count (uppercase)', + passthroughCSS, + 'h1{COLUMN-COUNT:3}' ); test( - 'should reduce inherit', - processCSS, - 'h1{column-width:inherit;column-count:inherit}', - 'h1{columns:inherit}' + 'should reduce inherit', + processCSS, + 'h1{column-width:inherit;column-count:inherit}', + 'h1{columns:inherit}' ); test( - 'should reduce inherit (uppercase)', - processCSS, - 'h1{COLUMN-WIDTH:inherit;COLUMN-COUNT:inherit}', - 'h1{columns:inherit}' + 'should reduce inherit (uppercase)', + processCSS, + 'h1{COLUMN-WIDTH:inherit;COLUMN-COUNT:inherit}', + 'h1{columns:inherit}' ); test( - 'should reduce inherit 1 (uppercase)', - processCSS, - 'h1{COLUMN-WIDTH:INHERIT;COLUMN-COUNT:INHERIT}', - 'h1{columns:inherit}' + 'should reduce inherit 1 (uppercase)', + processCSS, + 'h1{COLUMN-WIDTH:INHERIT;COLUMN-COUNT:INHERIT}', + 'h1{columns:inherit}' ); -test( - 'should pass through auto', - passthroughCSS, - 'h1{columns:auto}' -); +test('should pass through auto', passthroughCSS, 'h1{columns:auto}'); test( - 'should pass through auto (uppercase)', - processCSS, - 'h1{COLUMNS:auto}', - 'h1{columns:auto}' + 'should pass through auto (uppercase)', + processCSS, + 'h1{COLUMNS:auto}', + 'h1{columns:auto}' ); test( - 'should pass through auto 1 (uppercase)', - processCSS, - 'h1{columns:AUTO}', - 'h1{columns:auto}' + 'should pass through auto 1 (uppercase)', + processCSS, + 'h1{columns:AUTO}', + 'h1{columns:auto}' ); test( - 'should not merge declarations with hacks', - passthroughCSS, - 'h1{column-width:12em;_column-count:auto}' + 'should not merge declarations with hacks', + passthroughCSS, + 'h1{column-width:12em;_column-count:auto}' ); test( - 'should not merge declarations with hacks (uppercase)', - passthroughCSS, - 'h1{COLUMN-WIDTH:12em;_COLUMN-COUNT:auto}' + 'should not merge declarations with hacks (uppercase)', + passthroughCSS, + 'h1{COLUMN-WIDTH:12em;_COLUMN-COUNT:auto}' ); test( - 'should preserve nesting level', - processCSS, - 'section{h1{column-width:12em;column-count:auto}}', - 'section{h1{columns:12em}}' + 'should preserve nesting level', + processCSS, + 'section{h1{column-width:12em;column-count:auto}}', + 'section{h1{columns:12em}}' ); test( - 'should preserve nesting level (uppercase)', - processCSS, - 'section{h1{COLUMN-WIDTH:12em;COLUMN-COUNT:auto}}', - 'section{h1{columns:12em}}' + 'should preserve nesting level (uppercase)', + processCSS, + 'section{h1{COLUMN-WIDTH:12em;COLUMN-COUNT:auto}}', + 'section{h1{columns:12em}}' ); test( - 'should save fallbacks for column-width if after goes custom css props', - processCSS, - 'h1{column-width:12em;column-width:var(--variable)}', - 'h1{column-width:12em;column-width:var(--variable)}' + 'should save fallbacks for column-width if after goes custom css props', + processCSS, + 'h1{column-width:12em;column-width:var(--variable)}', + 'h1{column-width:12em;column-width:var(--variable)}' ); test( - 'should save fallbacks for column-width if after goes custom css props (uppercase)', - processCSS, - 'h1{COLUMN-WIDTH:12em;COLUMN-WIDTH:var(--variable)}', - 'h1{COLUMN-WIDTH:12em;COLUMN-WIDTH:var(--variable)}' + 'should save fallbacks for column-width if after goes custom css props (uppercase)', + processCSS, + 'h1{COLUMN-WIDTH:12em;COLUMN-WIDTH:var(--variable)}', + 'h1{COLUMN-WIDTH:12em;COLUMN-WIDTH:var(--variable)}' ); test( - 'should save fallbacks for column-width if after goes custom css props 1 (uppercase)', - processCSS, - 'h1{column-width:12em;column-width:VAR(--variable)}', - 'h1{column-width:12em;column-width:VAR(--variable)}' + 'should save fallbacks for column-width if after goes custom css props 1 (uppercase)', + processCSS, + 'h1{column-width:12em;column-width:VAR(--variable)}', + 'h1{column-width:12em;column-width:VAR(--variable)}' ); test( - 'should not explode columns with custom properties', - passthroughCSS, - 'h1{columns:var(--variable)}', + 'should not explode columns with custom properties', + passthroughCSS, + 'h1{columns:var(--variable)}' ); test( - 'should preserve case of custom properties', - passthroughCSS, - 'h1{columns:var(--fooBar)}', + 'should preserve case of custom properties', + passthroughCSS, + 'h1{columns:var(--fooBar)}' ); test( - 'should preserve case of custom properties (uppercase)', - passthroughCSS, - 'h1{COLUMN:var(--fooBar)}', + 'should preserve case of custom properties (uppercase)', + passthroughCSS, + 'h1{COLUMN:var(--fooBar)}' ); test( - 'should preserve case of custom properties 1 (uppercase)', - passthroughCSS, - 'h1{column:VAR(--fooBar)}', + 'should preserve case of custom properties 1 (uppercase)', + passthroughCSS, + 'h1{column:VAR(--fooBar)}' ); test( - 'should merge column values duplicate columns', - processCSS, - 'h1{column-width:12em;column-count:auto;columns:12em}', - 'h1{columns:12em}' + 'should merge column values duplicate columns', + processCSS, + 'h1{column-width:12em;column-count:auto;columns:12em}', + 'h1{columns:12em}' ); test( - 'should merge column values duplicate columns (uppercase)', - processCSS, - 'h1{COLUMN-WIDTH:12em;COLUMN-COUNT:auto;COLUMNS:12em}', - 'h1{columns:12em}' + 'should merge column values duplicate columns (uppercase)', + processCSS, + 'h1{COLUMN-WIDTH:12em;COLUMN-COUNT:auto;COLUMNS:12em}', + 'h1{columns:12em}' ); diff --git a/packages/postcss-merge-longhand/src/index.js b/packages/postcss-merge-longhand/src/index.js index 9a7ace1c7..e990cc2b0 100644 --- a/packages/postcss-merge-longhand/src/index.js +++ b/packages/postcss-merge-longhand/src/index.js @@ -2,12 +2,12 @@ import postcss from 'postcss'; import processors from './lib/decl'; export default postcss.plugin('postcss-merge-longhand', () => { - return css => { - css.walkRules(rule => { - processors.forEach(p => { - p.explode(rule); - p.merge(rule); - }); - }); - }; + return (css) => { + css.walkRules((rule) => { + processors.forEach((p) => { + p.explode(rule); + p.merge(rule); + }); + }); + }; }); diff --git a/packages/postcss-merge-longhand/src/lib/canExplode.js b/packages/postcss-merge-longhand/src/lib/canExplode.js index ef77faaf5..ebf724b69 100644 --- a/packages/postcss-merge-longhand/src/lib/canExplode.js +++ b/packages/postcss-merge-longhand/src/lib/canExplode.js @@ -1,13 +1,13 @@ -import isCustomProp from "./isCustomProp"; +import isCustomProp from './isCustomProp'; -const hasInherit = node => node.value.toLowerCase().includes("inherit"); -const hasInitial = node => node.value.toLowerCase().includes("initial"); -const hasUnset = node => node.value.toLowerCase().includes("unset"); +const hasInherit = (node) => node.value.toLowerCase().includes('inherit'); +const hasInitial = (node) => node.value.toLowerCase().includes('initial'); +const hasUnset = (node) => node.value.toLowerCase().includes('unset'); export default (prop, includeCustomProps = true) => { - if (includeCustomProps && isCustomProp(prop)) { - return false; - } + if (includeCustomProps && isCustomProp(prop)) { + return false; + } - return !hasInherit(prop) && !hasInitial(prop) && !hasUnset(prop); + return !hasInherit(prop) && !hasInitial(prop) && !hasUnset(prop); }; diff --git a/packages/postcss-merge-longhand/src/lib/canMerge.js b/packages/postcss-merge-longhand/src/lib/canMerge.js index bfd1e07ac..c691a9559 100644 --- a/packages/postcss-merge-longhand/src/lib/canMerge.js +++ b/packages/postcss-merge-longhand/src/lib/canMerge.js @@ -1,28 +1,32 @@ import isCustomProp from './isCustomProp'; -const important = node => node.important; -const unimportant = node => !node.important; +const important = (node) => node.important; +const unimportant = (node) => !node.important; -const hasInherit = node => node.value.toLowerCase() === 'inherit'; -const hasInitial = node => node.value.toLowerCase() === 'initial'; -const hasUnset = node => node.value.toLowerCase() === 'unset'; +const hasInherit = (node) => node.value.toLowerCase() === 'inherit'; +const hasInitial = (node) => node.value.toLowerCase() === 'initial'; +const hasUnset = (node) => node.value.toLowerCase() === 'unset'; export default (props, includeCustomProps = true) => { - if (props.some(hasInherit) && !props.every(hasInherit)) { - return false; - } + if (props.some(hasInherit) && !props.every(hasInherit)) { + return false; + } - if (props.some(hasInitial) && !props.every(hasInitial)) { - return false; - } + if (props.some(hasInitial) && !props.every(hasInitial)) { + return false; + } - if (props.some(hasUnset) && !props.every(hasUnset)) { - return false; - } + if (props.some(hasUnset) && !props.every(hasUnset)) { + return false; + } - if (includeCustomProps && props.some(isCustomProp) && !props.every(isCustomProp)) { - return false; - } + if ( + includeCustomProps && + props.some(isCustomProp) && + !props.every(isCustomProp) + ) { + return false; + } - return props.every(unimportant) || props.every(important); + return props.every(unimportant) || props.every(important); }; diff --git a/packages/postcss-merge-longhand/src/lib/decl/borders.js b/packages/postcss-merge-longhand/src/lib/decl/borders.js index 142819db9..51a53ea67 100644 --- a/packages/postcss-merge-longhand/src/lib/decl/borders.js +++ b/packages/postcss-merge-longhand/src/lib/decl/borders.js @@ -1,5 +1,5 @@ -import {list} from 'postcss'; -import {detect} from 'lerna:stylehacks'; +import { list } from 'postcss'; +import { detect } from 'lerna:stylehacks'; import insertCloned from '../insertCloned'; import parseTrbl from '../parseTrbl'; import hasAllProps from '../hasAllProps'; @@ -16,676 +16,719 @@ import isCustomProp from '../isCustomProp'; import canExplode from '../canExplode'; import getLastNode from '../getLastNode'; import parseWsc from '../parseWsc'; -import {isValidWsc} from '../validateWsc'; +import { isValidWsc } from '../validateWsc'; const wsc = ['width', 'style', 'color']; const defaults = ['medium', 'none', 'currentcolor']; -function borderProperty (...parts) { - return `border-${parts.join('-')}`; +function borderProperty(...parts) { + return `border-${parts.join('-')}`; } -function mapBorderProperty (value) { - return borderProperty(value); +function mapBorderProperty(value) { + return borderProperty(value); } const directions = trbl.map(mapBorderProperty); const properties = wsc.map(mapBorderProperty); const directionalProperties = directions.reduce( - (prev, curr) => prev.concat(wsc.map(prop => `${curr}-${prop}`)), []); + (prev, curr) => prev.concat(wsc.map((prop) => `${curr}-${prop}`)), + [] +); const precedence = [ - ['border'], - directions.concat(properties), - directionalProperties, + ['border'], + directions.concat(properties), + directionalProperties, ]; const allProperties = precedence.reduce((a, b) => a.concat(b)); -function getLevel (prop) { - for (let i = 0; i < precedence.length; i++) { - if (!!~precedence[i].indexOf(prop.toLowerCase())) { - return i; - } +function getLevel(prop) { + for (let i = 0; i < precedence.length; i++) { + if (~precedence[i].indexOf(prop.toLowerCase())) { + return i; } + } } -const isValueCustomProp = value => value && !!~value.search(/var\s*\(\s*--/i); +const isValueCustomProp = (value) => value && !!~value.search(/var\s*\(\s*--/i); -function canMergeValues (values) { - return !values.some(isValueCustomProp) || values.every(isValueCustomProp); +function canMergeValues(values) { + return !values.some(isValueCustomProp) || values.every(isValueCustomProp); } -function getColorValue (decl) { - if (decl.prop.substr(-5) === 'color') { - return decl.value; - } +function getColorValue(decl) { + if (decl.prop.substr(-5) === 'color') { + return decl.value; + } - return parseWsc(decl.value)[2] || defaults[2]; + return parseWsc(decl.value)[2] || defaults[2]; } -function diffingProps (values, nextValues) { - return wsc.reduce((prev, curr, i) => { - if (values[i] === nextValues[i]) { - return prev; - } +function diffingProps(values, nextValues) { + return wsc.reduce((prev, curr, i) => { + if (values[i] === nextValues[i]) { + return prev; + } - return [...prev, curr]; - }, []); + return [...prev, curr]; + }, []); } -function mergeRedundant ({values, nextValues, decl, nextDecl, index}) { - if (!canMerge([decl, nextDecl])) { - return; - } +function mergeRedundant({ values, nextValues, decl, nextDecl, index }) { + if (!canMerge([decl, nextDecl])) { + return; + } - if (detect(decl) || detect(nextDecl)) { - return; - } + if (detect(decl) || detect(nextDecl)) { + return; + } - const diff = diffingProps(values, nextValues); + const diff = diffingProps(values, nextValues); - if (diff.length > 1) { - return; - } + if (diff.length > 1) { + return; + } - const prop = diff.pop(); - const position = wsc.indexOf(prop); + const prop = diff.pop(); + const position = wsc.indexOf(prop); - const prop1 = `${nextDecl.prop}-${prop}`; - const prop2 = `border-${prop}`; + const prop1 = `${nextDecl.prop}-${prop}`; + const prop2 = `border-${prop}`; - let props = parseTrbl(values[position]); + let props = parseTrbl(values[position]); - props[index] = nextValues[position]; + props[index] = nextValues[position]; - const borderValue2 = values.filter((e, i) => i !== position).join(' '); - const propValue2 = minifyTrbl(props); + const borderValue2 = values.filter((e, i) => i !== position).join(' '); + const propValue2 = minifyTrbl(props); - const origLength = (minifyWsc(decl.value) + nextDecl.prop + nextDecl.value).length; - const newLength1 = decl.value.length + prop1.length + minifyWsc(nextValues[position]).length; - const newLength2 = borderValue2.length + prop2.length + propValue2.length; + const origLength = (minifyWsc(decl.value) + nextDecl.prop + nextDecl.value) + .length; + const newLength1 = + decl.value.length + prop1.length + minifyWsc(nextValues[position]).length; + const newLength2 = borderValue2.length + prop2.length + propValue2.length; - if (newLength1 < newLength2 && newLength1 < origLength) { - nextDecl.prop = prop1; - nextDecl.value = nextValues[position]; - } + if (newLength1 < newLength2 && newLength1 < origLength) { + nextDecl.prop = prop1; + nextDecl.value = nextValues[position]; + } - if (newLength2 < newLength1 && newLength2 < origLength) { - decl.value = borderValue2; - nextDecl.prop = prop2; - nextDecl.value = propValue2; - } + if (newLength2 < newLength1 && newLength2 < origLength) { + decl.value = borderValue2; + nextDecl.prop = prop2; + nextDecl.value = propValue2; + } } -function isCloseEnough (mapped) { - return (mapped[0] === mapped[1] && mapped[1] === mapped[2]) || - (mapped[1] === mapped[2] && mapped[2] === mapped[3]) || - (mapped[2] === mapped[3] && mapped[3] === mapped[0]) || - (mapped[3] === mapped[0] && mapped[0] === mapped[1]); +function isCloseEnough(mapped) { + return ( + (mapped[0] === mapped[1] && mapped[1] === mapped[2]) || + (mapped[1] === mapped[2] && mapped[2] === mapped[3]) || + (mapped[2] === mapped[3] && mapped[3] === mapped[0]) || + (mapped[3] === mapped[0] && mapped[0] === mapped[1]) + ); } -function getDistinctShorthands (mapped) { - return mapped.reduce((a, b) => { - a = Array.isArray(a) ? a : [a]; +function getDistinctShorthands(mapped) { + return mapped.reduce((a, b) => { + a = Array.isArray(a) ? a : [a]; - if (!~a.indexOf(b)) { - a.push(b); - } + if (!~a.indexOf(b)) { + a.push(b); + } - return a; - }); + return a; + }); } -function explode (rule) { - rule.walkDecls(/^border/i, decl => { - if (!canExplode(decl, false)) { - return; - } - - if (detect(decl)) { - return; - } +function explode(rule) { + rule.walkDecls(/^border/i, (decl) => { + if (!canExplode(decl, false)) { + return; + } - const prop = decl.prop.toLowerCase(); + if (detect(decl)) { + return; + } - // border -> border-trbl - if (prop === 'border') { - if (isValidWsc(parseWsc(decl.value))) { - directions.forEach((direction) => { - insertCloned(decl.parent, decl, {prop: direction}); - }); + const prop = decl.prop.toLowerCase(); - return decl.remove(); - } - } + // border -> border-trbl + if (prop === 'border') { + if (isValidWsc(parseWsc(decl.value))) { + directions.forEach((direction) => { + insertCloned(decl.parent, decl, { prop: direction }); + }); - // border-trbl -> border-trbl-wsc - if (directions.some(direction => prop === direction)) { - let values = parseWsc(decl.value); + return decl.remove(); + } + } - if (isValidWsc(values)) { - wsc.forEach((d, i) => { - insertCloned(decl.parent, decl, { - prop: `${prop}-${d}`, - value: values[i] || defaults[i], - }); - }); + // border-trbl -> border-trbl-wsc + if (directions.some((direction) => prop === direction)) { + let values = parseWsc(decl.value); - return decl.remove(); - } - } + if (isValidWsc(values)) { + wsc.forEach((d, i) => { + insertCloned(decl.parent, decl, { + prop: `${prop}-${d}`, + value: values[i] || defaults[i], + }); + }); - // border-wsc -> border-trbl-wsc - wsc.some(style => { - if (prop !== borderProperty(style)) { - return false; - } + return decl.remove(); + } + } - parseTrbl(decl.value).forEach((value, i) => { - insertCloned(decl.parent, decl, { - prop: borderProperty(trbl[i], style), - value, - }); - }); + // border-wsc -> border-trbl-wsc + wsc.some((style) => { + if (prop !== borderProperty(style)) { + return false; + } - return decl.remove(); + parseTrbl(decl.value).forEach((value, i) => { + insertCloned(decl.parent, decl, { + prop: borderProperty(trbl[i], style), + value, }); + }); + + return decl.remove(); }); + }); } -function merge (rule) { - // border-trbl-wsc -> border-trbl - trbl.forEach(direction => { - const prop = borderProperty(direction); - - mergeRules( - rule, - wsc.map(style => borderProperty(direction, style)), - (rules, lastNode) => { - if (canMerge(rules, false) && !rules.some(detect)) { - insertCloned(lastNode.parent, lastNode, { - prop, - value: rules.map(getValue).join(' '), - }); - - rules.forEach(remove); - - return true; - } - } - ); - }); +function merge(rule) { + // border-trbl-wsc -> border-trbl + trbl.forEach((direction) => { + const prop = borderProperty(direction); - // border-trbl-wsc -> border-wsc - wsc.forEach(style => { - const prop = borderProperty(style); - - mergeRules( - rule, - trbl.map(direction => borderProperty(direction, style)), - (rules, lastNode) => { - if (canMerge(rules) && !rules.some(detect)) { - insertCloned(lastNode.parent, lastNode, { - prop, - value: minifyTrbl(rules.map(getValue).join(' ')), - }); - - rules.forEach(remove); - - return true; - } - } - ); - }); + mergeRules( + rule, + wsc.map((style) => borderProperty(direction, style)), + (rules, lastNode) => { + if (canMerge(rules, false) && !rules.some(detect)) { + insertCloned(lastNode.parent, lastNode, { + prop, + value: rules.map(getValue).join(' '), + }); - // border-trbl -> border-wsc - mergeRules(rule, directions, (rules, lastNode) => { - if (rules.some(detect)) { - return; + rules.forEach(remove); + + return true; + } + } + ); + }); + + // border-trbl-wsc -> border-wsc + wsc.forEach((style) => { + const prop = borderProperty(style); + + mergeRules( + rule, + trbl.map((direction) => borderProperty(direction, style)), + (rules, lastNode) => { + if (canMerge(rules) && !rules.some(detect)) { + insertCloned(lastNode.parent, lastNode, { + prop, + value: minifyTrbl(rules.map(getValue).join(' ')), + }); + + rules.forEach(remove); + + return true; } + } + ); + }); + + // border-trbl -> border-wsc + mergeRules(rule, directions, (rules, lastNode) => { + if (rules.some(detect)) { + return; + } - const values = rules.map(({value}) => value); + const values = rules.map(({ value }) => value); - if (!canMergeValues(values)) { - return; - } + if (!canMergeValues(values)) { + return; + } - const parsed = values.map(value => parseWsc(value)); + const parsed = values.map((value) => parseWsc(value)); - if (!parsed.every(isValidWsc)) { - return; - } + if (!parsed.every(isValidWsc)) { + return; + } - wsc.forEach((d, i) => { - const value = parsed.map(v => v[i] || defaults[i]); - - if (canMergeValues(value)) { - insertCloned(lastNode.parent, lastNode, { - prop: borderProperty(d), - value: minifyTrbl(value), - }); - } else { - insertCloned(lastNode.parent, lastNode); - } + wsc.forEach((d, i) => { + const value = parsed.map((v) => v[i] || defaults[i]); + + if (canMergeValues(value)) { + insertCloned(lastNode.parent, lastNode, { + prop: borderProperty(d), + value: minifyTrbl(value), }); + } else { + insertCloned(lastNode.parent, lastNode); + } + }); - rules.forEach(remove); + rules.forEach(remove); - return true; - }); + return true; + }); - // border-wsc -> border - // border-wsc -> border + border-color - // border-wsc -> border + border-dir - mergeRules(rule, properties, (rules, lastNode) => { - if (rules.some(detect)) { - return; - } + // border-wsc -> border + // border-wsc -> border + border-color + // border-wsc -> border + border-dir + mergeRules(rule, properties, (rules, lastNode) => { + if (rules.some(detect)) { + return; + } - const values = rules.map(node => parseTrbl(node.value)); - const mapped = [0, 1, 2, 3].map(i => [values[0][i], values[1][i], values[2][i]].join(' ')); + const values = rules.map((node) => parseTrbl(node.value)); + const mapped = [0, 1, 2, 3].map((i) => + [values[0][i], values[1][i], values[2][i]].join(' ') + ); - if (!canMergeValues(mapped)) { - return; - } + if (!canMergeValues(mapped)) { + return; + } - const [width, style, color] = rules; - const reduced = getDistinctShorthands(mapped); - - if (isCloseEnough(mapped) && canMerge(rules, false)) { - const first = mapped.indexOf(reduced[0]) !== mapped.lastIndexOf(reduced[0]); - - const border = insertCloned(lastNode.parent, lastNode, { - prop: 'border', - value: first ? reduced[0] : reduced[1], - }); - - if (reduced[1]) { - const value = first ? reduced[1] : reduced[0]; - const prop = borderProperty(trbl[mapped.indexOf(value)]); - - rule.insertAfter(border, Object.assign(lastNode.clone(), { - prop, - value, - })); - } - rules.forEach(remove); - - return true; - } else if (reduced.length === 1) { - rule.insertBefore(color, Object.assign(lastNode.clone(), { - prop: 'border', - value: [width, style].map(getValue).join(' '), - })); - rules.filter(node => node.prop.toLowerCase() !== properties[2]).forEach(remove); - - return true; - } - }); + const [width, style, color] = rules; + const reduced = getDistinctShorthands(mapped); - // border-wsc -> border + border-trbl - mergeRules(rule, properties, (rules, lastNode) => { - if (rules.some(detect)) { - return; - } + if (isCloseEnough(mapped) && canMerge(rules, false)) { + const first = + mapped.indexOf(reduced[0]) !== mapped.lastIndexOf(reduced[0]); + + const border = insertCloned(lastNode.parent, lastNode, { + prop: 'border', + value: first ? reduced[0] : reduced[1], + }); + + if (reduced[1]) { + const value = first ? reduced[1] : reduced[0]; + const prop = borderProperty(trbl[mapped.indexOf(value)]); + + rule.insertAfter( + border, + Object.assign(lastNode.clone(), { + prop, + value, + }) + ); + } + rules.forEach(remove); + + return true; + } else if (reduced.length === 1) { + rule.insertBefore( + color, + Object.assign(lastNode.clone(), { + prop: 'border', + value: [width, style].map(getValue).join(' '), + }) + ); + rules + .filter((node) => node.prop.toLowerCase() !== properties[2]) + .forEach(remove); + + return true; + } + }); + + // border-wsc -> border + border-trbl + mergeRules(rule, properties, (rules, lastNode) => { + if (rules.some(detect)) { + return; + } - const values = rules.map(node => parseTrbl(node.value)); - const mapped = [0, 1, 2, 3].map(i => [values[0][i], values[1][i], values[2][i]].join(' ')); - const reduced = getDistinctShorthands(mapped); - const none = 'medium none currentcolor'; - - if (reduced.length > 1 && reduced.length < 4 && reduced.includes(none)) { - const filtered = mapped.filter(p => p !== none); - const mostCommon = reduced.sort((a, b) => - mapped.filter(v => v === b).length - mapped.filter(v => v === a).length)[0]; - const borderValue = reduced.length === 2 ? filtered[0] : mostCommon; - - rule.insertBefore(lastNode, Object.assign(lastNode.clone(), { - prop: 'border', - value: borderValue, - })); - - directions.forEach((dir, i) => { - if (mapped[i] !== borderValue) { - rule.insertBefore(lastNode, Object.assign(lastNode.clone(), { - prop: dir, - value: mapped[i], - })); - } - }); - - rules.forEach(remove); - - return true; + const values = rules.map((node) => parseTrbl(node.value)); + const mapped = [0, 1, 2, 3].map((i) => + [values[0][i], values[1][i], values[2][i]].join(' ') + ); + const reduced = getDistinctShorthands(mapped); + const none = 'medium none currentcolor'; + + if (reduced.length > 1 && reduced.length < 4 && reduced.includes(none)) { + const filtered = mapped.filter((p) => p !== none); + const mostCommon = reduced.sort( + (a, b) => + mapped.filter((v) => v === b).length - + mapped.filter((v) => v === a).length + )[0]; + const borderValue = reduced.length === 2 ? filtered[0] : mostCommon; + + rule.insertBefore( + lastNode, + Object.assign(lastNode.clone(), { + prop: 'border', + value: borderValue, + }) + ); + + directions.forEach((dir, i) => { + if (mapped[i] !== borderValue) { + rule.insertBefore( + lastNode, + Object.assign(lastNode.clone(), { + prop: dir, + value: mapped[i], + }) + ); } + }); + + rules.forEach(remove); + + return true; + } + }); + + // border-trbl -> border + // border-trbl -> border + border-trbl + mergeRules(rule, directions, (rules, lastNode) => { + if (rules.some(detect)) { + return; + } + + const values = rules.map((node) => { + const wscValue = parseWsc(node.value); + + if (!isValidWsc(wscValue)) { + return node.value; + } + + return wscValue.map((value, i) => value || defaults[i]).join(' '); }); - // border-trbl -> border - // border-trbl -> border + border-trbl - mergeRules(rule, directions, (rules, lastNode) => { - if (rules.some(detect)) { - return; + const reduced = getDistinctShorthands(values); + + if (isCloseEnough(values)) { + const first = + values.indexOf(reduced[0]) !== values.lastIndexOf(reduced[0]); + + rule.insertBefore( + lastNode, + Object.assign(lastNode.clone(), { + prop: 'border', + value: minifyWsc(first ? values[0] : values[1]), + }) + ); + + if (reduced[1]) { + const value = first ? reduced[1] : reduced[0]; + const prop = directions[values.indexOf(value)]; + rule.insertBefore( + lastNode, + Object.assign(lastNode.clone(), { + prop: prop, + value: minifyWsc(value), + }) + ); + } + + rules.forEach(remove); + + return true; + } + }); + + // border-trbl-wsc + border-trbl (custom prop) -> border-trbl + border-trbl-wsc (custom prop) + directions.forEach((direction) => { + wsc.forEach((style, i) => { + const prop = `${direction}-${style}`; + + mergeRules(rule, [direction, prop], (rules, lastNode) => { + if (lastNode.prop !== direction) { + return; } - const values = rules.map(node => { - const wscValue = parseWsc(node.value); + const values = parseWsc(lastNode.value); - if (!isValidWsc(wscValue)) { - return node.value; - } + if (!isValidWsc(values)) { + return; + } - return wscValue.map((value, i) => value || defaults[i]).join(' '); - }); + const wscProp = rules.filter((r) => r !== lastNode)[0]; - const reduced = getDistinctShorthands(values); + if (!isValueCustomProp(values[i]) || isCustomProp(wscProp)) { + return; + } - if (isCloseEnough(values)) { - const first = values.indexOf(reduced[0]) !== values.lastIndexOf(reduced[0]); + const wscValue = values[i]; - rule.insertBefore(lastNode, Object.assign(lastNode.clone(), { - prop: 'border', - value: minifyWsc(first ? values[0] : values[1]), - })); + values[i] = wscProp.value; - if (reduced[1]) { - const value = first ? reduced[1] : reduced[0]; - const prop = directions[values.indexOf(value)]; - rule.insertBefore(lastNode, Object.assign(lastNode.clone(), { - prop: prop, - value: minifyWsc(value), - })); - } + if (canMerge(rules, false) && !rules.some(detect)) { + insertCloned(lastNode.parent, lastNode, { + prop, + value: wscValue, + }); + lastNode.value = minifyWsc(values); - rules.forEach(remove); + wscProp.remove(); - return true; + return true; } + }); }); - - // border-trbl-wsc + border-trbl (custom prop) -> border-trbl + border-trbl-wsc (custom prop) - directions.forEach(direction => { - wsc.forEach((style, i) => { - const prop = `${direction}-${style}`; - - mergeRules( - rule, - [direction, prop], - (rules, lastNode) => { - if (lastNode.prop !== direction) { - return; - } - - const values = parseWsc(lastNode.value); - - if (!isValidWsc(values)) { - return; - } - - const wscProp = rules.filter(r => r !== lastNode)[0]; - - if (!isValueCustomProp(values[i]) || isCustomProp(wscProp)) { - return; - } - - const wscValue = values[i]; - - values[i] = wscProp.value; - - if (canMerge(rules, false) && !rules.some(detect)) { - insertCloned(lastNode.parent, lastNode, { - prop, - value: wscValue, - }); - lastNode.value = minifyWsc(values); - - wscProp.remove(); - - return true; - } - } - ); - }); - }); + }); - // border-wsc + border (custom prop) -> border + border-wsc (custom prop) - wsc.forEach((style, i) => { - const prop = borderProperty(style); - mergeRules( - rule, - ['border', prop], - (rules, lastNode) => { - if (lastNode.prop !== 'border') { - return; - } - - const values = parseWsc(lastNode.value); - - if (!isValidWsc(values)) { - return; - } - - const wscProp = rules.filter(r => r !== lastNode)[0]; - - if (!isValueCustomProp(values[i]) || isCustomProp(wscProp)) { - return; - } - - const wscValue = values[i]; - - values[i] = wscProp.value; - - if (canMerge(rules, false) && !rules.some(detect)) { - insertCloned(lastNode.parent, lastNode, { - prop, - value: wscValue, - }); - lastNode.value = minifyWsc(values); - wscProp.remove(); - - return true; - } - } - ); - }); - - // optimize border-trbl - let decls = getDecls(rule, directions); + // border-wsc + border (custom prop) -> border + border-wsc (custom prop) + wsc.forEach((style, i) => { + const prop = borderProperty(style); + mergeRules(rule, ['border', prop], (rules, lastNode) => { + if (lastNode.prop !== 'border') { + return; + } - while (decls.length) { - const lastNode = decls[decls.length - 1]; + const values = parseWsc(lastNode.value); - wsc.forEach((d, i) => { - const names = directions.filter(name => name !== lastNode.prop).map(name => `${name}-${d}`); + if (!isValidWsc(values)) { + return; + } - let nodes = rule.nodes.slice(0, rule.nodes.indexOf(lastNode)); + const wscProp = rules.filter((r) => r !== lastNode)[0]; - const border = getLastNode(nodes, 'border'); + if (!isValueCustomProp(values[i]) || isCustomProp(wscProp)) { + return; + } - if (border) { - nodes = nodes.slice(nodes.indexOf(border)); - } + const wscValue = values[i]; - const props = nodes.filter(node => node.prop && ~names.indexOf(node.prop) && node.important === lastNode.important); - const rules = getRules(props, names); + values[i] = wscProp.value; - if (hasAllProps(rules, ...names) && !rules.some(detect)) { - const values = rules.map(node => node ? node.value : null); - const filteredValues = values.filter(Boolean); - const lastNodeValue = list.space(lastNode.value)[i]; + if (canMerge(rules, false) && !rules.some(detect)) { + insertCloned(lastNode.parent, lastNode, { + prop, + value: wscValue, + }); + lastNode.value = minifyWsc(values); + wscProp.remove(); - values[directions.indexOf(lastNode.prop)] = lastNodeValue; + return true; + } + }); + }); - let value = minifyTrbl(values.join(' ')); + // optimize border-trbl + let decls = getDecls(rule, directions); - if ( - filteredValues[0] === filteredValues[1] && - filteredValues[1] === filteredValues[2] - ) { - value = filteredValues[0]; - } + while (decls.length) { + const lastNode = decls[decls.length - 1]; - let refNode = props[props.length - 1]; + wsc.forEach((d, i) => { + const names = directions + .filter((name) => name !== lastNode.prop) + .map((name) => `${name}-${d}`); - if (value === lastNodeValue) { - refNode = lastNode; - let valueArray = list.space(lastNode.value); - valueArray.splice(i, 1); - lastNode.value = valueArray.join(' '); - } + let nodes = rule.nodes.slice(0, rule.nodes.indexOf(lastNode)); - insertCloned(refNode.parent, refNode, { - prop: borderProperty(d), - value, - }); + const border = getLastNode(nodes, 'border'); - decls = decls.filter(node => !~rules.indexOf(node)); - rules.forEach(remove); - } - }); + if (border) { + nodes = nodes.slice(nodes.indexOf(border)); + } - decls = decls.filter(node => node !== lastNode); - } + const props = nodes.filter( + (node) => + node.prop && + ~names.indexOf(node.prop) && + node.important === lastNode.important + ); + const rules = getRules(props, names); - rule.walkDecls('border', decl => { - const nextDecl = decl.next(); + if (hasAllProps(rules, ...names) && !rules.some(detect)) { + const values = rules.map((node) => (node ? node.value : null)); + const filteredValues = values.filter(Boolean); + const lastNodeValue = list.space(lastNode.value)[i]; - if (!nextDecl || nextDecl.type !== 'decl') { - return; - } + values[directions.indexOf(lastNode.prop)] = lastNodeValue; - const index = directions.indexOf(nextDecl.prop); + let value = minifyTrbl(values.join(' ')); - if (!~index) { - return; + if ( + filteredValues[0] === filteredValues[1] && + filteredValues[1] === filteredValues[2] + ) { + value = filteredValues[0]; } - const values = parseWsc(decl.value); - const nextValues = parseWsc(nextDecl.value); - - if (!isValidWsc(values) || !isValidWsc(nextValues)) { - return; + let refNode = props[props.length - 1]; + + if (value === lastNodeValue) { + refNode = lastNode; + let valueArray = list.space(lastNode.value); + valueArray.splice(i, 1); + lastNode.value = valueArray.join(' '); } - - const config = { - values, - nextValues, - decl, - nextDecl, - index, - }; - - return mergeRedundant(config); + + insertCloned(refNode.parent, refNode, { + prop: borderProperty(d), + value, + }); + + decls = decls.filter((node) => !~rules.indexOf(node)); + rules.forEach(remove); + } }); - rule.walkDecls(/^border($|-(top|right|bottom|left)$)/i, decl => { - let values = parseWsc(decl.value); + decls = decls.filter((node) => node !== lastNode); + } - if (!isValidWsc(values)) { - return; - } + rule.walkDecls('border', (decl) => { + const nextDecl = decl.next(); - const position = directions.indexOf(decl.prop); - let dirs = [...directions]; + if (!nextDecl || nextDecl.type !== 'decl') { + return; + } - dirs.splice(position, 1); - wsc.forEach((d, i) => { - const props = dirs.map(dir => `${dir}-${d}`); + const index = directions.indexOf(nextDecl.prop); - mergeRules(rule, [decl.prop, ...props], (rules) => { - if (!rules.includes(decl)) { - return; - } + if (!~index) { + return; + } - const longhands = rules.filter(p => p !== decl); + const values = parseWsc(decl.value); + const nextValues = parseWsc(nextDecl.value); - if (longhands[0].value.toLowerCase() === longhands[1].value.toLowerCase() && - longhands[1].value.toLowerCase() === longhands[2].value.toLowerCase() && - longhands[0].value.toLowerCase() === values[i].toLowerCase() - ) { - longhands.forEach(remove); + if (!isValidWsc(values) || !isValidWsc(nextValues)) { + return; + } - insertCloned(decl.parent, decl, { - prop: borderProperty(d), - value: values[i], - }); + const config = { + values, + nextValues, + decl, + nextDecl, + index, + }; - values[i] = null; - } - }); + return mergeRedundant(config); + }); - const newValue = values.join(' '); + rule.walkDecls(/^border($|-(top|right|bottom|left)$)/i, (decl) => { + let values = parseWsc(decl.value); - if (newValue) { - decl.value = newValue; - } else { - decl.remove(); - } - }); - }); - - // clean-up values - rule.walkDecls(/^border($|-(top|right|bottom|left)$)/i, decl => { - decl.value = minifyWsc(decl.value); - }); + if (!isValidWsc(values)) { + return; + } - // border-spacing-hv -> border-spacing - rule.walkDecls(/^border-spacing$/i, decl => { - const value = list.space(decl.value); + const position = directions.indexOf(decl.prop); + let dirs = [...directions]; - // merge vertical and horizontal dups - if (value.length > 1 && value[0] === value[1]) { - decl.value = value.slice(1).join(' '); + dirs.splice(position, 1); + wsc.forEach((d, i) => { + const props = dirs.map((dir) => `${dir}-${d}`); + + mergeRules(rule, [decl.prop, ...props], (rules) => { + if (!rules.includes(decl)) { + return; } - }); - // clean-up rules - decls = getDecls(rule, allProperties); - - while (decls.length) { - const lastNode = decls[decls.length - 1]; - const lastPart = lastNode.prop.split('-').pop(); - - // remove properties of lower precedence - const lesser = decls.filter(node => - !detect(lastNode) && - !detect(node) && - !isCustomProp(lastNode) && - node !== lastNode && - node.important === lastNode.important && - getLevel(node.prop) > getLevel(lastNode.prop) && - (!!~node.prop.toLowerCase().indexOf(lastNode.prop) || node.prop.toLowerCase().endsWith(lastPart))); - - lesser.forEach(remove); - decls = decls.filter(node => !~lesser.indexOf(node)); - - // get duplicate properties - let duplicates = decls.filter(node => - !detect(lastNode) && - !detect(node) && - node !== lastNode && - node.important === lastNode.important && - node.prop === lastNode.prop && - !(!isCustomProp(node) && isCustomProp(lastNode)) - ); + const longhands = rules.filter((p) => p !== decl); - if (duplicates.length) { - if (/hsla\(|rgba\(/i.test(getColorValue(lastNode))) { - const preserve = duplicates - .filter(node => !/hsla\(|rgba\(/i.test(getColorValue(node))) - .pop(); + if ( + longhands[0].value.toLowerCase() === + longhands[1].value.toLowerCase() && + longhands[1].value.toLowerCase() === + longhands[2].value.toLowerCase() && + longhands[0].value.toLowerCase() === values[i].toLowerCase() + ) { + longhands.forEach(remove); - duplicates = duplicates.filter(node => node !== preserve); - } + insertCloned(decl.parent, decl, { + prop: borderProperty(d), + value: values[i], + }); - duplicates.forEach(remove); + values[i] = null; } + }); - decls = decls.filter(node => node !== lastNode && !~duplicates.indexOf(node)); + const newValue = values.join(' '); + + if (newValue) { + decl.value = newValue; + } else { + decl.remove(); + } + }); + }); + + // clean-up values + rule.walkDecls(/^border($|-(top|right|bottom|left)$)/i, (decl) => { + decl.value = minifyWsc(decl.value); + }); + + // border-spacing-hv -> border-spacing + rule.walkDecls(/^border-spacing$/i, (decl) => { + const value = list.space(decl.value); + + // merge vertical and horizontal dups + if (value.length > 1 && value[0] === value[1]) { + decl.value = value.slice(1).join(' '); + } + }); + + // clean-up rules + decls = getDecls(rule, allProperties); + + while (decls.length) { + const lastNode = decls[decls.length - 1]; + const lastPart = lastNode.prop.split('-').pop(); + + // remove properties of lower precedence + const lesser = decls.filter( + (node) => + !detect(lastNode) && + !detect(node) && + !isCustomProp(lastNode) && + node !== lastNode && + node.important === lastNode.important && + getLevel(node.prop) > getLevel(lastNode.prop) && + (!!~node.prop.toLowerCase().indexOf(lastNode.prop) || + node.prop.toLowerCase().endsWith(lastPart)) + ); + + lesser.forEach(remove); + decls = decls.filter((node) => !~lesser.indexOf(node)); + + // get duplicate properties + let duplicates = decls.filter( + (node) => + !detect(lastNode) && + !detect(node) && + node !== lastNode && + node.important === lastNode.important && + node.prop === lastNode.prop && + !(!isCustomProp(node) && isCustomProp(lastNode)) + ); + + if (duplicates.length) { + if (/hsla\(|rgba\(/i.test(getColorValue(lastNode))) { + const preserve = duplicates + .filter((node) => !/hsla\(|rgba\(/i.test(getColorValue(node))) + .pop(); + + duplicates = duplicates.filter((node) => node !== preserve); + } + + duplicates.forEach(remove); } + + decls = decls.filter( + (node) => node !== lastNode && !~duplicates.indexOf(node) + ); + } } export default { - explode, - merge, + explode, + merge, }; diff --git a/packages/postcss-merge-longhand/src/lib/decl/boxBase.js b/packages/postcss-merge-longhand/src/lib/decl/boxBase.js index 81ef44275..321f5f886 100644 --- a/packages/postcss-merge-longhand/src/lib/decl/boxBase.js +++ b/packages/postcss-merge-longhand/src/lib/decl/boxBase.js @@ -1,4 +1,4 @@ -import {detect} from 'lerna:stylehacks'; +import { detect } from 'lerna:stylehacks'; import canMerge from '../canMerge'; import getDecls from '../getDecls'; import minifyTrbl from '../minifyTrbl'; @@ -11,80 +11,86 @@ import trbl from '../trbl'; import isCustomProp from '../isCustomProp'; import canExplode from '../canExplode'; -export default prop => { - const properties = trbl.map(direction => `${prop}-${direction}`); +export default (prop) => { + const properties = trbl.map((direction) => `${prop}-${direction}`); - const cleanup = rule => { - let decls = getDecls(rule, [prop].concat(properties)); + const cleanup = (rule) => { + let decls = getDecls(rule, [prop].concat(properties)); - while (decls.length) { - const lastNode = decls[decls.length - 1]; + while (decls.length) { + const lastNode = decls[decls.length - 1]; - // remove properties of lower precedence - const lesser = decls.filter(node => - !detect(lastNode) && - !detect(node) && - node !== lastNode && - node.important === lastNode.important && - lastNode.prop === prop && node.prop !== lastNode.prop); + // remove properties of lower precedence + const lesser = decls.filter( + (node) => + !detect(lastNode) && + !detect(node) && + node !== lastNode && + node.important === lastNode.important && + lastNode.prop === prop && + node.prop !== lastNode.prop + ); - lesser.forEach(remove); - decls = decls.filter(node => !~lesser.indexOf(node)); + lesser.forEach(remove); + decls = decls.filter((node) => !~lesser.indexOf(node)); - // get duplicate properties - let duplicates = decls.filter(node => - !detect(lastNode) && - !detect(node) && - node !== lastNode && - node.important === lastNode.important && - node.prop === lastNode.prop && - !(!isCustomProp(node) && isCustomProp(lastNode)) - ); + // get duplicate properties + let duplicates = decls.filter( + (node) => + !detect(lastNode) && + !detect(node) && + node !== lastNode && + node.important === lastNode.important && + node.prop === lastNode.prop && + !(!isCustomProp(node) && isCustomProp(lastNode)) + ); - duplicates.forEach(remove); - decls = decls.filter(node => node !== lastNode && !~duplicates.indexOf(node)); - } - }; + duplicates.forEach(remove); + decls = decls.filter( + (node) => node !== lastNode && !~duplicates.indexOf(node) + ); + } + }; - const processor = { - explode: rule => { - rule.walkDecls(new RegExp("^" + prop + "$", "i"), decl => { - if (!canExplode(decl)) { - return; - } + const processor = { + explode: (rule) => { + rule.walkDecls(new RegExp('^' + prop + '$', 'i'), (decl) => { + if (!canExplode(decl)) { + return; + } - if (detect(decl)) { - return; - } + if (detect(decl)) { + return; + } - const values = parseTrbl(decl.value); + const values = parseTrbl(decl.value); - trbl.forEach((direction, index) => { - insertCloned(decl.parent, decl, { - prop: properties[index], - value: values[index], - }); - }); + trbl.forEach((direction, index) => { + insertCloned(decl.parent, decl, { + prop: properties[index], + value: values[index], + }); + }); - decl.remove(); - }); - }, - merge: rule => { - mergeRules(rule, properties, (rules, lastNode) => { - if (canMerge(rules) && !rules.some(detect)) { - insertCloned(lastNode.parent, lastNode, { - prop, - value: minifyTrbl(mergeValues(...rules)), - }); - rules.forEach(remove); + decl.remove(); + }); + }, + merge: (rule) => { + mergeRules(rule, properties, (rules, lastNode) => { + if (canMerge(rules) && !rules.some(detect)) { + insertCloned(lastNode.parent, lastNode, { + prop, + value: minifyTrbl(mergeValues(...rules)), + }); + rules.forEach(remove); - return true; - } - }); + return true; + } + }); - cleanup(rule); - }, - }; + cleanup(rule); + }, + }; - return processor; + return processor; }; diff --git a/packages/postcss-merge-longhand/src/lib/decl/columns.js b/packages/postcss-merge-longhand/src/lib/decl/columns.js index e9458ad36..0134e3121 100644 --- a/packages/postcss-merge-longhand/src/lib/decl/columns.js +++ b/packages/postcss-merge-longhand/src/lib/decl/columns.js @@ -1,6 +1,6 @@ -import {list} from 'postcss'; -import {unit} from 'postcss-value-parser'; -import {detect} from 'lerna:stylehacks'; +import { list } from 'postcss'; +import { unit } from 'postcss-value-parser'; +import { detect } from 'lerna:stylehacks'; import canMerge from '../canMerge'; import getDecls from '../getDecls'; import getValue from '../getValue'; @@ -23,107 +23,116 @@ const inherit = 'inherit'; * Specification link: https://www.w3.org/TR/css3-multicol/ */ -function normalize (values) { - if (values[0].toLowerCase() === auto) { - return values[1]; - } +function normalize(values) { + if (values[0].toLowerCase() === auto) { + return values[1]; + } - if (values[1].toLowerCase() === auto) { - return values[0]; - } + if (values[1].toLowerCase() === auto) { + return values[0]; + } - if (values[0].toLowerCase() === inherit && values[1].toLowerCase() === inherit) { - return inherit; - } + if ( + values[0].toLowerCase() === inherit && + values[1].toLowerCase() === inherit + ) { + return inherit; + } - return values.join(' '); + return values.join(' '); } -function explode (rule) { - rule.walkDecls(/^columns$/i, decl => { - if (!canExplode(decl)) { - return; - } - - if (detect(decl)) { - return; - } +function explode(rule) { + rule.walkDecls(/^columns$/i, (decl) => { + if (!canExplode(decl)) { + return; + } - let values = list.space(decl.value); + if (detect(decl)) { + return; + } - if (values.length === 1) { - values.push(auto); - } + let values = list.space(decl.value); - values.forEach((value, i) => { - let prop = properties[1]; + if (values.length === 1) { + values.push(auto); + } - if (value.toLowerCase() === auto) { - prop = properties[i]; - } else if (unit(value).unit) { - prop = properties[0]; - } + values.forEach((value, i) => { + let prop = properties[1]; - insertCloned(decl.parent, decl, { - prop, - value, - }); - }); + if (value.toLowerCase() === auto) { + prop = properties[i]; + } else if (unit(value).unit) { + prop = properties[0]; + } - decl.remove(); + insertCloned(decl.parent, decl, { + prop, + value, + }); }); + + decl.remove(); + }); } -function cleanup (rule) { - let decls = getDecls(rule, ['columns'].concat(properties)); - - while (decls.length) { - const lastNode = decls[decls.length - 1]; - - // remove properties of lower precedence - const lesser = decls.filter(node => - !detect(lastNode) && - !detect(node) && - node !== lastNode && - node.important === lastNode.important && - lastNode.prop === 'columns' && node.prop !== lastNode.prop); - - lesser.forEach(remove); - decls = decls.filter(node => !~lesser.indexOf(node)); - - // get duplicate properties - let duplicates = decls.filter(node => - !detect(lastNode) && - !detect(node) && - node !== lastNode && - node.important === lastNode.important && - node.prop === lastNode.prop && - !(!isCustomProp(node) && isCustomProp(lastNode)) - ); - - duplicates.forEach(remove); - decls = decls.filter(node => node !== lastNode && !~duplicates.indexOf(node)); - } +function cleanup(rule) { + let decls = getDecls(rule, ['columns'].concat(properties)); + + while (decls.length) { + const lastNode = decls[decls.length - 1]; + + // remove properties of lower precedence + const lesser = decls.filter( + (node) => + !detect(lastNode) && + !detect(node) && + node !== lastNode && + node.important === lastNode.important && + lastNode.prop === 'columns' && + node.prop !== lastNode.prop + ); + + lesser.forEach(remove); + decls = decls.filter((node) => !~lesser.indexOf(node)); + + // get duplicate properties + let duplicates = decls.filter( + (node) => + !detect(lastNode) && + !detect(node) && + node !== lastNode && + node.important === lastNode.important && + node.prop === lastNode.prop && + !(!isCustomProp(node) && isCustomProp(lastNode)) + ); + + duplicates.forEach(remove); + decls = decls.filter( + (node) => node !== lastNode && !~duplicates.indexOf(node) + ); + } } -function merge (rule) { - mergeRules(rule, properties, (rules, lastNode) => { - if (canMerge(rules) && !rules.some(detect)) { - insertCloned(lastNode.parent, lastNode, { - prop: 'columns', - value: normalize(rules.map(getValue)), - }); +function merge(rule) { + mergeRules(rule, properties, (rules, lastNode) => { + if (canMerge(rules) && !rules.some(detect)) { + insertCloned(lastNode.parent, lastNode, { + prop: 'columns', + value: normalize(rules.map(getValue)), + }); - rules.forEach(remove); + rules.forEach(remove); - return true; - } - }); + return true; + } + }); - cleanup(rule); + cleanup(rule); } export default { - explode, - merge, + explode, + merge, }; diff --git a/packages/postcss-merge-longhand/src/lib/decl/index.js b/packages/postcss-merge-longhand/src/lib/decl/index.js index 75af8ecb7..a1ccbe50f 100644 --- a/packages/postcss-merge-longhand/src/lib/decl/index.js +++ b/packages/postcss-merge-longhand/src/lib/decl/index.js @@ -3,9 +3,4 @@ import columns from './columns'; import margin from './margin'; import padding from './padding'; -export default [ - borders, - columns, - margin, - padding, -]; +export default [borders, columns, margin, padding]; diff --git a/packages/postcss-merge-longhand/src/lib/getDecls.js b/packages/postcss-merge-longhand/src/lib/getDecls.js index d2827be9a..05cf5e1c8 100644 --- a/packages/postcss-merge-longhand/src/lib/getDecls.js +++ b/packages/postcss-merge-longhand/src/lib/getDecls.js @@ -1,3 +1,5 @@ -export default function getDecls (rule, properties) { - return rule.nodes.filter(({prop}) => prop && ~properties.indexOf(prop.toLowerCase())); +export default function getDecls(rule, properties) { + return rule.nodes.filter( + ({ prop }) => prop && ~properties.indexOf(prop.toLowerCase()) + ); } diff --git a/packages/postcss-merge-longhand/src/lib/getLastNode.js b/packages/postcss-merge-longhand/src/lib/getLastNode.js index d6ccef1ad..9c105f1f5 100644 --- a/packages/postcss-merge-longhand/src/lib/getLastNode.js +++ b/packages/postcss-merge-longhand/src/lib/getLastNode.js @@ -1,3 +1,3 @@ export default (rule, prop) => { - return rule.filter(n => n.prop && n.prop.toLowerCase() === prop).pop(); + return rule.filter((n) => n.prop && n.prop.toLowerCase() === prop).pop(); }; diff --git a/packages/postcss-merge-longhand/src/lib/getRules.js b/packages/postcss-merge-longhand/src/lib/getRules.js index c77b9014a..73f737c9c 100644 --- a/packages/postcss-merge-longhand/src/lib/getRules.js +++ b/packages/postcss-merge-longhand/src/lib/getRules.js @@ -1,9 +1,9 @@ -import getLastNode from "./getLastNode"; +import getLastNode from './getLastNode'; -export default function getRules (props, properties) { - return properties - .map(property => { - return getLastNode(props, property); - }) - .filter(Boolean); +export default function getRules(props, properties) { + return properties + .map((property) => { + return getLastNode(props, property); + }) + .filter(Boolean); } diff --git a/packages/postcss-merge-longhand/src/lib/getValue.js b/packages/postcss-merge-longhand/src/lib/getValue.js index 9b4c78d75..9bfe81ba1 100644 --- a/packages/postcss-merge-longhand/src/lib/getValue.js +++ b/packages/postcss-merge-longhand/src/lib/getValue.js @@ -1,3 +1,3 @@ -export default function getValue ({value}) { - return value; +export default function getValue({ value }) { + return value; } diff --git a/packages/postcss-merge-longhand/src/lib/hasAllProps.js b/packages/postcss-merge-longhand/src/lib/hasAllProps.js index b3b850242..546f5c539 100644 --- a/packages/postcss-merge-longhand/src/lib/hasAllProps.js +++ b/packages/postcss-merge-longhand/src/lib/hasAllProps.js @@ -1,3 +1,5 @@ export default (rule, ...props) => { - return props.every(p => rule.some(({prop}) => prop && ~prop.toLowerCase().indexOf(p))); + return props.every((p) => + rule.some(({ prop }) => prop && ~prop.toLowerCase().indexOf(p)) + ); }; diff --git a/packages/postcss-merge-longhand/src/lib/insertCloned.js b/packages/postcss-merge-longhand/src/lib/insertCloned.js index 564702aaa..b6a60bc68 100644 --- a/packages/postcss-merge-longhand/src/lib/insertCloned.js +++ b/packages/postcss-merge-longhand/src/lib/insertCloned.js @@ -1,7 +1,7 @@ -export default function insertCloned (rule, decl, props) { - const newNode = Object.assign(decl.clone(), props); +export default function insertCloned(rule, decl, props) { + const newNode = Object.assign(decl.clone(), props); - rule.insertAfter(decl, newNode); + rule.insertAfter(decl, newNode); - return newNode; -}; + return newNode; +} diff --git a/packages/postcss-merge-longhand/src/lib/isCustomProp.js b/packages/postcss-merge-longhand/src/lib/isCustomProp.js index 0a5c284d6..391c2cffc 100644 --- a/packages/postcss-merge-longhand/src/lib/isCustomProp.js +++ b/packages/postcss-merge-longhand/src/lib/isCustomProp.js @@ -1,2 +1 @@ -export default node => - ~node.value.search(/var\s*\(\s*--/i); +export default (node) => ~node.value.search(/var\s*\(\s*--/i); diff --git a/packages/postcss-merge-longhand/src/lib/mergeRules.js b/packages/postcss-merge-longhand/src/lib/mergeRules.js index 85933e817..c05bdddb5 100644 --- a/packages/postcss-merge-longhand/src/lib/mergeRules.js +++ b/packages/postcss-merge-longhand/src/lib/mergeRules.js @@ -2,42 +2,42 @@ import hasAllProps from './hasAllProps'; import getDecls from './getDecls'; import getRules from './getRules'; -function isConflictingProp (propA, propB) { - if (!propB.prop || propB.important !== propA.important) { - return; - } +function isConflictingProp(propA, propB) { + if (!propB.prop || propB.important !== propA.important) { + return; + } - const parts = propA.prop.split('-'); + const parts = propA.prop.split('-'); - return parts.some(() => { - parts.pop(); + return parts.some(() => { + parts.pop(); - return parts.join('-') === propB.prop; - }); + return parts.join('-') === propB.prop; + }); } -function hasConflicts (match, nodes) { - const firstNode = Math.min.apply(null, match.map(n => nodes.indexOf(n))); - const lastNode = Math.max.apply(null, match.map(n => nodes.indexOf(n))); - const between = nodes.slice(firstNode + 1, lastNode); +function hasConflicts(match, nodes) { + const firstNode = Math.min.apply(null, match.map((n) => nodes.indexOf(n))); + const lastNode = Math.max.apply(null, match.map((n) => nodes.indexOf(n))); + const between = nodes.slice(firstNode + 1, lastNode); - return match.some(a => between.some(b => isConflictingProp(a, b))); + return match.some((a) => between.some((b) => isConflictingProp(a, b))); } -export default function mergeRules (rule, properties, callback) { - let decls = getDecls(rule, properties); - - while (decls.length) { - const last = decls[decls.length - 1]; - const props = decls.filter(node => node.important === last.important); - const rules = getRules(props, properties); +export default function mergeRules(rule, properties, callback) { + let decls = getDecls(rule, properties); - if (hasAllProps(rules, ...properties) && !hasConflicts(rules, rule.nodes)) { - if (callback(rules, last, props)) { - decls = decls.filter(node => !~rules.indexOf(node)); - } - } + while (decls.length) { + const last = decls[decls.length - 1]; + const props = decls.filter((node) => node.important === last.important); + const rules = getRules(props, properties); - decls = decls.filter(node => node !== last); + if (hasAllProps(rules, ...properties) && !hasConflicts(rules, rule.nodes)) { + if (callback(rules, last, props)) { + decls = decls.filter((node) => !~rules.indexOf(node)); + } } + + decls = decls.filter((node) => node !== last); + } } diff --git a/packages/postcss-merge-longhand/src/lib/minifyTrbl.js b/packages/postcss-merge-longhand/src/lib/minifyTrbl.js index 108d21d40..a84e0ca01 100644 --- a/packages/postcss-merge-longhand/src/lib/minifyTrbl.js +++ b/packages/postcss-merge-longhand/src/lib/minifyTrbl.js @@ -1,19 +1,19 @@ import parseTrbl from './parseTrbl'; -export default v => { - const value = parseTrbl(v); +export default (v) => { + const value = parseTrbl(v); - if (value[3] === value[1]) { - value.pop(); + if (value[3] === value[1]) { + value.pop(); - if (value[2] === value[0]) { - value.pop(); + if (value[2] === value[0]) { + value.pop(); - if (value[0] === value[1]) { - value.pop(); - } - } + if (value[0] === value[1]) { + value.pop(); + } } + } - return value.join(' '); + return value.join(' '); }; diff --git a/packages/postcss-merge-longhand/src/lib/minifyWsc.js b/packages/postcss-merge-longhand/src/lib/minifyWsc.js index 2b2e47964..96c070a09 100644 --- a/packages/postcss-merge-longhand/src/lib/minifyWsc.js +++ b/packages/postcss-merge-longhand/src/lib/minifyWsc.js @@ -1,23 +1,29 @@ import parseWsc from './parseWsc'; import minifyTrbl from './minifyTrbl'; -import {isValidWsc} from './validateWsc'; +import { isValidWsc } from './validateWsc'; const defaults = ['medium', 'none', 'currentcolor']; -export default v => { - const values = parseWsc(v); +export default (v) => { + const values = parseWsc(v); - if (!isValidWsc(values)) { - return minifyTrbl(v); - } + if (!isValidWsc(values)) { + return minifyTrbl(v); + } - const value = [...values, ''].reduceRight((prev, cur, i, arr) => { - if (cur === undefined || cur.toLowerCase() === defaults[i] && (!i ||(arr[i-1] || '').toLowerCase() !== cur.toLowerCase())) { - return prev; - } + const value = [...values, ''] + .reduceRight((prev, cur, i, arr) => { + if ( + cur === undefined || + (cur.toLowerCase() === defaults[i] && + (!i || (arr[i - 1] || '').toLowerCase() !== cur.toLowerCase())) + ) { + return prev; + } - return cur + ' ' + prev; - }).trim(); + return cur + ' ' + prev; + }) + .trim(); - return minifyTrbl(value || 'none'); + return minifyTrbl(value || 'none'); }; diff --git a/packages/postcss-merge-longhand/src/lib/parseTrbl.js b/packages/postcss-merge-longhand/src/lib/parseTrbl.js index d3567724f..f77f6390f 100644 --- a/packages/postcss-merge-longhand/src/lib/parseTrbl.js +++ b/packages/postcss-merge-longhand/src/lib/parseTrbl.js @@ -1,11 +1,11 @@ -import {list} from 'postcss'; +import { list } from 'postcss'; -export default v => { - const s = typeof v === 'string' ? list.space(v) : v; - return [ - s[0], // top - s[1] || s[0], // right - s[2] || s[0], // bottom - s[3] || s[1] || s[0], // left - ]; +export default (v) => { + const s = typeof v === 'string' ? list.space(v) : v; + return [ + s[0], // top + s[1] || s[0], // right + s[2] || s[0], // bottom + s[3] || s[1] || s[0], // left + ]; }; diff --git a/packages/postcss-merge-longhand/src/lib/parseWsc.js b/packages/postcss-merge-longhand/src/lib/parseWsc.js index 0dd83d0e8..58eee6e6e 100644 --- a/packages/postcss-merge-longhand/src/lib/parseWsc.js +++ b/packages/postcss-merge-longhand/src/lib/parseWsc.js @@ -1,56 +1,61 @@ -import {list} from 'postcss'; -import {isWidth, isStyle, isColor} from './validateWsc'; +import { list } from 'postcss'; +import { isWidth, isStyle, isColor } from './validateWsc'; const none = /^\s*(none|medium)(\s+none(\s+(none|currentcolor))?)?\s*$/i; const varRE = /(^.*var)(.*\(.*--.*\))(.*)/i; -const varPreserveCase = p => `${p[1].toLowerCase()}${p[2]}${p[3].toLowerCase()}`; -const toLower = v => { - const match = varRE.exec(v); - return match ? varPreserveCase(match) : v.toLowerCase(); +const varPreserveCase = (p) => + `${p[1].toLowerCase()}${p[2]}${p[3].toLowerCase()}`; +const toLower = (v) => { + const match = varRE.exec(v); + return match ? varPreserveCase(match) : v.toLowerCase(); }; -export default function parseWsc (value) { - if (none.test(value)) { - return [ 'medium', 'none', 'currentcolor']; +export default function parseWsc(value) { + if (none.test(value)) { + return ['medium', 'none', 'currentcolor']; + } + + let width, style, color; + + const values = list.space(value); + + if ( + values.length > 1 && + isStyle(values[1]) && + values[0].toLowerCase() === 'none' + ) { + values.unshift(); + width = '0'; + } + + const unknown = []; + + values.forEach((v) => { + if (isStyle(v)) { + style = toLower(v); + } else if (isWidth(v)) { + width = toLower(v); + } else if (isColor(v)) { + color = toLower(v); + } else { + unknown.push(v); } + }); - let width, style, color; - - const values = list.space(value); + if (unknown.length) { + if (!width && style && color) { + width = unknown.pop(); + } - if (values.length > 1 && isStyle(values[1]) && values[0].toLowerCase() === 'none') { - values.unshift(); - width = '0'; + if (width && !style && color) { + style = unknown.pop(); } - const unknown = []; - - values.forEach(v => { - if (isStyle(v)) { - style = toLower(v); - } else if (isWidth(v)) { - width = toLower(v); - } else if (isColor(v)) { - color = toLower(v); - } else { - unknown.push(v); - } - }); - - if (unknown.length) { - if (!width && style && color) { - width = unknown.pop(); - } - - if (width && !style && color) { - style = unknown.pop(); - } - - if (width && style && !color) { - color = unknown.pop(); - } + if (width && style && !color) { + color = unknown.pop(); } + } - return [ width, style, color ]; + return [width, style, color]; } diff --git a/packages/postcss-merge-longhand/src/lib/remove.js b/packages/postcss-merge-longhand/src/lib/remove.js index 2f356cb82..700e91889 100644 --- a/packages/postcss-merge-longhand/src/lib/remove.js +++ b/packages/postcss-merge-longhand/src/lib/remove.js @@ -1,3 +1,3 @@ -export default function remove (node) { - return node.remove(); +export default function remove(node) { + return node.remove(); } diff --git a/packages/postcss-merge-longhand/src/lib/validateWsc.js b/packages/postcss-merge-longhand/src/lib/validateWsc.js index 725e43085..a137b1a73 100644 --- a/packages/postcss-merge-longhand/src/lib/validateWsc.js +++ b/packages/postcss-merge-longhand/src/lib/validateWsc.js @@ -1,69 +1,69 @@ -import colorNames from "css-color-names"; +import colorNames from 'css-color-names'; -const widths = ["thin", "medium", "thick"]; +const widths = ['thin', 'medium', 'thick']; const styles = [ - "none", - "hidden", - "dotted", - "dashed", - "solid", - "double", - "groove", - "ridge", - "inset", - "outset", + 'none', + 'hidden', + 'dotted', + 'dashed', + 'solid', + 'double', + 'groove', + 'ridge', + 'inset', + 'outset', ]; const colors = Object.keys(colorNames); -export function isStyle (value) { - return value && !!~styles.indexOf(value.toLowerCase()); +export function isStyle(value) { + return value && !!~styles.indexOf(value.toLowerCase()); } -export function isWidth (value) { - return ( - (value && !!~widths.indexOf(value.toLowerCase())) || - /^(\d+(\.\d+)?|\.\d+)(\w+)?$/.test(value) - ); +export function isWidth(value) { + return ( + (value && !!~widths.indexOf(value.toLowerCase())) || + /^(\d+(\.\d+)?|\.\d+)(\w+)?$/.test(value) + ); } -export function isColor (value) { - if (!value) { - return false; - } +export function isColor(value) { + if (!value) { + return false; + } - value = value.toLowerCase(); + value = value.toLowerCase(); - if (/rgba?\(/.test(value)) { - return true; - } + if (/rgba?\(/.test(value)) { + return true; + } - if (/hsla?\(/.test(value)) { - return true; - } + if (/hsla?\(/.test(value)) { + return true; + } - if (/#([0-9a-z]{6}|[0-9a-z]{3})/.test(value)) { - return true; - } + if (/#([0-9a-z]{6}|[0-9a-z]{3})/.test(value)) { + return true; + } - if (value === "transparent") { - return true; - } + if (value === 'transparent') { + return true; + } - if (value === "currentcolor") { - return true; - } + if (value === 'currentcolor') { + return true; + } - return !!~colors.indexOf(value); + return !!~colors.indexOf(value); } -export function isValidWsc (wscs) { - const validWidth = isWidth(wscs[0]); - const validStyle = isStyle(wscs[1]); - const validColor = isColor(wscs[2]); +export function isValidWsc(wscs) { + const validWidth = isWidth(wscs[0]); + const validStyle = isStyle(wscs[1]); + const validColor = isColor(wscs[2]); - return ( - (validWidth && validStyle) || - (validWidth && validColor) || - (validStyle && validColor) - ); + return ( + (validWidth && validStyle) || + (validWidth && validColor) || + (validStyle && validColor) + ); } diff --git a/packages/postcss-merge-rules/src/__tests__/index.js b/packages/postcss-merge-rules/src/__tests__/index.js index 5a342f43d..b8f2ba0b2 100644 --- a/packages/postcss-merge-rules/src/__tests__/index.js +++ b/packages/postcss-merge-rules/src/__tests__/index.js @@ -3,726 +3,730 @@ import postcss from 'postcss'; import vars from 'postcss-simple-vars'; import comments from 'postcss-discard-comments'; // alias not loading correctly import plugin from '..'; -import {pseudoElements} from '../lib/ensureCompatibility'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { pseudoElements } from '../lib/ensureCompatibility'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); test( - 'should merge based on declarations', - processCSS, - 'h1{display:block}h2{display:block}', - 'h1,h2{display:block}' + 'should merge based on declarations', + processCSS, + 'h1{display:block}h2{display:block}', + 'h1,h2{display:block}' ); test( - 'should merge based on declarations (2)', - processCSS, - 'h1{color:red;line-height:1.5;font-size:2em}h2{color:red;line-height:1.5;font-size:2em}', - 'h1,h2{color:red;line-height:1.5;font-size:2em}' + 'should merge based on declarations (2)', + processCSS, + 'h1{color:red;line-height:1.5;font-size:2em}h2{color:red;line-height:1.5;font-size:2em}', + 'h1,h2{color:red;line-height:1.5;font-size:2em}' ); test( - 'should merge based on declarations, with a different property order', - processCSS, - 'h1{color:red;line-height:1.5;font-size:2em}h2{font-size:2em;color:red;line-height:1.5}', - 'h1,h2{color:red;line-height:1.5;font-size:2em}' + 'should merge based on declarations, with a different property order', + processCSS, + 'h1{color:red;line-height:1.5;font-size:2em}h2{font-size:2em;color:red;line-height:1.5}', + 'h1,h2{color:red;line-height:1.5;font-size:2em}' ); test( - 'should merge based on selectors', - processCSS, - 'h1{display:block}h1{text-decoration:underline}', - 'h1{display:block;text-decoration:underline}' + 'should merge based on selectors', + processCSS, + 'h1{display:block}h1{text-decoration:underline}', + 'h1{display:block;text-decoration:underline}' ); test( - 'should merge based on selectors (2)', - processCSS, - 'h1{color:red;display:block}h1{text-decoration:underline}', - 'h1{color:red;display:block;text-decoration:underline}' + 'should merge based on selectors (2)', + processCSS, + 'h1{color:red;display:block}h1{text-decoration:underline}', + 'h1{color:red;display:block;text-decoration:underline}' ); test( - 'should merge based on selectors (3)', - processCSS, - 'h1{font-size:2em;color:#000}h1{background:#fff;line-height:1.5}', - 'h1{font-size:2em;color:#000;background:#fff;line-height:1.5}' + 'should merge based on selectors (3)', + processCSS, + 'h1{font-size:2em;color:#000}h1{background:#fff;line-height:1.5}', + 'h1{font-size:2em;color:#000;background:#fff;line-height:1.5}' ); test( - 'should merge in media queries', - processCSS, - '@media print{h1{display:block}h1{color:red}}', - '@media print{h1{display:block;color:red}}' + 'should merge in media queries', + processCSS, + '@media print{h1{display:block}h1{color:red}}', + '@media print{h1{display:block;color:red}}' ); test( - 'should merge in media queries (2)', - processCSS, - '@media print{h1{display:block}p{display:block}}', - '@media print{h1,p{display:block}}' + 'should merge in media queries (2)', + processCSS, + '@media print{h1{display:block}p{display:block}}', + '@media print{h1,p{display:block}}' ); test( - 'should merge in media queries (3)', - processCSS, - '@media print{h1{color:red;text-decoration:none}h2{text-decoration:none}}h3{text-decoration:none}', - '@media print{h1{color:red}h1,h2{text-decoration:none}}h3{text-decoration:none}' + 'should merge in media queries (3)', + processCSS, + '@media print{h1{color:red;text-decoration:none}h2{text-decoration:none}}h3{text-decoration:none}', + '@media print{h1{color:red}h1,h2{text-decoration:none}}h3{text-decoration:none}' ); test( - 'should merge in media queries (4)', - processCSS, - 'h3{text-decoration:none}@media print{h1{color:red;text-decoration:none}h2{text-decoration:none}}', - 'h3{text-decoration:none}@media print{h1{color:red}h1,h2{text-decoration:none}}' + 'should merge in media queries (4)', + processCSS, + 'h3{text-decoration:none}@media print{h1{color:red;text-decoration:none}h2{text-decoration:none}}', + 'h3{text-decoration:none}@media print{h1{color:red}h1,h2{text-decoration:none}}' ); test( - 'should not merge across media queries', - passthroughCSS, - '@media screen and (max-width:480px){h1{display:block}}@media screen and (min-width:480px){h2{display:block}}' + 'should not merge across media queries', + passthroughCSS, + '@media screen and (max-width:480px){h1{display:block}}@media screen and (min-width:480px){h2{display:block}}' ); test( - 'should not merge across media queries (2)', - passthroughCSS, - '@media screen and (max-width:200px){h1{color:red}}@media screen and (min-width:480px){h1{display:block}}' + 'should not merge across media queries (2)', + passthroughCSS, + '@media screen and (max-width:200px){h1{color:red}}@media screen and (min-width:480px){h1{display:block}}' ); test( - 'should not merge across keyframes', - passthroughCSS, - '@-webkit-keyframes test{0%{color:#000}to{color:#fff}}@keyframes test{0%{color:#000}to{color:#fff}}' + 'should not merge across keyframes', + passthroughCSS, + '@-webkit-keyframes test{0%{color:#000}to{color:#fff}}@keyframes test{0%{color:#000}to{color:#fff}}' ); test( - 'should not merge across keyframes (2)', - passthroughCSS, - [ - '@-webkit-keyframes slideInDown{', - '0%{-webkit-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}', - 'to{-webkit-transform:translateY(0);transform:translateY(0)}', - '}', - '@keyframes slideInDown{', - '0%{-webkit-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}', - 'to{-webkit-transform:translateY(0);transform:translateY(0)}', - '}', - ].join('') + 'should not merge across keyframes (2)', + passthroughCSS, + [ + '@-webkit-keyframes slideInDown{', + '0%{-webkit-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}', + 'to{-webkit-transform:translateY(0);transform:translateY(0)}', + '}', + '@keyframes slideInDown{', + '0%{-webkit-transform:translateY(-100%);transform:translateY(-100%);visibility:visible}', + 'to{-webkit-transform:translateY(0);transform:translateY(0)}', + '}', + ].join('') ); test( - 'should not merge across keyframes (3)', - passthroughCSS, - [ - '#foo {-webkit-animation-name:some-animation;-moz-animation-name:some-animation;-o-animation-name:some-animation;animation-name:some-animation}', - '@-webkit-keyframes some-animation{100%{-webkit-transform:scale(2);transform:scale(2)}}', - '@-moz-keyframes some-animation{100%{-moz-transform:scale(2);transform:scale(2)}}', - '@-o-keyframes some-animation {100%{-o-transform:scale(2);transform:scale(2)}}', - '@keyframes some-animation {100%{-webkit-transform:scale(2);-moz-transform:scale(2);-o-transform:scale(2);transform:scale(2)}}', - ].join('') + 'should not merge across keyframes (3)', + passthroughCSS, + [ + '#foo {-webkit-animation-name:some-animation;-moz-animation-name:some-animation;-o-animation-name:some-animation;animation-name:some-animation}', + '@-webkit-keyframes some-animation{100%{-webkit-transform:scale(2);transform:scale(2)}}', + '@-moz-keyframes some-animation{100%{-moz-transform:scale(2);transform:scale(2)}}', + '@-o-keyframes some-animation {100%{-o-transform:scale(2);transform:scale(2)}}', + '@keyframes some-animation {100%{-webkit-transform:scale(2);-moz-transform:scale(2);-o-transform:scale(2);transform:scale(2)}}', + ].join('') ); test( - 'should not merge in different contexts', - passthroughCSS, - 'h1{display:block}@media print{h1{color:red}}' + 'should not merge in different contexts', + passthroughCSS, + 'h1{display:block}@media print{h1{color:red}}' ); test( - 'should not merge in different contexts (2)', - passthroughCSS, - '@media print{h1{display:block}}h1{color:red}' + 'should not merge in different contexts (2)', + passthroughCSS, + '@media print{h1{display:block}}h1{color:red}' ); test( - 'should perform partial merging of selectors', - processCSS, - 'h1{color:red}h2{color:red;text-decoration:underline}', - 'h1,h2{color:red}h2{text-decoration:underline}' + 'should perform partial merging of selectors', + processCSS, + 'h1{color:red}h2{color:red;text-decoration:underline}', + 'h1,h2{color:red}h2{text-decoration:underline}' ); test( - 'should perform partial merging of selectors (2)', - processCSS, - 'h1{color:red}h2{color:red;text-decoration:underline}h3{color:green;text-decoration:underline}', - 'h1,h2{color:red}h2,h3{text-decoration:underline}h3{color:green}' + 'should perform partial merging of selectors (2)', + processCSS, + 'h1{color:red}h2{color:red;text-decoration:underline}h3{color:green;text-decoration:underline}', + 'h1,h2{color:red}h2,h3{text-decoration:underline}h3{color:green}' ); test( - 'should perform partial merging of selectors (3)', - processCSS, - 'h1{color:red;text-decoration:underline}h2{text-decoration:underline;color:green}h3{font-weight:bold;color:green}', - 'h1{color:red}h1,h2{text-decoration:underline}h2,h3{color:green}h3{font-weight:bold}' + 'should perform partial merging of selectors (3)', + processCSS, + 'h1{color:red;text-decoration:underline}h2{text-decoration:underline;color:green}h3{font-weight:bold;color:green}', + 'h1{color:red}h1,h2{text-decoration:underline}h2,h3{color:green}h3{font-weight:bold}' ); test( - 'should perform partial merging of selectors (4)', - processCSS, - '.test0{color:red;border:none;margin:0}.test1{color:green;border:none;margin:0}', - '.test0{color:red}.test0,.test1{border:none;margin:0}.test1{color:green}' + 'should perform partial merging of selectors (4)', + processCSS, + '.test0{color:red;border:none;margin:0}.test1{color:green;border:none;margin:0}', + '.test0{color:red}.test0,.test1{border:none;margin:0}.test1{color:green}' ); test( - 'should perform partial merging of selectors (5)', - processCSS, - 'h1{color:red;font-weight:bold}h2{font-weight:bold}h3{text-decoration:none}', - 'h1{color:red}h1,h2{font-weight:bold}h3{text-decoration:none}' + 'should perform partial merging of selectors (5)', + processCSS, + 'h1{color:red;font-weight:bold}h2{font-weight:bold}h3{text-decoration:none}', + 'h1{color:red}h1,h2{font-weight:bold}h3{text-decoration:none}' ); test( - 'should perform partial merging of selectors (6)', - processCSS, - '.test-1,.test-2{margin-top:10px}.another-test{margin-top:10px;margin-bottom:30px}', - '.test-1,.test-2,.another-test{margin-top:10px}.another-test{margin-bottom:30px}' + 'should perform partial merging of selectors (6)', + processCSS, + '.test-1,.test-2{margin-top:10px}.another-test{margin-top:10px;margin-bottom:30px}', + '.test-1,.test-2,.another-test{margin-top:10px}.another-test{margin-bottom:30px}' ); test( - 'should perform partial merging of selectors (7)', - processCSS, - '.test-1{margin-top:10px;margin-bottom:20px}.test-2{margin-top:10px}.another-test{margin-top:10px;margin-bottom:30px}', - '.test-1{margin-bottom:20px}.test-1,.test-2,.another-test{margin-top:10px}.another-test{margin-bottom:30px}' + 'should perform partial merging of selectors (7)', + processCSS, + '.test-1{margin-top:10px;margin-bottom:20px}.test-2{margin-top:10px}.another-test{margin-top:10px;margin-bottom:30px}', + '.test-1{margin-bottom:20px}.test-1,.test-2,.another-test{margin-top:10px}.another-test{margin-bottom:30px}' ); test( - 'should perform partial merging of selectors (8)', - processCSS, - '.foo{margin:0;display:block}.barim{display:block;line-height:1}.bazaz{font-size:3em;margin:0}', - '.foo{margin:0}.foo,.barim{display:block}.barim{line-height:1}.bazaz{font-size:3em;margin:0}' + 'should perform partial merging of selectors (8)', + processCSS, + '.foo{margin:0;display:block}.barim{display:block;line-height:1}.bazaz{font-size:3em;margin:0}', + '.foo{margin:0}.foo,.barim{display:block}.barim{line-height:1}.bazaz{font-size:3em;margin:0}' ); test( - 'should not merge over-eagerly (cssnano#36 [case 3])', - passthroughCSS, - '.foobam{font-family:serif;display:block}.barim{display:block;line-height:1}.bazaz{font-size:3em;font-family:serif}' + 'should not merge over-eagerly (cssnano#36 [case 3])', + passthroughCSS, + '.foobam{font-family:serif;display:block}.barim{display:block;line-height:1}.bazaz{font-size:3em;font-family:serif}' ); test( - 'should not merge over-eagerly (cssnano#36 [case 4])', - processCSS, - '.foo{font-family:serif;display:block}.barim{display:block;line-height:1}.bazaz{font-size:3em;font-family:serif}', - '.foo{font-family:serif}.foo,.barim{display:block}.barim{line-height:1}.bazaz{font-size:3em;font-family:serif}' + 'should not merge over-eagerly (cssnano#36 [case 4])', + processCSS, + '.foo{font-family:serif;display:block}.barim{display:block;line-height:1}.bazaz{font-size:3em;font-family:serif}', + '.foo{font-family:serif}.foo,.barim{display:block}.barim{line-height:1}.bazaz{font-size:3em;font-family:serif}' ); test( - 'should merge multiple values (cssnano#49)', - processCSS, - 'h1{border:1px solid red;background-color:red;background-position:50% 100%}h1{border:1px solid red;background-color:red}h1{border:1px solid red}', - 'h1{border:1px solid red;background-color:red;background-position:50% 100%}' + 'should merge multiple values (cssnano#49)', + processCSS, + 'h1{border:1px solid red;background-color:red;background-position:50% 100%}h1{border:1px solid red;background-color:red}h1{border:1px solid red}', + 'h1{border:1px solid red;background-color:red;background-position:50% 100%}' ); test( - 'should perform partial merging of selectors in the opposite direction', - processCSS, - 'h1{color:black}h2{color:black;font-weight:bold}h3{color:black;font-weight:bold}', - 'h1{color:black}h2,h3{color:black;font-weight:bold}' + 'should perform partial merging of selectors in the opposite direction', + processCSS, + 'h1{color:black}h2{color:black;font-weight:bold}h3{color:black;font-weight:bold}', + 'h1{color:black}h2,h3{color:black;font-weight:bold}' ); test( - 'should not perform partial merging of selectors if the output would be longer', - passthroughCSS, - '.test0{color:red;border:none;margin:0}.longlonglonglong{color:green;border:none;margin:0}' + 'should not perform partial merging of selectors if the output would be longer', + passthroughCSS, + '.test0{color:red;border:none;margin:0}.longlonglonglong{color:green;border:none;margin:0}' ); test( - 'should merge vendor prefixed selectors when vendors are the same', - processCSS, - 'code ::-moz-selection{background:red}code::-moz-selection{background:red}', - 'code ::-moz-selection,code::-moz-selection{background:red}' + 'should merge vendor prefixed selectors when vendors are the same', + processCSS, + 'code ::-moz-selection{background:red}code::-moz-selection{background:red}', + 'code ::-moz-selection,code::-moz-selection{background:red}' ); test( - 'should not merge mixed vendor prefixes', - passthroughCSS, - 'code ::-webkit-selection{background:red}code::-moz-selection{background:red}' + 'should not merge mixed vendor prefixes', + passthroughCSS, + 'code ::-webkit-selection{background:red}code::-moz-selection{background:red}' ); test( - 'should not merge ms vendor prefixes', - passthroughCSS, - 'code :-ms-input-placeholder{background:red}code::-ms-input-placeholder{background:red}' + 'should not merge ms vendor prefixes', + passthroughCSS, + 'code :-ms-input-placeholder{background:red}code::-ms-input-placeholder{background:red}' ); test( - 'should not merge mixed vendor prefixes (2)', - passthroughCSS, - 'input[type=range] { -webkit-appearance: none !important; } input[type=range]::-webkit-slider-runnable-track { height: 2px; width: 100px; background: red; border: none; } input[type=range]::-webkit-slider-thumb { -webkit-appearance: none !important; border: none; width: 10px; height: 10px; background: red; } input[type=range]::-moz-range-thumb { border: none; width: 10px; height: 10px; background: red; }' + 'should not merge mixed vendor prefixes (2)', + passthroughCSS, + 'input[type=range] { -webkit-appearance: none !important; } input[type=range]::-webkit-slider-runnable-track { height: 2px; width: 100px; background: red; border: none; } input[type=range]::-webkit-slider-thumb { -webkit-appearance: none !important; border: none; width: 10px; height: 10px; background: red; } input[type=range]::-moz-range-thumb { border: none; width: 10px; height: 10px; background: red; }' ); test( - 'should not merge mixed vendor prefixed and non-vendor prefixed', - passthroughCSS, - 'code ::selection{background:red}code ::-moz-selection{background:red}' + 'should not merge mixed vendor prefixed and non-vendor prefixed', + passthroughCSS, + 'code ::selection{background:red}code ::-moz-selection{background:red}' ); test( - 'should merge text-* properties', - processCSS, - 'h1{color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline}', - 'h1{color:red}h1,h2{text-align:right;text-decoration:underline}' + 'should merge text-* properties', + processCSS, + 'h1{color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline}', + 'h1{color:red}h1,h2{text-align:right;text-decoration:underline}' ); test( - 'should merge text-* properties (2)', - processCSS, - 'h1{color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline;color:green}', - 'h1{color:red}h1,h2{text-align:right;text-decoration:underline}h2{color:green}' + 'should merge text-* properties (2)', + processCSS, + 'h1{color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline;color:green}', + 'h1{color:red}h1,h2{text-align:right;text-decoration:underline}h2{color:green}' ); test( - 'should merge text-* properties (3)', - processCSS, - 'h1{background:white;color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline;color:red}', - 'h1{background:white}h1,h2{color:red;text-align:right;text-decoration:underline}' + 'should merge text-* properties (3)', + processCSS, + 'h1{background:white;color:red;text-align:right;text-decoration:underline}h2{text-align:right;text-decoration:underline;color:red}', + 'h1{background:white}h1,h2{color:red;text-align:right;text-decoration:underline}' ); test( - 'should merge text-* properties (4)', - processCSS, - 'h1{color:red;text-align:center;text-transform:small-caps}h2{text-align:center;color:red}', - 'h1{text-transform:small-caps}h1,h2{color:red;text-align:center}' + 'should merge text-* properties (4)', + processCSS, + 'h1{color:red;text-align:center;text-transform:small-caps}h2{text-align:center;color:red}', + 'h1{text-transform:small-caps}h1,h2{color:red;text-align:center}' ); test( - 'should merge text-* properties (5)', - processCSS, - 'h1{text-align:left;text-transform:small-caps}h2{text-align:right;text-transform:small-caps}', - 'h1{text-align:left}h1,h2{text-transform:small-caps}h2{text-align:right}' + 'should merge text-* properties (5)', + processCSS, + 'h1{text-align:left;text-transform:small-caps}h2{text-align:right;text-transform:small-caps}', + 'h1{text-align:left}h1,h2{text-transform:small-caps}h2{text-align:right}' ); test( - 'should not incorrectly extract transform properties', - passthroughCSS, - '@keyframes a {0%{transform-origin:right bottom;transform:rotate(-90deg);opacity:0}100%{transform-origin:right bottom;transform:rotate(0);opacity:1}}' + 'should not incorrectly extract transform properties', + passthroughCSS, + '@keyframes a {0%{transform-origin:right bottom;transform:rotate(-90deg);opacity:0}100%{transform-origin:right bottom;transform:rotate(0);opacity:1}}' ); test( - 'should not incorrectly extract background properties', - passthroughCSS, - '.iPhone{background:url(a.png);background-image:url(../../../sprites/c.png);background-repeat:no-repeat;background-position:-102px -74px}.logo{background:url(b.png);background-image:url(../../../sprites/c.png);background-repeat:no-repeat;background-position:-2px -146px}' + 'should not incorrectly extract background properties', + passthroughCSS, + '.iPhone{background:url(a.png);background-image:url(../../../sprites/c.png);background-repeat:no-repeat;background-position:-102px -74px}.logo{background:url(b.png);background-image:url(../../../sprites/c.png);background-repeat:no-repeat;background-position:-2px -146px}' ); test( - 'should not incorrectly extract margin properties', - passthroughCSS, - 'h2{margin-bottom:20px}h1{margin:10px;margin-bottom:20px}' + 'should not incorrectly extract margin properties', + passthroughCSS, + 'h2{margin-bottom:20px}h1{margin:10px;margin-bottom:20px}' ); test( - 'should not incorrectly extract margin properties (2)', - processCSS, - 'h2{color:red;margin-bottom:20px}h1{color:red;margin:10px;margin-bottom:20px}', - 'h2{margin-bottom:20px}h2,h1{color:red}h1{margin:10px;margin-bottom:20px}' + 'should not incorrectly extract margin properties (2)', + processCSS, + 'h2{color:red;margin-bottom:20px}h1{color:red;margin:10px;margin-bottom:20px}', + 'h2{margin-bottom:20px}h2,h1{color:red}h1{margin:10px;margin-bottom:20px}' ); test( - 'should not incorrectly extract margin properties (3)', - passthroughCSS, - 'h2{margin:0;margin-bottom:20px}h1{margin:0;margin-top:20px}' + 'should not incorrectly extract margin properties (3)', + passthroughCSS, + 'h2{margin:0;margin-bottom:20px}h1{margin:0;margin-top:20px}' ); test( - 'should not incorrectly extract margin properties (4)', - passthroughCSS, - 'h2{margin:0}h1{margin-top:20px;margin:0}' + 'should not incorrectly extract margin properties (4)', + passthroughCSS, + 'h2{margin:0}h1{margin-top:20px;margin:0}' ); test( - 'should not incorrectly extract display properties', - passthroughCSS, - '.box1{display:inline-block;display:block}.box2{display:inline-block}' + 'should not incorrectly extract display properties', + passthroughCSS, + '.box1{display:inline-block;display:block}.box2{display:inline-block}' ); test( - 'should handle selector hacks', - processCSS, - '.classA{*zoom:1}.classB{box-sizing:border-box;position:relative;min-height:100%}.classC{box-sizing:border-box;position:relative}.classD{box-sizing:border-box;position:relative}', - '.classA{*zoom:1}.classB{min-height:100%}.classB,.classC,.classD{box-sizing:border-box;position:relative}' + 'should handle selector hacks', + processCSS, + '.classA{*zoom:1}.classB{box-sizing:border-box;position:relative;min-height:100%}.classC{box-sizing:border-box;position:relative}.classD{box-sizing:border-box;position:relative}', + '.classA{*zoom:1}.classB{min-height:100%}.classB,.classC,.classD{box-sizing:border-box;position:relative}' ); -test( - 'should handle empty rulesets', - processCSS, - 'h1{h2{}h3{}}', - 'h1{h2,h3{}}' -); +test('should handle empty rulesets', processCSS, 'h1{h2{}h3{}}', 'h1{h2,h3{}}'); test( - 'should not throw on charset declarations', - processCSS, - '@charset "utf-8";@charset "utf-8";@charset "utf-8";h1{}h2{}', - '@charset "utf-8";@charset "utf-8";@charset "utf-8";h1,h2{}' + 'should not throw on charset declarations', + processCSS, + '@charset "utf-8";@charset "utf-8";@charset "utf-8";h1{}h2{}', + '@charset "utf-8";@charset "utf-8";@charset "utf-8";h1,h2{}' ); test( - 'should not throw on comment nodes', - passthroughCSS, - '.navbar-soft .navbar-nav > .active > a{color:#fff;background-color:#303030}.navbar-soft .navbar-nav > .open > a{color:#fff;background-color:rgba(48,48,48,0.8)}/* caret */.navbar-soft .navbar-nav > .dropdown > a .caret{border-top-color:#777;border-bottom-color:#777}' + 'should not throw on comment nodes', + passthroughCSS, + '.navbar-soft .navbar-nav > .active > a{color:#fff;background-color:#303030}.navbar-soft .navbar-nav > .open > a{color:#fff;background-color:rgba(48,48,48,0.8)}/* caret */.navbar-soft .navbar-nav > .dropdown > a .caret{border-top-color:#777;border-bottom-color:#777}' ); test( - 'should not throw on comment nodes (2)', - processCSS, - 'h1{color:black;background:blue/*test*/}h2{background:blue}', - 'h1{color:black/*test*/}h1,h2{background:blue}' + 'should not throw on comment nodes (2)', + processCSS, + 'h1{color:black;background:blue/*test*/}h2{background:blue}', + 'h1{color:black/*test*/}h1,h2{background:blue}' ); test( - 'should not be responsible for deduping declarations when merging', - processCSS, - 'h1{display:block;display:block}h2{display:block;display:block}', - 'h1,h2{display:block;display:block}' + 'should not be responsible for deduping declarations when merging', + processCSS, + 'h1{display:block;display:block}h2{display:block;display:block}', + 'h1,h2{display:block;display:block}' ); test( - 'should not be responsible for deduping selectors when merging', - processCSS, - 'h1,h2{display:block}h2,h1{display:block}', - 'h1,h2,h2,h1{display:block}' + 'should not be responsible for deduping selectors when merging', + processCSS, + 'h1,h2{display:block}h2,h1{display:block}', + 'h1,h2,h2,h1{display:block}' ); test( - 'should not merge across font face rules', - processCSS, - '.one, .two, .three { font-family: "lorem"; font-weight: normal; } .four { font-family: "lorem", serif; font-weight: normal; }.five { font-family: "lorem"; font-weight: normal; } @font-face { font-family: "lorem"; font-weight: normal; src: url(/assets/lorem.eot); src: url(/assets/lorem.eot?#iefix) format("embedded-opentype"), url(/assets/lorem.woff) format("woff"), url(/assets/lorem.ttf) format("truetype"); }', - '.one, .two, .three { font-family: "lorem"; font-weight: normal; } .four { font-family: "lorem", serif; }.four,.five { font-weight: normal; }.five { font-family: "lorem"; } @font-face { font-family: "lorem"; font-weight: normal; src: url(/assets/lorem.eot); src: url(/assets/lorem.eot?#iefix) format("embedded-opentype"), url(/assets/lorem.woff) format("woff"), url(/assets/lorem.ttf) format("truetype"); }' + 'should not merge across font face rules', + processCSS, + '.one, .two, .three { font-family: "lorem"; font-weight: normal; } .four { font-family: "lorem", serif; font-weight: normal; }.five { font-family: "lorem"; font-weight: normal; } @font-face { font-family: "lorem"; font-weight: normal; src: url(/assets/lorem.eot); src: url(/assets/lorem.eot?#iefix) format("embedded-opentype"), url(/assets/lorem.woff) format("woff"), url(/assets/lorem.ttf) format("truetype"); }', + '.one, .two, .three { font-family: "lorem"; font-weight: normal; } .four { font-family: "lorem", serif; }.four,.five { font-weight: normal; }.five { font-family: "lorem"; } @font-face { font-family: "lorem"; font-weight: normal; src: url(/assets/lorem.eot); src: url(/assets/lorem.eot?#iefix) format("embedded-opentype"), url(/assets/lorem.woff) format("woff"), url(/assets/lorem.ttf) format("truetype"); }' ); test( - 'should not merge across font face rules (2)', - processCSS, - '.foo { font-weight: normal; } .bar { font-family: "my-font"; font-weight: normal; } @font-face { font-family: "my-font"; font-weight: normal; src: url("my-font.ttf"); }', - '.foo,.bar { font-weight: normal; } .bar { font-family: "my-font"; } @font-face { font-family: "my-font"; font-weight: normal; src: url("my-font.ttf"); }' + 'should not merge across font face rules (2)', + processCSS, + '.foo { font-weight: normal; } .bar { font-family: "my-font"; font-weight: normal; } @font-face { font-family: "my-font"; font-weight: normal; src: url("my-font.ttf"); }', + '.foo,.bar { font-weight: normal; } .bar { font-family: "my-font"; } @font-face { font-family: "my-font"; font-weight: normal; src: url("my-font.ttf"); }' ); test( - 'should not merge conflicting rules', - passthroughCSS, - '.a{font-family:Arial;font-family:Helvetica;}.b{font-family:Arial;}' + 'should not merge conflicting rules', + passthroughCSS, + '.a{font-family:Arial;font-family:Helvetica;}.b{font-family:Arial;}' ); test( - 'should merge properties with vendor prefixes', - processCSS, - '.a{-webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);-webkit-overflow-scrolling: touch}.b{-webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);}', - '.a{-webkit-overflow-scrolling: touch}.a,.b{-webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);}' + 'should merge properties with vendor prefixes', + processCSS, + '.a{-webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);-webkit-overflow-scrolling: touch}.b{-webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);}', + '.a{-webkit-overflow-scrolling: touch}.a,.b{-webkit-transform: translateX(-50%) translateY(-50%) rotate(-90deg);}' ); test( - 'should respect property order and do nothing', - passthroughCSS, - 'body { overflow: hidden; overflow-y: scroll; overflow-x: hidden;} main { overflow: hidden }' + 'should respect property order and do nothing', + passthroughCSS, + 'body { overflow: hidden; overflow-y: scroll; overflow-x: hidden;} main { overflow: hidden }' ); test( - 'should respect property order and do nothing (2)', - passthroughCSS, - '.a{ border-color:transparent; border-bottom-color:#111111; border-bottom-style:solid; }.b{ border-color:transparent; border-bottom-color:#222222; border-bottom-style:solid; }' + 'should respect property order and do nothing (2)', + passthroughCSS, + '.a{ border-color:transparent; border-bottom-color:#111111; border-bottom-style:solid; }.b{ border-color:transparent; border-bottom-color:#222222; border-bottom-style:solid; }' ); test( - 'should respect property order and do nothing (3)', - processCSS, - '.fb-col-md-6 { color: red; border-color:blue; flex: 0 0 auto; flex-basis: 50%; } .fb-col-md-7 { color: red; border-color:blue; flex: 0 0 auto; flex-basis: 58.3%; }', - '.fb-col-md-6 { flex: 0 0 auto; flex-basis: 50%; } .fb-col-md-6,.fb-col-md-7 { color: red; border-color:blue; } .fb-col-md-7 { flex: 0 0 auto; flex-basis: 58.3%; }' + 'should respect property order and do nothing (3)', + processCSS, + '.fb-col-md-6 { color: red; border-color:blue; flex: 0 0 auto; flex-basis: 50%; } .fb-col-md-7 { color: red; border-color:blue; flex: 0 0 auto; flex-basis: 58.3%; }', + '.fb-col-md-6 { flex: 0 0 auto; flex-basis: 50%; } .fb-col-md-6,.fb-col-md-7 { color: red; border-color:blue; } .fb-col-md-7 { flex: 0 0 auto; flex-basis: 58.3%; }' ); test( - 'should respect property order and do nothing (4) (cssnano#160)', - passthroughCSS, - 'one { border: 1px solid black; border-top: none; } two { border: 1px solid black; }' + 'should respect property order and do nothing (4) (cssnano#160)', + passthroughCSS, + 'one { border: 1px solid black; border-top: none; } two { border: 1px solid black; }' ); test( - 'should respect property order and do nothing (5) (cssnano#87)', - passthroughCSS, - '.dispendium-theme.fr-toolbar.fr-top { border-radius: 0; background-clip: padding-box; box-shadow: none; border: 1px solid #E0E0E0; border-bottom: 0; } .dispendium-theme.fr-toolbar.fr-bottom { border-radius: 0; background-clip: padding-box; box-shadow: none; border: 1px solid #E0E0E0; border-top: 0; }' + 'should respect property order and do nothing (5) (cssnano#87)', + passthroughCSS, + '.dispendium-theme.fr-toolbar.fr-top { border-radius: 0; background-clip: padding-box; box-shadow: none; border: 1px solid #E0E0E0; border-bottom: 0; } .dispendium-theme.fr-toolbar.fr-bottom { border-radius: 0; background-clip: padding-box; box-shadow: none; border: 1px solid #E0E0E0; border-top: 0; }' ); test( - 'should respect property order and do nothing (6) (issue #19)', - passthroughCSS, - '.share .comment-count:before { content: \' \'; position: absolute; width: 0; height: 0; right: 7px; top: 26px; border: 5px solid; border-color: #326891 #326891 transparent transparent; } .share .comment-count:after { content: \' \'; position: absolute; width: 0; height: 0; right: 8px; top: 24px; border: 5px solid; border-color: #fff #fff transparent transparent; }' + 'should respect property order and do nothing (6) (issue #19)', + passthroughCSS, + ".share .comment-count:before { content: ' '; position: absolute; width: 0; height: 0; right: 7px; top: 26px; border: 5px solid; border-color: #326891 #326891 transparent transparent; } .share .comment-count:after { content: ' '; position: absolute; width: 0; height: 0; right: 8px; top: 24px; border: 5px solid; border-color: #fff #fff transparent transparent; }" ); test( - 'should not merge @keyframes rules', - passthroughCSS, - '@keyframes foo{0%{visibility:visible;transform:scale3d(.85,.85,.85);opacity:0}to{visibility:visible;opacity:1}}' + 'should not merge @keyframes rules', + passthroughCSS, + '@keyframes foo{0%{visibility:visible;transform:scale3d(.85,.85,.85);opacity:0}to{visibility:visible;opacity:1}}' ); test( - 'should not merge overlapping rules with vendor prefixes', - passthroughCSS, - '.foo{background:#fff;-webkit-background-clip:text}.bar{background:#000;-webkit-background-clip:text}' + 'should not merge overlapping rules with vendor prefixes', + passthroughCSS, + '.foo{background:#fff;-webkit-background-clip:text}.bar{background:#000;-webkit-background-clip:text}' ); test( - 'should not destroy any declarations when merging', - processCSS, - '.a{background-color:#fff}.a{background-color:#717F83;color:#fff}', - '.a{background-color:#fff;background-color:#717F83;color:#fff}', + 'should not destroy any declarations when merging', + processCSS, + '.a{background-color:#fff}.a{background-color:#717F83;color:#fff}', + '.a{background-color:#fff;background-color:#717F83;color:#fff}' ); test( - 'should merge ::placeholder selectors when supported', - processCSS, - '::placeholder{color:blue}h1{color:blue}', - '::placeholder,h1{color:blue}', - {env: 'chrome58'} + 'should merge ::placeholder selectors when supported', + processCSS, + '::placeholder{color:blue}h1{color:blue}', + '::placeholder,h1{color:blue}', + { env: 'chrome58' } ); test( - 'should not merge general sibling combinators', - passthroughCSS, - 'div{color:#fff}a ~ b{color:#fff}', - {env: 'ie6'} + 'should not merge general sibling combinators', + passthroughCSS, + 'div{color:#fff}a ~ b{color:#fff}', + { env: 'ie6' } ); test( - 'should not merge child combinators', - passthroughCSS, - 'div{color:#fff}a > b{color:#fff}', - {env: 'ie6'} + 'should not merge child combinators', + passthroughCSS, + 'div{color:#fff}a > b{color:#fff}', + { env: 'ie6' } ); test( - 'should not merge attribute selectors (css 2.1)', - passthroughCSS, - 'div{color:#fff}[href]{color:#fff}', - {env: 'ie6'} + 'should not merge attribute selectors (css 2.1)', + passthroughCSS, + 'div{color:#fff}[href]{color:#fff}', + { env: 'ie6' } ); test( - 'should not merge attribute selectors (css 2.1) (2)', - passthroughCSS, - 'div{color:#fff}[href="foo"]{color:#fff}', - {env: 'ie6'} + 'should not merge attribute selectors (css 2.1) (2)', + passthroughCSS, + 'div{color:#fff}[href="foo"]{color:#fff}', + { env: 'ie6' } ); test( - 'should not merge attribute selectors (css 2.1) (3)', - passthroughCSS, - 'div{color:#fff}[href~="foo"]{color:#fff}', - {env: 'ie6'} + 'should not merge attribute selectors (css 2.1) (3)', + passthroughCSS, + 'div{color:#fff}[href~="foo"]{color:#fff}', + { env: 'ie6' } ); test( - 'should not merge attribute selectors (css 2.1) (4)', - passthroughCSS, - 'div{color:#fff}[href|="foo"]{color:#fff}', - {env: 'ie6'} + 'should not merge attribute selectors (css 2.1) (4)', + passthroughCSS, + 'div{color:#fff}[href|="foo"]{color:#fff}', + { env: 'ie6' } ); test( - 'should not merge attribute selectors (css 3)', - passthroughCSS, - 'div{color:#fff}[href^="foo"]{color:#fff}', - {env: 'ie7'} + 'should not merge attribute selectors (css 3)', + passthroughCSS, + 'div{color:#fff}[href^="foo"]{color:#fff}', + { env: 'ie7' } ); test( - 'should not merge attribute selectors (css 3) (2)', - passthroughCSS, - 'div{color:#fff}[href$="foo"]{color:#fff}', - {env: 'ie7'} + 'should not merge attribute selectors (css 3) (2)', + passthroughCSS, + 'div{color:#fff}[href$="foo"]{color:#fff}', + { env: 'ie7' } ); test( - 'should not merge attribute selectors (css 3) (3)', - passthroughCSS, - 'div{color:#fff}[href*="foo"]{color:#fff}', - {env: 'ie7'} + 'should not merge attribute selectors (css 3) (3)', + passthroughCSS, + 'div{color:#fff}[href*="foo"]{color:#fff}', + { env: 'ie7' } ); test( - 'should not merge case insensitive attribute selectors', - passthroughCSS, - 'div{color:#fff}[href="foo" i]{color:#fff}', - {env: 'edge15'} + 'should not merge case insensitive attribute selectors', + passthroughCSS, + 'div{color:#fff}[href="foo" i]{color:#fff}', + { env: 'edge15' } ); const pseudoKeys = Object.keys(pseudoElements); -test(`should not merge ${pseudoKeys.length} pseudo elements`, t => { - return Promise.all( - pseudoKeys.reduce((promises, pseudo) => { - return [...promises, processCSS( - t, - `${pseudo}{color:blue}h1{color:blue}`, - `${pseudo}{color:blue}h1{color:blue}`, - {env: 'ie6'} - )]; - }, []) - ); +test(`should not merge ${pseudoKeys.length} pseudo elements`, (t) => { + return Promise.all( + pseudoKeys.reduce((promises, pseudo) => { + return [ + ...promises, + processCSS( + t, + `${pseudo}{color:blue}h1{color:blue}`, + `${pseudo}{color:blue}h1{color:blue}`, + { env: 'ie6' } + ), + ]; + }, []) + ); }); test( - 'should handle css mixins', - passthroughCSS, - `paper-card{--paper-card-content:{padding-top:0};margin:0 auto 16px;width:768px;max-width:calc(100% - 32px)}` + 'should handle css mixins', + passthroughCSS, + `paper-card{--paper-card-content:{padding-top:0};margin:0 auto 16px;width:768px;max-width:calc(100% - 32px)}` ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); -test('should not crash when node.raws.value is null', t => { - const css = '$color: red; h1{box-shadow:inset 0 -10px 12px 0 $color, /* some comment */ inset 0 0 5px 0 $color;color:blue}h2{color:blue}'; - const res = postcss([ vars(), comments(), plugin]).process(css).css; +test('should not crash when node.raws.value is null', (t) => { + const css = + '$color: red; h1{box-shadow:inset 0 -10px 12px 0 $color, /* some comment */ inset 0 0 5px 0 $color;color:blue}h2{color:blue}'; + const res = postcss([vars(), comments(), plugin]).process(css).css; - t.deepEqual(res, 'h1{box-shadow:inset 0 -10px 12px 0 red, inset 0 0 5px 0 red}h1,h2{color:blue}'); + t.deepEqual( + res, + 'h1{box-shadow:inset 0 -10px 12px 0 red, inset 0 0 5px 0 red}h1,h2{color:blue}' + ); }); -test('should not crash when node.raws.value is null (2)', t => { - const css = '#foo .bar { margin-left: auto ; margin-right: auto ; } #foo .qux { margin-right: auto ; }'; - const res = postcss([ comments(), plugin]).process(css).css; +test('should not crash when node.raws.value is null (2)', (t) => { + const css = + '#foo .bar { margin-left: auto ; margin-right: auto ; } #foo .qux { margin-right: auto ; }'; + const res = postcss([comments(), plugin]).process(css).css; - t.deepEqual(res, '#foo .bar{ margin-left:auto; } #foo .bar,#foo .qux{ margin-right:auto; }'); + t.deepEqual( + res, + '#foo .bar{ margin-left:auto; } #foo .bar,#foo .qux{ margin-right:auto; }' + ); }); test( - 'should place rules with "all" at the top', - processCSS, - '.a{margin: 10px 20px;display: flex;all: initial;font-size: 10px;border-radius: 2px;color: blue;}.b{margin: 10px 30px;display: flex;all: initial;font-size: 20px;border-radius: 2px;color: blue;}', - '.a,.b{display: flex;all: initial;border-radius: 2px;color: blue;}.a{margin: 10px 20px;font-size: 10px;}.b{margin: 10px 30px;font-size: 20px;}' + 'should place rules with "all" at the top', + processCSS, + '.a{margin: 10px 20px;display: flex;all: initial;font-size: 10px;border-radius: 2px;color: blue;}.b{margin: 10px 30px;display: flex;all: initial;font-size: 20px;border-radius: 2px;color: blue;}', + '.a,.b{display: flex;all: initial;border-radius: 2px;color: blue;}.a{margin: 10px 20px;font-size: 10px;}.b{margin: 10px 30px;font-size: 20px;}' ); test( - 'should place rules with "all" at the top (2)', - processCSS, - '.a{margin: 10px 20px;display: flex;ALL: initial;font-size: 10px;border-radius: 2px;color: blue;}.b{margin: 10px 30px;display: flex;ALL: initial;font-size: 20px;border-radius: 2px;color: blue;}', - '.a,.b{display: flex;ALL: initial;border-radius: 2px;color: blue;}.a{margin: 10px 20px;font-size: 10px;}.b{margin: 10px 30px;font-size: 20px;}' + 'should place rules with "all" at the top (2)', + processCSS, + '.a{margin: 10px 20px;display: flex;ALL: initial;font-size: 10px;border-radius: 2px;color: blue;}.b{margin: 10px 30px;display: flex;ALL: initial;font-size: 20px;border-radius: 2px;color: blue;}', + '.a,.b{display: flex;ALL: initial;border-radius: 2px;color: blue;}.a{margin: 10px 20px;font-size: 10px;}.b{margin: 10px 30px;font-size: 20px;}' ); test( - 'should merge multiple media queries', - processCSS, - '@media print{h1{display:block}}@media print{h1{color:red}}', - '@media print{h1{display:block;color:red}}@media print{}' + 'should merge multiple media queries', + processCSS, + '@media print{h1{display:block}}@media print{h1{color:red}}', + '@media print{h1{display:block;color:red}}@media print{}' ); test( - 'should merge multiple media queries (uppercase)', - processCSS, - '@media print{h1{display:block}}@MEDIA print{h1{color:red}}', - '@media print{h1{display:block;color:red}}@MEDIA print{}' + 'should merge multiple media queries (uppercase)', + processCSS, + '@media print{h1{display:block}}@MEDIA print{h1{color:red}}', + '@media print{h1{display:block;color:red}}@MEDIA print{}' ); test( - 'should not merge nested at-rules', - passthroughCSS, - [ - '@media (min-width: 48rem){.wrapper{display: block}}', - '@supports (display: flex){@media (min-width: 48rem){.wrapper{display:flex}}}', - ].join(''), + 'should not merge nested at-rules', + passthroughCSS, + [ + '@media (min-width: 48rem){.wrapper{display: block}}', + '@supports (display: flex){@media (min-width: 48rem){.wrapper{display:flex}}}', + ].join('') ); test( - 'should merge with same at-rule parent', - processCSS, - [ - '@media print{h1{display:block}}', - '@media print{h1{color:red}h2{padding:10px}}', - ].join(''), - [ - '@media print{h1{display:block;color:red}h2{padding:10px}}', - '@media print{}', - ].join('') + 'should merge with same at-rule parent', + processCSS, + [ + '@media print{h1{display:block}}', + '@media print{h1{color:red}h2{padding:10px}}', + ].join(''), + [ + '@media print{h1{display:block;color:red}h2{padding:10px}}', + '@media print{}', + ].join('') ); test( - 'should merge with same at-rule parent (2)', - processCSS, - [ - '@media (width:40px){.red{color:red}}', - '@media (width:40px){.green{color:green}}', - '@media (width:40px){.blue{color:blue}}', - '@supports (--var:var){.white{color:white}}', - '@supports (--var:var){.black{color:black}}', - ].join(''), - [ - '@media (width:40px){.red{color:red}.green{color:green}.blue{color:blue}}', - '@media (width:40px){}', - '@media (width:40px){}', - '@supports (--var:var){.white{color:white}.black{color:black}}', - '@supports (--var:var){}', - ].join('') + 'should merge with same at-rule parent (2)', + processCSS, + [ + '@media (width:40px){.red{color:red}}', + '@media (width:40px){.green{color:green}}', + '@media (width:40px){.blue{color:blue}}', + '@supports (--var:var){.white{color:white}}', + '@supports (--var:var){.black{color:black}}', + ].join(''), + [ + '@media (width:40px){.red{color:red}.green{color:green}.blue{color:blue}}', + '@media (width:40px){}', + '@media (width:40px){}', + '@supports (--var:var){.white{color:white}.black{color:black}}', + '@supports (--var:var){}', + ].join('') ); test( - 'should merge with same nested at-rule parents', - processCSS, - [ - '@media (width:40px){.red{color:red}}', - '@media (width:40px){.green{color:green}}', - '@media (width:40px){.blue{color:blue}}', - '@supports (--var:var){@media (width:40px){.white{color:white}}}', - '@supports (--var:var){@media (width:40px){.black{color:black}}}', - ].join(''), - [ - '@media (width:40px){.red{color:red}.green{color:green}.blue{color:blue}}', - '@media (width:40px){}', - '@media (width:40px){}', - '@supports (--var:var){@media (width:40px){.white{color:white}.black{color:black}}}', - '@supports (--var:var){@media (width:40px){}}', - ].join('') + 'should merge with same nested at-rule parents', + processCSS, + [ + '@media (width:40px){.red{color:red}}', + '@media (width:40px){.green{color:green}}', + '@media (width:40px){.blue{color:blue}}', + '@supports (--var:var){@media (width:40px){.white{color:white}}}', + '@supports (--var:var){@media (width:40px){.black{color:black}}}', + ].join(''), + [ + '@media (width:40px){.red{color:red}.green{color:green}.blue{color:blue}}', + '@media (width:40px){}', + '@media (width:40px){}', + '@supports (--var:var){@media (width:40px){.white{color:white}.black{color:black}}}', + '@supports (--var:var){@media (width:40px){}}', + ].join('') ); test( - 'should not merge with different at-rule parent', - passthroughCSS, - [ - '@media print{h1{display:block}}', - '@media screen{h1{color:red}h2{padding:10px}}', - ].join('') + 'should not merge with different at-rule parent', + passthroughCSS, + [ + '@media print{h1{display:block}}', + '@media screen{h1{color:red}h2{padding:10px}}', + ].join('') ); test( - 'should not merge with different nested at-rules parents', - passthroughCSS, - [ - '@media (min-width: 48rem){.wrapper{display: block}}', - '@supports (display: flex){@media (min-width: 48rem){.wrapper{display:flex}}}', - ].join('') + 'should not merge with different nested at-rules parents', + passthroughCSS, + [ + '@media (min-width: 48rem){.wrapper{display: block}}', + '@supports (display: flex){@media (min-width: 48rem){.wrapper{display:flex}}}', + ].join('') ); test( - 'should not merge with different nested at-rule parents (2)', - passthroughCSS, - [ - '@media print{h1{display:block}}', - '@support (color:red){@media print (color:red){h1{color:red}h2{padding:10px}}}', - ].join('') + 'should not merge with different nested at-rule parents (2)', + passthroughCSS, + [ + '@media print{h1{display:block}}', + '@support (color:red){@media print (color:red){h1{color:red}h2{padding:10px}}}', + ].join('') ); - -test( - 'should merge multiple values across at-rules', - processCSS, - [ - '@media (width:40px){h1{border:1px solid red;background-color:red;background-position:50% 100%}}', - '@media (width:40px){h1{border:1px solid red;background-color:red}}', - '@media (width:40px){h1{border:1px solid red}}', - ].join(''), - [ - '@media (width:40px){h1{border:1px solid red;background-color:red;background-position:50% 100%}}', - '@media (width:40px){}', - '@media (width:40px){}', - ].join(''), -); - -test( - 'should partially merge selectors in the opposite direction across at-rules', - processCSS, - [ - '@media (width:40px){h1{color:black}h2{color:black;font-weight:bold}}', - '@media (width:40px){h3{color:black;font-weight:bold}}', - ].join(''), - [ - '@media (width:40px){h1{color:black}h2,h3{color:black;font-weight:bold}}', - '@media (width:40px){}', - ].join('') - + +test( + 'should merge multiple values across at-rules', + processCSS, + [ + '@media (width:40px){h1{border:1px solid red;background-color:red;background-position:50% 100%}}', + '@media (width:40px){h1{border:1px solid red;background-color:red}}', + '@media (width:40px){h1{border:1px solid red}}', + ].join(''), + [ + '@media (width:40px){h1{border:1px solid red;background-color:red;background-position:50% 100%}}', + '@media (width:40px){}', + '@media (width:40px){}', + ].join('') +); + +test( + 'should partially merge selectors in the opposite direction across at-rules', + processCSS, + [ + '@media (width:40px){h1{color:black}h2{color:black;font-weight:bold}}', + '@media (width:40px){h3{color:black;font-weight:bold}}', + ].join(''), + [ + '@media (width:40px){h1{color:black}h2,h3{color:black;font-weight:bold}}', + '@media (width:40px){}', + ].join('') ); diff --git a/packages/postcss-merge-rules/src/index.js b/packages/postcss-merge-rules/src/index.js index a4f0b4bb8..6e877733f 100644 --- a/packages/postcss-merge-rules/src/index.js +++ b/packages/postcss-merge-rules/src/index.js @@ -5,15 +5,17 @@ import sameParent from 'lerna:cssnano-util-same-parent'; import ensureCompatibility from './lib/ensureCompatibility'; /** @type {string[]} */ -const prefixes = vendors.map(v => `-${v}-`); +const prefixes = vendors.map((v) => `-${v}-`); /** * @param {postcss.Declaration} a * @param {postcss.Declaration} b * @return {boolean} */ -function declarationIsEqual (a, b) { - return (a.important === b.important && a.prop === b.prop && a.value === b.value); +function declarationIsEqual(a, b) { + return ( + a.important === b.important && a.prop === b.prop && a.value === b.value + ); } /** @@ -21,8 +23,8 @@ function declarationIsEqual (a, b) { * @param {postcss.Declaration} decl * @return {number} */ -function indexOfDeclaration (array, decl) { - return array.findIndex(d => declarationIsEqual(d, decl)); +function indexOfDeclaration(array, decl) { + return array.findIndex((d) => declarationIsEqual(d, decl)); } /** @@ -32,11 +34,11 @@ function indexOfDeclaration (array, decl) { * @param {boolean} [not=false] * @return {postcss.Declaration[]} */ -function intersect (a, b, not) { - return a.filter((c) => { - const index = ~indexOfDeclaration(b, c); - return not ? !index : index; - }); +function intersect(a, b, not) { + return a.filter((c) => { + const index = ~indexOfDeclaration(b, c); + return not ? !index : index; + }); } /** @@ -44,37 +46,41 @@ function intersect (a, b, not) { * @param {postcss.Declaration[]} b * @return {boolean} */ -function sameDeclarationsAndOrder (a, b) { - if (a.length !== b.length) { - return false; - } - return a.every((d, index) => declarationIsEqual(d, b[index])); +function sameDeclarationsAndOrder(a, b) { + if (a.length !== b.length) { + return false; + } + return a.every((d, index) => declarationIsEqual(d, b[index])); } // Internet Explorer use :-ms-input-placeholder. // Microsoft Edge use ::-ms-input-placeholder. -const findMsInputPlaceholder = selector => ~selector.search(/-ms-input-placeholder/i); +const findMsInputPlaceholder = (selector) => + ~selector.search(/-ms-input-placeholder/i); /** * @param {string} selector * @return {string[]} */ -function filterPrefixes (selector) { - return prefixes.filter(prefix => selector.indexOf(prefix) !== -1); +function filterPrefixes(selector) { + return prefixes.filter((prefix) => selector.indexOf(prefix) !== -1); } -function sameVendor (selectorsA, selectorsB) { - let same = selectors => selectors.map(filterPrefixes).join(); - let findMsVendor = selectors => selectors.find(findMsInputPlaceholder); - return same(selectorsA) === same(selectorsB) && !(findMsVendor(selectorsA) && findMsVendor(selectorsB)); +function sameVendor(selectorsA, selectorsB) { + let same = (selectors) => selectors.map(filterPrefixes).join(); + let findMsVendor = (selectors) => selectors.find(findMsInputPlaceholder); + return ( + same(selectorsA) === same(selectorsB) && + !(findMsVendor(selectorsA) && findMsVendor(selectorsB)) + ); } /** * @param {string} selector * @return {boolean} */ -function noVendor (selector) { - return !filterPrefixes(selector).length; +function noVendor(selector) { + return !filterPrefixes(selector).length; } /** @@ -84,99 +90,98 @@ function noVendor (selector) { * @param {Object.=} compatibilityCache * @return {boolean} */ -function canMerge (ruleA, ruleB, browsers, compatibilityCache) { - const a = ruleA.selectors; - const b = ruleB.selectors; - - const selectors = a.concat(b); - - if (!ensureCompatibility(selectors, browsers, compatibilityCache)) { - return false; - } - - const parent = sameParent(ruleA, ruleB); - const {name} = ruleA.parent; - if (parent && name && ~name.indexOf('keyframes')) { - return false; - } - return parent && (selectors.every(noVendor) || sameVendor(a, b)); +function canMerge(ruleA, ruleB, browsers, compatibilityCache) { + const a = ruleA.selectors; + const b = ruleB.selectors; + + const selectors = a.concat(b); + + if (!ensureCompatibility(selectors, browsers, compatibilityCache)) { + return false; + } + + const parent = sameParent(ruleA, ruleB); + const { name } = ruleA.parent; + if (parent && name && ~name.indexOf('keyframes')) { + return false; + } + return parent && (selectors.every(noVendor) || sameVendor(a, b)); } /** * @param {postcss.Rule} rule * @return {postcss.Declaration[]} */ -function getDecls (rule) { - return rule.nodes.filter(node => node.type === 'decl'); +function getDecls(rule) { + return rule.nodes.filter((node) => node.type === 'decl'); } -const joinSelectors = (...rules) => rules.map(s => s.selector).join(); +const joinSelectors = (...rules) => rules.map((s) => s.selector).join(); -function ruleLength (...rules) { - return rules.map(r => r.nodes.length ? String(r) : '').join('').length; +function ruleLength(...rules) { + return rules.map((r) => (r.nodes.length ? String(r) : '')).join('').length; } /** * @param {string} prop * @return {{prefix: string, base:string, rest:string[]}} */ -function splitProp (prop) { - // Treat vendor prefixed properties as if they were unprefixed; - // moving them when combined with non-prefixed properties can - // cause issues. e.g. moving -webkit-background-clip when there - // is a background shorthand definition. - - const parts = prop.split('-'); - if (prop[0] !== '-') { - return { - prefix: '', - base: parts[0], - rest: parts.slice(1), - }; - } - // Don't split css variables - if (prop[1] === '-') { - return { - prefix: null, - base: null, - rest: [prop], - }; - } - // Found prefix +function splitProp(prop) { + // Treat vendor prefixed properties as if they were unprefixed; + // moving them when combined with non-prefixed properties can + // cause issues. e.g. moving -webkit-background-clip when there + // is a background shorthand definition. + + const parts = prop.split('-'); + if (prop[0] !== '-') { + return { + prefix: '', + base: parts[0], + rest: parts.slice(1), + }; + } + // Don't split css variables + if (prop[1] === '-') { return { - prefix: parts[1], - base: parts[2], - rest: parts.slice(3), + prefix: null, + base: null, + rest: [prop], }; - + } + // Found prefix + return { + prefix: parts[1], + base: parts[2], + rest: parts.slice(3), + }; } /** * @param {string} propA * @param {string} propB */ -function isConflictingProp (propA, propB) { - if (propA === propB) { - // Same specificity - return true; - } - const a = splitProp(propA); - const b = splitProp(propB); - // Don't resort css variables - if (!a.base && !b.base) { - return true; - } - // Different base; - if (a.base !== b.base) { - return false; - } - // Conflict if rest-count mismatches - if (a.rest.length !== b.rest.length) { - return true; - } +function isConflictingProp(propA, propB) { + if (propA === propB) { + // Same specificity + return true; + } + const a = splitProp(propA); + const b = splitProp(propB); + // Don't resort css variables + if (!a.base && !b.base) { + return true; + } + // Different base; + if (a.base !== b.base) { + return false; + } + // Conflict if rest-count mismatches + if (a.rest.length !== b.rest.length) { + return true; + } - // Conflict if rest parameters are equal (same but unprefixed) - return (a.rest.every((s, index) => b.rest[index] === s)); + // Conflict if rest parameters are equal (same but unprefixed) + return a.rest.every((s, index) => b.rest[index] === s); } /** @@ -184,22 +189,22 @@ function isConflictingProp (propA, propB) { * @param {postcss.Rule} second * @return {boolean} merged */ -function mergeParents (first, second) { - // Null check for detached rules - if (!first.parent || !second.parent) { - return false; - } - - // Check if parents share node - if (first.parent === second.parent) { - return false; - } - - // sameParent() already called by canMerge() - - second.remove(); - first.parent.append(second); - return true; +function mergeParents(first, second) { + // Null check for detached rules + if (!first.parent || !second.parent) { + return false; + } + + // Check if parents share node + if (first.parent === second.parent) { + return false; + } + + // sameParent() already called by canMerge() + + second.remove(); + first.parent.append(second); + return true; } /** @@ -207,113 +212,123 @@ function mergeParents (first, second) { * @param {postcss.Rule} second * @return {postcss.Rule} mergedRule */ -function partialMerge (first, second) { - let intersection = intersect(getDecls(first), getDecls(second)); - if (!intersection.length) { - return second; +function partialMerge(first, second) { + let intersection = intersect(getDecls(first), getDecls(second)); + if (!intersection.length) { + return second; + } + let nextRule = second.next(); + if (!nextRule) { + // Grab next cousin + const parentSibling = second.parent.next(); + nextRule = parentSibling && parentSibling.nodes && parentSibling.nodes[0]; + } + if (nextRule && nextRule.type === 'rule' && canMerge(second, nextRule)) { + let nextIntersection = intersect(getDecls(second), getDecls(nextRule)); + if (nextIntersection.length > intersection.length) { + mergeParents(second, nextRule); + first = second; + second = nextRule; + intersection = nextIntersection; } - let nextRule = second.next(); - if (!nextRule) { - // Grab next cousin - const parentSibling = second.parent.next(); - nextRule = parentSibling && parentSibling.nodes && parentSibling.nodes[0]; + } + + const firstDecls = getDecls(first); + + // Filter out intersections with later conflicts in First + intersection = intersection.filter((decl, intersectIndex) => { + const index = indexOfDeclaration(firstDecls, decl); + const nextConflictInFirst = firstDecls + .slice(index + 1) + .find((d) => isConflictingProp(d.prop, decl.prop)); + if (!nextConflictInFirst) { + return true; } - if (nextRule && nextRule.type === 'rule' && canMerge(second, nextRule)) { - let nextIntersection = intersect(getDecls(second), getDecls(nextRule)); - if (nextIntersection.length > intersection.length) { - mergeParents(second, nextRule); - first = second; second = nextRule; intersection = nextIntersection; - } + const nextConflictInIntersection = intersection + .slice(intersectIndex + 1) + .find((d) => isConflictingProp(d.prop, decl.prop)); + if (!nextConflictInIntersection) { + return false; } - - const firstDecls = getDecls(first); - - // Filter out intersections with later conflicts in First - intersection = intersection.filter((decl, intersectIndex) => { - const index = indexOfDeclaration(firstDecls, decl); - const nextConflictInFirst = firstDecls.slice(index + 1) - .find(d => isConflictingProp(d.prop, decl.prop)); - if (!nextConflictInFirst) { - return true; - } - const nextConflictInIntersection = intersection.slice(intersectIndex + 1) - .find(d => isConflictingProp(d.prop, decl.prop)); - if (!nextConflictInIntersection) { - return false; - } - if (declarationIsEqual(nextConflictInFirst, nextConflictInIntersection)) { - return true; - } - return false; - }); - - // Filter out intersections with previous conflicts in Second - const secondDecls = getDecls(second); - intersection = intersection.filter((decl) => { - const nextConflictIndex = secondDecls.findIndex(d => isConflictingProp(d.prop, decl.prop)); - if (nextConflictIndex === -1) { - return false; - } - if (!declarationIsEqual(secondDecls[nextConflictIndex], decl)) { - return false; - } - secondDecls.splice(nextConflictIndex, 1); - return true; - }); - - if (!intersection.length) { - // Nothing to merge - return second; + if (declarationIsEqual(nextConflictInFirst, nextConflictInIntersection)) { + return true; } - - const receivingBlock = second.clone(); - receivingBlock.selector = joinSelectors(first, second); - receivingBlock.nodes = []; - - // Rules with "all" declarations must be on top - if (intersection.some(declaration => declaration.prop.toLowerCase() === 'all')) { - second.parent.insertBefore(first, receivingBlock); - } else { - second.parent.insertBefore(second, receivingBlock); + return false; + }); + + // Filter out intersections with previous conflicts in Second + const secondDecls = getDecls(second); + intersection = intersection.filter((decl) => { + const nextConflictIndex = secondDecls.findIndex((d) => + isConflictingProp(d.prop, decl.prop) + ); + if (nextConflictIndex === -1) { + return false; } - - const firstClone = first.clone(); - const secondClone = second.clone(); - - /** - * @param {function(postcss.Declaration):void} callback - * @return {function(postcss.Declaration)} - */ - function moveDecl (callback) { - return (decl) => { - if (~indexOfDeclaration(intersection, decl)) { - callback.call(this, decl); - } - }; + if (!declarationIsEqual(secondDecls[nextConflictIndex], decl)) { + return false; + } + secondDecls.splice(nextConflictIndex, 1); + return true; + }); + + if (!intersection.length) { + // Nothing to merge + return second; + } + + const receivingBlock = second.clone(); + receivingBlock.selector = joinSelectors(first, second); + receivingBlock.nodes = []; + + // Rules with "all" declarations must be on top + if ( + intersection.some((declaration) => declaration.prop.toLowerCase() === 'all') + ) { + second.parent.insertBefore(first, receivingBlock); + } else { + second.parent.insertBefore(second, receivingBlock); + } + + const firstClone = first.clone(); + const secondClone = second.clone(); + + /** + * @param {function(postcss.Declaration):void} callback + * @return {function(postcss.Declaration)} + */ + function moveDecl(callback) { + return (decl) => { + if (~indexOfDeclaration(intersection, decl)) { + callback.call(this, decl); + } }; - firstClone.walkDecls(moveDecl(decl => { - decl.remove(); - receivingBlock.append(decl); - })); - secondClone.walkDecls(moveDecl(decl => decl.remove())); - const merged = ruleLength(firstClone, receivingBlock, secondClone); - const original = ruleLength(first, second); - if (merged < original) { - first.replaceWith(firstClone); - second.replaceWith(secondClone); - [firstClone, receivingBlock, secondClone].forEach(r => { - if (!r.nodes.length) { - r.remove(); - } - }); - if (!secondClone.parent) { - return receivingBlock; - } - return secondClone; - } else { - receivingBlock.remove(); - return second; + } + firstClone.walkDecls( + moveDecl((decl) => { + decl.remove(); + receivingBlock.append(decl); + }) + ); + secondClone.walkDecls(moveDecl((decl) => decl.remove())); + const merged = ruleLength(firstClone, receivingBlock, secondClone); + const original = ruleLength(first, second); + if (merged < original) { + first.replaceWith(firstClone); + second.replaceWith(secondClone); + [firstClone, receivingBlock, secondClone].forEach((r) => { + if (!r.nodes.length) { + r.remove(); + } + }); + if (!secondClone.parent) { + return receivingBlock; } + return secondClone; + } else { + receivingBlock.remove(); + return second; + } } /** @@ -321,62 +336,62 @@ function partialMerge (first, second) { * @param {Object.} compatibilityCache * @return {function(postcss.Rule)} */ -function selectorMerger (browsers, compatibilityCache) { - /** @type {postcss.Rule} */ - let cache = null; - return function (rule) { - // Prime the cache with the first rule, or alternately ensure that it is - // safe to merge both declarations before continuing - if (!cache || !canMerge(rule, cache, browsers, compatibilityCache)) { - cache = rule; - return; - } - // Ensure that we don't deduplicate the same rule; this is sometimes - // caused by a partial merge - if (cache === rule) { - cache = rule; - return; - } +function selectorMerger(browsers, compatibilityCache) { + /** @type {postcss.Rule} */ + let cache = null; + return function(rule) { + // Prime the cache with the first rule, or alternately ensure that it is + // safe to merge both declarations before continuing + if (!cache || !canMerge(rule, cache, browsers, compatibilityCache)) { + cache = rule; + return; + } + // Ensure that we don't deduplicate the same rule; this is sometimes + // caused by a partial merge + if (cache === rule) { + cache = rule; + return; + } - // Parents merge: check if the rules have same parents, but not same parent nodes - mergeParents(cache, rule); + // Parents merge: check if the rules have same parents, but not same parent nodes + mergeParents(cache, rule); - // Merge when declarations are exactly equal - // e.g. h1 { color: red } h2 { color: red } - if (sameDeclarationsAndOrder(getDecls(rule), getDecls(cache))) { - rule.selector = joinSelectors(cache, rule); - cache.remove(); - cache = rule; - return; - } - // Merge when both selectors are exactly equal - // e.g. a { color: blue } a { font-weight: bold } - if (cache.selector === rule.selector) { - const cached = getDecls(cache); - rule.walk(decl => { - if (~indexOfDeclaration(cached, decl)) { - return decl.remove(); - } - cache.append(decl); - }); - rule.remove(); - return; + // Merge when declarations are exactly equal + // e.g. h1 { color: red } h2 { color: red } + if (sameDeclarationsAndOrder(getDecls(rule), getDecls(cache))) { + rule.selector = joinSelectors(cache, rule); + cache.remove(); + cache = rule; + return; + } + // Merge when both selectors are exactly equal + // e.g. a { color: blue } a { font-weight: bold } + if (cache.selector === rule.selector) { + const cached = getDecls(cache); + rule.walk((decl) => { + if (~indexOfDeclaration(cached, decl)) { + return decl.remove(); } - // Partial merge: check if the rule contains a subset of the last; if - // so create a joined selector with the subset, if smaller. - cache = partialMerge(cache, rule); - }; + cache.append(decl); + }); + rule.remove(); + return; + } + // Partial merge: check if the rule contains a subset of the last; if + // so create a joined selector with the subset, if smaller. + cache = partialMerge(cache, rule); + }; } export default postcss.plugin('postcss-merge-rules', () => { - return (css, result) => { - const resultOpts = result.opts || {}; - const browsers = browserslist(null, { - stats: resultOpts.stats, - path: __dirname, - env: resultOpts.env, - }); - const compatibilityCache = {}; - css.walkRules(selectorMerger(browsers, compatibilityCache)); - }; + return (css, result) => { + const resultOpts = result.opts || {}; + const browsers = browserslist(null, { + stats: resultOpts.stats, + path: __dirname, + env: resultOpts.env, + }); + const compatibilityCache = {}; + css.walkRules(selectorMerger(browsers, compatibilityCache)); + }; }); diff --git a/packages/postcss-merge-rules/src/lib/ensureCompatibility.js b/packages/postcss-merge-rules/src/lib/ensureCompatibility.js index 0c001c495..3de1038ca 100644 --- a/packages/postcss-merge-rules/src/lib/ensureCompatibility.js +++ b/packages/postcss-merge-rules/src/lib/ensureCompatibility.js @@ -1,4 +1,4 @@ -import {isSupported} from 'caniuse-api'; +import { isSupported } from 'caniuse-api'; import selectorParser from 'postcss-selector-parser'; const simpleSelectorRe = /^#?[-._a-z0-9 ]+$/i; @@ -11,132 +11,136 @@ const cssFirstLine = 'css-first-line'; const cssInOutOfRange = 'css-in-out-of-range'; export const pseudoElements = { - ':active': cssSel2, - ':after': cssGencontent, - ':before': cssGencontent, - ':checked': cssSel3, - ':default': 'css-default-pseudo', - ':dir': 'css-dir-pseudo', - ':disabled': cssSel3, - ':empty': cssSel3, - ':enabled': cssSel3, - ':first-child': cssSel2, - ':first-letter': cssFirstLetter, - ':first-line': cssFirstLine, - ':first-of-type': cssSel3, - ':focus': cssSel2, - ':focus-within': 'css-focus-within', - ':has': 'css-has', - ':hover': cssSel2, - ':in-range': cssInOutOfRange, - ':indeterminate': 'css-indeterminate-pseudo', - ':lang': cssSel2, - ':last-child': cssSel3, - ':last-of-type': cssSel3, - ':matches': 'css-matches-pseudo', - ':not': cssSel3, - ':nth-child': cssSel3, - ':nth-last-child': cssSel3, - ':nth-last-of-type': cssSel3, - ':nth-of-type': cssSel3, - ':only-child': cssSel3, - ':only-of-type': cssSel3, - ':optional': 'css-optional-pseudo', - ':out-of-range': cssInOutOfRange, - ':placeholder-shown': 'css-placeholder-shown', - ':root': cssSel3, - ':target': cssSel3, - '::after': cssGencontent, - '::backdrop': 'dialog', - '::before': cssGencontent, - '::first-letter': cssFirstLetter, - '::first-line': cssFirstLine, - '::marker': 'css-marker-pseudo', - '::placeholder': 'css-placeholder', - '::selection': 'css-selection', + ':active': cssSel2, + ':after': cssGencontent, + ':before': cssGencontent, + ':checked': cssSel3, + ':default': 'css-default-pseudo', + ':dir': 'css-dir-pseudo', + ':disabled': cssSel3, + ':empty': cssSel3, + ':enabled': cssSel3, + ':first-child': cssSel2, + ':first-letter': cssFirstLetter, + ':first-line': cssFirstLine, + ':first-of-type': cssSel3, + ':focus': cssSel2, + ':focus-within': 'css-focus-within', + ':has': 'css-has', + ':hover': cssSel2, + ':in-range': cssInOutOfRange, + ':indeterminate': 'css-indeterminate-pseudo', + ':lang': cssSel2, + ':last-child': cssSel3, + ':last-of-type': cssSel3, + ':matches': 'css-matches-pseudo', + ':not': cssSel3, + ':nth-child': cssSel3, + ':nth-last-child': cssSel3, + ':nth-last-of-type': cssSel3, + ':nth-of-type': cssSel3, + ':only-child': cssSel3, + ':only-of-type': cssSel3, + ':optional': 'css-optional-pseudo', + ':out-of-range': cssInOutOfRange, + ':placeholder-shown': 'css-placeholder-shown', + ':root': cssSel3, + ':target': cssSel3, + '::after': cssGencontent, + '::backdrop': 'dialog', + '::before': cssGencontent, + '::first-letter': cssFirstLetter, + '::first-line': cssFirstLine, + '::marker': 'css-marker-pseudo', + '::placeholder': 'css-placeholder', + '::selection': 'css-selection', }; -function isCssMixin (selector) { - return selector[selector.length - 1] === ':'; +function isCssMixin(selector) { + return selector[selector.length - 1] === ':'; } const isSupportedCache = {}; // Move to util in future -function isSupportedCached (feature, browsers) { - const key = JSON.stringify({feature, browsers}); - let result = isSupportedCache[key]; +function isSupportedCached(feature, browsers) { + const key = JSON.stringify({ feature, browsers }); + let result = isSupportedCache[key]; - if (!result) { - result = isSupported(feature, browsers); - isSupportedCache[key] = result; - } + if (!result) { + result = isSupported(feature, browsers); + isSupportedCache[key] = result; + } - return result; + return result; } -export default function ensureCompatibility (selectors, browsers, compatibilityCache) { - // Should not merge mixins - if (selectors.some(isCssMixin)) { - return false; +export default function ensureCompatibility( + selectors, + browsers, + compatibilityCache +) { + // Should not merge mixins + if (selectors.some(isCssMixin)) { + return false; + } + return selectors.every((selector) => { + if (simpleSelectorRe.test(selector)) { + return true; + } + if (compatibilityCache && selector in compatibilityCache) { + return compatibilityCache[selector]; } - return selectors.every(selector => { - if (simpleSelectorRe.test(selector)) { - return true; + let compatible = true; + selectorParser((ast) => { + ast.walk((node) => { + const { type, value } = node; + if (type === 'pseudo') { + const entry = pseudoElements[value]; + if (entry && compatible) { + compatible = isSupportedCached(entry, browsers); + } } - if (compatibilityCache && (selector in compatibilityCache)) { - return compatibilityCache[selector]; + if (type === 'combinator') { + if (~value.indexOf('~')) { + compatible = isSupportedCached(cssSel3, browsers); + } + if (~value.indexOf('>') || ~value.indexOf('+')) { + compatible = isSupportedCached(cssSel2, browsers); + } } - let compatible = true; - selectorParser(ast => { - ast.walk(node => { - const {type, value} = node; - if (type === 'pseudo') { - const entry = pseudoElements[value]; - if (entry && compatible) { - compatible = isSupportedCached(entry, browsers); - } - } - if (type === 'combinator') { - if (~value.indexOf('~')) { - compatible = isSupportedCached(cssSel3, browsers); - } - if (~value.indexOf('>') || ~value.indexOf('+')) { - compatible = isSupportedCached(cssSel2, browsers); - } - } - if (type === 'attribute' && node.attribute) { - // [foo] - if (!node.operator) { - compatible = isSupportedCached(cssSel2, browsers); - } + if (type === 'attribute' && node.attribute) { + // [foo] + if (!node.operator) { + compatible = isSupportedCached(cssSel2, browsers); + } - if (value) { - // [foo="bar"], [foo~="bar"], [foo|="bar"] - if (~['=', '~=', '|='].indexOf(node.operator)) { - compatible = isSupportedCached(cssSel2, browsers); - } - // [foo^="bar"], [foo$="bar"], [foo*="bar"] - if (~['^=', '$=', '*='].indexOf(node.operator)) { - compatible = isSupportedCached(cssSel3, browsers); - } - } + if (value) { + // [foo="bar"], [foo~="bar"], [foo|="bar"] + if (~['=', '~=', '|='].indexOf(node.operator)) { + compatible = isSupportedCached(cssSel2, browsers); + } + // [foo^="bar"], [foo$="bar"], [foo*="bar"] + if (~['^=', '$=', '*='].indexOf(node.operator)) { + compatible = isSupportedCached(cssSel3, browsers); + } + } - // [foo="bar" i] - if (node.insensitive) { - compatible = isSupportedCached('css-case-insensitive', browsers); - } - } - if (!compatible) { - // If this node was not compatible, - // break out early from walking the rest - return false; - } - }); - }).processSync(selector); - if (compatibilityCache) { - compatibilityCache[selector] = compatible; + // [foo="bar" i] + if (node.insensitive) { + compatible = isSupportedCached('css-case-insensitive', browsers); + } } - return compatible; - }); + if (!compatible) { + // If this node was not compatible, + // break out early from walking the rest + return false; + } + }); + }).processSync(selector); + if (compatibilityCache) { + compatibilityCache[selector] = compatible; + } + return compatible; + }); } diff --git a/packages/postcss-minify-font-values/src/__tests__/index.js b/packages/postcss-minify-font-values/src/__tests__/index.js index badc24e84..860c2f6e9 100644 --- a/packages/postcss-minify-font-values/src/__tests__/index.js +++ b/packages/postcss-minify-font-values/src/__tests__/index.js @@ -1,402 +1,398 @@ import test from 'ava'; import plugin from '..'; import { - usePostCSSPlugin, - processCSSFactory, - processCSSWithPresetFactory, + usePostCSSPlugin, + processCSSFactory, + processCSSWithPresetFactory, } from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); const { - processCSS: withDefaultPreset, - passthroughCSS: passthroughDefault, + processCSS: withDefaultPreset, + passthroughCSS: passthroughDefault, } = processCSSWithPresetFactory('default'); test( - 'should not unquote font names with a leading number', - passthroughCSS, - 'h1{font-family:"11880-icons"!important;}' + 'should not unquote font names with a leading number', + passthroughCSS, + 'h1{font-family:"11880-icons"!important;}' ); test( - 'should unquote font names', - processCSS, - 'h1{font-family:"Helvetica Neue"}', - 'h1{font-family:Helvetica Neue}' + 'should unquote font names', + processCSS, + 'h1{font-family:"Helvetica Neue"}', + 'h1{font-family:Helvetica Neue}' ); test( - 'should unquote font names with one character name', - processCSS, - 'h1{font-family:"A"}', - 'h1{font-family:A}' + 'should unquote font names with one character name', + processCSS, + 'h1{font-family:"A"}', + 'h1{font-family:A}' ); test( - 'should unquote font names with space at the start', - processCSS, - 'h1{font-family:" Helvetica Neue"}', - 'h1{font-family:\\ Helvetica Neue}' + 'should unquote font names with space at the start', + processCSS, + 'h1{font-family:" Helvetica Neue"}', + 'h1{font-family:\\ Helvetica Neue}' ); test( - 'should unquote font names with space at the end', - processCSS, - 'h1{font-family:"Helvetica Neue "}', - 'h1{font-family:Helvetica Neue\\ }' + 'should unquote font names with space at the end', + processCSS, + 'h1{font-family:"Helvetica Neue "}', + 'h1{font-family:Helvetica Neue\\ }' ); test( - 'should unquote and join identifiers with a slash, if numeric', - processCSS, - 'h1{font-family:"Bond 007"}', - 'h1{font-family:Bond\\ 007}' + 'should unquote and join identifiers with a slash, if numeric', + processCSS, + 'h1{font-family:"Bond 007"}', + 'h1{font-family:Bond\\ 007}' ); test( - 'should not unquote font name contains generic font family word at start', - passthroughCSS, - 'h1{font-family:"serif custom font"}' + 'should not unquote font name contains generic font family word at start', + passthroughCSS, + 'h1{font-family:"serif custom font"}' ); test( - 'should not unquote font name contains generic font family word at middle', - passthroughCSS, - 'h1{font-family:"custom serif font"}' + 'should not unquote font name contains generic font family word at middle', + passthroughCSS, + 'h1{font-family:"custom serif font"}' ); test( - 'should not unquote font name contains generic font family word at end', - passthroughCSS, - 'h1{font-family:"custom font serif"}' + 'should not unquote font name contains generic font family word at end', + passthroughCSS, + 'h1{font-family:"custom font serif"}' ); test( - 'should not unquote font name contains monospace generic font family word', - passthroughCSS, - 'h1{font-family:"Monospace Custom Font"}' + 'should not unquote font name contains monospace generic font family word', + passthroughCSS, + 'h1{font-family:"Monospace Custom Font"}' ); test( - 'should not unquote font name contains monospace generic font family word with spaces', - passthroughCSS, - 'h1{font-family:" Monospace Custom Font "}' + 'should not unquote font name contains monospace generic font family word with spaces', + passthroughCSS, + 'h1{font-family:" Monospace Custom Font "}' ); test( - 'should unquote and join identifiers with a slash', - passthroughCSS, - 'h1{font-family:Ahem\\!}' + 'should unquote and join identifiers with a slash', + passthroughCSS, + 'h1{font-family:Ahem\\!}' ); test( - 'should unquote with escaped `/` character', - passthroughCSS, - 'h1{font-family:Red\\/Black}' + 'should unquote with escaped `/` character', + passthroughCSS, + 'h1{font-family:Red\\/Black}' ); test( - 'should unquote with multiple escaped ` ` character', - passthroughCSS, - 'h1{font-family:Hawaii \\ \\ \\ \\ \\ \\\\35}' + 'should unquote with multiple escaped ` ` character', + passthroughCSS, + 'h1{font-family:Hawaii \\ \\ \\ \\ \\ \\\\35}' ); test( - 'should not unquote if it would produce a bigger identifier', - passthroughCSS, - 'h1{font-family:"Call 0118 999 881 999 119 725 3"}' + 'should not unquote if it would produce a bigger identifier', + passthroughCSS, + 'h1{font-family:"Call 0118 999 881 999 119 725 3"}' ); test( - 'should not unquote font names if they contain keywords', - passthroughCSS, - 'h1{font-family:"slab serif"}' + 'should not unquote font names if they contain keywords', + passthroughCSS, + 'h1{font-family:"slab serif"}' ); test( - 'should not unquote font names with multiple \\', - passthroughCSS, - 'h1{font-family:"\\5FAE\\8F6F\\96C5\\9ED1"}' + 'should not unquote font names with multiple \\', + passthroughCSS, + 'h1{font-family:"\\5FAE\\8F6F\\96C5\\9ED1"}' ); test( - 'should not unquote font names with multiple \\', - passthroughCSS, - 'h1{font-family:"\\5B8B\\4F53"}' + 'should not unquote font names with multiple \\', + passthroughCSS, + 'h1{font-family:"\\5B8B\\4F53"}' ); test( - 'should minimise space inside a legal font name', - processCSS, - 'h1{font-family:Lucida Grande}', - 'h1{font-family:Lucida Grande}' + 'should minimise space inside a legal font name', + processCSS, + 'h1{font-family:Lucida Grande}', + 'h1{font-family:Lucida Grande}' ); test( - 'should minimise space around a list of font names', - processCSS, - 'h1{font-family:Arial, Helvetica, sans-serif}', - 'h1{font-family:Arial,Helvetica,sans-serif}' + 'should minimise space around a list of font names', + processCSS, + 'h1{font-family:Arial, Helvetica, sans-serif}', + 'h1{font-family:Arial,Helvetica,sans-serif}' ); test( - 'should dedupe font family names', - processCSS, - 'h1{font-family:Helvetica,Arial,Helvetica,sans-serif}', - 'h1{font-family:Helvetica,Arial,sans-serif}' + 'should dedupe font family names', + processCSS, + 'h1{font-family:Helvetica,Arial,Helvetica,sans-serif}', + 'h1{font-family:Helvetica,Arial,sans-serif}' ); test( - 'should dedupe lowercase generic font family name', - processCSS, - 'h1{font-family:Helvetica,Arial,sans-serif,sans-serif}', - 'h1{font-family:Helvetica,Arial,sans-serif}' + 'should dedupe lowercase generic font family name', + processCSS, + 'h1{font-family:Helvetica,Arial,sans-serif,sans-serif}', + 'h1{font-family:Helvetica,Arial,sans-serif}' ); test( - 'should dedupe uppercase generic font family name', - processCSS, - 'h1{font-family:Helvetica,Arial,SANS-SERIF,SANS-SERIF}', - 'h1{font-family:Helvetica,Arial,SANS-SERIF}' + 'should dedupe uppercase generic font family name', + processCSS, + 'h1{font-family:Helvetica,Arial,SANS-SERIF,SANS-SERIF}', + 'h1{font-family:Helvetica,Arial,SANS-SERIF}' ); test( - 'should discard the rest of the declaration after a keyword', - processCSS, - 'h1{font-family:Arial,sans-serif,Arial,"Trebuchet MS"}', - 'h1{font-family:Arial,sans-serif}', - {removeAfterKeyword: true} + 'should discard the rest of the declaration after a keyword', + processCSS, + 'h1{font-family:Arial,sans-serif,Arial,"Trebuchet MS"}', + 'h1{font-family:Arial,sans-serif}', + { removeAfterKeyword: true } ); test( - 'should convert the font shorthand property', - processCSS, - 'h1{font:italic small-caps normal 13px/150% "Helvetica Neue", sans-serif}', - 'h1{font:italic small-caps normal 13px/150% Helvetica Neue,sans-serif}' + 'should convert the font shorthand property', + processCSS, + 'h1{font:italic small-caps normal 13px/150% "Helvetica Neue", sans-serif}', + 'h1{font:italic small-caps normal 13px/150% Helvetica Neue,sans-serif}' ); test( - 'should convert shorthand with zero unit line height', - processCSS, - 'h1{font:italic small-caps normal 13px/1.5 "Helvetica Neue", sans-serif}', - 'h1{font:italic small-caps normal 13px/1.5 Helvetica Neue,sans-serif}' + 'should convert shorthand with zero unit line height', + processCSS, + 'h1{font:italic small-caps normal 13px/1.5 "Helvetica Neue", sans-serif}', + 'h1{font:italic small-caps normal 13px/1.5 Helvetica Neue,sans-serif}' ); test( - 'should convert the font shorthand property with upperlower keyword, unquoted', - processCSS, - 'h1{font:italic Helvetica Neue,sans-serif,Arial}', - 'h1{font:italic Helvetica Neue,sans-serif}', - {removeAfterKeyword: true} + 'should convert the font shorthand property with upperlower keyword, unquoted', + processCSS, + 'h1{font:italic Helvetica Neue,sans-serif,Arial}', + 'h1{font:italic Helvetica Neue,sans-serif}', + { removeAfterKeyword: true } ); test( - 'should convert the font shorthand property with uppercase keyword, unquoted', - processCSS, - 'h1{font:italic Helvetica Neue,SANS-SERIF,Arial}', - 'h1{font:italic Helvetica Neue,SANS-SERIF}', - {removeAfterKeyword: true} + 'should convert the font shorthand property with uppercase keyword, unquoted', + processCSS, + 'h1{font:italic Helvetica Neue,SANS-SERIF,Arial}', + 'h1{font:italic Helvetica Neue,SANS-SERIF}', + { removeAfterKeyword: true } ); test( - 'should join identifiers in the shorthand property', - processCSS, - 'h1{font:italic "Bond 007",sans-serif}', - 'h1{font:italic Bond\\ 007,sans-serif}' + 'should join identifiers in the shorthand property', + processCSS, + 'h1{font:italic "Bond 007",sans-serif}', + 'h1{font:italic Bond\\ 007,sans-serif}' ); test( - 'should join non-digit identifiers in the shorthand property', - processCSS, - 'h1{font:italic "Bond !",serif}', - 'h1{font:italic Bond \\!,serif}' + 'should join non-digit identifiers in the shorthand property', + processCSS, + 'h1{font:italic "Bond !",serif}', + 'h1{font:italic Bond \\!,serif}' ); test( - 'should correctly escape special characters at the start', - processCSS, - 'h1{font-family:"$42"}', - 'h1{font-family:\\$42}' + 'should correctly escape special characters at the start', + processCSS, + 'h1{font-family:"$42"}', + 'h1{font-family:\\$42}' ); test( - 'should correctly escape special characters at the end', - passthroughCSS, - 'h1{font-family:Helvetica Neue\\ }' + 'should correctly escape special characters at the end', + passthroughCSS, + 'h1{font-family:Helvetica Neue\\ }' ); test( - 'should correctly escape multiple special with numbers font name', - passthroughCSS, - 'h1{font-family:\\31 \\ 2\\ 3\\ 4\\ 5}' + 'should correctly escape multiple special with numbers font name', + passthroughCSS, + 'h1{font-family:\\31 \\ 2\\ 3\\ 4\\ 5}' ); test( - 'should correctly escape multiple ` `', - passthroughCSS, - 'h1{font-family:f \\ \\ \\ \\ o \\ \\ \\ \\ o}' + 'should correctly escape multiple ` `', + passthroughCSS, + 'h1{font-family:f \\ \\ \\ \\ o \\ \\ \\ \\ o}' ); test( - 'should correctly escape only spaces font name', - passthroughCSS, - 'h1{font-family:\\ \\ \\ }' + 'should correctly escape only spaces font name', + passthroughCSS, + 'h1{font-family:\\ \\ \\ }' ); test( - 'should correctly escape only two spaces font name', - passthroughCSS, - 'h1{font-family:" "}' + 'should correctly escape only two spaces font name', + passthroughCSS, + 'h1{font-family:" "}' ); test( - 'should correctly escape only spaces font name with quotes', - passthroughCSS, - 'h1{font-family:" "}' + 'should correctly escape only spaces font name with quotes', + passthroughCSS, + 'h1{font-family:" "}' ); test( - 'should unquote multiple escape `[` characters', - passthroughCSS, - 'h1{font-family:"STHeiti Light [STXihei]"}' + 'should unquote multiple escape `[` characters', + passthroughCSS, + 'h1{font-family:"STHeiti Light [STXihei]"}' ); test( - 'should not escape legal characters', - passthroughDefault, - 'h1{font-family:€42}' + 'should not escape legal characters', + passthroughDefault, + 'h1{font-family:€42}' ); test( - 'should not join identifiers in the shorthand property', - passthroughDefault, - 'h1{font:italic "Bond 007 008 009",sans-serif}' + 'should not join identifiers in the shorthand property', + passthroughDefault, + 'h1{font:italic "Bond 007 008 009",sans-serif}' ); test( - 'should escape special characters if unquoting', - withDefaultPreset, - 'h1{font-family:"Ahem!"}', - 'h1{font-family:Ahem\\!}' + 'should escape special characters if unquoting', + withDefaultPreset, + 'h1{font-family:"Ahem!"}', + 'h1{font-family:Ahem\\!}' ); test( - 'should not escape multiple special characters', - passthroughDefault, - 'h1{font-family:"Ahem!!"}' + 'should not escape multiple special characters', + passthroughDefault, + 'h1{font-family:"Ahem!!"}' ); test( - 'should not mangle legal unquoted values', - passthroughDefault, - 'h1{font-family:\\$42}' + 'should not mangle legal unquoted values', + passthroughDefault, + 'h1{font-family:\\$42}' ); test( - 'should not mangle font names', - passthroughDefault, - 'h1{font-family:Glyphicons Halflings}' + 'should not mangle font names', + passthroughDefault, + 'h1{font-family:Glyphicons Halflings}' ); test( - 'should not mangle font names (2)', - passthroughCSS, - 'h1{font-family:FF Din Pro,FF Din Pro Medium}' + 'should not mangle font names (2)', + passthroughCSS, + 'h1{font-family:FF Din Pro,FF Din Pro Medium}' ); test( - 'should handle rem values', - processCSS, - 'h1{font:bold 2.2rem/.9 "Open Sans Condensed", sans-serif}', - 'h1{font:700 2.2rem/.9 Open Sans Condensed,sans-serif}' + 'should handle rem values', + processCSS, + 'h1{font:bold 2.2rem/.9 "Open Sans Condensed", sans-serif}', + 'h1{font:700 2.2rem/.9 Open Sans Condensed,sans-serif}' ); test( - 'should pass through when it doesn\'t find a font property', - passthroughCSS, - 'h1{color:black;text-decoration:none}' + "should pass through when it doesn't find a font property", + passthroughCSS, + 'h1{color:black;text-decoration:none}' ); test( - 'should not remove duplicates', - passthroughCSS, - 'h1{font-family:Helvetica,Helvetica}', - {removeDuplicates: false} + 'should not remove duplicates', + passthroughCSS, + 'h1{font-family:Helvetica,Helvetica}', + { removeDuplicates: false } ); test( - 'should not remove after keyword', - passthroughCSS, - 'h1{font-family:serif,Times}', - {removeAfterKeyword: false} + 'should not remove after keyword', + passthroughCSS, + 'h1{font-family:serif,Times}', + { removeAfterKeyword: false } ); test( - 'should not remove quotes', - passthroughCSS, - 'h1{font-family:"Glyphicons Halflings","Arial"}', - {removeQuotes: false} + 'should not remove quotes', + passthroughCSS, + 'h1{font-family:"Glyphicons Halflings","Arial"}', + { removeQuotes: false } ); test( - 'should not dedupe lower case monospace', - passthroughCSS, - 'font-family:monospace,monospace' + 'should not dedupe lower case monospace', + passthroughCSS, + 'font-family:monospace,monospace' ); test( - 'should not dedupe uppercase monospace', - passthroughCSS, - 'font-family:MONOSPACE,MONOSPACE' + 'should not dedupe uppercase monospace', + passthroughCSS, + 'font-family:MONOSPACE,MONOSPACE' ); test( - 'should not dedupe monospace (2)', - passthroughCSS, - 'font:italic small-caps normal 13px/150% monospace,monospace' + 'should not dedupe monospace (2)', + passthroughCSS, + 'font:italic small-caps normal 13px/150% monospace,monospace' ); test( - 'should not mangle custom props', - passthroughCSS, - ':root{--sans:Helvetica}header{font-family:var(--sans)}' + 'should not mangle custom props', + passthroughCSS, + ':root{--sans:Helvetica}header{font-family:var(--sans)}' ); test( - 'should minify lowercase font-weight', - processCSS, - 'h1{font-weight:bold}', - 'h1{font-weight:700}' + 'should minify lowercase font-weight', + processCSS, + 'h1{font-weight:bold}', + 'h1{font-weight:700}' ); test( - 'should minify uppercase font-weight', - processCSS, - 'h1{font-weight:BOLD}', - 'h1{font-weight:700}' + 'should minify uppercase font-weight', + processCSS, + 'h1{font-weight:BOLD}', + 'h1{font-weight:700}' ); test( - 'should pass through unrelated font lowercase properties', - passthroughCSS, - 'h1{font-style:normal}' + 'should pass through unrelated font lowercase properties', + passthroughCSS, + 'h1{font-style:normal}' ); test( - 'should pass through unrelated font uppercase properties', - passthroughCSS, - 'h1{font-style:NORMAL}' + 'should pass through unrelated font uppercase properties', + passthroughCSS, + 'h1{font-style:NORMAL}' ); test( - 'should minify font property', - processCSS, - 'h1{font: BOLD italic 20px Times New Roman;}', - 'h1{font: 700 italic 20px Times New Roman;}', + 'should minify font property', + processCSS, + 'h1{font: BOLD italic 20px Times New Roman;}', + 'h1{font: 700 italic 20px Times New Roman;}' ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-minify-font-values/src/__tests__/minify-family.js b/packages/postcss-minify-font-values/src/__tests__/minify-family.js index 74e17ec1e..6a1993993 100644 --- a/packages/postcss-minify-font-values/src/__tests__/minify-family.js +++ b/packages/postcss-minify-font-values/src/__tests__/minify-family.js @@ -2,96 +2,96 @@ import test from 'ava'; import minifyFamily from '../lib/minify-family'; const tests = [ - { - message: 'Should strip quotes for names without keywords', - options: { - removeQuotes: true, - }, - fixture: [ - {type: 'space', value: ' '}, - {type: 'word', value: 'Times'}, - {type: 'space', value: ' '}, - {type: 'word', value: 'new'}, - {type: 'space', value: ' '}, - {type: 'word', value: 'Roman'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'word', value: 'sans-serif'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'string', quote: '"', value: 'serif'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'string', quote: '"', value: 'Roboto Plus'}, - {type: 'div', value: ',', before: ' ', after: ' '}, - {type: 'word', value: 'Georgia'}, - {type: 'space', value: ' '}, - ], - expected: [ - { - type: 'word', - value: 'Times new Roman,sans-serif,"serif",Roboto Plus,Georgia', - }, - ], + { + message: 'Should strip quotes for names without keywords', + options: { + removeQuotes: true, }, - { - message: 'Should remove fonts after keywords', - options: { - removeAfterKeyword: true, - }, - fixture: [ - {type: 'space', value: ' '}, - {type: 'word', value: 'Times'}, - {type: 'space', value: ' '}, - {type: 'word', value: 'new'}, - {type: 'space', value: ' '}, - {type: 'word', value: 'Roman'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'string', quote: '"', value: 'serif'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'word', value: 'sans-serif'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'string', quote: '"', value: 'Roboto Plus'}, - {type: 'div', value: ',', before: ' ', after: ' '}, - {type: 'word', value: 'Georgia'}, - {type: 'space', value: ' '}, - ], - expected: [ - { - type: 'word', - value: 'Times new Roman,"serif",sans-serif', - }, - ], + fixture: [ + { type: 'space', value: ' ' }, + { type: 'word', value: 'Times' }, + { type: 'space', value: ' ' }, + { type: 'word', value: 'new' }, + { type: 'space', value: ' ' }, + { type: 'word', value: 'Roman' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'word', value: 'sans-serif' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'string', quote: '"', value: 'serif' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'string', quote: '"', value: 'Roboto Plus' }, + { type: 'div', value: ',', before: ' ', after: ' ' }, + { type: 'word', value: 'Georgia' }, + { type: 'space', value: ' ' }, + ], + expected: [ + { + type: 'word', + value: 'Times new Roman,sans-serif,"serif",Roboto Plus,Georgia', + }, + ], + }, + { + message: 'Should remove fonts after keywords', + options: { + removeAfterKeyword: true, }, - { - message: 'Should dublicates', - options: { - removeQuotes: true, - removeDuplicates: true, - }, - fixture: [ - {type: 'space', value: ' '}, - {type: 'word', value: 'Roman'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'string', quote: '"', value: 'serif'}, - {type: 'word', value: 'Roman'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'word', value: 'serif'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'string', quote: '"', value: 'Roman'}, - {type: 'div', value: ',', before: ' ', after: ' '}, - {type: 'word', value: 'Georgia'}, - {type: 'space', value: ' '}, - ], - expected: [ - { - type: 'word', - value: 'Roman,"serif",serif,Georgia', - }, - ], + fixture: [ + { type: 'space', value: ' ' }, + { type: 'word', value: 'Times' }, + { type: 'space', value: ' ' }, + { type: 'word', value: 'new' }, + { type: 'space', value: ' ' }, + { type: 'word', value: 'Roman' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'string', quote: '"', value: 'serif' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'word', value: 'sans-serif' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'string', quote: '"', value: 'Roboto Plus' }, + { type: 'div', value: ',', before: ' ', after: ' ' }, + { type: 'word', value: 'Georgia' }, + { type: 'space', value: ' ' }, + ], + expected: [ + { + type: 'word', + value: 'Times new Roman,"serif",sans-serif', + }, + ], + }, + { + message: 'Should dublicates', + options: { + removeQuotes: true, + removeDuplicates: true, }, + fixture: [ + { type: 'space', value: ' ' }, + { type: 'word', value: 'Roman' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'string', quote: '"', value: 'serif' }, + { type: 'word', value: 'Roman' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'word', value: 'serif' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'string', quote: '"', value: 'Roman' }, + { type: 'div', value: ',', before: ' ', after: ' ' }, + { type: 'word', value: 'Georgia' }, + { type: 'space', value: ' ' }, + ], + expected: [ + { + type: 'word', + value: 'Roman,"serif",serif,Georgia', + }, + ], + }, ]; test('minify-family', (t) => { - t.plan(tests.length); - tests.forEach(({fixture, options, expected, message}) => { - t.deepEqual(minifyFamily(fixture, options), expected, message); - }); + t.plan(tests.length); + tests.forEach(({ fixture, options, expected, message }) => { + t.deepEqual(minifyFamily(fixture, options), expected, message); + }); }); diff --git a/packages/postcss-minify-font-values/src/__tests__/minify-font.js b/packages/postcss-minify-font-values/src/__tests__/minify-font.js index 46bd2b563..2eaa08996 100644 --- a/packages/postcss-minify-font-values/src/__tests__/minify-font.js +++ b/packages/postcss-minify-font-values/src/__tests__/minify-font.js @@ -2,37 +2,37 @@ import test from 'ava'; import minifyFont from '../lib/minify-font'; const tests = [ - { - options: {}, - fixture: [ - {type: 'word', value: 'bold'}, - {type: 'space', value: ' '}, - {type: 'word', value: 'italic'}, - {type: 'space', value: ' \t '}, - {type: 'word', value: '20px'}, - {type: 'space', value: ' \n '}, - {type: 'word', value: 'Times'}, - {type: 'space', value: ' '}, - {type: 'word', value: 'New'}, - {type: 'space', value: ' \t '}, - {type: 'word', value: 'Roman'}, - {type: 'div', value: ',', before: '', after: ' '}, - {type: 'word', value: 'serif'}, - ], - expected: [ - {type: 'word', value: '700'}, - {type: 'space', value: ' '}, - {type: 'word', value: 'italic'}, - {type: 'space', value: ' \t '}, - {type: 'word', value: '20px'}, - {type: 'space', value: ' \n '}, - {type: 'word', value: 'Times New Roman,serif'}, - ], - }, + { + options: {}, + fixture: [ + { type: 'word', value: 'bold' }, + { type: 'space', value: ' ' }, + { type: 'word', value: 'italic' }, + { type: 'space', value: ' \t ' }, + { type: 'word', value: '20px' }, + { type: 'space', value: ' \n ' }, + { type: 'word', value: 'Times' }, + { type: 'space', value: ' ' }, + { type: 'word', value: 'New' }, + { type: 'space', value: ' \t ' }, + { type: 'word', value: 'Roman' }, + { type: 'div', value: ',', before: '', after: ' ' }, + { type: 'word', value: 'serif' }, + ], + expected: [ + { type: 'word', value: '700' }, + { type: 'space', value: ' ' }, + { type: 'word', value: 'italic' }, + { type: 'space', value: ' \t ' }, + { type: 'word', value: '20px' }, + { type: 'space', value: ' \n ' }, + { type: 'word', value: 'Times New Roman,serif' }, + ], + }, ]; test('minify-font', (t) => { - tests.forEach(({fixture, options, expected}) => { - t.deepEqual(minifyFont(fixture, options), expected); - }); + tests.forEach(({ fixture, options, expected }) => { + t.deepEqual(minifyFont(fixture, options), expected); + }); }); diff --git a/packages/postcss-minify-font-values/src/__tests__/minify-weight.js b/packages/postcss-minify-font-values/src/__tests__/minify-weight.js index 7771575a8..1cb226197 100644 --- a/packages/postcss-minify-font-values/src/__tests__/minify-weight.js +++ b/packages/postcss-minify-font-values/src/__tests__/minify-weight.js @@ -2,8 +2,8 @@ import test from 'ava'; import minifyWeight from '../lib/minify-weight'; test('minify-weight', (t) => { - t.is(minifyWeight('normal'), '400', 'should convert normal -> 400'); - t.is(minifyWeight('bold'), '700', 'should convert bold -> 700'); - t.is(minifyWeight('lighter'), 'lighter', 'shouldn\'t convert lighter'); - t.is(minifyWeight('bolder'), 'bolder', 'shouldn\'t convert bolder'); + t.is(minifyWeight('normal'), '400', 'should convert normal -> 400'); + t.is(minifyWeight('bold'), '700', 'should convert bold -> 700'); + t.is(minifyWeight('lighter'), 'lighter', "shouldn't convert lighter"); + t.is(minifyWeight('bolder'), 'bolder', "shouldn't convert bolder"); }); diff --git a/packages/postcss-minify-font-values/src/index.js b/packages/postcss-minify-font-values/src/index.js index 75784a363..0bdaac9cc 100644 --- a/packages/postcss-minify-font-values/src/index.js +++ b/packages/postcss-minify-font-values/src/index.js @@ -4,29 +4,33 @@ import minifyWeight from './lib/minify-weight'; import minifyFamily from './lib/minify-family'; import minifyFont from './lib/minify-font'; -function transform (opts, decl) { - let tree; - let prop = decl.prop.toLowerCase(); +function transform(opts, decl) { + let tree; + let prop = decl.prop.toLowerCase(); - if (prop === 'font-weight') { - decl.value = minifyWeight(decl.value); - } else if (prop === 'font-family') { - tree = valueParser(decl.value); - tree.nodes = minifyFamily(tree.nodes, opts); - decl.value = tree.toString(); - } else if (prop === 'font') { - tree = valueParser(decl.value); - tree.nodes = minifyFont(tree.nodes, opts); - decl.value = tree.toString(); - } + if (prop === 'font-weight') { + decl.value = minifyWeight(decl.value); + } else if (prop === 'font-family') { + tree = valueParser(decl.value); + tree.nodes = minifyFamily(tree.nodes, opts); + decl.value = tree.toString(); + } else if (prop === 'font') { + tree = valueParser(decl.value); + tree.nodes = minifyFont(tree.nodes, opts); + decl.value = tree.toString(); + } } export default postcss.plugin('postcss-minify-font-values', (opts) => { - opts = Object.assign({}, { - removeAfterKeyword: false, - removeDuplicates: true, - removeQuotes: true, - }, opts); + opts = Object.assign( + {}, + { + removeAfterKeyword: false, + removeDuplicates: true, + removeQuotes: true, + }, + opts + ); - return css => css.walkDecls(/font/i, transform.bind(null, opts)); + return (css) => css.walkDecls(/font/i, transform.bind(null, opts)); }); diff --git a/packages/postcss-minify-font-values/src/lib/keywords.js b/packages/postcss-minify-font-values/src/lib/keywords.js index 4aef775e3..37e5626d0 100644 --- a/packages/postcss-minify-font-values/src/lib/keywords.js +++ b/packages/postcss-minify-font-values/src/lib/keywords.js @@ -1,44 +1,39 @@ export default { - style: [ - 'italic', - 'oblique', - ], - variant: [ - 'small-caps', - ], - weight: [ - '100', - '200', - '300', - '400', - '500', - '600', - '700', - '800', - '900', - 'bold', - 'lighter', - 'bolder', - ], - stretch: [ - 'ultra-condensed', - 'extra-condensed', - 'condensed', - 'semi-condensed', - 'semi-expanded', - 'expanded', - 'extra-expanded', - 'ultra-expanded', - ], - size: [ - 'xx-small', - 'x-small', - 'small', - 'medium', - 'large', - 'x-large', - 'xx-large', - 'larger', - 'smaller', - ], + style: ['italic', 'oblique'], + variant: ['small-caps'], + weight: [ + '100', + '200', + '300', + '400', + '500', + '600', + '700', + '800', + '900', + 'bold', + 'lighter', + 'bolder', + ], + stretch: [ + 'ultra-condensed', + 'extra-condensed', + 'condensed', + 'semi-condensed', + 'semi-expanded', + 'expanded', + 'extra-expanded', + 'ultra-expanded', + ], + size: [ + 'xx-small', + 'x-small', + 'small', + 'medium', + 'large', + 'x-large', + 'xx-large', + 'larger', + 'smaller', + ], }; diff --git a/packages/postcss-minify-font-values/src/lib/minify-family.js b/packages/postcss-minify-font-values/src/lib/minify-family.js index c3fa3d3a8..015b16b76 100644 --- a/packages/postcss-minify-font-values/src/lib/minify-family.js +++ b/packages/postcss-minify-font-values/src/lib/minify-family.js @@ -1,71 +1,74 @@ -import {stringify} from 'postcss-value-parser'; +import { stringify } from 'postcss-value-parser'; import uniqueExcept from './uniqs'; const uniqs = uniqueExcept('monospace'); -const globalKeywords = [ - 'inherit', - 'initial', - 'unset', -]; +const globalKeywords = ['inherit', 'initial', 'unset']; const genericFontFamilykeywords = [ - 'sans-serif', - 'serif', - 'fantasy', - 'cursive', - 'monospace', - 'system-ui', + 'sans-serif', + 'serif', + 'fantasy', + 'cursive', + 'monospace', + 'system-ui', ]; -function makeArray (value, length) { - let array = []; - while (length--) { - array[length] = value; - } - return array; +function makeArray(value, length) { + let array = []; + while (length--) { + array[length] = value; + } + return array; } +// eslint-disable-next-line no-useless-escape const regexSimpleEscapeCharacters = /[ !"#$%&'()*+,.\/;<=>?@\[\\\]^`{|}~]/; -function escape (string, escapeForString) { - let counter = 0; - let character = null; - let charCode = null; - let value = null; - let output = ''; - - while (counter < string.length) { - character = string.charAt(counter++); - charCode = character.charCodeAt(); - - // \r is already tokenized away at this point - // `:` can be escaped as `\:`, but that fails in IE < 8 - if (!escapeForString && /[\t\n\v\f:]/.test(character)) { - value = '\\' + charCode.toString(16) + ' '; - } else if (!escapeForString && regexSimpleEscapeCharacters.test(character)) { - value = '\\' + character; - } else { - value = character; - } - - output += value; +function escape(string, escapeForString) { + let counter = 0; + let character = null; + let charCode = null; + let value = null; + let output = ''; + + while (counter < string.length) { + character = string.charAt(counter++); + charCode = character.charCodeAt(); + + // \r is already tokenized away at this point + // `:` can be escaped as `\:`, but that fails in IE < 8 + if (!escapeForString && /[\t\n\v\f:]/.test(character)) { + value = '\\' + charCode.toString(16) + ' '; + } else if ( + !escapeForString && + regexSimpleEscapeCharacters.test(character) + ) { + value = '\\' + character; + } else { + value = character; } - if (!escapeForString) { - if (/^-[-\d]/.test(output)) { - output = '\\-' + output.slice(1); - } + output += value; + } + + if (!escapeForString) { + if (/^-[-\d]/.test(output)) { + output = '\\-' + output.slice(1); + } - const firstChar = string.charAt(0); + const firstChar = string.charAt(0); - if (/\d/.test(firstChar)) { - output = '\\3' + firstChar + ' ' + output.slice(1); - } + if (/\d/.test(firstChar)) { + output = '\\3' + firstChar + ' ' + output.slice(1); } + } - return output; + return output; } -const regexKeyword = new RegExp(genericFontFamilykeywords.concat(globalKeywords).join('|'), 'i'); +const regexKeyword = new RegExp( + genericFontFamilykeywords.concat(globalKeywords).join('|'), + 'i' +); const regexInvalidIdentifier = /^(-?\d|--)/; const regexSpaceAtStart = /^\x20/; const regexWhitespace = /[\t\n\f\r\x20]/g; @@ -74,133 +77,136 @@ const regexConsecutiveSpaces = /(\\(?:[a-fA-F0-9]{1,6}\x20|\x20))?(\x20{2,})/g; const regexTrailingEscape = /\\[a-fA-F0-9]{0,6}\x20$/; const regexTrailingSpace = /\x20$/; -function escapeIdentifierSequence (string) { - let identifiers = string.split(regexWhitespace); - let index = 0; - let result = []; - let escapeResult; +function escapeIdentifierSequence(string) { + let identifiers = string.split(regexWhitespace); + let index = 0; + let result = []; + let escapeResult; - while (index < identifiers.length) { - let subString = identifiers[index++]; + while (index < identifiers.length) { + let subString = identifiers[index++]; - if (subString === '') { - result.push(subString); - continue; - } + if (subString === '') { + result.push(subString); + continue; + } - escapeResult = escape(subString, false); - - if (regexIdentifierCharacter.test(subString)) { - // the font family name part consists of allowed characters exclusively - if (regexInvalidIdentifier.test(subString)) { - // the font family name part starts with two hyphens, a digit, or a - // hyphen followed by a digit - if (index === 1) { // if this is the first item - result.push(escapeResult); - } else { - // if it’s not the first item, we can simply escape the space - // between the two identifiers to merge them into a single - // identifier rather than escaping the start characters of the - // second identifier - result[index - 2] += '\\'; - result.push(escape(subString, true)); - } - } else { - // the font family name part doesn’t start with two hyphens, a digit, - // or a hyphen followed by a digit - result.push(escapeResult); - } + escapeResult = escape(subString, false); + + if (regexIdentifierCharacter.test(subString)) { + // the font family name part consists of allowed characters exclusively + if (regexInvalidIdentifier.test(subString)) { + // the font family name part starts with two hyphens, a digit, or a + // hyphen followed by a digit + if (index === 1) { + // if this is the first item + result.push(escapeResult); } else { - // the font family name part contains invalid identifier characters - result.push(escapeResult); + // if it’s not the first item, we can simply escape the space + // between the two identifiers to merge them into a single + // identifier rather than escaping the start characters of the + // second identifier + result[index - 2] += '\\'; + result.push(escape(subString, true)); } + } else { + // the font family name part doesn’t start with two hyphens, a digit, + // or a hyphen followed by a digit + result.push(escapeResult); + } + } else { + // the font family name part contains invalid identifier characters + result.push(escapeResult); } + } - result = result.join(' ').replace(regexConsecutiveSpaces, ($0, $1, $2) => { - const spaceCount = $2.length; - const escapesNeeded = Math.floor(spaceCount / 2); - const array = makeArray('\\ ', escapesNeeded); + result = result.join(' ').replace(regexConsecutiveSpaces, ($0, $1, $2) => { + const spaceCount = $2.length; + const escapesNeeded = Math.floor(spaceCount / 2); + const array = makeArray('\\ ', escapesNeeded); - if (spaceCount % 2) { - array[escapesNeeded - 1] += '\\ '; - } + if (spaceCount % 2) { + array[escapesNeeded - 1] += '\\ '; + } - return ($1 || '') + ' ' + array.join(' '); - }); + return ($1 || '') + ' ' + array.join(' '); + }); - // Escape trailing spaces unless they’re already part of an escape - if (regexTrailingSpace.test(result) && !regexTrailingEscape.test(result)) { - result = result.replace(regexTrailingSpace, '\\ '); - } + // Escape trailing spaces unless they’re already part of an escape + if (regexTrailingSpace.test(result) && !regexTrailingEscape.test(result)) { + result = result.replace(regexTrailingSpace, '\\ '); + } - if (regexSpaceAtStart.test(result)) { - result = '\\ ' + result.slice(1); - } + if (regexSpaceAtStart.test(result)) { + result = '\\ ' + result.slice(1); + } - return result; + return result; } -export default function (nodes, opts) { - let family = []; - let last = null; - let i, max; - - nodes.forEach((node, index, arr) => { - if (node.type === 'string' || node.type === 'function') { - family.push(node); - } else if (node.type === 'word') { - if (!last) { - last = {type: 'word', value: ''}; - family.push(last); - } - - last.value += node.value; - } else if (node.type === 'space') { - if (last && index !== arr.length - 1) { - last.value += ' '; - } - } else { - last = null; - } - }); +export default function(nodes, opts) { + let family = []; + let last = null; + let i, max; + + nodes.forEach((node, index, arr) => { + if (node.type === 'string' || node.type === 'function') { + family.push(node); + } else if (node.type === 'word') { + if (!last) { + last = { type: 'word', value: '' }; + family.push(last); + } + + last.value += node.value; + } else if (node.type === 'space') { + if (last && index !== arr.length - 1) { + last.value += ' '; + } + } else { + last = null; + } + }); - family = family.map((node) => { - if (node.type === 'string') { - const isKeyword = regexKeyword.test(node.value); + family = family.map((node) => { + if (node.type === 'string') { + const isKeyword = regexKeyword.test(node.value); - if ( - !opts.removeQuotes || - isKeyword || - /[0-9]/.test(node.value.slice(0, 1)) - ) { - return stringify(node); - } + if ( + !opts.removeQuotes || + isKeyword || + /[0-9]/.test(node.value.slice(0, 1)) + ) { + return stringify(node); + } - let escaped = escapeIdentifierSequence(node.value); + let escaped = escapeIdentifierSequence(node.value); - if (escaped.length < node.value.length + 2) { - return escaped; - } - } - - return stringify(node); - }); - - if (opts.removeAfterKeyword) { - for (i = 0, max = family.length; i < max; i += 1) { - if (~genericFontFamilykeywords.indexOf(family[i].toLowerCase())) { - family = family.slice(0, i + 1); - break; - } - } + if (escaped.length < node.value.length + 2) { + return escaped; + } } - if (opts.removeDuplicates) { - family = uniqs(family); - } + return stringify(node); + }); - return [{ - type: 'word', - value: family.join(), - }]; -}; + if (opts.removeAfterKeyword) { + for (i = 0, max = family.length; i < max; i += 1) { + if (~genericFontFamilykeywords.indexOf(family[i].toLowerCase())) { + family = family.slice(0, i + 1); + break; + } + } + } + + if (opts.removeDuplicates) { + family = uniqs(family); + } + + return [ + { + type: 'word', + value: family.join(), + }, + ]; +} diff --git a/packages/postcss-minify-font-values/src/lib/minify-font.js b/packages/postcss-minify-font-values/src/lib/minify-font.js index f7896ab65..37b39e175 100644 --- a/packages/postcss-minify-font-values/src/lib/minify-font.js +++ b/packages/postcss-minify-font-values/src/lib/minify-font.js @@ -1,40 +1,40 @@ -import {unit} from 'postcss-value-parser'; +import { unit } from 'postcss-value-parser'; import keywords from './keywords'; import minifyFamily from './minify-family'; import minifyWeight from './minify-weight'; -export default function (nodes, opts) { - let i, max, node, familyStart, family; - let hasSize = false; +export default function(nodes, opts) { + let i, max, node, familyStart, family; + let hasSize = false; - for (i = 0, max = nodes.length; i < max; i += 1) { - node = nodes[i]; - if (node.type === 'word') { - if (hasSize) { - continue; - } - const value = node.value.toLowerCase(); - if ( - value === 'normal' || - ~keywords.style.indexOf(value) || - ~keywords.variant.indexOf(value) || - ~keywords.stretch.indexOf(value) - ) { - familyStart = i; - } else if (~keywords.weight.indexOf(value)) { - node.value = minifyWeight(value); - familyStart = i; - } else if (~keywords.size.indexOf(value) || unit(value)) { - familyStart = i; - hasSize = true; - } - } else if (node.type === 'div' && node.value === '/') { - familyStart = i + 1; - break; - } + for (i = 0, max = nodes.length; i < max; i += 1) { + node = nodes[i]; + if (node.type === 'word') { + if (hasSize) { + continue; + } + const value = node.value.toLowerCase(); + if ( + value === 'normal' || + ~keywords.style.indexOf(value) || + ~keywords.variant.indexOf(value) || + ~keywords.stretch.indexOf(value) + ) { + familyStart = i; + } else if (~keywords.weight.indexOf(value)) { + node.value = minifyWeight(value); + familyStart = i; + } else if (~keywords.size.indexOf(value) || unit(value)) { + familyStart = i; + hasSize = true; + } + } else if (node.type === 'div' && node.value === '/') { + familyStart = i + 1; + break; } + } - familyStart += 2; - family = minifyFamily(nodes.slice(familyStart), opts); - return nodes.slice(0, familyStart).concat(family); -}; + familyStart += 2; + family = minifyFamily(nodes.slice(familyStart), opts); + return nodes.slice(0, familyStart).concat(family); +} diff --git a/packages/postcss-minify-font-values/src/lib/minify-weight.js b/packages/postcss-minify-font-values/src/lib/minify-weight.js index 8a85e9b3f..392f40c20 100644 --- a/packages/postcss-minify-font-values/src/lib/minify-weight.js +++ b/packages/postcss-minify-font-values/src/lib/minify-weight.js @@ -1,5 +1,9 @@ -export default function (value) { - const valueInLowerCase = value.toLowerCase(); +export default function(value) { + const valueInLowerCase = value.toLowerCase(); - return valueInLowerCase === 'normal' ? '400' : valueInLowerCase === 'bold' ? '700' : value; -}; + return valueInLowerCase === 'normal' + ? '400' + : valueInLowerCase === 'bold' + ? '700' + : value; +} diff --git a/packages/postcss-minify-font-values/src/lib/uniqs.js b/packages/postcss-minify-font-values/src/lib/uniqs.js index e4f2d9b0f..a844abe1b 100644 --- a/packages/postcss-minify-font-values/src/lib/uniqs.js +++ b/packages/postcss-minify-font-values/src/lib/uniqs.js @@ -1,11 +1,11 @@ -export default function uniqueExcept (exclude) { - return function unique () { - const list = Array.prototype.concat.apply([], arguments); - return list.filter((item, i) => { - if (item.toLowerCase() === exclude) { - return true; - } - return i === list.indexOf(item); - }); - }; -}; +export default function uniqueExcept(exclude) { + return function unique() { + const list = Array.prototype.concat.apply([], arguments); + return list.filter((item, i) => { + if (item.toLowerCase() === exclude) { + return true; + } + return i === list.indexOf(item); + }); + }; +} diff --git a/packages/postcss-minify-gradients/src/__tests__/index.js b/packages/postcss-minify-gradients/src/__tests__/index.js index 435584e84..b1cad3e10 100644 --- a/packages/postcss-minify-gradients/src/__tests__/index.js +++ b/packages/postcss-minify-gradients/src/__tests__/index.js @@ -1,390 +1,383 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); test( - 'linear: should convert "to top" to 0deg', - processCSS, - 'background:linear-gradient(to top,#ffe500,#121)', - 'background:linear-gradient(0deg,#ffe500,#121)' + 'linear: should convert "to top" to 0deg', + processCSS, + 'background:linear-gradient(to top,#ffe500,#121)', + 'background:linear-gradient(0deg,#ffe500,#121)' ); test( - 'linear: should convert "to top" to 0deg (uppercase property and value)', - processCSS, - 'BACKGROUND:LINEAR-GRADIENT(TO TOP,#ffe500,#121)', - 'BACKGROUND:LINEAR-GRADIENT(0deg,#ffe500,#121)' + 'linear: should convert "to top" to 0deg (uppercase property and value)', + processCSS, + 'BACKGROUND:LINEAR-GRADIENT(TO TOP,#ffe500,#121)', + 'BACKGROUND:LINEAR-GRADIENT(0deg,#ffe500,#121)' ); test( - 'linear: should convert "to right" to 90deg', - processCSS, - 'background:linear-gradient(to right,#ffe500,#121)', - 'background:linear-gradient(90deg,#ffe500,#121)' + 'linear: should convert "to right" to 90deg', + processCSS, + 'background:linear-gradient(to right,#ffe500,#121)', + 'background:linear-gradient(90deg,#ffe500,#121)' ); test( - 'linear: should convert "to right" to 90deg (uppercase property and value)', - processCSS, - 'BACKGROUND:LINEAR-GRADIENT(TO RIGHT,#FFE500,#121)', - 'BACKGROUND:LINEAR-GRADIENT(90deg,#FFE500,#121)' + 'linear: should convert "to right" to 90deg (uppercase property and value)', + processCSS, + 'BACKGROUND:LINEAR-GRADIENT(TO RIGHT,#FFE500,#121)', + 'BACKGROUND:LINEAR-GRADIENT(90deg,#FFE500,#121)' ); test( - 'linear: should convert "to bottom" to 180deg', - processCSS, - 'background:linear-gradient(to bottom,#ffe500,#121)', - 'background:linear-gradient(180deg,#ffe500,#121)' + 'linear: should convert "to bottom" to 180deg', + processCSS, + 'background:linear-gradient(to bottom,#ffe500,#121)', + 'background:linear-gradient(180deg,#ffe500,#121)' ); test( - 'linear: should convert "to left" to 270deg', - processCSS, - 'background:linear-gradient(to left,#ffe500,#121)', - 'background:linear-gradient(270deg,#ffe500,#121)' + 'linear: should convert "to left" to 270deg', + processCSS, + 'background:linear-gradient(to left,#ffe500,#121)', + 'background:linear-gradient(270deg,#ffe500,#121)' ); test( - 'linear: should convert "to top" to 0deg (with a solid colour)', - processCSS, - 'background:#ffe500 linear-gradient(to top,#ffe500,#121)', - 'background:#ffe500 linear-gradient(0deg,#ffe500,#121)' + 'linear: should convert "to top" to 0deg (with a solid colour)', + processCSS, + 'background:#ffe500 linear-gradient(to top,#ffe500,#121)', + 'background:#ffe500 linear-gradient(0deg,#ffe500,#121)' ); test( - 'repeating-linear: should convert "to top" to 0deg', - processCSS, - 'background:repeating-linear-gradient(to top,#ffe500,#121)', - 'background:repeating-linear-gradient(0deg,#ffe500,#121)' + 'repeating-linear: should convert "to top" to 0deg', + processCSS, + 'background:repeating-linear-gradient(to top,#ffe500,#121)', + '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)' + '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, - 'background:repeating-linear-gradient(to right,#ffe500,#121)', - 'background:repeating-linear-gradient(90deg,#ffe500,#121)' + 'repeating-linear: should convert "to right" to 90deg', + processCSS, + 'background:repeating-linear-gradient(to right,#ffe500,#121)', + 'background:repeating-linear-gradient(90deg,#ffe500,#121)' ); test( - 'repeating-linear: should convert "to bottom" to 180deg', - processCSS, - 'background:repeating-linear-gradient(to bottom,#ffe500,#121)', - 'background:repeating-linear-gradient(180deg,#ffe500,#121)' + 'repeating-linear: should convert "to bottom" to 180deg', + processCSS, + 'background:repeating-linear-gradient(to bottom,#ffe500,#121)', + 'background:repeating-linear-gradient(180deg,#ffe500,#121)' ); test( - 'repeating-linear: should convert "to left" to 270deg', - processCSS, - 'background:repeating-linear-gradient(to left,#ffe500,#121)', - 'background:repeating-linear-gradient(270deg,#ffe500,#121)' + 'repeating-linear: should convert "to left" to 270deg', + processCSS, + 'background:repeating-linear-gradient(to left,#ffe500,#121)', + 'background:repeating-linear-gradient(270deg,#ffe500,#121)' ); test( - 'linear: should not convert "to top right" to an angle', - passthroughCSS, - 'background:linear-gradient(to top right,#ffe500,#121)' + 'linear: should not convert "to top right" to an angle', + passthroughCSS, + 'background:linear-gradient(to top right,#ffe500,#121)' ); test( - 'linear: should not convert "to bottom left" to an angle', - passthroughCSS, - 'background:linear-gradient(to bottom left,#ffe500,#121)' + 'linear: should not convert "to bottom left" to an angle', + passthroughCSS, + 'background:linear-gradient(to bottom left,#ffe500,#121)' ); test( - 'linear: should reduce length values if they are the same', - processCSS, - 'background:linear-gradient(45deg,#ffe500 50%,#121 50%)', - 'background:linear-gradient(45deg,#ffe500 50%,#121 0)' + 'linear: should reduce length values if they are the same', + 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 the same (uppercase)', - processCSS, - 'background:LINEAR-GRADIENT(45DEG,#FFE500 50%,#121 50%)', - 'background:LINEAR-GRADIENT(45DEG,#FFE500 50%,#121 0)' + '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, - 'background:linear-gradient(45deg,#ffe500 50%,#121 25%)', - 'background:linear-gradient(45deg,#ffe500 50%,#121 0)' + 'linear: should reduce length values if they are less', + processCSS, + 'background:linear-gradient(45deg,#ffe500 50%,#121 25%)', + 'background:linear-gradient(45deg,#ffe500 50%,#121 0)' ); test( - 'linear: should not reduce length values with different units', - passthroughCSS, - 'background:linear-gradient(45deg,#ffe500 25px,#121 20%)' + 'linear: should not reduce length values with different units', + passthroughCSS, + 'background:linear-gradient(45deg,#ffe500 25px,#121 20%)' ); test( - 'linear: should remove the (unnecessary) start/end length values', - processCSS, - 'background:linear-gradient(#ffe500 0%,#121 100%)', - 'background:linear-gradient(#ffe500,#121)' + 'linear: should remove the (unnecessary) start/end length values', + processCSS, + 'background:linear-gradient(#ffe500 0%,#121 100%)', + 'background:linear-gradient(#ffe500,#121)' ); test( - 'repeating-radial: should reduce length values if they are the same', - processCSS, - 'background:repeating-radial-gradient(#121,#121 5px,#ffe500 5px,#ffe500 10px)', - 'background:repeating-radial-gradient(#121,#121 5px,#ffe500 0,#ffe500 10px)' + 'repeating-radial: should reduce length values if they are the same', + processCSS, + 'background:repeating-radial-gradient(#121,#121 5px,#ffe500 5px,#ffe500 10px)', + 'background:repeating-radial-gradient(#121,#121 5px,#ffe500 0,#ffe500 10px)' ); test( - 'repeating-radial: should reduce length values if they are the same (uppercase property and value)', - processCSS, - 'BACKGROUND:REPEATING-RADIAL-GRADIENT(#121,#121 5PX,#FFE500 5PX,#FFE500 10PX)', - 'BACKGROUND:REPEATING-RADIAL-GRADIENT(#121,#121 5PX,#FFE500 0,#FFE500 10PX)' + 'repeating-radial: should reduce length values if they are the same (uppercase property and value)', + processCSS, + 'BACKGROUND:REPEATING-RADIAL-GRADIENT(#121,#121 5PX,#FFE500 5PX,#FFE500 10PX)', + '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)' + '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%);' + '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, - 'background:radial-gradient(AT 50% 0%,rgba(74,74,74,.15),transparent 40%);' + 'radial: should correctly account for uppercase "at"', + passthroughCSS, + 'background:radial-gradient(AT 50% 0%,rgba(74,74,74,.15),transparent 40%);' ); - test( - 'radial: should correctly account for "at" (2)', - 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);' + 'radial: should correctly account for "at" (2)', + 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 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);' + '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)', - 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%)' + 'radial: should correctly account with prefix "-webkit" (1)', + 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) (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%)' + '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%)' + '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%)' + '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, - 'background: -webkit-radial-gradient(center, 30% 30%, white 20%, black 10%)', - 'background: -webkit-radial-gradient(center, 30% 30%, white 20%, black 0)' + 'radial: should correctly account with prefix "-webkit" (2)', + 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" (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)' + '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, - 'background: -webkit-radial-gradient(50% 26%, #fff, rgba(255, 255, 255, 0) 24%)', - 'background: -webkit-radial-gradient(50% 26%, #fff, rgba(255, 255, 255, 0) 24%)' + 'radial: should correctly account with prefix "-webkit" (3)', + processCSS, + 'background: -webkit-radial-gradient(50% 26%, #fff, rgba(255, 255, 255, 0) 24%)', + 'background: -webkit-radial-gradient(50% 26%, #fff, rgba(255, 255, 255, 0) 24%)' ); test( - 'radial: should correctly account with prefix "-webkit" (4)', - processCSS, - 'background: -webkit-radial-gradient(white 50%, black 40%)', - 'background: -webkit-radial-gradient(white 50%, black 0)' + 'radial: should correctly account with prefix "-webkit" (4)', + 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) (uppercase)', - processCSS, - 'background: -WEBKIT-RADIAL-GRADIENT(WHITE 50%, BLACK 40%)', - 'background: -WEBKIT-RADIAL-GRADIENT(WHITE 50%, BLACK 0)' + '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)' + '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, - 'background: -webkit-radial-gradient(white calc(30%), black calc(50%))', - 'background: -webkit-radial-gradient(white calc(30%), black calc(50%))' + 'radial: should correctly account with prefix "-webkit" (5)', + processCSS, + 'background: -webkit-radial-gradient(white calc(30%), black calc(50%))', + '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%))' + '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, - '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)' + 'should not mangle floating point numbers', + 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 (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)' + '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)' + '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, - 'background: linear-gradient(90deg,transparent,#00aeef 0)' + 'should not remove the trailing zero if it is the last stop', + passthroughCSS, + 'background: linear-gradient(90deg,transparent,#00aeef 0)' ); test( - 'should not remove point number if it its different type from a previous one', - passthroughCSS, - 'background: linear-gradient(to left bottom,transparent calc(50% - 2px),#a7a7a8 0,#a7a7a8 calc(50% + 2px),transparent 0)' + 'should not remove point number if it its different type from a previous one', + passthroughCSS, + 'background: linear-gradient(to left bottom,transparent calc(50% - 2px),#a7a7a8 0,#a7a7a8 calc(50% + 2px),transparent 0)' ); test( - 'should not throw on empty linear gradients', - passthroughCSS, - 'background: linear-gradient()' + 'should not throw on empty linear gradients', + passthroughCSS, + 'background: linear-gradient()' ); test( - 'should not throw on empty radial gradients', - passthroughCSS, - 'background: radial-gradient()', + 'should not throw on empty radial gradients', + passthroughCSS, + 'background: radial-gradient()' ); test( - 'should pass through custom property references', - passthroughCSS, - 'background-image:var(--bg),linear-gradient(red,blue)' + 'should pass through custom property references', + passthroughCSS, + 'background-image:var(--bg),linear-gradient(red,blue)' ); test( - 'should pass through custom property references #2', - passthroughCSS, - 'background:linear-gradient(to var(--var), transparent, black) 0% 50% no-repeat' + 'should pass through custom property references #2', + passthroughCSS, + 'background:linear-gradient(to var(--var), transparent, black) 0% 50% no-repeat' ); test( - 'should pass through custom property references #3', - passthroughCSS, - 'background:linear-gradient(var(--var), transparent, black) 0% 50% no-repeat' + 'should pass through custom property references #3', + passthroughCSS, + 'background:linear-gradient(var(--var), transparent, black) 0% 50% no-repeat' ); test( - 'should pass through custom property references #4', - passthroughCSS, - 'background:linear-gradient(var(--var), black) 0% 50% no-repeat' + 'should pass through custom property references #4', + passthroughCSS, + 'background:linear-gradient(var(--var), black) 0% 50% no-repeat' ); test( - 'should pass through custom property references #5', - passthroughCSS, - 'background:linear-gradient(var(--var)) 0% 50% no-repeat' + 'should pass through custom property references #5', + passthroughCSS, + 'background:linear-gradient(var(--var)) 0% 50% no-repeat' ); test( - 'should pass through custom property references #6', - passthroughCSS, - 'background:linear-gradient(var(--var), rgba(255,0,0,0) 70.71%)' + 'should pass through custom property references #6', + passthroughCSS, + 'background:linear-gradient(var(--var), rgba(255,0,0,0) 70.71%)' ); test( - 'should pass through custom property references #7', - passthroughCSS, - 'background:linear-gradient(to env(--var), transparent, black) 0% 50% no-repeat' + 'should pass through custom property references #7', + passthroughCSS, + 'background:linear-gradient(to env(--var), transparent, black) 0% 50% no-repeat' ); test( - 'should pass through custom property references #8', - passthroughCSS, - 'background:linear-gradient(var(--var))' + 'should pass through custom property references #8', + passthroughCSS, + 'background:linear-gradient(var(--var))' ); test( - 'should pass through custom property references #9', - passthroughCSS, - 'background:linear-gradient(var(--foo), var(--bar), var(--baz))' + 'should pass through custom property references #9', + passthroughCSS, + 'background:linear-gradient(var(--foo), var(--bar), var(--baz))' ); test( - 'should pass through env property references', - passthroughCSS, - 'background:linear-gradient(env(--var))' + 'should pass through env property references', + passthroughCSS, + 'background:linear-gradient(env(--var))' ); -test( - 'should not throw error on broken syntax', - passthroughCSS, - 'background:' -); +test('should not throw error on broken syntax', passthroughCSS, 'background:'); test( - 'should not operate on declarations without gradients', - passthroughCSS, - 'background:red' + 'should not operate on declarations without gradients', + passthroughCSS, + 'background:red' ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-minify-gradients/src/index.js b/packages/postcss-minify-gradients/src/index.js index 101218ff7..5f9d66cd0 100644 --- a/packages/postcss-minify-gradients/src/index.js +++ b/packages/postcss-minify-gradients/src/index.js @@ -1,216 +1,197 @@ -import postcss from "postcss"; -import valueParser, {unit, stringify} from "postcss-value-parser"; -import getArguments from "lerna:cssnano-util-get-arguments"; -import isColorStop from "is-color-stop"; +import postcss from 'postcss'; +import valueParser, { unit, stringify } from 'postcss-value-parser'; +import getArguments from 'lerna:cssnano-util-get-arguments'; +import isColorStop from 'is-color-stop'; const angles = { - top: "0deg", - right: "90deg", - bottom: "180deg", - left: "270deg", + top: '0deg', + right: '90deg', + bottom: '180deg', + left: '270deg', }; -function isLessThan (a, b) { - return ( - a.unit.toLowerCase() === b.unit.toLowerCase() && - parseFloat(a.number) >= parseFloat(b.number) - ); +function isLessThan(a, b) { + return ( + a.unit.toLowerCase() === b.unit.toLowerCase() && + parseFloat(a.number) >= parseFloat(b.number) + ); } -function optimise (decl) { - const value = decl.value; +function optimise(decl) { + const value = decl.value; - if (!value) { - return; - } + if (!value) { + return; + } - const normalizedValue = value.toLowerCase(); + const normalizedValue = value.toLowerCase(); - if (normalizedValue.includes("var(") || normalizedValue.includes("env(")) { - return; - } + if (normalizedValue.includes('var(') || normalizedValue.includes('env(')) { + return; + } - if (!normalizedValue.includes("gradient")) { - return; - } + if (!normalizedValue.includes('gradient')) { + return; + } - decl.value = valueParser(value) - .walk(node => { - if (node.type !== "function" || !node.nodes.length) { - return false; - } + 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' || + lowerCasedValue === '-webkit-linear-gradient' || + 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()]; + } - const lowerCasedValue = node.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 ( - lowerCasedValue === "linear-gradient" || - lowerCasedValue === "repeating-linear-gradient" || - lowerCasedValue === "-webkit-linear-gradient" || - lowerCasedValue === "-webkit-repeating-linear-gradient" + !isFinalStop && + lastStop && + lastStop.number === '0' && + lastStop.unit.toLowerCase() !== 'deg' ) { - 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 ( - 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; + arg[1].value = arg[2].value = ''; } - if ( - lowerCasedValue === "radial-gradient" || - lowerCasedValue === "repeating-radial-gradient" - ) { - let args = getArguments(node); - let lastStop; + return; + } + + 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'); - const hasAt = args[0].find(n => n.value.toLowerCase() === "at"); + args.forEach((arg, index) => { + if (!arg[2] || (!index && hasAt)) { + return; + } - args.forEach((arg, index) => { - if (!arg[2] || (!index && hasAt)) { - return; - } + let thisStop = unit(arg[2].value); - let thisStop = unit(arg[2].value); + if (!lastStop) { + lastStop = thisStop; - if (!lastStop) { - lastStop = thisStop; + return; + } - return; - } + if (lastStop && thisStop && isLessThan(lastStop, thisStop)) { + arg[2].value = 0; + } - if ( - lastStop && - thisStop && - isLessThan(lastStop, thisStop) - ) { - arg[2].value = 0; - } + lastStop = thisStop; + }); - lastStop = thisStop; - }); + return false; + } - 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 ( - 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 { - stop = arg[2].value; - } - } else { - 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.toLowerCase()) - : isColorStop(color); - - if (!colorStop || !arg[2]) { - return; - } - - let thisStop = unit(arg[2].value); - - if (!lastStop) { - lastStop = thisStop; - - return; - } - - if ( - lastStop && - thisStop && - isLessThan(lastStop, thisStop) - ) { - arg[2].value = 0; - } - - lastStop = thisStop; - }); - - return false; + if (arg[2].type === 'function') { + stop = `${arg[2].value}(${stringify(arg[2].nodes)})`; + } else { + stop = arg[2].value; } - }) - .toString(); + } else { + 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.toLowerCase()) + : isColorStop(color); + + if (!colorStop || !arg[2]) { + return; + } + + let thisStop = unit(arg[2].value); + + if (!lastStop) { + lastStop = thisStop; + + return; + } + + if (lastStop && thisStop && isLessThan(lastStop, thisStop)) { + arg[2].value = 0; + } + + lastStop = thisStop; + }); + + return false; + } + }) + .toString(); } -export default postcss.plugin("postcss-minify-gradients", () => { - return css => css.walkDecls(optimise); +export default postcss.plugin('postcss-minify-gradients', () => { + return (css) => css.walkDecls(optimise); }); diff --git a/packages/postcss-minify-params/src/__tests__/index.js b/packages/postcss-minify-params/src/__tests__/index.js index b25a2ca11..944352d02 100644 --- a/packages/postcss-minify-params/src/__tests__/index.js +++ b/packages/postcss-minify-params/src/__tests__/index.js @@ -1,293 +1,272 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); test( - 'should normalise @media queries', - processCSS, - '@media SCREEN ,\tprint {h1{color:red}}@media print,screen{h2{color:blue}}', - '@media print,SCREEN {h1{color:red}}@media print,screen{h2{color:blue}}' + 'should normalise @media queries', + processCSS, + '@media SCREEN ,\tprint {h1{color:red}}@media print,screen{h2{color:blue}}', + '@media print,SCREEN {h1{color:red}}@media print,screen{h2{color:blue}}' ); test( - 'should normalise @media queries (uppercase)', - processCSS, - '@MEDIA SCREEN ,\tPRINT {h1{color:red}}@MEDIA PRINT,SCREEN{h2{color:blue}}', - '@MEDIA PRINT,SCREEN {h1{color:red}}@MEDIA PRINT,SCREEN{h2{color:blue}}' + 'should normalise @media queries (uppercase)', + processCSS, + '@MEDIA SCREEN ,\tPRINT {h1{color:red}}@MEDIA PRINT,SCREEN{h2{color:blue}}', + '@MEDIA PRINT,SCREEN {h1{color:red}}@MEDIA PRINT,SCREEN{h2{color:blue}}' ); test( - 'should normalise @media queries (2)', - processCSS, - '@media only screen \n and ( min-width: 400px, min-height: 500px ){h1{color:blue}}', - '@media only screen and (min-width:400px,min-height:500px){h1{color:blue}}' + 'should normalise @media queries (2)', + processCSS, + '@media only screen \n and ( min-width: 400px, min-height: 500px ){h1{color:blue}}', + '@media only screen and (min-width:400px,min-height:500px){h1{color:blue}}' ); test( - 'should normalise @media queries (3)', - processCSS, - '@media (min-height: 680px),(min-height: 680px){h1{color:red}}', - '@media (min-height:680px){h1{color:red}}' + 'should normalise @media queries (3)', + processCSS, + '@media (min-height: 680px),(min-height: 680px){h1{color:red}}', + '@media (min-height:680px){h1{color:red}}' ); test.skip( - 'should normalise @media queries (3) (lowercase and uppercase)', - processCSS, - '@media (min-height: 680px),(MIN-HEIGHT: 680PX){h1{color:red}}', - '@media (min-height:680px){h1{color:red}}' + 'should normalise @media queries (3) (lowercase and uppercase)', + processCSS, + '@media (min-height: 680px),(MIN-HEIGHT: 680PX){h1{color:red}}', + '@media (min-height:680px){h1{color:red}}' ); test( - 'should normalise "all" in @media queries', - processCSS, - '@media all{h1{color:blue}}', - '@media{h1{color:blue}}', - {env: 'chrome58'} + 'should normalise "all" in @media queries', + processCSS, + '@media all{h1{color:blue}}', + '@media{h1{color:blue}}', + { env: 'chrome58' } ); test( - 'should normalise "all" in @media queries (uppercase)', - processCSS, - '@MEDIA ALL{h1{color:blue}}', - '@MEDIA{h1{color:blue}}', - {env: 'chrome58'} + 'should normalise "all" in @media queries (uppercase)', + processCSS, + '@MEDIA ALL{h1{color:blue}}', + '@MEDIA{h1{color:blue}}', + { env: 'chrome58' } ); test( - 'should not normalise "all" in @media queries', - processCSS, - '@media all{h1{color:blue}}', - '@media all{h1{color:blue}}', - {env: 'ie11'} + 'should not normalise "all" in @media queries', + processCSS, + '@media all{h1{color:blue}}', + '@media all{h1{color:blue}}', + { env: 'ie11' } ); test( - 'should normalise "all and" in @media queries', - processCSS, - '@media all and (min-width:500px){h1{color:blue}}', - '@media (min-width:500px){h1{color:blue}}' + 'should normalise "all and" in @media queries', + processCSS, + '@media all and (min-width:500px){h1{color:blue}}', + '@media (min-width:500px){h1{color:blue}}' ); test( - 'should normalise "all and" in @media queries (uppercase)', - processCSS, - '@media ALL AND (min-width:500px){h1{color:blue}}', - '@media (min-width:500px){h1{color:blue}}' + 'should normalise "all and" in @media queries (uppercase)', + processCSS, + '@media ALL AND (min-width:500px){h1{color:blue}}', + '@media (min-width:500px){h1{color:blue}}' ); test( - 'should not normalise "not all and" in @media queries', - processCSS, - '@media not all and (min-width: 768px){h1{color:blue}}', - '@media not all and (min-width:768px){h1{color:blue}}' + 'should not normalise "not all and" in @media queries', + processCSS, + '@media not all and (min-width: 768px){h1{color:blue}}', + '@media not all and (min-width:768px){h1{color:blue}}' ); test( - 'should not remove "all" from other at-rules', - passthroughCSS, - '@foo all;' + 'should not remove "all" from other at-rules', + passthroughCSS, + '@foo all;' ); test( - 'should not mangle @keyframe from & 100% in other values', - passthroughCSS, - '@keyframes test{x-from-tag{color:red}5100%{color:blue}}' + 'should not mangle @keyframe from & 100% in other values', + passthroughCSS, + '@keyframes test{x-from-tag{color:red}5100%{color:blue}}' ); test( - 'should not parse at rules without params', - passthroughCSS, - '@font-face{font-family:test;src:local(test)}' + 'should not parse at rules without params', + passthroughCSS, + '@font-face{font-family:test;src:local(test)}' ); test( - 'should reduce min-aspect-ratio', - processCSS, - '@media (min-aspect-ratio: 32/18){h1{color:blue}}', - '@media (min-aspect-ratio:16/9){h1{color:blue}}' + 'should reduce min-aspect-ratio', + processCSS, + '@media (min-aspect-ratio: 32/18){h1{color:blue}}', + '@media (min-aspect-ratio:16/9){h1{color:blue}}' ); test( - 'should reduce min-aspect-ratio (uppercase)', - processCSS, - '@media (MIN-ASPECT-RATIO: 32/18){h1{color:blue}}', - '@media (MIN-ASPECT-RATIO:16/9){h1{color:blue}}' + 'should reduce min-aspect-ratio (uppercase)', + processCSS, + '@media (MIN-ASPECT-RATIO: 32/18){h1{color:blue}}', + '@media (MIN-ASPECT-RATIO:16/9){h1{color:blue}}' ); test( - 'should reduce max-aspect-ratio', - processCSS, - '@media (max-aspect-ratio: 48000000/32000000){h1{color:blue}}', - '@media (max-aspect-ratio:3/2){h1{color:blue}}' + 'should reduce max-aspect-ratio', + processCSS, + '@media (max-aspect-ratio: 48000000/32000000){h1{color:blue}}', + '@media (max-aspect-ratio:3/2){h1{color:blue}}' ); test( - 'should multiply aspect ratio', - processCSS, - '@media (max-aspect-ratio: 1.5/1){h1{color:blue}}', - '@media (max-aspect-ratio:3/2){h1{color:blue}}' + 'should multiply aspect ratio', + processCSS, + '@media (max-aspect-ratio: 1.5/1){h1{color:blue}}', + '@media (max-aspect-ratio:3/2){h1{color:blue}}' ); test( - 'should multiply aspect ratio (2)', - processCSS, - '@media (max-aspect-ratio: .5 / 1){h1{color:blue}}', - '@media (max-aspect-ratio:1/2){h1{color:blue}}' + 'should multiply aspect ratio (2)', + processCSS, + '@media (max-aspect-ratio: .5 / 1){h1{color:blue}}', + '@media (max-aspect-ratio:1/2){h1{color:blue}}' ); test( - 'should normalise @supports queries', - processCSS, - '@supports (display: grid) {}', - '@supports (display:grid) {}' + 'should normalise @supports queries', + processCSS, + '@supports (display: grid) {}', + '@supports (display:grid) {}' ); test( - 'should normalise @supports with not', - processCSS, - '@supports not (display: grid) {}', - '@supports not (display:grid) {}' + 'should normalise @supports with not', + processCSS, + '@supports not (display: grid) {}', + '@supports not (display:grid) {}' ); test( - 'should normalise @supports with multiple conditions', - processCSS, - '@supports ((text-align-last: justify) or (-moz-text-align-last: justify)) {}', - '@supports ((text-align-last:justify) or (-moz-text-align-last:justify)) {}' + 'should normalise @supports with multiple conditions', + processCSS, + '@supports ((text-align-last: justify) or (-moz-text-align-last: justify)) {}', + '@supports ((text-align-last:justify) or (-moz-text-align-last:justify)) {}' ); test( - 'should normalise @supports with var', - processCSS, - '@supports (--foo: green) {}', - '@supports (--foo:green) {}' + 'should normalise @supports with var', + processCSS, + '@supports (--foo: green) {}', + '@supports (--foo:green) {}' ); test( - 'should normalise @supports with :is', - processCSS, - '@supports not selector(:is(a, b)) {}', - '@supports not selector(:is(a,b)) {}' + 'should normalise @supports with :is', + processCSS, + '@supports not selector(:is(a, b)) {}', + '@supports not selector(:is(a,b)) {}' ); test( - 'should not throw on empty parentheses', - passthroughCSS, - '@media (){h1{color:blue}}' + 'should not throw on empty parentheses', + passthroughCSS, + '@media (){h1{color:blue}}' ); test( - 'should not mangle @value', - passthroughCSS, - `@value vertical, center from './Flex.mod.css';` + 'should not mangle @value', + passthroughCSS, + `@value vertical, center from './Flex.mod.css';` ); test( - 'should not mangle @value (uppercase)', - passthroughCSS, - `@VALUE vertical, center from './Flex.mod.css';` + 'should not mangle @value (uppercase)', + passthroughCSS, + `@VALUE vertical, center from './Flex.mod.css';` ); -test( - 'should not mangle @page', - passthroughCSS, - '@page :first { margin: 0; }' -); +test('should not mangle @page', passthroughCSS, '@page :first { margin: 0; }'); test( - 'should not mangle @page (uppercase)', - passthroughCSS, - '@PAGE :first { margin: 0; }' + 'should not mangle @page (uppercase)', + passthroughCSS, + '@PAGE :first { margin: 0; }' ); -test( - 'should not mangle @charset', - passthroughCSS, - '@charset "utf-8";' -); +test('should not mangle @charset', passthroughCSS, '@charset "utf-8";'); test( - 'should not mangle @charset (uppercase)', - passthroughCSS, - '@CHARSET "utf-8";' + 'should not mangle @charset (uppercase)', + passthroughCSS, + '@CHARSET "utf-8";' ); test( - 'should not mangle @import', - passthroughCSS, - '@import url("fineprint.css") print;' + 'should not mangle @import', + passthroughCSS, + '@import url("fineprint.css") print;' ); test( - 'should not mangle @import (uppercase)', - passthroughCSS, - '@IMPORT url("fineprint.css") print;' + 'should not mangle @import (uppercase)', + passthroughCSS, + '@IMPORT url("fineprint.css") print;' ); test( - 'should not mangle @namespace', - passthroughCSS, - '@namespace svg url(http://www.w3.org/2000/svg);' + 'should not mangle @namespace', + passthroughCSS, + '@namespace svg url(http://www.w3.org/2000/svg);' ); test( - 'should not mangle @namespace (uppercase)', - passthroughCSS, - '@NAMESPACE svg url(http://www.w3.org/2000/svg);' + 'should not mangle @namespace (uppercase)', + passthroughCSS, + '@NAMESPACE svg url(http://www.w3.org/2000/svg);' ); -test( - 'should not mangle @font-face', - passthroughCSS, - '@font-face {}' -); +test('should not mangle @font-face', passthroughCSS, '@font-face {}'); test( - 'should not mangle @font-face (uppercase)', - passthroughCSS, - '@FONT-FACE {}' + 'should not mangle @font-face (uppercase)', + passthroughCSS, + '@FONT-FACE {}' ); -test( - 'should not mangle @viewport', - passthroughCSS, - '@viewport {}' -); +test('should not mangle @viewport', passthroughCSS, '@viewport {}'); -test( - 'should not mangle @viewport (uppercase)', - passthroughCSS, - '@VIEWPORT {}' -); +test('should not mangle @viewport (uppercase)', passthroughCSS, '@VIEWPORT {}'); test( - 'should not mangle @counter-style', - passthroughCSS, - '@counter-style thumbs {}' + 'should not mangle @counter-style', + passthroughCSS, + '@counter-style thumbs {}' ); test( - 'should not mangle @counter-style (uppercase)', - passthroughCSS, - '@COUNTER-STYLE thumbs {}' + 'should not mangle @counter-style (uppercase)', + passthroughCSS, + '@COUNTER-STYLE thumbs {}' ); test( - 'should not mangle @font-feature-values', - passthroughCSS, - '@font-feature-values Font One {}' + 'should not mangle @font-feature-values', + passthroughCSS, + '@font-feature-values Font One {}' ); test( - 'should not mangle @font-feature-values (uppercase)', - passthroughCSS, - '@FONT-FEATURE-VALUES Font One {}' + 'should not mangle @font-feature-values (uppercase)', + passthroughCSS, + '@FONT-FEATURE-VALUES Font One {}' ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-minify-params/src/index.js b/packages/postcss-minify-params/src/index.js index 50e62825f..94dcc20f9 100644 --- a/packages/postcss-minify-params/src/index.js +++ b/packages/postcss-minify-params/src/index.js @@ -1,6 +1,6 @@ import browserslist from 'browserslist'; import postcss from 'postcss'; -import valueParser, {stringify} from 'postcss-value-parser'; +import valueParser, { stringify } from 'postcss-value-parser'; import sort from 'alphanum-sort'; import uniqs from 'uniqs'; import getArguments from 'lerna:cssnano-util-get-arguments'; @@ -10,102 +10,99 @@ import getArguments from 'lerna:cssnano-util-get-arguments'; * of two numbers. */ -function gcd (a, b) { - return b ? gcd(b, a % b) : a; +function gcd(a, b) { + return b ? gcd(b, a % b) : a; } -function aspectRatio (a, b) { - const divisor = gcd(a, b); +function aspectRatio(a, b) { + const divisor = gcd(a, b); - return [a / divisor, b / divisor]; + return [a / divisor, b / divisor]; } -function split (args) { - return args.map(arg => stringify(arg)).join(''); +function split(args) { + return args.map((arg) => stringify(arg)).join(''); } -function removeNode (node) { - node.value = ''; - node.type = 'word'; +function removeNode(node) { + node.value = ''; + node.type = 'word'; } -function transform (legacy, rule) { - const ruleName = rule.name.toLowerCase(); +function transform(legacy, rule) { + const ruleName = rule.name.toLowerCase(); + + // We should re-arrange parameters only for `@media` and `@supports` at-rules + if (!rule.params || !['media', 'supports'].includes(ruleName)) { + return; + } + + const params = valueParser(rule.params); + + params.walk((node, index) => { + if (node.type === 'div' || node.type === 'function') { + node.before = node.after = ''; + + if ( + node.type === 'function' && + node.nodes[4] && + node.nodes[0].value.toLowerCase().indexOf('-aspect-ratio') === 3 + ) { + const [a, b] = aspectRatio(node.nodes[2].value, node.nodes[4].value); + + node.nodes[2].value = a; + node.nodes[4].value = b; + } + } else if (node.type === 'space') { + node.value = ' '; + } else { + const prevWord = params.nodes[index - 2]; + + if ( + node.value.toLowerCase() === 'all' && + rule.name.toLowerCase() === 'media' && + !prevWord + ) { + const nextWord = params.nodes[index + 2]; + + if (!legacy || nextWord) { + removeNode(node); + } - // We should re-arrange parameters only for `@media` and `@supports` at-rules - if (!rule.params || !["media", "supports"].includes(ruleName)) { - return; - } + if (nextWord && nextWord.value.toLowerCase() === 'and') { + const nextSpace = params.nodes[index + 1]; + const secondSpace = params.nodes[index + 3]; - const params = valueParser(rule.params); - - params.walk((node, index) => { - if (node.type === 'div' || node.type === 'function') { - node.before = node.after = ''; - - if ( - node.type === 'function' && - node.nodes[4] && - node.nodes[0].value.toLowerCase().indexOf('-aspect-ratio') === 3 - ) { - const [a, b] = aspectRatio( - node.nodes[2].value, - node.nodes[4].value - ); - - node.nodes[2].value = a; - node.nodes[4].value = b; - } - } else if (node.type === 'space') { - node.value = ' '; - } else { - const prevWord = params.nodes[index - 2]; - - if ( - node.value.toLowerCase() === 'all' && - rule.name.toLowerCase() === 'media' && - !prevWord - ) { - const nextWord = params.nodes[index + 2]; - - if (!legacy || nextWord) { - removeNode(node); - } - - if (nextWord && nextWord.value.toLowerCase() === 'and') { - const nextSpace = params.nodes[index + 1]; - const secondSpace = params.nodes[index + 3]; - - removeNode(nextWord); - removeNode(nextSpace); - removeNode(secondSpace); - } - } + removeNode(nextWord); + removeNode(nextSpace); + removeNode(secondSpace); } - }, true); + } + } + }, true); - rule.params = sort(uniqs(getArguments(params).map(split)), { - insensitive: true, - }).join(); + rule.params = sort(uniqs(getArguments(params).map(split)), { + insensitive: true, + }).join(); - if (!rule.params.length) { - rule.raws.afterName = ''; - } + if (!rule.params.length) { + rule.raws.afterName = ''; + } } -function hasAllBug (browser) { - return ~['ie 10', 'ie 11'].indexOf(browser); +function hasAllBug(browser) { + return ~['ie 10', 'ie 11'].indexOf(browser); } export default postcss.plugin('postcss-minify-params', () => { - return (css, result) => { - const resultOpts = result.opts || {}; - const browsers = browserslist(null, { - stats: resultOpts.stats, - path: __dirname, - env: resultOpts.env, - }); - - return css.walkAtRules(transform.bind(null, browsers.some(hasAllBug))); - }; + return (css, result) => { + const resultOpts = result.opts || {}; + const browsers = browserslist(null, { + stats: resultOpts.stats, + path: __dirname, + env: resultOpts.env, + }); + + return css.walkAtRules(transform.bind(null, browsers.some(hasAllBug))); + }; }); diff --git a/packages/postcss-minify-selectors/src/__tests__/index.js b/packages/postcss-minify-selectors/src/__tests__/index.js index ab85e95c7..ffd725811 100644 --- a/packages/postcss-minify-selectors/src/__tests__/index.js +++ b/packages/postcss-minify-selectors/src/__tests__/index.js @@ -2,509 +2,513 @@ import test from 'ava'; import postcss from 'postcss'; import magician from 'postcss-font-magician'; import plugin from '../'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); test( - 'should trim spaces in simple selectors', - processCSS, - 'h1, h2, h3{color:blue}', - 'h1,h2,h3{color:blue}' + 'should trim spaces in simple selectors', + processCSS, + 'h1, h2, h3{color:blue}', + 'h1,h2,h3{color:blue}' ); test( - 'should trim spaces around combinators', - processCSS, - 'h1 + p, h1 > p, h1 ~ p{color:blue}', - 'h1+p,h1>p,h1~p{color:blue}' + 'should trim spaces around combinators', + processCSS, + 'h1 + p, h1 > p, h1 ~ p{color:blue}', + 'h1+p,h1>p,h1~p{color:blue}' ); test( - 'should not trim meaningful spaces', - passthroughCSS, - 'h1 p,H2 p{color:blue}' + 'should not trim meaningful spaces', + passthroughCSS, + 'h1 p,H2 p{color:blue}' ); test( - 'should reduce meaningful spaces', - processCSS, - 'h1 p,h2 p{color:blue}', - 'h1 p,h2 p{color:blue}' + 'should reduce meaningful spaces', + processCSS, + 'h1 p,h2 p{color:blue}', + 'h1 p,h2 p{color:blue}' ); test( - 'should remove qualified universal selectors', - processCSS, - '*#id,*.test,*:not(.green),*[href]{color:blue}', - '#id,.test,:not(.green),[href]{color:blue}' + 'should remove qualified universal selectors', + processCSS, + '*#id,*.test,*:not(.green),*[href]{color:blue}', + '#id,.test,:not(.green),[href]{color:blue}' ); test( - 'should remove complex qualified universal selectors', - processCSS, - '[class] + *[href] *:not(*.green){color:blue}', - '[class]+[href] :not(.green){color:blue}' + 'should remove complex qualified universal selectors', + processCSS, + '[class] + *[href] *:not(*.green){color:blue}', + '[class]+[href] :not(.green){color:blue}' ); test( - 'should remove complex qualified universal selectors (2)', - processCSS, - '*:not(*.green) ~ *{color:blue}', - ':not(.green)~*{color:blue}' + 'should remove complex qualified universal selectors (2)', + processCSS, + '*:not(*.green) ~ *{color:blue}', + ':not(.green)~*{color:blue}' ); test( - 'should not remove meaningful universal selectors', - processCSS, - '* + *, * > *, * h1, * ~ *{color:blue}', - '*+*,*>*,* h1,*~*{color:blue}' + 'should not remove meaningful universal selectors', + processCSS, + '* + *, * > *, * h1, * ~ *{color:blue}', + '*+*,*>*,* h1,*~*{color:blue}' ); test( - 'should preserve the universal selector between comments', - passthroughCSS, - '/*comment*/*/*comment*/{color:blue}' + 'should preserve the universal selector between comments', + passthroughCSS, + '/*comment*/*/*comment*/{color:blue}' ); test( - 'should preserve the universal selector in attribute selectors', - processCSS, - 'h1[class=" *.js "] + *.js{color:blue}', - 'h1[class=" *.js "]+.js{color:blue}' + 'should preserve the universal selector in attribute selectors', + processCSS, + 'h1[class=" *.js "] + *.js{color:blue}', + 'h1[class=" *.js "]+.js{color:blue}' ); test( - 'should preserve the universal selector in filenames', - passthroughCSS, - '[filename="*.js"]{color:blue}' + 'should preserve the universal selector in filenames', + passthroughCSS, + '[filename="*.js"]{color:blue}' ); test( - 'should preserve the universal selector in file globs', - passthroughCSS, - '[glob="/**/*.js"]{color:blue}' + 'should preserve the universal selector in file globs', + passthroughCSS, + '[glob="/**/*.js"]{color:blue}' ); test( - 'should preserve escaped zero plus sequences', - passthroughCSS, - '.\\31 0\\+,.\\31 5\\+,.\\32 0\\+{color:blue}' + 'should preserve escaped zero plus sequences', + passthroughCSS, + '.\\31 0\\+,.\\31 5\\+,.\\32 0\\+{color:blue}' ); test( - 'should handle deep combinators', - passthroughCSS, - 'body /deep/ .theme-element{color:blue}' + 'should handle deep combinators', + passthroughCSS, + 'body /deep/ .theme-element{color:blue}' ); test( - 'should sort using natural sort', - processCSS, - '.item1, .item10, .item11, .item2{color:blue}', - '.item1,.item2,.item10,.item11{color:blue}' + 'should sort using natural sort', + processCSS, + '.item1, .item10, .item11, .item2{color:blue}', + '.item1,.item2,.item10,.item11{color:blue}' ); test( - 'should dedupe selectors', - processCSS, - 'h1,h2,h3,h4,h5,h5,h6{color:blue}', - 'h1,h2,h3,h4,h5,h6{color:blue}' + 'should dedupe selectors', + processCSS, + 'h1,h2,h3,h4,h5,h5,h6{color:blue}', + 'h1,h2,h3,h4,h5,h6{color:blue}' ); test( - 'should trim spaces in :not()', - processCSS, - 'h1:not(.article, .comments){color:blue}', - 'h1:not(.article,.comments){color:blue}' + 'should trim spaces in :not()', + processCSS, + 'h1:not(.article, .comments){color:blue}', + 'h1:not(.article,.comments){color:blue}' ); test( - 'should trim spaces in :not() (2)', - processCSS, - 'h1:not(.article, .comments), h2:not(.lead, .recommendation){color:blue}', - 'h1:not(.article,.comments),h2:not(.lead,.recommendation){color:blue}' + 'should trim spaces in :not() (2)', + processCSS, + 'h1:not(.article, .comments), h2:not(.lead, .recommendation){color:blue}', + 'h1:not(.article,.comments),h2:not(.lead,.recommendation){color:blue}' ); test( - 'should dedupe simple selectors inside :not()', - processCSS, - 'h1:not(h2, h3, h4, h5, h5, h6){color:blue}', - 'h1:not(h2,h3,h4,h5,h6){color:blue}' + 'should dedupe simple selectors inside :not()', + processCSS, + 'h1:not(h2, h3, h4, h5, h5, h6){color:blue}', + 'h1:not(h2,h3,h4,h5,h6){color:blue}' ); test( - 'should normalise attribute selectors', - processCSS, - 'a[ color= "blue" ]{color:blue}', - 'a[color=blue]{color:blue}' + 'should normalise attribute selectors', + processCSS, + 'a[ color= "blue" ]{color:blue}', + 'a[color=blue]{color:blue}' ); test( - 'should normalise attribute selectors (2)', - passthroughCSS, - 'a[class^="options["]:after{color:blue}' + 'should normalise attribute selectors (2)', + passthroughCSS, + 'a[class^="options["]:after{color:blue}' ); test( - 'should normalise attribute selectors (3)', - processCSS, - 'a[class="woop_woop_woop"]{color:blue}', - 'a[class=woop_woop_woop]{color:blue}' + 'should normalise attribute selectors (3)', + processCSS, + 'a[class="woop_woop_woop"]{color:blue}', + 'a[class=woop_woop_woop]{color:blue}' ); test( - 'should normalise attribute selectors (4)', - processCSS, - 'a[class="woop \\\nwoop woop"]{color:blue}', - 'a[class="woop woop woop"]{color:blue}' + 'should normalise attribute selectors (4)', + processCSS, + 'a[class="woop \\\nwoop woop"]{color:blue}', + 'a[class="woop woop woop"]{color:blue}' ); test( - 'should normalise attribute selectors (5)', - processCSS, - 'a[ color = "blue" ]{color:blue}', - 'a[color=blue]{color:blue}' + 'should normalise attribute selectors (5)', + processCSS, + 'a[ color = "blue" ]{color:blue}', + 'a[color=blue]{color:blue}' ); test( - 'should normalise attribute selectors (6)', - processCSS, - 'a[color="blue" i ]{color:blue}', - 'a[color=blue i]{color:blue}' + 'should normalise attribute selectors (6)', + processCSS, + 'a[color="blue" i ]{color:blue}', + 'a[color=blue i]{color:blue}' ); test( - 'should normalise attribute selectors (6)', - processCSS, - 'a[ target ]{color:blue}', - 'a[target]{color:blue}' + 'should normalise attribute selectors (6)', + processCSS, + 'a[ target ]{color:blue}', + 'a[target]{color:blue}' ); test( - 'should convert @keyframe from & 100%', - processCSS, - '@keyframes test{from{color:red}100%{color:blue}}', - '@keyframes test{0%{color:red}to{color:blue}}' + 'should convert @keyframe from & 100%', + processCSS, + '@keyframes test{from{color:red}100%{color:blue}}', + '@keyframes test{0%{color:red}to{color:blue}}' ); test( - 'should convert @keyframe from & 100% (2)', - processCSS, - '@keyframes test{FROM{color:red}100%{color:blue}}', - '@keyframes test{0%{color:red}to{color:blue}}' + 'should convert @keyframe from & 100% (2)', + processCSS, + '@keyframes test{FROM{color:red}100%{color:blue}}', + '@keyframes test{0%{color:red}to{color:blue}}' ); test( - 'should not mangle @keyframe from & 100% in other values', - passthroughCSS, - '@keyframes test{x-from-tag{color:red}5100%{color:blue}}' + 'should not mangle @keyframe from & 100% in other values', + passthroughCSS, + '@keyframes test{x-from-tag{color:red}5100%{color:blue}}' ); test( - 'should not be responsible for normalising comments', - processCSS, - 'h1 /*!test comment*/, h2{color:blue}', - 'h1 /*!test comment*/,h2{color:blue}' + 'should not be responsible for normalising comments', + processCSS, + 'h1 /*!test comment*/, h2{color:blue}', + 'h1 /*!test comment*/,h2{color:blue}' ); test( - 'should not be responsible for normalising coments (2)', - processCSS, - '/*!test comment*/h1, h2{color:blue}', - '/*!test comment*/h1,h2{color:blue}' + 'should not be responsible for normalising coments (2)', + processCSS, + '/*!test comment*/h1, h2{color:blue}', + '/*!test comment*/h1,h2{color:blue}' ); test( - 'should transform ::before to :before', - processCSS, - 'h1::before{color:blue}', - 'h1:before{color:blue}' + 'should transform ::before to :before', + processCSS, + 'h1::before{color:blue}', + 'h1:before{color:blue}' ); test( - 'should transform ::before to :before (2)', - processCSS, - 'h1::BEFORE{color:blue}', - 'h1:BEFORE{color:blue}' + 'should transform ::before to :before (2)', + processCSS, + 'h1::BEFORE{color:blue}', + 'h1:BEFORE{color:blue}' ); test( - 'should transform ::after to :after', - processCSS, - 'h1::after{color:blue}', - 'h1:after{color:blue}' + 'should transform ::after to :after', + processCSS, + 'h1::after{color:blue}', + 'h1:after{color:blue}' ); test( - 'should transform ::first-letter to :first-letter', - processCSS, - 'h1::first-letter{color:blue}', - 'h1:first-letter{color:blue}' + 'should transform ::first-letter to :first-letter', + processCSS, + 'h1::first-letter{color:blue}', + 'h1:first-letter{color:blue}' ); test( - 'should transform ::first-line to :first-line', - processCSS, - 'h1::first-line{color:blue}', - 'h1:first-line{color:blue}' + 'should transform ::first-line to :first-line', + processCSS, + 'h1::first-line{color:blue}', + 'h1:first-line{color:blue}' ); test( - 'should not change strings', - passthroughCSS, - ':not([attr=" h1 a + b /* not a comment */ end of :not from 100% "]){color:blue}' + 'should not change strings', + passthroughCSS, + ':not([attr=" h1 a + b /* not a comment */ end of :not from 100% "]){color:blue}' ); test( - 'should not change strings (2)', - passthroughCSS, - ':not([attr=" h1 a + b /* not a comment */ not end of `:not`: ) from 100% "]){color:blue}' + 'should not change strings (2)', + passthroughCSS, + ':not([attr=" h1 a + b /* not a comment */ not end of `:not`: ) from 100% "]){color:blue}' ); test( - 'should not change strings (3)', - passthroughCSS, - '[a=":not( *.b, h1, h1 )"]{color:blue}' + 'should not change strings (3)', + passthroughCSS, + '[a=":not( *.b, h1, h1 )"]{color:blue}' ); test( - 'should not change strings (4)', - passthroughCSS, - '[a="escaped quotes \\" h1, h1, h1 \\" h1, h1, h1"]{color:blue}' + 'should not change strings (4)', + passthroughCSS, + '[a="escaped quotes \\" h1, h1, h1 \\" h1, h1, h1"]{color:blue}' ); test( - 'should not change strings (5)', - passthroughCSS, - "[a='escaped quotes \\' h1, h1, h1 \\' h1, h1, h1']{color:blue}" + 'should not change strings (5)', + passthroughCSS, + "[a='escaped quotes \\' h1, h1, h1 \\' h1, h1, h1']{color:blue}" ); test( - 'should transform qualified attribute selector inside not', - processCSS, - ':not( *[href="foo"] ){color:blue}', - ':not([href=foo]){color:blue}' + 'should transform qualified attribute selector inside not', + processCSS, + ':not( *[href="foo"] ){color:blue}', + ':not([href=foo]){color:blue}' ); test( - 'should not mangle attribute selectors', - processCSS, - '[class*=" icon-"]+.label, [class^="icon-"]+.label{color:blue}', - '[class*=" icon-"]+.label,[class^=icon-]+.label{color:blue}' + 'should not mangle attribute selectors', + processCSS, + '[class*=" icon-"]+.label, [class^="icon-"]+.label{color:blue}', + '[class*=" icon-"]+.label,[class^=icon-]+.label{color:blue}' ); test( - 'should not mangle attribute selectors (2)', - processCSS, - '.control-group-inline>input[type="radio"]{color:blue}', - '.control-group-inline>input[type=radio]{color:blue}' + 'should not mangle attribute selectors (2)', + processCSS, + '.control-group-inline>input[type="radio"]{color:blue}', + '.control-group-inline>input[type=radio]{color:blue}' ); test( - 'should not mangle quoted attribute selectors that contain =', - passthroughCSS, - '.parent>.child[data-attr~="key1=1"]{color:blue}' + 'should not mangle quoted attribute selectors that contain =', + passthroughCSS, + '.parent>.child[data-attr~="key1=1"]{color:blue}' ); test( - 'should not mangle .from/#from etc', - passthroughCSS, - '#from,.from{color:blue}' + 'should not mangle .from/#from etc', + passthroughCSS, + '#from,.from{color:blue}' ); test( - 'should not mangle pseudo classes', - passthroughCSS, - '.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){color:blue}' + 'should not mangle pseudo classes', + passthroughCSS, + '.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){color:blue}' ); test( - 'should not mangle pseudo classes (2)', - passthroughCSS, - '.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{color:blue}' + 'should not mangle pseudo classes (2)', + passthroughCSS, + '.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{color:blue}' ); test( - 'should not throw on polymer mixins', - passthroughCSS, - '--my-toolbar-theme:{color:blue};' + 'should not throw on polymer mixins', + passthroughCSS, + '--my-toolbar-theme:{color:blue};' ); test( - 'should not throw on polymer mixins (2)', - passthroughCSS, - 'paper-button{--paper-button-ink-color:#009688}' + 'should not throw on polymer mixins (2)', + passthroughCSS, + 'paper-button{--paper-button-ink-color:#009688}' ); test( - 'should not unquote a single hyphen as an attribute value', - passthroughCSS, - '[title="-"]{color:blue}' + 'should not unquote a single hyphen as an attribute value', + passthroughCSS, + '[title="-"]{color:blue}' ); test( - 'should handle case insensitive attribute selectors with extra spaces', - processCSS, - '[title="foo" i ]{color:blue}', - '[title=foo i]{color:blue}' + 'should handle case insensitive attribute selectors with extra spaces', + processCSS, + '[title="foo" i ]{color:blue}', + '[title=foo i]{color:blue}' ); test( - 'should not remove quotes around an empty attribute selector', - passthroughCSS, - '[title=""]{color:blue}' + 'should not remove quotes around an empty attribute selector', + passthroughCSS, + '[title=""]{color:blue}' ); test( - 'should convert :nth-child(1) to :first-child', - processCSS, - 'p:nth-child(1){color:blue}', - 'p:first-child{color:blue}' + 'should convert :nth-child(1) to :first-child', + processCSS, + 'p:nth-child(1){color:blue}', + 'p:first-child{color:blue}' ); test( - 'should convert :nth-child(1) to :first-child (2)', - processCSS, - 'p:NTH-CHILD(1){color:blue}', - 'p:first-child{color:blue}' + 'should convert :nth-child(1) to :first-child (2)', + processCSS, + 'p:NTH-CHILD(1){color:blue}', + 'p:first-child{color:blue}' ); test( - 'should convert :nth-child(2n + 1) to :nth-child(odd)', - processCSS, - 'p:nth-child(2n + 1){color:blue}', - 'p:nth-child(odd){color:blue}' + 'should convert :nth-child(2n + 1) to :nth-child(odd)', + processCSS, + 'p:nth-child(2n + 1){color:blue}', + 'p:nth-child(odd){color:blue}' ); test( - 'should convert :nth-child(even) to :nth-child(2n)', - processCSS, - 'p:nth-child(even){color:blue}', - 'p:nth-child(2n){color:blue}' + 'should convert :nth-child(even) to :nth-child(2n)', + processCSS, + 'p:nth-child(even){color:blue}', + 'p:nth-child(2n){color:blue}' ); test( - 'should convert :nth-child(even) to :nth-child(2n) (2)', - processCSS, - 'p:nth-child(EVEN){color:blue}', - 'p:nth-child(2n){color:blue}' + 'should convert :nth-child(even) to :nth-child(2n) (2)', + processCSS, + 'p:nth-child(EVEN){color:blue}', + 'p:nth-child(2n){color:blue}' ); test( - 'should convert :nth-of-type(1) to :first-of-type', - processCSS, - 'p:nth-of-type(1){color:blue}', - 'p:first-of-type{color:blue}' + 'should convert :nth-of-type(1) to :first-of-type', + processCSS, + 'p:nth-of-type(1){color:blue}', + 'p:first-of-type{color:blue}' ); test( - 'should convert :nth-of-type(2n + 1) to :nth-of-type(odd)', - processCSS, - 'p:nth-of-type(2n + 1){color:blue}', - 'p:nth-of-type(odd){color:blue}' + 'should convert :nth-of-type(2n + 1) to :nth-of-type(odd)', + processCSS, + 'p:nth-of-type(2n + 1){color:blue}', + 'p:nth-of-type(odd){color:blue}' ); test( - 'should convert :nth-of-type(2n + 1) to :nth-of-type(odd) (2)', - processCSS, - 'p:nth-of-type(2N + 1){color:blue}', - 'p:nth-of-type(odd){color:blue}' + 'should convert :nth-of-type(2n + 1) to :nth-of-type(odd) (2)', + processCSS, + 'p:nth-of-type(2N + 1){color:blue}', + 'p:nth-of-type(odd){color:blue}' ); test( - 'should convert :nth-of-type(even) to :nth-of-type(2n)', - processCSS, - 'p:nth-of-type(even){color:blue}', - 'p:nth-of-type(2n){color:blue}' + 'should convert :nth-of-type(even) to :nth-of-type(2n)', + processCSS, + 'p:nth-of-type(even){color:blue}', + 'p:nth-of-type(2n){color:blue}' ); test( - 'should convert :nth-last-child(1) to :last-child', - processCSS, - 'p:nth-last-child(1){color:blue}', - 'p:last-child{color:blue}' + 'should convert :nth-last-child(1) to :last-child', + processCSS, + 'p:nth-last-child(1){color:blue}', + 'p:last-child{color:blue}' ); test( - 'should convert :nth-last-child(2n + 1) to :nth-last-child(odd)', - processCSS, - 'p:nth-last-child(2n + 1){color:blue}', - 'p:nth-last-child(odd){color:blue}' + 'should convert :nth-last-child(2n + 1) to :nth-last-child(odd)', + processCSS, + 'p:nth-last-child(2n + 1){color:blue}', + 'p:nth-last-child(odd){color:blue}' ); test( - 'should convert :nth-last-child(even) to :nth-last-child(2n)', - processCSS, - 'p:nth-last-child(even){color:blue}', - 'p:nth-last-child(2n){color:blue}' + 'should convert :nth-last-child(even) to :nth-last-child(2n)', + processCSS, + 'p:nth-last-child(even){color:blue}', + 'p:nth-last-child(2n){color:blue}' ); test( - 'should convert :nth-last-of-type(1) to :last-of-type', - processCSS, - 'p:nth-last-of-type(1){color:blue}', - 'p:last-of-type{color:blue}' + 'should convert :nth-last-of-type(1) to :last-of-type', + processCSS, + 'p:nth-last-of-type(1){color:blue}', + 'p:last-of-type{color:blue}' ); test( - 'should convert :nth-last-of-type(2n + 1) to :nth-last-of-type(odd)', - processCSS, - 'p:nth-last-of-type(2n + 1){color:blue}', - 'p:nth-last-of-type(odd){color:blue}' + 'should convert :nth-last-of-type(2n + 1) to :nth-last-of-type(odd)', + processCSS, + 'p:nth-last-of-type(2n + 1){color:blue}', + 'p:nth-last-of-type(odd){color:blue}' ); test( - 'should handle :nth-last-of-type(2n + 2)', - processCSS, - 'p:nth-last-of-type(2n + 2){color:blue}', - 'p:nth-last-of-type(2n+2){color:blue}' + 'should handle :nth-last-of-type(2n + 2)', + processCSS, + 'p:nth-last-of-type(2n + 2){color:blue}', + 'p:nth-last-of-type(2n+2){color:blue}' ); test( - 'should convert :nth-last-of-type(even) to :nth-last-of-type(2n)', - processCSS, - 'p:nth-last-of-type(even){color:blue}', - 'p:nth-last-of-type(2n){color:blue}' + 'should convert :nth-last-of-type(even) to :nth-last-of-type(2n)', + processCSS, + 'p:nth-last-of-type(even){color:blue}', + 'p:nth-last-of-type(2n){color:blue}' ); test( - 'should handle first/last of type without parameters', - passthroughCSS, - 'body>h2:not(:first-of-type):not(:last-of-type){color:blue}' + 'should handle first/last of type without parameters', + passthroughCSS, + 'body>h2:not(:first-of-type):not(:last-of-type){color:blue}' ); -test('cssnano issue 39', t => { - const css = 'body{font:100%/1.25 "Open Sans", sans-serif;background:#F6F5F4;overflow-x:hidden}'; - t.notThrows(() => postcss([ magician(), plugin() ]).process(css).css); +test('cssnano issue 39', (t) => { + const css = + 'body{font:100%/1.25 "Open Sans", sans-serif;background:#F6F5F4;overflow-x:hidden}'; + t.notThrows(() => postcss([magician(), plugin()]).process(css).css); }); /* * Reference: https://github.com/tivac/modular-css/issues/228 */ -test('should handle selectors from other plugins', t => { - function encode (str) { - let result = ""; - for (let i = 0; i < str.length; i++) { - result += str.charCodeAt(i).toString(16); - } - - return result; +test('should handle selectors from other plugins', (t) => { + function encode(str) { + let result = ''; + for (let i = 0; i < str.length; i++) { + result += str.charCodeAt(i).toString(16); } - const toModules = postcss.plugin('toModules', () => { - return css => { - css.walkRules(rule => { - rule.selectors = rule.selectors.map(selector => { - const slice = selector.slice(1); - return `.${encode(slice).slice(0, 7)}__${slice}`; - }); - }); - }; - }); - - const css = `.test, /* comment #1 - this comment breaks stuff */ + return result; + } + + const toModules = postcss.plugin('toModules', () => { + return (css) => { + css.walkRules((rule) => { + rule.selectors = rule.selectors.map((selector) => { + const slice = selector.slice(1); + return `.${encode(slice).slice(0, 7)}__${slice}`; + }); + }); + }; + }); + + const css = `.test, /* comment #1 - this comment breaks stuff */ .test:hover { /* comment #2 - ...but this comment is fine */ position: absolute; } @@ -512,19 +516,15 @@ test('should handle selectors from other plugins', t => { .ok { padding: 4px; }`; - const expected = `.7465737__test,.7465737__test:hover { /* comment #2 - ...but this comment is fine */ + const expected = `.7465737__test,.7465737__test:hover { /* comment #2 - ...but this comment is fine */ position: absolute; } .6f6b__ok { padding: 4px; }`; - const res = postcss([toModules, plugin]).process(css).css; - t.deepEqual(res, expected); + const res = postcss([toModules, plugin]).process(css).css; + t.deepEqual(res, expected); }); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-minify-selectors/src/index.js b/packages/postcss-minify-selectors/src/index.js index c5f68649a..5b5d8cc7d 100644 --- a/packages/postcss-minify-selectors/src/index.js +++ b/packages/postcss-minify-selectors/src/index.js @@ -1,220 +1,220 @@ -import {plugin} from "postcss"; -import sort from "alphanum-sort"; -import has from "has"; -import parser from "postcss-selector-parser"; -import unquote from "./lib/unquote"; -import canUnquote from "./lib/canUnquote"; +import { plugin } from 'postcss'; +import sort from 'alphanum-sort'; +import has from 'has'; +import parser from 'postcss-selector-parser'; +import unquote from './lib/unquote'; +import canUnquote from './lib/canUnquote'; const pseudoElements = [ - "::before", - "::after", - "::first-letter", - "::first-line", + '::before', + '::after', + '::first-letter', + '::first-line', ]; -function getParsed (selectors, callback) { - return parser(callback).processSync(selectors); +function getParsed(selectors, callback) { + return parser(callback).processSync(selectors); } -function attribute (selector) { - if (selector.value) { - // Join selectors that are split over new lines - selector.value = selector.value.replace(/\\\n/g, "").trim(); +function attribute(selector) { + if (selector.value) { + // Join selectors that are split over new lines + selector.value = selector.value.replace(/\\\n/g, '').trim(); - if (canUnquote(selector.value)) { - selector.value = unquote(selector.value); - } - - selector.operator = selector.operator.trim(); + if (canUnquote(selector.value)) { + selector.value = unquote(selector.value); } - if (!selector.raws) { - selector.raws = {}; - } + selector.operator = selector.operator.trim(); + } - if (!selector.raws.spaces) { - selector.raws.spaces = {}; - } + if (!selector.raws) { + selector.raws = {}; + } - selector.raws.spaces.attribute = { - before: "", - after: "", - }; + if (!selector.raws.spaces) { + selector.raws.spaces = {}; + } - selector.raws.spaces.operator = { - before: "", - after: "", - }; + selector.raws.spaces.attribute = { + before: '', + after: '', + }; - selector.raws.spaces.value = { - before: "", - after: selector.insensitive ? " " : "", - }; + selector.raws.spaces.operator = { + before: '', + after: '', + }; - if (selector.insensitive) { - selector.raws.spaces.insensitive = { - before: "", - after: "", - }; - } + selector.raws.spaces.value = { + before: '', + after: selector.insensitive ? ' ' : '', + }; + + if (selector.insensitive) { + selector.raws.spaces.insensitive = { + before: '', + after: '', + }; + } - selector.attribute = selector.attribute.trim(); + selector.attribute = selector.attribute.trim(); } -function combinator (selector) { - const value = selector.value.trim(); +function combinator(selector) { + const value = selector.value.trim(); - selector.value = value.length ? value : " "; + selector.value = value.length ? value : ' '; } const pseudoReplacements = { - ":nth-child": ":first-child", - ":nth-of-type": ":first-of-type", - ":nth-last-child": ":last-child", - ":nth-last-of-type": ":last-of-type", + ':nth-child': ':first-child', + ':nth-of-type': ':first-of-type', + ':nth-last-child': ':last-child', + ':nth-last-of-type': ':last-of-type', }; -function pseudo (selector) { - const value = selector.value.toLowerCase(); - - if (selector.nodes.length === 1 && pseudoReplacements[value]) { - const first = selector.at(0); - const one = first.at(0); - - if (first.length === 1) { - if (one.value === "1") { - selector.replaceWith( - parser.pseudo({ - value: pseudoReplacements[value], - }) - ); - } - - if (one.value.toLowerCase() === "even") { - one.value = "2n"; - } - } - - if (first.length === 3) { - const two = first.at(1); - const three = first.at(2); +function pseudo(selector) { + const value = selector.value.toLowerCase(); + + if (selector.nodes.length === 1 && pseudoReplacements[value]) { + const first = selector.at(0); + const one = first.at(0); + + if (first.length === 1) { + if (one.value === '1') { + selector.replaceWith( + parser.pseudo({ + value: pseudoReplacements[value], + }) + ); + } + + if (one.value.toLowerCase() === 'even') { + one.value = '2n'; + } + } - if ( - one.value.toLowerCase() === "2n" && - two.value === "+" && - three.value === "1" - ) { - one.value = "odd"; + if (first.length === 3) { + const two = first.at(1); + const three = first.at(2); - two.remove(); - three.remove(); - } - } + if ( + one.value.toLowerCase() === '2n' && + two.value === '+' && + three.value === '1' + ) { + one.value = 'odd'; - return; + two.remove(); + three.remove(); + } } - const uniques = []; + return; + } - selector.walk(child => { - if (child.type === "selector") { - const childStr = String(child); + const uniques = []; - if (!~uniques.indexOf(childStr)) { - uniques.push(childStr); - } else { - child.remove(); - } - } - }); + selector.walk((child) => { + if (child.type === 'selector') { + const childStr = String(child); - if (~pseudoElements.indexOf(value)) { - selector.value = selector.value.slice(1); + if (!~uniques.indexOf(childStr)) { + uniques.push(childStr); + } else { + child.remove(); + } } + }); + + if (~pseudoElements.indexOf(value)) { + selector.value = selector.value.slice(1); + } } const tagReplacements = { - from: "0%", - "100%": "to", + from: '0%', + '100%': 'to', }; -function tag (selector) { - const value = selector.value.toLowerCase(); +function tag(selector) { + const value = selector.value.toLowerCase(); - if (has(tagReplacements, value)) { - selector.value = tagReplacements[value]; - } + if (has(tagReplacements, value)) { + selector.value = tagReplacements[value]; + } } -function universal (selector) { - const next = selector.next(); +function universal(selector) { + const next = selector.next(); - if (next && next.type !== "combinator") { - selector.remove(); - } + if (next && next.type !== 'combinator') { + selector.remove(); + } } const reducers = { - attribute, - combinator, - pseudo, - tag, - universal, + attribute, + combinator, + pseudo, + tag, + universal, }; -export default plugin("postcss-minify-selectors", () => { - return css => { - const cache = {}; +export default plugin('postcss-minify-selectors', () => { + return (css) => { + const cache = {}; - css.walkRules(rule => { - const selector = - rule.raws.selector && rule.raws.selector.value === rule.selector - ? rule.raws.selector.raw - : rule.selector; - - // If the selector ends with a ':' it is likely a part of a custom mixin, - // so just pass through. - if (selector[selector.length - 1] === ":") { - return; - } + css.walkRules((rule) => { + const selector = + rule.raws.selector && rule.raws.selector.value === rule.selector + ? rule.raws.selector.raw + : rule.selector; - if (cache[selector]) { - rule.selector = cache[selector]; + // If the selector ends with a ':' it is likely a part of a custom mixin, + // so just pass through. + if (selector[selector.length - 1] === ':') { + return; + } - return; - } + if (cache[selector]) { + rule.selector = cache[selector]; - const optimizedSelector = getParsed(selector, selectors => { - selectors.nodes = sort(selectors.nodes, {insensitive: true}); + return; + } - const uniqueSelectors = []; + const optimizedSelector = getParsed(selector, (selectors) => { + selectors.nodes = sort(selectors.nodes, { insensitive: true }); - selectors.walk(sel => { - const {type} = sel; + const uniqueSelectors = []; - // Trim whitespace around the value - sel.spaces.before = sel.spaces.after = ""; + selectors.walk((sel) => { + const { type } = sel; - if (has(reducers, type)) { - reducers[type](sel); + // Trim whitespace around the value + sel.spaces.before = sel.spaces.after = ''; - return; - } + if (has(reducers, type)) { + reducers[type](sel); - const toString = String(sel); + return; + } - if (type === "selector" && sel.parent.type !== "pseudo") { - if (!~uniqueSelectors.indexOf(toString)) { - uniqueSelectors.push(toString); - } else { - sel.remove(); - } - } - }); - }); + const toString = String(sel); - rule.selector = optimizedSelector; - cache[selector] = optimizedSelector; + if (type === 'selector' && sel.parent.type !== 'pseudo') { + if (!~uniqueSelectors.indexOf(toString)) { + uniqueSelectors.push(toString); + } else { + sel.remove(); + } + } }); - }; + }); + + rule.selector = optimizedSelector; + cache[selector] = optimizedSelector; + }); + }; }); diff --git a/packages/postcss-minify-selectors/src/lib/canUnquote.js b/packages/postcss-minify-selectors/src/lib/canUnquote.js index dd54cbed6..b6adac79b 100644 --- a/packages/postcss-minify-selectors/src/lib/canUnquote.js +++ b/packages/postcss-minify-selectors/src/lib/canUnquote.js @@ -6,13 +6,17 @@ import unquote from './unquote'; * https://github.com/mathiasbynens/mothereff.in */ const escapes = /\\([0-9A-Fa-f]{1,6})[ \t\n\f\r]?/g; +// eslint-disable-next-line no-control-regex const range = /[\u0000-\u002c\u002e\u002f\u003A-\u0040\u005B-\u005E\u0060\u007B-\u009f]/; -export default function canUnquote (value) { - value = unquote(value); - if (value === '-' || value === '') { - return false; - } - value = value.replace(escapes, 'a').replace(/\\./g, 'a'); - return !(range.test(value) || /^(?:-?\d|--)/.test(value)); +export default function canUnquote(value) { + value = unquote(value); + + if (value === '-' || value === '') { + return false; + } + + value = value.replace(escapes, 'a').replace(/\\./g, 'a'); + + return !(range.test(value) || /^(?:-?\d|--)/.test(value)); } diff --git a/packages/postcss-minify-selectors/src/lib/unquote.js b/packages/postcss-minify-selectors/src/lib/unquote.js index 588807eaa..1420aacd7 100644 --- a/packages/postcss-minify-selectors/src/lib/unquote.js +++ b/packages/postcss-minify-selectors/src/lib/unquote.js @@ -1 +1 @@ -export default string => string.replace(/["']/g, ''); +export default (string) => string.replace(/["']/g, ''); diff --git a/packages/postcss-normalize-charset/src/__tests__/index.js b/packages/postcss-normalize-charset/src/__tests__/index.js index 290601ba6..001a09b75 100644 --- a/packages/postcss-normalize-charset/src/__tests__/index.js +++ b/packages/postcss-normalize-charset/src/__tests__/index.js @@ -2,91 +2,95 @@ import test from 'ava'; import devtools from 'postcss-devtools'; import postcss from 'postcss'; import plugin from '../'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); -function sourceTest (t, origin) { - return postcss.plugin('source-test', () => { - return function (css) { - let node = css.first; - let source; - if (node.name === 'charset') { - source = node.source; - source = source.input.css.slice(source.start.column - 1, source.end.column); - } - t.deepEqual(source, origin, 'should correctly set source code'); - }; - }); +function sourceTest(t, origin) { + return postcss.plugin('source-test', () => { + return function(css) { + let node = css.first; + let source; + if (node.name === 'charset') { + source = node.source; + source = source.input.css.slice( + source.start.column - 1, + source.end.column + ); + } + t.deepEqual(source, origin, 'should correctly set source code'); + }; + }); } -function processCssWithSource (t, fixture, expected, source) { - const {processCSS: withSource} = processCSSFactory([plugin(), sourceTest(t, source)]); - return withSource(t, fixture, expected); +function processCssWithSource(t, fixture, expected, source) { + const { processCSS: withSource } = processCSSFactory([ + plugin(), + sourceTest(t, source), + ]); + return withSource(t, fixture, expected); } -function processCssBenchmark (t, fixture, expected, options) { - const {processCSS: benchmark} = processCSSFactory([devtools(), plugin(options)]); - return benchmark(t, fixture, expected); +function processCssBenchmark(t, fixture, expected, options) { + const { processCSS: benchmark } = processCSSFactory([ + devtools(), + plugin(options), + ]); + return benchmark(t, fixture, expected); } let copyright = 'a{content:"©"}'; test( - 'should add a charset if a file contains non-ascii', - processCssWithSource, - copyright, - '@charset "utf-8";\n' + copyright, - copyright + 'should add a charset if a file contains non-ascii', + processCssWithSource, + copyright, + '@charset "utf-8";\n' + copyright, + copyright ); test( - 'should move up first existing charset', - processCssWithSource, - 'b{жизнь:калька}@charset "windows-1251";' + copyright, - '@charset "windows-1251";b{жизнь:калька}' + copyright, - 'b{жизнь:калька}' + 'should move up first existing charset', + processCssWithSource, + 'b{жизнь:калька}@charset "windows-1251";' + copyright, + '@charset "windows-1251";b{жизнь:калька}' + copyright, + 'b{жизнь:калька}' ); test( - 'should remove extra charset rules', - processCssWithSource, - copyright + '@charset "utf-8";@charset "windows-1251";', - '@charset "utf-8";\n' + copyright, - copyright + 'should remove extra charset rules', + processCssWithSource, + copyright + '@charset "utf-8";@charset "windows-1251";', + '@charset "utf-8";\n' + copyright, + copyright ); test( - 'should remove all charset rules if a file doesn\'t contain non-ascii', - processCSS, - 'a{content:"c"}@charset "utf-8";@charset "windows-1251";', - 'a{content:"c"}' + "should remove all charset rules if a file doesn't contain non-ascii", + processCSS, + 'a{content:"c"}@charset "utf-8";@charset "windows-1251";', + 'a{content:"c"}' ); test( - 'should not add a charset with add set to false', - passthroughCSS, - copyright, - {add: false} + 'should not add a charset with add set to false', + passthroughCSS, + copyright, + { add: false } ); test( - 'benchmark (add on)', - processCssBenchmark, - copyright, - '@charset "utf-8";\n' + copyright + 'benchmark (add on)', + processCssBenchmark, + copyright, + '@charset "utf-8";\n' + copyright ); -test( - 'benchmark (add off)', - processCssBenchmark, - copyright, - copyright, - {add: false} -); +test('benchmark (add off)', processCssBenchmark, copyright, copyright, { + add: false, +}); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-normalize-charset/src/index.js b/packages/postcss-normalize-charset/src/index.js index a3db44b47..907aca256 100644 --- a/packages/postcss-normalize-charset/src/index.js +++ b/packages/postcss-normalize-charset/src/index.js @@ -3,33 +3,34 @@ import postcss from 'postcss'; let charset = 'charset'; export default postcss.plugin('postcss-normalize-' + charset, (opts = {}) => { - return css => { - let charsetRule; - let nonAsciiNode; - let nonAscii = /[^\x00-\x7F]/; + return (css) => { + let charsetRule; + let nonAsciiNode; + // eslint-disable-next-line no-control-regex + let nonAscii = /[^\x00-\x7F]/; - css.walk(node => { - if (node.type === 'atrule' && node.name === charset) { - if (!charsetRule) { - charsetRule = node; - } - node.remove(); - } else if (!nonAsciiNode && node.parent === css && nonAscii.test(node)) { - nonAsciiNode = node; - } - }); - - if (nonAsciiNode) { - if (!charsetRule && opts.add !== false) { - charsetRule = postcss.atRule({ - name: charset, - params: '"utf-8"', - }); - } - if (charsetRule) { - charsetRule.source = nonAsciiNode.source; - css.prepend(charsetRule); - } + css.walk((node) => { + if (node.type === 'atrule' && node.name === charset) { + if (!charsetRule) { + charsetRule = node; } - }; + node.remove(); + } else if (!nonAsciiNode && node.parent === css && nonAscii.test(node)) { + nonAsciiNode = node; + } + }); + + if (nonAsciiNode) { + if (!charsetRule && opts.add !== false) { + charsetRule = postcss.atRule({ + name: charset, + params: '"utf-8"', + }); + } + if (charsetRule) { + charsetRule.source = nonAsciiNode.source; + css.prepend(charsetRule); + } + } + }; }); diff --git a/packages/postcss-normalize-display-values/src/__tests__/index.js b/packages/postcss-normalize-display-values/src/__tests__/index.js index 8ec9c0a73..0ee3de872 100644 --- a/packages/postcss-normalize-display-values/src/__tests__/index.js +++ b/packages/postcss-normalize-display-values/src/__tests__/index.js @@ -2,44 +2,35 @@ import test from 'ava'; import mappings from '../lib/map'; import plugin from '..'; import getData from '../../../../util/getData'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); const data = getData(mappings); -test( - 'should pass through "block ruby"', - passthroughCSS, - 'display:block ruby;' -); +test('should pass through "block ruby"', passthroughCSS, 'display:block ruby;'); -test( - 'should pass through single values', - passthroughCSS, - 'display:block;' -); +test('should pass through single values', passthroughCSS, 'display:block;'); -Object.keys(data).forEach(key => { - const actual = data[key]; - const expected = key; +Object.keys(data).forEach((key) => { + const actual = data[key]; + const expected = key; - test( - `display: ${actual} => display: ${expected}`, - processCSS, - `display:${actual}`, - `display:${expected}` - ); + test( + `display: ${actual} => display: ${expected}`, + processCSS, + `display:${actual}`, + `display:${expected}` + ); }); test( - `display: block flow => display: block (uppercase property and values)`, - processCSS, - `DISPLAY:BLOCK FLOW`, - `DISPLAY:block` + `display: block flow => display: block (uppercase property and values)`, + processCSS, + `DISPLAY:BLOCK FLOW`, + `DISPLAY:block` ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-normalize-display-values/src/index.js b/packages/postcss-normalize-display-values/src/index.js index 21f56aa78..87ae190d9 100644 --- a/packages/postcss-normalize-display-values/src/index.js +++ b/packages/postcss-normalize-display-values/src/index.js @@ -1,49 +1,49 @@ -import postcss from "postcss"; -import valueParser from "postcss-value-parser"; -import getMatchFactory from "lerna:cssnano-util-get-match"; -import mappings from "./lib/map"; +import postcss from 'postcss'; +import valueParser from 'postcss-value-parser'; +import getMatchFactory from 'lerna:cssnano-util-get-match'; +import mappings from './lib/map'; const getMatch = getMatchFactory(mappings); -function evenValues (list, index) { - return index % 2 === 0; +function evenValues(list, index) { + return index % 2 === 0; } -export default postcss.plugin("postcss-normalize-display-values", () => { - return css => { - const cache = {}; +export default postcss.plugin('postcss-normalize-display-values', () => { + return (css) => { + const cache = {}; - css.walkDecls(/display/i, decl => { - const value = decl.value; + css.walkDecls(/display/i, (decl) => { + const value = decl.value; - if (cache[value]) { - decl.value = cache[value]; + if (cache[value]) { + decl.value = cache[value]; - return; - } + return; + } - const {nodes} = valueParser(value); + const { nodes } = valueParser(value); - if (nodes.length === 1) { - cache[value] = value; + if (nodes.length === 1) { + cache[value] = value; - return; - } + return; + } - const match = getMatch( - nodes.filter(evenValues).map(n => n.value.toLowerCase()) - ); + const match = getMatch( + nodes.filter(evenValues).map((n) => n.value.toLowerCase()) + ); - if (!match) { - cache[value] = value; + if (!match) { + cache[value] = value; - return; - } + return; + } - const result = match; + const result = match; - decl.value = result; - cache[value] = result; - }); - }; + decl.value = result; + cache[value] = result; + }); + }; }); diff --git a/packages/postcss-normalize-display-values/src/lib/map.js b/packages/postcss-normalize-display-values/src/lib/map.js index 32bf3750f..c296f3347 100644 --- a/packages/postcss-normalize-display-values/src/lib/map.js +++ b/packages/postcss-normalize-display-values/src/lib/map.js @@ -22,23 +22,23 @@ const tableCaption = 'table-caption'; */ export default [ - [block, [block, flow]], - [flowRoot, [block, flowRoot]], - [inline, [inline, flow]], - [inlineBlock, [inline, flowRoot]], - [runIn, [runIn, flow]], - [listItem, [listItem, block, flow]], - [inline + ' ' + listItem, [inline, flow, listItem]], - [flex, [block, flex]], - [inlineFlex, [inline, flex]], - [grid, [block, grid]], - [inlineGrid, [inline, grid]], - [ruby, [inline, ruby]], - // `block ruby` is same - [table, [block, table]], - [inlineTable, [inline, table]], - [tableCell, [tableCell, flow]], - [tableCaption, [tableCaption, flow]], - [rubyBase, [rubyBase, flow]], - [rubyText, [rubyText, flow]], + [block, [block, flow]], + [flowRoot, [block, flowRoot]], + [inline, [inline, flow]], + [inlineBlock, [inline, flowRoot]], + [runIn, [runIn, flow]], + [listItem, [listItem, block, flow]], + [inline + ' ' + listItem, [inline, flow, listItem]], + [flex, [block, flex]], + [inlineFlex, [inline, flex]], + [grid, [block, grid]], + [inlineGrid, [inline, grid]], + [ruby, [inline, ruby]], + // `block ruby` is same + [table, [block, table]], + [inlineTable, [inline, table]], + [tableCell, [tableCell, flow]], + [tableCaption, [tableCaption, flow]], + [rubyBase, [rubyBase, flow]], + [rubyText, [rubyText, flow]], ]; diff --git a/packages/postcss-normalize-positions/src/__tests__/index.js b/packages/postcss-normalize-positions/src/__tests__/index.js index a668f2735..b98371a93 100644 --- a/packages/postcss-normalize-positions/src/__tests__/index.js +++ b/packages/postcss-normalize-positions/src/__tests__/index.js @@ -1,200 +1,199 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); const directions = ['top', 'right', 'bottom', 'left', 'center']; const horizontal = { - right: '100%', - left: '0', + right: '100%', + left: '0', }; const vertical = { - bottom: '100%', - top: '0', + bottom: '100%', + top: '0', }; const hkeys = Object.keys(horizontal); const vkeys = Object.keys(vertical); -function suite (t, property, additional = '', tail = '') { - const tests = []; - const push = (...examples) => { - examples.forEach(({fixture, expected}) => { - tests.push( - processCSS( - t, - `${property}${additional}${fixture}${tail}`, - `${property}${additional}${expected}${tail}` - ), - processCSS( - t, - `${property}${additional}${fixture}${tail},${fixture}${tail}`, - `${property}${additional}${expected}${tail},${expected}${tail}` - ), - ); - }); - }; - - if (property === 'background:') { - push({ - message: 'should convert center/50% 50% to /50% 50%', - fixture: '30% center/50% 50%', - expected: '30%/50% 50%', - }); +function suite(t, property, additional = '', tail = '') { + const tests = []; + const push = (...examples) => { + examples.forEach(({ fixture, expected }) => { + tests.push( + processCSS( + t, + `${property}${additional}${fixture}${tail}`, + `${property}${additional}${expected}${tail}` + ), + processCSS( + t, + `${property}${additional}${fixture}${tail},${fixture}${tail}`, + `${property}${additional}${expected}${tail},${expected}${tail}` + ) + ); + }); + }; + + if (property === 'background:') { + push({ + message: + 'should convert center/50% 50% to /50% 50%', + fixture: '30% center/50% 50%', + expected: '30%/50% 50%', + }); + } + push( + { + message: 'should convert center to ', + fixture: `30% center`, + expected: `30%`, + }, + { + message: 'should not convert ', + fixture: `45% 60%`, + expected: `45% 60%`, + } + ); + directions.forEach((direction) => { + let conversion = horizontal[direction] || direction; + if (direction === 'center') { + conversion = '50%'; + } + if ( + direction === 'right' || + direction === 'left' || + direction === 'center' + ) { + push({ + message: `should convert "${direction}" to "${conversion}"`, + fixture: `${direction}`, + expected: `${conversion}`, + }); } push({ - message: 'should convert center to ', - fixture: `30% center`, - expected: `30%`, - }, { - message: 'should not convert ', - fixture: `45% 60%`, - expected: `45% 60%`, + message: `should convert "${direction} center" to "${conversion}"`, + fixture: `${direction} center`, + expected: `${conversion}`, }); - directions.forEach(direction => { - let conversion = horizontal[direction] || direction; - if (direction === 'center') { - conversion = '50%'; + if (direction === 'center') { + return; + } + push({ + message: `should convert "center ${direction}" to "${conversion}"`, + fixture: `center ${direction}`, + expected: `${conversion}`, + }); + directions + .slice(0, -1) + .filter((d) => { + if ( + d === direction || + (~hkeys.indexOf(d) && ~hkeys.indexOf(direction)) || + (~vkeys.indexOf(d) && ~vkeys.indexOf(direction)) + ) { + return false; } - if (direction === 'right' || direction === 'left' || direction === 'center') { - push({ - message: `should convert "${direction}" to "${conversion}"`, - fixture: `${direction}`, - expected: `${conversion}`, - }); + return true; + }) + .forEach((other) => { + let result; + if (~Object.keys(horizontal).indexOf(direction)) { + result = horizontal[direction] + ' ' + vertical[other]; + } else { + result = horizontal[other] + ' ' + vertical[direction]; } - push({ - message: `should convert "${direction} center" to "${conversion}"`, - fixture: `${direction} center`, - expected: `${conversion}`, - }); - if (direction === 'center') { - return; + push( + { + message: `should convert "${direction} ${other}" to "${result}"`, + fixture: `${direction} ${other}`, + expected: `${result}`, + }, + { + message: `should not convert the three value syntax "${direction} ${other} 60px"`, + fixture: `${direction} ${other} 60px`, + expected: `${direction} ${other} 60px`, + } + ); + if (property === 'background:') { + push({ + message: `should convert "${direction} ${other}"/50% 50% to "${result}/50% 50%"`, + fixture: `${direction} ${other}/50% 50%`, + expected: `${result}/50% 50%`, + }); } - push({ - message: `should convert "center ${direction}" to "${conversion}"`, - fixture: `center ${direction}`, - expected: `${conversion}`, - }); - directions.slice(0, -1).filter(d => { - if ( - d === direction || - (~hkeys.indexOf(d) && ~hkeys.indexOf(direction)) || - (~vkeys.indexOf(d) && ~vkeys.indexOf(direction)) - ) { - return false; - } - return true; - }).forEach(other => { - let result; - if (~Object.keys(horizontal).indexOf(direction)) { - result = horizontal[direction] + ' ' + vertical[other]; - } else { - result = horizontal[other] + ' ' + vertical[direction]; - } - push({ - message: `should convert "${direction} ${other}" to "${result}"`, - fixture: `${direction} ${other}`, - expected: `${result}`, - }, { - message: `should not convert the three value syntax "${direction} ${other} 60px"`, - fixture: `${direction} ${other} 60px`, - expected: `${direction} ${other} 60px`, - }); - if (property === 'background:') { - push({ - message: `should convert "${direction} ${other}"/50% 50% to "${result}/50% 50%"`, - fixture: `${direction} ${other}/50% 50%`, - expected: `${result}/50% 50%`, - }); - } - }); - }); + }); + }); - return Promise.all(tests); + return Promise.all(tests); } test( - suite, - 'background:', - 'url(http://example.com/testing/test.png) no-repeat ', - ' #fff' + suite, + 'background:', + 'url(http://example.com/testing/test.png) no-repeat ', + ' #fff' ); -test( - suite, - 'background-position:' -); +test(suite, 'background-position:'); -test( - suite, - 'background:', - '#000 url(cat.jpg) ' -); +test(suite, 'background:', '#000 url(cat.jpg) '); -test( - suite, - 'perspective-origin:' -); +test(suite, 'perspective-origin:'); -test( - suite, - '-webkit-perspective-origin:' -); +test(suite, '-webkit-perspective-origin:'); test( - 'should pass through when there are no position values', - passthroughCSS, - 'background:url(cat.jpg)' + 'should pass through when there are no position values', + passthroughCSS, + 'background:url(cat.jpg)' ); test( - 'should normalize when property in uppercase', - processCSS, - 'BACKGROUND-POSITION: center', - 'BACKGROUND-POSITION: 50%' + 'should normalize when property in uppercase', + processCSS, + 'BACKGROUND-POSITION: center', + 'BACKGROUND-POSITION: 50%' ); test( - 'should normalize when value in uppercase', - processCSS, - 'BACKGROUND-POSITION: CENTER', - 'BACKGROUND-POSITION: 50%' + 'should normalize when value in uppercase', + processCSS, + 'BACKGROUND-POSITION: CENTER', + 'BACKGROUND-POSITION: 50%' ); test( - 'should normalize when value in uppercase (2)', - processCSS, - 'BACKGROUND-POSITION: CENTER, CENTER', - 'BACKGROUND-POSITION: 50%, 50%' + 'should normalize when value in uppercase (2)', + processCSS, + 'BACKGROUND-POSITION: CENTER, CENTER', + 'BACKGROUND-POSITION: 50%, 50%' ); test( - 'should normalize when value in uppercase (3)', - processCSS, - 'BACKGROUND-POSITION: LEFT BOTTOM, LEFT BOTTOM', - 'BACKGROUND-POSITION: 0 100%, 0 100%' + 'should normalize when value in uppercase (3)', + processCSS, + 'BACKGROUND-POSITION: LEFT BOTTOM, LEFT BOTTOM', + 'BACKGROUND-POSITION: 0 100%, 0 100%' ); - test( - 'should normalize when value in uppercase (4)', - processCSS, - 'BACKGROUND-POSITION: BOTTOM LEFT, BOTTOM LEFT', - 'BACKGROUND-POSITION: 0 100%, 0 100%' + 'should normalize when value in uppercase (4)', + processCSS, + 'BACKGROUND-POSITION: BOTTOM LEFT, BOTTOM LEFT', + 'BACKGROUND-POSITION: 0 100%, 0 100%' ); test( - 'should normalize when value in uppercase (5)', - processCSS, - 'BACKGROUND-POSITION: CENTER LEFT, CENTER LEFT', - 'BACKGROUND-POSITION: 0, 0' + 'should normalize when value in uppercase (5)', + processCSS, + 'BACKGROUND-POSITION: CENTER LEFT, CENTER LEFT', + 'BACKGROUND-POSITION: 0, 0' ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-normalize-positions/src/index.js b/packages/postcss-normalize-positions/src/index.js index 46ed071b1..eed4176d4 100644 --- a/packages/postcss-normalize-positions/src/index.js +++ b/packages/postcss-normalize-positions/src/index.js @@ -1,5 +1,5 @@ -import {plugin} from 'postcss'; -import valueParser, {unit} from 'postcss-value-parser'; +import { plugin } from 'postcss'; +import valueParser, { unit } from 'postcss-value-parser'; import getArguments from 'lerna:cssnano-util-get-arguments'; import has from 'has'; @@ -8,126 +8,129 @@ const directions = ['top', 'right', 'bottom', 'left', 'center']; const center = '50%'; const horizontal = { - right: '100%', - left: '0', + right: '100%', + left: '0', }; const vertical = { - bottom: '100%', - top: '0', + bottom: '100%', + top: '0', }; -function transform (value) { - const parsed = valueParser(value); - const args = getArguments(parsed); - const relevant = []; +function transform(value) { + const parsed = valueParser(value); + const args = getArguments(parsed); + const relevant = []; - args.forEach(arg => { - relevant.push({ - start: null, - end: null, - }); + args.forEach((arg) => { + relevant.push({ + start: null, + end: null, + }); - arg.forEach((part, index) => { - const isPosition = ~directions.indexOf(part.value.toLowerCase()) || unit(part.value); - const len = relevant.length - 1; + arg.forEach((part, index) => { + const isPosition = + ~directions.indexOf(part.value.toLowerCase()) || unit(part.value); + const len = relevant.length - 1; - if (relevant[len].start === null && isPosition) { - relevant[len].start = index; - relevant[len].end = index; + if (relevant[len].start === null && isPosition) { + relevant[len].start = index; + relevant[len].end = index; - return; - } + return; + } - if (relevant[len].start !== null) { - if (part.type === 'space') { - return; - } else if (isPosition) { - relevant[len].end = index; + if (relevant[len].start !== null) { + if (part.type === 'space') { + return; + } else if (isPosition) { + relevant[len].end = index; - return; - } + return; + } - return; - } - }); + return; + } }); + }); - relevant.forEach((range, index) => { - if (range.start === null) { - return; - } + relevant.forEach((range, index) => { + if (range.start === null) { + return; + } - const position = args[index].slice(range.start, range.end + 1); + const position = args[index].slice(range.start, range.end + 1); - if (position.length > 3) { - return; - } + if (position.length > 3) { + return; + } - const firstValue = position[0].value.toLowerCase(); - const secondValue = position[2] && position[2].value - ? position[2].value.toLowerCase() - : null; + const firstValue = position[0].value.toLowerCase(); + const secondValue = + position[2] && position[2].value ? position[2].value.toLowerCase() : null; - if (position.length === 1 || secondValue === 'center') { - if (secondValue) { - position[2].value = position[1].value = ''; - } + if (position.length === 1 || secondValue === 'center') { + if (secondValue) { + position[2].value = position[1].value = ''; + } - const map = Object.assign({}, horizontal, { - center, - }); + const map = Object.assign({}, horizontal, { + center, + }); - if (has(map, firstValue)) { - position[0].value = map[firstValue]; - } + if (has(map, firstValue)) { + position[0].value = map[firstValue]; + } - return; - } + return; + } - if (firstValue === 'center' && ~directions.indexOf(secondValue)) { - position[0].value = position[1].value = ''; + if (firstValue === 'center' && ~directions.indexOf(secondValue)) { + position[0].value = position[1].value = ''; - if (has(horizontal, secondValue)) { - position[2].value = horizontal[secondValue]; - } + if (has(horizontal, secondValue)) { + position[2].value = horizontal[secondValue]; + } - return; - } + return; + } - if (has(horizontal, firstValue) && has(vertical, secondValue)) { - position[0].value = horizontal[firstValue]; - position[2].value = vertical[secondValue]; + if (has(horizontal, firstValue) && has(vertical, secondValue)) { + position[0].value = horizontal[firstValue]; + position[2].value = vertical[secondValue]; - return; - } else if (has(vertical, firstValue) && has(horizontal, secondValue)) { - position[0].value = horizontal[secondValue]; - position[2].value = vertical[firstValue]; + return; + } else if (has(vertical, firstValue) && has(horizontal, secondValue)) { + position[0].value = horizontal[secondValue]; + position[2].value = vertical[firstValue]; - return; - } - }); + return; + } + }); - return parsed.toString(); + return parsed.toString(); } export default plugin('postcss-normalize-positions', () => { - return css => { - const cache = {}; + return (css) => { + const cache = {}; - css.walkDecls(/^(background(-position)?|(-webkit-)?perspective-origin)$/i, (decl) => { - const value = decl.value; + css.walkDecls( + /^(background(-position)?|(-webkit-)?perspective-origin)$/i, + (decl) => { + const value = decl.value; - if (cache[value]) { - decl.value = cache[value]; + if (cache[value]) { + decl.value = cache[value]; - return; - } + return; + } - const result = transform(value); + const result = transform(value); - decl.value = result; - cache[value] = result; - }); - }; + decl.value = result; + cache[value] = result; + } + ); + }; }); diff --git a/packages/postcss-normalize-repeat-style/src/__tests__/index.js b/packages/postcss-normalize-repeat-style/src/__tests__/index.js index fcdf829b0..bba260d31 100644 --- a/packages/postcss-normalize-repeat-style/src/__tests__/index.js +++ b/packages/postcss-normalize-repeat-style/src/__tests__/index.js @@ -2,79 +2,73 @@ import test from 'ava'; import mappings from '../lib/map'; import plugin from '..'; import getData from '../../../../util/getData'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); const data = getData(mappings); test( - 'should pass through two value syntax', - passthroughCSS, - 'background:space round' + 'should pass through two value syntax', + passthroughCSS, + 'background:space round' ); -function suite (t, fixture, expected) { - return Promise.all([ - processCSS( - t, - `background:#000 url(cat.jpg) ${fixture} 50%`, - `background:#000 url(cat.jpg) ${expected} 50%` - ), - processCSS( - t, - `background-repeat:${fixture}`, - `background-repeat:${expected}` - ), - processCSS( - t, - `background-repeat:#000 url(cat.jpg) ${fixture} 50%,#000 url(cat.jpg) ${fixture} 50%`, - `background-repeat:#000 url(cat.jpg) ${expected} 50%,#000 url(cat.jpg) ${expected} 50%` - ), - processCSS( - t, - `background-repeat:${fixture},${fixture}`, - `background-repeat:${expected},${expected}` - ), - ]); +function suite(t, fixture, expected) { + return Promise.all([ + processCSS( + t, + `background:#000 url(cat.jpg) ${fixture} 50%`, + `background:#000 url(cat.jpg) ${expected} 50%` + ), + processCSS( + t, + `background-repeat:${fixture}`, + `background-repeat:${expected}` + ), + processCSS( + t, + `background-repeat:#000 url(cat.jpg) ${fixture} 50%,#000 url(cat.jpg) ${fixture} 50%`, + `background-repeat:#000 url(cat.jpg) ${expected} 50%,#000 url(cat.jpg) ${expected} 50%` + ), + processCSS( + t, + `background-repeat:${fixture},${fixture}`, + `background-repeat:${expected},${expected}` + ), + ]); } -Object.keys(data).forEach(conversion => { - const fixture = data[conversion]; - test( - suite, - fixture, - conversion - ); +Object.keys(data).forEach((conversion) => { + const fixture = data[conversion]; + test(suite, fixture, conversion); }); - test( - 'should normalize uppercase property and value', - processCSS, - 'BACKGROUND:#000 url(cat.jpg) REPEAT NO-REPEAT 50%', - 'BACKGROUND:#000 url(cat.jpg) repeat-x 50%' + 'should normalize uppercase property and value', + processCSS, + 'BACKGROUND:#000 url(cat.jpg) REPEAT NO-REPEAT 50%', + 'BACKGROUND:#000 url(cat.jpg) repeat-x 50%' ); test( - 'should pass through when there are no repeat values', - passthroughCSS, - 'background:url(cat.jpg)' + 'should pass through when there are no repeat values', + passthroughCSS, + 'background:url(cat.jpg)' ); test( - 'should pass through when there are no repeat values (2)', - passthroughCSS, - 'background:#000 url(cat.jpg)' + 'should pass through when there are no repeat values (2)', + passthroughCSS, + 'background:#000 url(cat.jpg)' ); test( - 'should pass through the single value syntax', - passthroughCSS, - 'background:#000 url(cat.jpg) repeat' + 'should pass through the single value syntax', + passthroughCSS, + 'background:#000 url(cat.jpg) repeat' ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-normalize-repeat-style/src/index.js b/packages/postcss-normalize-repeat-style/src/index.js index f2b967663..e507c862a 100644 --- a/packages/postcss-normalize-repeat-style/src/index.js +++ b/packages/postcss-normalize-repeat-style/src/index.js @@ -4,8 +4,8 @@ import getArguments from 'lerna:cssnano-util-get-arguments'; import getMatchFactory from 'lerna:cssnano-util-get-match'; import mappings from './lib/map'; -function evenValues (list, index) { - return index % 2 === 0; +function evenValues(list, index) { + return index % 2 === 0; } const repeatKeywords = mappings.map((mapping) => mapping[0]); @@ -13,88 +13,86 @@ const repeatKeywords = mappings.map((mapping) => mapping[0]); const getMatch = getMatchFactory(mappings); export default postcss.plugin('postcss-normalize-repeat-style', () => { - return css => { - const cache = {}; + return (css) => { + const cache = {}; - css.walkDecls(/background(-repeat)?|(-webkit-)?mask-repeat/i, (decl) => { - const value = decl.value; + css.walkDecls(/background(-repeat)?|(-webkit-)?mask-repeat/i, (decl) => { + const value = decl.value; - if (cache[value]) { - decl.value = cache[value]; + if (cache[value]) { + decl.value = cache[value]; - return; - } + return; + } - const parsed = valueParser(value); + const parsed = valueParser(value); - if (parsed.nodes.length === 1) { - cache[value] = value; + if (parsed.nodes.length === 1) { + cache[value] = value; - return; - } + return; + } - const args = getArguments(parsed); - const relevant = []; + const args = getArguments(parsed); + const relevant = []; - args.forEach(arg => { - relevant.push({ - start: null, - end: null, - }); + args.forEach((arg) => { + relevant.push({ + start: null, + end: null, + }); - arg.forEach((part, index) => { - const isRepeat = ~repeatKeywords.indexOf( - part.value.toLowerCase() - ); - const len = relevant.length - 1; + arg.forEach((part, index) => { + const isRepeat = ~repeatKeywords.indexOf(part.value.toLowerCase()); + const len = relevant.length - 1; - if (relevant[len].start === null && isRepeat) { - relevant[len].start = index; - relevant[len].end = index; + if (relevant[len].start === null && isRepeat) { + relevant[len].start = index; + relevant[len].end = index; - return; - } + return; + } - if (relevant[len].start !== null) { - if (part.type === 'space') { - return; - } else if (isRepeat) { - relevant[len].end = index; + if (relevant[len].start !== null) { + if (part.type === 'space') { + return; + } else if (isRepeat) { + relevant[len].end = index; - return; - } + return; + } - return; - } - }); - }); + return; + } + }); + }); - relevant.forEach((range, index) => { - if (range.start === null) { - return; - } + relevant.forEach((range, index) => { + if (range.start === null) { + return; + } - const val = args[index].slice(range.start, range.end + 1); + const val = args[index].slice(range.start, range.end + 1); - if (val.length !== 3) { - return; - } + if (val.length !== 3) { + return; + } - const match = getMatch( - val.filter(evenValues).map(n => n.value.toLowerCase()) - ); + const match = getMatch( + val.filter(evenValues).map((n) => n.value.toLowerCase()) + ); - if (match) { - args[index][range.start].value = match; - args[index][range.start + 1].value = ''; - args[index][range.end].value = ''; - } - }); + if (match) { + args[index][range.start].value = match; + args[index][range.start + 1].value = ''; + args[index][range.end].value = ''; + } + }); - const result = parsed.toString(); + const result = parsed.toString(); - decl.value = result; - cache[value] = result; - }); - }; + decl.value = result; + cache[value] = result; + }); + }; }); diff --git a/packages/postcss-normalize-repeat-style/src/lib/map.js b/packages/postcss-normalize-repeat-style/src/lib/map.js index 2190319b6..87a242d40 100644 --- a/packages/postcss-normalize-repeat-style/src/lib/map.js +++ b/packages/postcss-normalize-repeat-style/src/lib/map.js @@ -1,8 +1,8 @@ export default [ - ['repeat-x', ['repeat', 'no-repeat']], - ['repeat-y', ['no-repeat', 'repeat']], - ['repeat', ['repeat', 'repeat']], - ['space', ['space', 'space']], - ['round', ['round', 'round']], - ['no-repeat', ['no-repeat', 'no-repeat']], + ['repeat-x', ['repeat', 'no-repeat']], + ['repeat-y', ['no-repeat', 'repeat']], + ['repeat', ['repeat', 'repeat']], + ['space', ['space', 'space']], + ['round', ['round', 'round']], + ['no-repeat', ['no-repeat', 'no-repeat']], ]; diff --git a/packages/postcss-normalize-string/src/__tests__/index.js b/packages/postcss-normalize-string/src/__tests__/index.js index 2d658cf06..c4f1ab53a 100644 --- a/packages/postcss-normalize-string/src/__tests__/index.js +++ b/packages/postcss-normalize-string/src/__tests__/index.js @@ -1,87 +1,90 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); test( - 'should prefer double quotes by default', - passthroughCSS, - `p:after{content:""}` + 'should prefer double quotes by default', + passthroughCSS, + `p:after{content:""}` ); test( - 'should transform single quotes to double quotes by default', - processCSS, - `p:after{content:''}`, - `p:after{content:""}` + 'should transform single quotes to double quotes by default', + processCSS, + `p:after{content:''}`, + `p:after{content:""}` ); test( - 'should transform double quotes to single quotes via an option', - processCSS, - `p:after{content:""}`, - `p:after{content:''}`, - {preferredQuote: 'single'} + 'should transform double quotes to single quotes via an option', + processCSS, + `p:after{content:""}`, + `p:after{content:''}`, + { preferredQuote: 'single' } ); test( - 'should keep single quotes inside a double quoted string intact', - passthroughCSS, - `p:after{content:"'string' is intact"}` + 'should keep single quotes inside a double quoted string intact', + passthroughCSS, + `p:after{content:"'string' is intact"}` ); test( - 'should keep double quotes inside a single quoted string intact', - passthroughCSS, - `p:after{content:'"string" is intact'}` + 'should keep double quotes inside a single quoted string intact', + passthroughCSS, + `p:after{content:'"string" is intact'}` ); test( - 'should transform escaped single quotation marks if possible', - processCSS, - `p:after{content:'\\'string\\' is intact'}`, - `p:after{content:"'string' is intact"}` + 'should transform escaped single quotation marks if possible', + processCSS, + `p:after{content:'\\'string\\' is intact'}`, + `p:after{content:"'string' is intact"}` ); test( - 'should transform escaped double quotation marks if possible', - processCSS, - `p:after{content:"\\"string\\" is intact"}`, - `p:after{content:'"string" is intact'}` + 'should transform escaped double quotation marks if possible', + processCSS, + `p:after{content:"\\"string\\" is intact"}`, + `p:after{content:'"string" is intact'}` ); test( - 'should not transform quotation marks when mixed', - passthroughCSS, - `p:after{content:"\\"string\\" is 'intact'"}` + 'should not transform quotation marks when mixed', + passthroughCSS, + `p:after{content:"\\"string\\" is 'intact'"}` ); test( - 'should not transform quotation marks when mixed (2)', - passthroughCSS, - `p:after{content:'"string" is \\'intact\\''}` + 'should not transform quotation marks when mixed (2)', + passthroughCSS, + `p:after{content:'"string" is \\'intact\\''}` ); test( - 'should transform escaped single quotation marks when mixed', - processCSS, - `p:after{content:'\\'string\\' is \\"intact\\"'}`, - `p:after{content:'\\'string\\' is "intact"'}` + 'should transform escaped single quotation marks when mixed', + processCSS, + `p:after{content:'\\'string\\' is \\"intact\\"'}`, + `p:after{content:'\\'string\\' is "intact"'}` ); test( - 'should transform escaped double quotation marks when mixed', - processCSS, - `p:after{content:"\\'string\\' is \\"intact\\""}`, - `p:after{content:"'string' is \\"intact\\""}` + 'should transform escaped double quotation marks when mixed', + processCSS, + `p:after{content:"\\'string\\' is \\"intact\\""}`, + `p:after{content:"'string' is \\"intact\\""}` ); test( - 'should work with the attr function', - processCSS, - `p:after{content:'(' attr(href) ')'}`, - `p:after{content:"(" attr(href) ")"}` + 'should work with the attr function', + processCSS, + `p:after{content:'(' attr(href) ')'}`, + `p:after{content:"(" attr(href) ")"}` ); /* @@ -90,138 +93,134 @@ test( */ test( - 'should join multiple line strings', - processCSS, - `p:after{content:" > this is some really\\ + 'should join multiple line strings', + processCSS, + `p:after{content:" > this is some really\\ long text which is broken\\ over several lines."}`, - `p:after{content:" > this is some really long text which is broken over several lines."}` + `p:after{content:" > this is some really long text which is broken over several lines."}` ); test( - 'should work with quotes', - processCSS, - `q{quotes:'«' "»"}`, - `q{quotes:"«" "»"}` + 'should work with quotes', + processCSS, + `q{quotes:'«' "»"}`, + `q{quotes:"«" "»"}` ); test( - 'should work with language overrides', - processCSS, - `p{font-language-override:'DAN'}`, - `p{font-language-override:"DAN"}` + 'should work with language overrides', + processCSS, + `p{font-language-override:'DAN'}`, + `p{font-language-override:"DAN"}` ); test( - 'should work with css grids', - processCSS, - `p{grid-template:'a a a' "b b b"}`, - `p{grid-template:"a a a" "b b b"}` + 'should work with css grids', + processCSS, + `p{grid-template:'a a a' "b b b"}`, + `p{grid-template:"a a a" "b b b"}` ); test( - 'should work with css grids (2)', - processCSS, - `p{grid-template-areas:'a a a' "b b b"}`, - `p{grid-template-areas:"a a a" "b b b"}` + 'should work with css grids (2)', + processCSS, + `p{grid-template-areas:'a a a' "b b b"}`, + `p{grid-template-areas:"a a a" "b b b"}` ); test( - 'should work with list styles', - processCSS, - `ul{list-style-type:'-'}`, - `ul{list-style-type:"-"}` + 'should work with list styles', + processCSS, + `ul{list-style-type:'-'}`, + `ul{list-style-type:"-"}` ); test( - 'should work with text emphasis styles', - processCSS, - `p{text-emphasis-style:'\\25B2'}`, - `p{text-emphasis-style:"\\25B2"}` + 'should work with text emphasis styles', + processCSS, + `p{text-emphasis-style:'\\25B2'}`, + `p{text-emphasis-style:"\\25B2"}` ); test( - 'should work with text overflow', - processCSS, - `p{text-overflow:'…' '…'}`, - `p{text-overflow:"…" "…"}` + 'should work with text overflow', + processCSS, + `p{text-overflow:'…' '…'}`, + `p{text-overflow:"…" "…"}` ); test( - 'should work with font', - processCSS, - `p{font:1em/1.5 'slab serif'}`, - `p{font:1em/1.5 "slab serif"}` + 'should work with font', + processCSS, + `p{font:1em/1.5 'slab serif'}`, + `p{font:1em/1.5 "slab serif"}` ); test( - 'should work with font family', - processCSS, - `p{font-family:'slab serif'}`, - `p{font-family:"slab serif"}` + 'should work with font family', + processCSS, + `p{font-family:'slab serif'}`, + `p{font-family:"slab serif"}` ); test( - 'should work with font feature settings', - processCSS, - `p{font-feature-settings:'frac'}`, - `p{font-feature-settings:"frac"}` + 'should work with font feature settings', + processCSS, + `p{font-feature-settings:'frac'}`, + `p{font-feature-settings:"frac"}` ); test( - 'should work with web fonts', - processCSS, - `@font-face{font-family:'slab serif';src:local('slab serif'),url(slab.ttf) format('truetype')}`, - `@font-face{font-family:"slab serif";src:local("slab serif"),url(slab.ttf) format("truetype")}`, - // {discardUnused: false} + 'should work with web fonts', + processCSS, + `@font-face{font-family:'slab serif';src:local('slab serif'),url(slab.ttf) format('truetype')}`, + `@font-face{font-family:"slab serif";src:local("slab serif"),url(slab.ttf) format("truetype")}` + // {discardUnused: false} ); test( - 'should remove unnecessary backslashes in urls', - processCSS, - `p{background:url('http://example.com/foo\\\'bar.jpg')}`, - `p{background:url("http://example.com/foo\'bar.jpg")}` - // {normalizeUrl: false} + 'should remove unnecessary backslashes in urls', + processCSS, + `p{background:url('http://example.com/foo\\'bar.jpg')}`, + `p{background:url("http://example.com/foo'bar.jpg")}` + // {normalizeUrl: false} ); test( - 'should remove unnecessary backslashes in urls', - processCSS, - `p{background:url("http://example.com/foo\\\"bar.jpg")}`, - `p{background:url('http://example.com/foo\"bar.jpg')}` - // {normalizeUrl: false} + 'should remove unnecessary backslashes in urls', + processCSS, + `p{background:url("http://example.com/foo\\"bar.jpg")}`, + `p{background:url('http://example.com/foo"bar.jpg')}` + // {normalizeUrl: false} ); test( - 'should work in attribute selectors', - processCSS, - `[rel='external link']{color:#00f}`, - `[rel="external link"]{color:#00f}`, + 'should work in attribute selectors', + processCSS, + `[rel='external link']{color:#00f}`, + `[rel="external link"]{color:#00f}` ); test( - 'should change strings (1)', - processCSS, - `[a="escaped quotes \\" h1, h1, h1 \\" h1, h1, h1"]{color:#00f}`, - `[a='escaped quotes " h1, h1, h1 " h1, h1, h1']{color:#00f}`, + 'should change strings (1)', + processCSS, + `[a="escaped quotes \\" h1, h1, h1 \\" h1, h1, h1"]{color:#00f}`, + `[a='escaped quotes " h1, h1, h1 " h1, h1, h1']{color:#00f}` ); test( - 'should change strings (2)', - processCSS, - `[a='escaped quotes \\' h1, h1, h1 \\' h1, h1, h1']{color:#00f}`, - `[a="escaped quotes ' h1, h1, h1 ' h1, h1, h1"]{color:#00f}`, + 'should change strings (2)', + processCSS, + `[a='escaped quotes \\' h1, h1, h1 \\' h1, h1, h1']{color:#00f}`, + `[a="escaped quotes ' h1, h1, h1 ' h1, h1, h1"]{color:#00f}` ); test( - 'should work for @import', - processCSS, - `@import url('foo.css')`, - `@import url("foo.css")` + 'should work for @import', + processCSS, + `@import url('foo.css')`, + `@import url("foo.css")` ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-normalize-string/src/index.js b/packages/postcss-normalize-string/src/index.js index 70eeb4b50..e539c3b8b 100644 --- a/packages/postcss-normalize-string/src/index.js +++ b/packages/postcss-normalize-string/src/index.js @@ -6,14 +6,14 @@ import has from 'has'; * Constants (parser usage) */ -const SINGLE_QUOTE = '\''.charCodeAt(0); +const SINGLE_QUOTE = "'".charCodeAt(0); const DOUBLE_QUOTE = '"'.charCodeAt(0); -const BACKSLASH = '\\'.charCodeAt(0); -const NEWLINE = '\n'.charCodeAt(0); -const SPACE = ' '.charCodeAt(0); -const FEED = '\f'.charCodeAt(0); -const TAB = '\t'.charCodeAt(0); -const CR = '\r'.charCodeAt(0); +const BACKSLASH = '\\'.charCodeAt(0); +const NEWLINE = '\n'.charCodeAt(0); +const SPACE = ' '.charCodeAt(0); +const FEED = '\f'.charCodeAt(0); +const TAB = '\t'.charCodeAt(0); +const CR = '\r'.charCodeAt(0); const WORD_END = /[ \n\t\r\f'"\\]/g; @@ -41,215 +41,229 @@ const L_NEWLINE = `\\\n`; * Parser nodes */ -const T_ESCAPED_SINGLE_QUOTE = {type: C_ESCAPED_SINGLE_QUOTE, value: `\\'`}; -const T_ESCAPED_DOUBLE_QUOTE = {type: C_ESCAPED_DOUBLE_QUOTE, value: `\\"`}; -const T_SINGLE_QUOTE = {type: C_SINGLE_QUOTE, value: L_SINGLE_QUOTE}; -const T_DOUBLE_QUOTE = {type: C_DOUBLE_QUOTE, value: L_DOUBLE_QUOTE}; -const T_NEWLINE = {type: C_NEWLINE, value: L_NEWLINE}; - -function stringify (ast) { - return ast.nodes.reduce((str, {value}) => { - // Collapse multiple line strings automatically - if (value === L_NEWLINE) { - return str; - } +const T_ESCAPED_SINGLE_QUOTE = { type: C_ESCAPED_SINGLE_QUOTE, value: `\\'` }; +const T_ESCAPED_DOUBLE_QUOTE = { type: C_ESCAPED_DOUBLE_QUOTE, value: `\\"` }; +const T_SINGLE_QUOTE = { type: C_SINGLE_QUOTE, value: L_SINGLE_QUOTE }; +const T_DOUBLE_QUOTE = { type: C_DOUBLE_QUOTE, value: L_DOUBLE_QUOTE }; +const T_NEWLINE = { type: C_NEWLINE, value: L_NEWLINE }; + +function stringify(ast) { + return ast.nodes.reduce((str, { value }) => { + // Collapse multiple line strings automatically + if (value === L_NEWLINE) { + return str; + } - return str + value; - }, ''); + return str + value; + }, ''); } -function parse (str) { - let code, next, value; - let pos = 0; - let len = str.length; - - const ast = { - nodes: [], - types: { - escapedSingleQuote: 0, - escapedDoubleQuote: 0, - singleQuote: 0, - doubleQuote: 0, - }, - quotes: false, - }; - - while (pos < len) { - code = str.charCodeAt(pos); - - switch (code) { - case SPACE: - case TAB: - case CR: - case FEED: - next = pos; - - do { - next += 1; - code = str.charCodeAt(next); - } while (code === SPACE || - code === NEWLINE || - code === TAB || - code === CR || - code === FEED); - - ast.nodes.push({ - type: 'space', - value: str.slice(pos, next), - }); - pos = next - 1; - break; - case SINGLE_QUOTE: - ast.nodes.push(T_SINGLE_QUOTE); - ast.types[C_SINGLE_QUOTE]++; - ast.quotes = true; - break; - case DOUBLE_QUOTE: - ast.nodes.push(T_DOUBLE_QUOTE); - ast.types[C_DOUBLE_QUOTE]++; - ast.quotes = true; - break; - case BACKSLASH: - next = pos + 1; - - if (str.charCodeAt(next) === SINGLE_QUOTE) { - ast.nodes.push(T_ESCAPED_SINGLE_QUOTE); - ast.types[C_ESCAPED_SINGLE_QUOTE]++; - ast.quotes = true; - pos = next; - break; - } else if (str.charCodeAt(next) === DOUBLE_QUOTE) { - ast.nodes.push(T_ESCAPED_DOUBLE_QUOTE); - ast.types[C_ESCAPED_DOUBLE_QUOTE]++; - ast.quotes = true; - pos = next; - break; - } else if (str.charCodeAt(next) === NEWLINE) { - ast.nodes.push(T_NEWLINE); - pos = next; - break; - } - /* - * We need to fall through here to handle the token as - * a whole word. The missing 'break' is intentional. - */ - default: - WORD_END.lastIndex = pos + 1; - WORD_END.test(str); - - if (WORD_END.lastIndex === 0) { - next = len - 1; - } else { - next = WORD_END.lastIndex - 2; - } - - value = str.slice(pos, next + 1); - - ast.nodes.push({ - type: C_STRING, - value, - }); - - pos = next; +function parse(str) { + let code, next, value; + let pos = 0; + let len = str.length; + + const ast = { + nodes: [], + types: { + escapedSingleQuote: 0, + escapedDoubleQuote: 0, + singleQuote: 0, + doubleQuote: 0, + }, + quotes: false, + }; + + while (pos < len) { + code = str.charCodeAt(pos); + + switch (code) { + case SPACE: + case TAB: + case CR: + case FEED: + next = pos; + + do { + next += 1; + code = str.charCodeAt(next); + } while ( + code === SPACE || + code === NEWLINE || + code === TAB || + code === CR || + code === FEED + ); + + ast.nodes.push({ + type: 'space', + value: str.slice(pos, next), + }); + pos = next - 1; + break; + case SINGLE_QUOTE: + ast.nodes.push(T_SINGLE_QUOTE); + ast.types[C_SINGLE_QUOTE]++; + ast.quotes = true; + break; + case DOUBLE_QUOTE: + ast.nodes.push(T_DOUBLE_QUOTE); + ast.types[C_DOUBLE_QUOTE]++; + ast.quotes = true; + break; + case BACKSLASH: + next = pos + 1; + + if (str.charCodeAt(next) === SINGLE_QUOTE) { + ast.nodes.push(T_ESCAPED_SINGLE_QUOTE); + ast.types[C_ESCAPED_SINGLE_QUOTE]++; + ast.quotes = true; + pos = next; + break; + } else if (str.charCodeAt(next) === DOUBLE_QUOTE) { + ast.nodes.push(T_ESCAPED_DOUBLE_QUOTE); + ast.types[C_ESCAPED_DOUBLE_QUOTE]++; + ast.quotes = true; + pos = next; + break; + } else if (str.charCodeAt(next) === NEWLINE) { + ast.nodes.push(T_NEWLINE); + pos = next; + break; + } + /* + * We need to fall through here to handle the token as + * a whole word. The missing 'break' is intentional. + */ + default: + WORD_END.lastIndex = pos + 1; + WORD_END.test(str); + + if (WORD_END.lastIndex === 0) { + next = len - 1; + } else { + next = WORD_END.lastIndex - 2; } - pos ++; - } - return ast; -} + value = str.slice(pos, next + 1); -function changeWrappingQuotes (node, ast) { - const {types} = ast; + ast.nodes.push({ + type: C_STRING, + value, + }); - if (types[C_SINGLE_QUOTE] || types[C_DOUBLE_QUOTE]) { - return; + pos = next; } + pos++; + } + + return ast; +} +function changeWrappingQuotes(node, ast) { + const { types } = ast; + + if (types[C_SINGLE_QUOTE] || types[C_DOUBLE_QUOTE]) { + return; + } + + if ( + node.quote === L_SINGLE_QUOTE && + types[C_ESCAPED_SINGLE_QUOTE] > 0 && + !types[C_ESCAPED_DOUBLE_QUOTE] + ) { + node.quote = L_DOUBLE_QUOTE; + } + + if ( + node.quote === L_DOUBLE_QUOTE && + types[C_ESCAPED_DOUBLE_QUOTE] > 0 && + !types[C_ESCAPED_SINGLE_QUOTE] + ) { + node.quote = L_SINGLE_QUOTE; + } + + ast.nodes = ast.nodes.reduce((newAst, child) => { if ( - node.quote === L_SINGLE_QUOTE && - types[C_ESCAPED_SINGLE_QUOTE] > 0 && - !types[C_ESCAPED_DOUBLE_QUOTE] + child.type === C_ESCAPED_DOUBLE_QUOTE && + node.quote === L_SINGLE_QUOTE ) { - node.quote = L_DOUBLE_QUOTE; + return [...newAst, T_DOUBLE_QUOTE]; } if ( - node.quote === L_DOUBLE_QUOTE && - types[C_ESCAPED_DOUBLE_QUOTE] > 0 && - !types[C_ESCAPED_SINGLE_QUOTE] + child.type === C_ESCAPED_SINGLE_QUOTE && + node.quote === L_DOUBLE_QUOTE ) { - node.quote = L_SINGLE_QUOTE; + return [...newAst, T_SINGLE_QUOTE]; } - ast.nodes = ast.nodes.reduce((newAst, child) => { - if (child.type === C_ESCAPED_DOUBLE_QUOTE && node.quote === L_SINGLE_QUOTE) { - return [...newAst, T_DOUBLE_QUOTE]; - } - - if (child.type === C_ESCAPED_SINGLE_QUOTE && node.quote === L_DOUBLE_QUOTE) { - return [...newAst, T_SINGLE_QUOTE]; - } - - return [...newAst, child]; - }, []); + return [...newAst, child]; + }, []); } -function normalize (value, preferredQuote) { - if (!value || !value.length) { - return value; - } +function normalize(value, preferredQuote) { + if (!value || !value.length) { + return value; + } - return valueParser(value).walk(child => { - if (child.type !== C_STRING) { - return; - } + return valueParser(value) + .walk((child) => { + if (child.type !== C_STRING) { + return; + } - const ast = parse(child.value); + const ast = parse(child.value); - if (ast.quotes) { - changeWrappingQuotes(child, ast); - } else if (preferredQuote === C_SINGLE) { - child.quote = L_SINGLE_QUOTE; - } else { - child.quote = L_DOUBLE_QUOTE; - } + if (ast.quotes) { + changeWrappingQuotes(child, ast); + } else if (preferredQuote === C_SINGLE) { + child.quote = L_SINGLE_QUOTE; + } else { + child.quote = L_DOUBLE_QUOTE; + } - child.value = stringify(ast); - }).toString(); + child.value = stringify(ast); + }) + .toString(); } const params = { - rule: 'selector', - decl: 'value', - atrule: 'params', + rule: 'selector', + decl: 'value', + atrule: 'params', }; -export default postcss.plugin('postcss-normalize-string', opts => { - const {preferredQuote} = Object.assign({}, { - preferredQuote: 'double', - }, opts); +export default postcss.plugin('postcss-normalize-string', (opts) => { + const { preferredQuote } = Object.assign( + {}, + { + preferredQuote: 'double', + }, + opts + ); - return css => { - const cache = {}; + return (css) => { + const cache = {}; - css.walk(node => { - const {type} = node; + css.walk((node) => { + const { type } = node; - if (has(params, type)) { - const param = params[type]; - const key = node[param] + '|' + preferredQuote; + if (has(params, type)) { + const param = params[type]; + const key = node[param] + '|' + preferredQuote; - if (cache[key]) { - node[param] = cache[key]; + if (cache[key]) { + node[param] = cache[key]; - return; - } + return; + } - const result = normalize(node[param], preferredQuote); + const result = normalize(node[param], preferredQuote); - node[param] = result; - cache[key] = result; - } - }); - }; + node[param] = result; + cache[key] = result; + } + }); + }; }); diff --git a/packages/postcss-normalize-timing-functions/src/__tests__/index.js b/packages/postcss-normalize-timing-functions/src/__tests__/index.js index cc2a034d8..62d3f1091 100644 --- a/packages/postcss-normalize-timing-functions/src/__tests__/index.js +++ b/packages/postcss-normalize-timing-functions/src/__tests__/index.js @@ -1,127 +1,80 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; - -const {processCSS} = processCSSFactory(plugin); - -function testTimingFunction (t, fixture, expected) { - return Promise.all([ - processCSS(t, `animation:fade 3s ${fixture}`, `animation:fade 3s ${expected}`), - processCSS(t, `animation-timing-function:${fixture}`, `animation-timing-function:${expected}`), - processCSS(t, `transition:color 3s ${fixture}`, `transition:color 3s ${expected}`), - processCSS(t, `transition-timing-function:${fixture}`, `transition-timing-function:${expected}`), - ]); +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; + +const { processCSS } = processCSSFactory(plugin); + +function testTimingFunction(t, fixture, expected) { + return Promise.all([ + processCSS( + t, + `animation:fade 3s ${fixture}`, + `animation:fade 3s ${expected}` + ), + processCSS( + t, + `animation-timing-function:${fixture}`, + `animation-timing-function:${expected}` + ), + processCSS( + t, + `transition:color 3s ${fixture}`, + `transition:color 3s ${expected}` + ), + processCSS( + t, + `transition-timing-function:${fixture}`, + `transition-timing-function:${expected}` + ), + ]); } -function testPassthrough (t, fixture) { - return testTimingFunction(t, fixture, fixture); +function testPassthrough(t, fixture) { + return testTimingFunction(t, fixture, fixture); } -test( - testTimingFunction, - 'cubic-bezier(0.25, 0.1, 0.25, 1)', - 'ease' -); +test(testTimingFunction, 'cubic-bezier(0.25, 0.1, 0.25, 1)', 'ease'); test( - 'should support uppercase property ', - processCSS, - 'h1{animation: cubic-bezier(0.25, 0.1, 0.25, 1)}', - 'h1{animation: ease}' + 'should support uppercase property ', + processCSS, + 'h1{animation: cubic-bezier(0.25, 0.1, 0.25, 1)}', + 'h1{animation: ease}' ); -test( - testTimingFunction, - 'CUBIC-BEZIER(0.25, 0.1, 0.25, 1)', - 'ease' -); +test(testTimingFunction, 'CUBIC-BEZIER(0.25, 0.1, 0.25, 1)', 'ease'); -test( - testTimingFunction, - 'cubic-bezier(0, 0, 1, 1)', - 'linear' -); +test(testTimingFunction, 'cubic-bezier(0, 0, 1, 1)', 'linear'); -test( - testTimingFunction, - 'cubic-bezier(0.42, 0, 1, 1)', - 'ease-in' -); +test(testTimingFunction, 'cubic-bezier(0.42, 0, 1, 1)', 'ease-in'); -test( - testTimingFunction, - 'cubic-bezier(0, 0, 0.58, 1)', - 'ease-out' -); +test(testTimingFunction, 'cubic-bezier(0, 0, 0.58, 1)', 'ease-out'); -test( - testTimingFunction, - 'cubic-bezier(0.42, 0, 0.58, 1)', - 'ease-in-out' -); +test(testTimingFunction, 'cubic-bezier(0.42, 0, 0.58, 1)', 'ease-in-out'); -test( - testTimingFunction, - 'steps(1, start)', - 'step-start' -); +test(testTimingFunction, 'steps(1, start)', 'step-start'); -test( - testTimingFunction, - 'steps(1, START)', - 'step-start' -); +test(testTimingFunction, 'steps(1, START)', 'step-start'); -test( - testTimingFunction, - 'STEPS(1, start)', - 'step-start' -); +test(testTimingFunction, 'STEPS(1, start)', 'step-start'); -test( - testPassthrough, - 'steps(1)' -); +test(testPassthrough, 'steps(1)'); -test( - testPassthrough, - 'STEPS(1)' -); +test(testPassthrough, 'STEPS(1)'); -test( - testPassthrough, - 'steps(5,start)' -); +test(testPassthrough, 'steps(5,start)'); -test( - testTimingFunction, - 'steps(10, end)', - 'steps(10)' -); +test(testTimingFunction, 'steps(10, end)', 'steps(10)'); -test( - testTimingFunction, - 'steps(10, END)', - 'steps(10)' -); +test(testTimingFunction, 'steps(10, END)', 'steps(10)'); -test( - testPassthrough, - 'steps(15)' -); +test(testPassthrough, 'steps(15)'); -test( - testPassthrough, - 'var(--anim1)' -); +test(testPassthrough, 'var(--anim1)'); -test( - testPassthrough, - 'VAR(--anim1)' -); +test(testPassthrough, 'VAR(--anim1)'); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-normalize-timing-functions/src/index.js b/packages/postcss-normalize-timing-functions/src/index.js index c8f5e2f37..90a90fcbc 100644 --- a/packages/postcss-normalize-timing-functions/src/index.js +++ b/packages/postcss-normalize-timing-functions/src/index.js @@ -1,4 +1,4 @@ -import {plugin} from 'postcss'; +import { plugin } from 'postcss'; import valueParser from 'postcss-value-parser'; import getMatchFactory from 'lerna:cssnano-util-get-match'; import mappings from './lib/map'; @@ -6,74 +6,76 @@ import mappings from './lib/map'; const getMatch = getMatchFactory(mappings); const getValue = (node) => parseFloat(node.value); -function evenValues (list, index) { - return index % 2 === 0; +function evenValues(list, index) { + return index % 2 === 0; } -function reduce (node) { - if (node.type !== 'function') { - return false; - } - - const lowerCasedValue = node.value.toLowerCase(); +function reduce(node) { + if (node.type !== 'function') { + return false; + } - if (lowerCasedValue === 'steps') { - // Don't bother checking the step-end case as it has the same length - // as steps(1) - if ( - getValue(node.nodes[0]) === 1 && - node.nodes[2] && - node.nodes[2].value.toLowerCase() === 'start' - ) { - node.type = 'word'; - node.value = 'step-start'; + const lowerCasedValue = node.value.toLowerCase(); - delete node.nodes; + if (lowerCasedValue === 'steps') { + // Don't bother checking the step-end case as it has the same length + // as steps(1) + if ( + getValue(node.nodes[0]) === 1 && + node.nodes[2] && + node.nodes[2].value.toLowerCase() === 'start' + ) { + node.type = 'word'; + node.value = 'step-start'; - return; - } + delete node.nodes; - // The end case is actually the browser default, so it isn't required. - if (node.nodes[2] && node.nodes[2].value.toLowerCase() === 'end') { - node.nodes = [node.nodes[0]]; + return; + } - return; - } + // The end case is actually the browser default, so it isn't required. + if (node.nodes[2] && node.nodes[2].value.toLowerCase() === 'end') { + node.nodes = [node.nodes[0]]; - return false; + return; } - if (lowerCasedValue === 'cubic-bezier') { - const match = getMatch(node.nodes.filter(evenValues).map(getValue)); + return false; + } + + if (lowerCasedValue === 'cubic-bezier') { + const match = getMatch(node.nodes.filter(evenValues).map(getValue)); - if (match) { - node.type = 'word'; - node.value = match; + if (match) { + node.type = 'word'; + node.value = match; - delete node.nodes; + delete node.nodes; - return; - } + return; } + } } export default plugin('postcss-normalize-timing-functions', () => { - return css => { - const cache = {}; + return (css) => { + const cache = {}; - css.walkDecls(/(animation|transition)(-timing-function|$)/i, decl => { - const value = decl.value; + css.walkDecls(/(animation|transition)(-timing-function|$)/i, (decl) => { + const value = decl.value; - if (cache[value]) { - decl.value = cache[value]; + if (cache[value]) { + decl.value = cache[value]; - return; - } + return; + } - const result = valueParser(value).walk(reduce).toString(); + const result = valueParser(value) + .walk(reduce) + .toString(); - decl.value = result; - cache[value] = result; - }); - }; + decl.value = result; + cache[value] = result; + }); + }; }); diff --git a/packages/postcss-normalize-timing-functions/src/lib/map.js b/packages/postcss-normalize-timing-functions/src/lib/map.js index 3bd35d5c6..54beac9f1 100644 --- a/packages/postcss-normalize-timing-functions/src/lib/map.js +++ b/packages/postcss-normalize-timing-functions/src/lib/map.js @@ -1,7 +1,7 @@ export default [ - ['ease', [0.25, 0.1, 0.25, 1]], - ['linear', [0, 0, 1, 1]], - ['ease-in', [0.42, 0, 1, 1]], - ['ease-out', [0, 0, 0.58, 1]], - ['ease-in-out', [0.42, 0, 0.58, 1]], + ['ease', [0.25, 0.1, 0.25, 1]], + ['linear', [0, 0, 1, 1]], + ['ease-in', [0.42, 0, 1, 1]], + ['ease-out', [0, 0, 0.58, 1]], + ['ease-in-out', [0.42, 0, 0.58, 1]], ]; diff --git a/packages/postcss-normalize-unicode/src/__tests__/index.js b/packages/postcss-normalize-unicode/src/__tests__/index.js index fc93d0228..2b8828dff 100644 --- a/packages/postcss-normalize-unicode/src/__tests__/index.js +++ b/packages/postcss-normalize-unicode/src/__tests__/index.js @@ -1,119 +1,114 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {passthroughCSS, processCSS} = processCSSFactory(plugin); +const { passthroughCSS, processCSS } = processCSSFactory(plugin); -function fixture (range) { - return `@font-face{font-family:test;unicode-range:${range}}*{font-family:test}`; +function fixture(range) { + return `@font-face{font-family:test;unicode-range:${range}}*{font-family:test}`; } test( - 'should convert a unicode range to a wildcard range', - processCSS, - fixture('u+2b00-2bff'), // Miscellaneous Symbols and Arrows - fixture('u+2b??'), - {env: 'not ie'} + 'should convert a unicode range to a wildcard range', + processCSS, + fixture('u+2b00-2bff'), // Miscellaneous Symbols and Arrows + fixture('u+2b??'), + { env: 'not ie' } ); test( - 'should convert a unicode range to a wildcard range (2)', - processCSS, - fixture('u+1e00-1eff'), // Latin Extended Additional - fixture('u+1e??'), - {env: 'not ie'} + 'should convert a unicode range to a wildcard range (2)', + processCSS, + fixture('u+1e00-1eff'), // Latin Extended Additional + fixture('u+1e??'), + { env: 'not ie' } ); test( - 'should convert a unicode range to a wildcard range (3)', - processCSS, - fixture('u+2120-212f'), - fixture('u+212?'), - {env: 'not ie'} + 'should convert a unicode range to a wildcard range (3)', + processCSS, + fixture('u+2120-212f'), + fixture('u+212?'), + { env: 'not ie' } ); test( - 'should convert a unicode range to a wildcard range (4)', - processCSS, - fixture('u+2100-21ff'), - fixture('u+21??'), - {env: 'not ie'} + 'should convert a unicode range to a wildcard range (4)', + processCSS, + fixture('u+2100-21ff'), + fixture('u+21??'), + { env: 'not ie' } ); test( - 'should convert a unicode range to a wildcard range (5)', - processCSS, - fixture('u+2000-2fff'), - fixture('u+2???'), - {env: 'not ie'} + 'should convert a unicode range to a wildcard range (5)', + processCSS, + fixture('u+2000-2fff'), + fixture('u+2???'), + { env: 'not ie' } ); test( - 'should pass through a unicode range that cannot be reduced', - passthroughCSS, - fixture('u+0-7f'), // Basic Latin - {env: 'not ie'} + 'should pass through a unicode range that cannot be reduced', + passthroughCSS, + fixture('u+0-7f'), // Basic Latin + { env: 'not ie' } ); test( - 'should pass through a unicode range that cannot be reduced (2)', - passthroughCSS, - fixture('u+2125-2128'), - {env: 'not ie'} + 'should pass through a unicode range that cannot be reduced (2)', + passthroughCSS, + fixture('u+2125-2128'), + { env: 'not ie' } ); test( - 'should pass through a unicode range that cannot be reduced (3)', - passthroughCSS, - fixture('u+2012-2f12'), - {env: 'not ie'} + 'should pass through a unicode range that cannot be reduced (3)', + passthroughCSS, + fixture('u+2012-2f12'), + { env: 'not ie' } ); test( - 'should pass through a unicode range that cannot be reduced (4)', - passthroughCSS, - fixture('u+2002-2ff2'), - {env: 'not ie'} + 'should pass through a unicode range that cannot be reduced (4)', + passthroughCSS, + fixture('u+2002-2ff2'), + { env: 'not ie' } ); test( - 'should pass through css variables', - passthroughCSS, - fixture('var(--caseInsensitive)') + 'should pass through css variables', + passthroughCSS, + fixture('var(--caseInsensitive)') ); -test( - 'should pass through initial', - passthroughCSS, - fixture('initial') -); +test('should pass through initial', passthroughCSS, fixture('initial')); test( - 'should downcase the unicode-range property/value pair', - processCSS, - '@font-face{font-family:test;UNICODE-RANGE:U+07-F}*{font-family:test}', - '@font-face{font-family:test;UNICODE-RANGE:u+07-f}*{font-family:test}', - {env: 'not ie'} + 'should downcase the unicode-range property/value pair', + processCSS, + '@font-face{font-family:test;UNICODE-RANGE:U+07-F}*{font-family:test}', + '@font-face{font-family:test;UNICODE-RANGE:u+07-f}*{font-family:test}', + { env: 'not ie' } ); test( - 'should upcase the "u" prefix (IE)', - processCSS, - fixture('u+2002-2ff2'), - fixture('U+2002-2ff2'), - {env: 'ie9'} + 'should upcase the "u" prefix (IE)', + processCSS, + fixture('u+2002-2ff2'), + fixture('U+2002-2ff2'), + { env: 'ie9' } ); test( - 'should upcase the "u" prefix (Edge 15)', - processCSS, - fixture('u+2002-2ff2'), - fixture('U+2002-2ff2'), - {env: 'edge15'} + 'should upcase the "u" prefix (Edge 15)', + processCSS, + fixture('u+2002-2ff2'), + fixture('U+2002-2ff2'), + { env: 'edge15' } ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-normalize-unicode/src/index.js b/packages/postcss-normalize-unicode/src/index.js index 797a13c26..224f9af87 100644 --- a/packages/postcss-normalize-unicode/src/index.js +++ b/packages/postcss-normalize-unicode/src/index.js @@ -4,43 +4,45 @@ import valueParser from 'postcss-value-parser'; const regexLowerCaseUPrefix = /^u(?=\+)/; -function unicode (range) { - const values = range.slice(2).split('-'); - if (values.length < 2) { - return range; +function unicode(range) { + const values = range.slice(2).split('-'); + + if (values.length < 2) { + return range; + } + + const left = values[0].split(''); + const right = values[1].split(''); + + if (left.length !== right.length) { + return range; + } + + let questionCounter = 0; + + const merged = left.reduce((group, value, index) => { + if (group === false) { + return false; } - const left = values[0].split(''); - const right = values[1].split(''); - if (left.length !== right.length) { - return range; + if (value === right[index] && !questionCounter) { + return group + value; } - let questionCounter = 0; - - const merged = left.reduce((group, value, index) => { - if (group === false) { - return false; - } - if (value === right[index] && !questionCounter) { - return group + value; - } - if (value === '0' && right[index] === 'f') { - questionCounter ++; - return group + '?'; - } - return false; - }, 'u+'); - - /* - * The maximum number of wildcard characters (?) for ranges is 5. - */ - - if (merged && questionCounter < 6) { - return merged; + if (value === '0' && right[index] === 'f') { + questionCounter++; + return group + '?'; } - return range; + return false; + }, 'u+'); + + // The maximum number of wildcard characters (?) for ranges is 5. + if (merged && questionCounter < 6) { + return merged; + } + + return range; } /* @@ -49,28 +51,36 @@ function unicode (range) { * https://caniuse.com/#search=unicode-range */ -function hasLowerCaseUPrefixBug (browser) { - return ~browserslist('ie <=11, edge <= 15').indexOf(browser); +function hasLowerCaseUPrefixBug(browser) { + return ~browserslist('ie <=11, edge <= 15').indexOf(browser); } -function transform (legacy = false, node) { - node.value = valueParser(node.value).walk(child => { - if (child.type === 'word') { - const transformed = unicode(child.value.toLowerCase()); - child.value = legacy ? transformed.replace(regexLowerCaseUPrefix, 'U') : transformed; - } - return false; - }).toString(); +function transform(legacy = false, node) { + node.value = valueParser(node.value) + .walk((child) => { + if (child.type === 'word') { + const transformed = unicode(child.value.toLowerCase()); + child.value = legacy + ? transformed.replace(regexLowerCaseUPrefix, 'U') + : transformed; + } + return false; + }) + .toString(); } export default postcss.plugin('postcss-normalize-unicode', () => { - return (css, result) => { - const resultOpts = result.opts || {}; - const browsers = browserslist(null, { - stats: resultOpts.stats, - path: __dirname, - env: resultOpts.env, - }); - css.walkDecls(/^unicode-range$/i, transform.bind(null, browsers.some(hasLowerCaseUPrefixBug))); - }; + return (css, result) => { + const resultOpts = result.opts || {}; + const browsers = browserslist(null, { + stats: resultOpts.stats, + path: __dirname, + env: resultOpts.env, + }); + + css.walkDecls( + /^unicode-range$/i, + transform.bind(null, browsers.some(hasLowerCaseUPrefixBug)) + ); + }; }); diff --git a/packages/postcss-normalize-url/src/__tests__/index.js b/packages/postcss-normalize-url/src/__tests__/index.js index 5b4225fa0..88799039b 100644 --- a/packages/postcss-normalize-url/src/__tests__/index.js +++ b/packages/postcss-normalize-url/src/__tests__/index.js @@ -1,289 +1,284 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); test( - 'should strip double quotes', - processCSS, - 'h1{background:url("cat.jpg")}', - 'h1{background:url(cat.jpg)}' + 'should strip double quotes', + processCSS, + 'h1{background:url("cat.jpg")}', + 'h1{background:url(cat.jpg)}' ); test( - 'should strip single quotes', - processCSS, - 'h1{background:url(\'cat.jpg\')}', - 'h1{background:url(cat.jpg)}' + 'should strip single quotes', + processCSS, + "h1{background:url('cat.jpg')}", + 'h1{background:url(cat.jpg)}' ); test( - 'should strip double quotes uppercase URL', - processCSS, - 'h1{background:URL("cat.jpg")}', - 'h1{background:URL(cat.jpg)}' + 'should strip double quotes uppercase URL', + processCSS, + 'h1{background:URL("cat.jpg")}', + 'h1{background:URL(cat.jpg)}' ); test( - 'should escape special characters', - processCSS, - 'h1{background:url("http://website.com/assets)_test.png")}', - 'h1{background:url(http://website.com/assets\\)_test.png)}' + 'should escape special characters', + processCSS, + 'h1{background:url("http://website.com/assets)_test.png")}', + 'h1{background:url(http://website.com/assets\\)_test.png)}' ); test( - 'should not escape more than one special character', - passthroughCSS, - 'h1{background:url("http://website.com/assets_(test).png")}' + 'should not escape more than one special character', + passthroughCSS, + 'h1{background:url("http://website.com/assets_(test).png")}' ); test( - 'should remove the default port', - processCSS, - 'h1{background:url(http://website.com:80/image.png)}', - 'h1{background:url(http://website.com/image.png)}' + 'should remove the default port', + processCSS, + 'h1{background:url(http://website.com:80/image.png)}', + 'h1{background:url(http://website.com/image.png)}' ); test( - 'should not remove the fragment', - passthroughCSS, - 'h1{background:url(test.svg#icon)}' + 'should not remove the fragment', + passthroughCSS, + 'h1{background:url(test.svg#icon)}' ); test( - 'should not remove the fragment in absolute urls', - passthroughCSS, - 'h1{background:url(http://website.com/test.svg#icon)}' + 'should not remove the fragment in absolute urls', + passthroughCSS, + 'h1{background:url(http://website.com/test.svg#icon)}' ); test( - 'should normalize directory traversal', - processCSS, - 'h1{background:url(http://website.com/assets/css/../font/t.eot)}', - 'h1{background:url(http://website.com/assets/font/t.eot)}' + 'should normalize directory traversal', + processCSS, + 'h1{background:url(http://website.com/assets/css/../font/t.eot)}', + 'h1{background:url(http://website.com/assets/font/t.eot)}' ); test( - 'should normalize directory traversal in relative urls', - processCSS, - 'h1{background:url(css/../font/t.eot)}', - 'h1{background:url(font/t.eot)}' + 'should normalize directory traversal in relative urls', + processCSS, + 'h1{background:url(css/../font/t.eot)}', + 'h1{background:url(font/t.eot)}' ); test( - 'should trim current directory indicator in relative urls', - processCSS, - 'h1{background:url(./images/cat.png)}', - 'h1{background:url(images/cat.png)}' + 'should trim current directory indicator in relative urls', + processCSS, + 'h1{background:url(./images/cat.png)}', + 'h1{background:url(images/cat.png)}' ); test( - 'should do the above tests, stripping quotes', - processCSS, - 'h1{background:url("./css/../font/t.eot")}', - 'h1{background:url(font/t.eot)}' + 'should do the above tests, stripping quotes', + processCSS, + 'h1{background:url("./css/../font/t.eot")}', + 'h1{background:url(font/t.eot)}' ); test( - 'should normalize urls with special characters', - processCSS, - 'h1{background:url("http://website.com/test/../(images)/1.png")}', - 'h1{background:url("http://website.com/(images)/1.png")}' + 'should normalize urls with special characters', + processCSS, + 'h1{background:url("http://website.com/test/../(images)/1.png")}', + 'h1{background:url("http://website.com/(images)/1.png")}' ); test( - 'should normalize relative urls with special characters', - processCSS, - 'h1{background:url("test/../(images)/1.png")}', - 'h1{background:url("(images)/1.png")}' + 'should normalize relative urls with special characters', + processCSS, + 'h1{background:url("test/../(images)/1.png")}', + 'h1{background:url("(images)/1.png")}' ); test( - 'should minimise whitespace inside the url function', - processCSS, - 'h1{background:url( test.png )}', - 'h1{background:url(test.png)}' + 'should minimise whitespace inside the url function', + processCSS, + 'h1{background:url( test.png )}', + 'h1{background:url(test.png)}' ); test( - 'should minimise whitespace inside the url function (2)', - processCSS, - 'h1{background:url( )}', - 'h1{background:url()}' + 'should minimise whitespace inside the url function (2)', + processCSS, + 'h1{background:url( )}', + 'h1{background:url()}' ); test( - 'should minimise whitespace inside the url string', - processCSS, - 'h1{background:url(" test.png ")}', - 'h1{background:url(test.png)}' + 'should minimise whitespace inside the url string', + processCSS, + 'h1{background:url(" test.png ")}', + 'h1{background:url(test.png)}' ); test( - 'should minimise whitespace inside the url string (2)', - processCSS, - 'h1{background:url(" ")}', - 'h1{background:url()}' + 'should minimise whitespace inside the url string (2)', + processCSS, + 'h1{background:url(" ")}', + 'h1{background:url()}' ); test( - 'should minimise whitespace with special characters', - processCSS, - 'h1{background:url(" test (2015).png ")}', - 'h1{background:url("test (2015).png")}' + 'should minimise whitespace with special characters', + processCSS, + 'h1{background:url(" test (2015).png ")}', + 'h1{background:url("test (2015).png")}' ); test( - 'should join multiline url functions', - processCSS, - 'h1{background:url("some really long string \\\nspanning multiple lines")}', - 'h1{background:url("some really long string spanning multiple lines")}' + 'should join multiline url functions', + processCSS, + 'h1{background:url("some really long string \\\nspanning multiple lines")}', + 'h1{background:url("some really long string spanning multiple lines")}' ); test( - 'should process multiple backgrounds', - processCSS, - 'h1{background:url( "./test/../foo/bar.jpg" ), url("http://website.com/img.jpg")}', - 'h1{background:url(foo/bar.jpg), url(http://website.com/img.jpg)}' + 'should process multiple backgrounds', + processCSS, + 'h1{background:url( "./test/../foo/bar.jpg" ), url("http://website.com/img.jpg")}', + 'h1{background:url(foo/bar.jpg), url(http://website.com/img.jpg)}' ); test( - 'should not mangle chrome extension urls', - processCSS, - 'h1{background-image:url(\'chrome-extension://__MSG_@@extension_id__/someFile.png\')}', - 'h1{background-image:url(chrome-extension://__MSG_@@extension_id__/someFile.png)}' + 'should not mangle chrome extension urls', + processCSS, + "h1{background-image:url('chrome-extension://__MSG_@@extension_id__/someFile.png')}", + 'h1{background-image:url(chrome-extension://__MSG_@@extension_id__/someFile.png)}' ); test( - 'should not mangle mozila extension urls', - processCSS, - 'h1{background-image:url(\'moz-extension://__MSG_@@extension_id__/someFile.png\')}', - 'h1{background-image:url(moz-extension://__MSG_@@extension_id__/someFile.png)}' + 'should not mangle mozila extension urls', + processCSS, + "h1{background-image:url('moz-extension://__MSG_@@extension_id__/someFile.png')}", + 'h1{background-image:url(moz-extension://__MSG_@@extension_id__/someFile.png)}' ); test( - 'should not mangle other extension urls', - processCSS, - 'h1{background-image:url(\'other-other-extension://__MSG_@@extension_id__/someFile.png\')}', - 'h1{background-image:url(other-other-extension://__MSG_@@extension_id__/someFile.png)}' + 'should not mangle other extension urls', + processCSS, + "h1{background-image:url('other-other-extension://__MSG_@@extension_id__/someFile.png')}", + 'h1{background-image:url(other-other-extension://__MSG_@@extension_id__/someFile.png)}' ); test( - 'should not mangle data urls', - passthroughCSS, - '.has-svg:before{content:url("data:image/svg+xml;utf8,")}', + 'should not mangle data urls', + passthroughCSS, + '.has-svg:before{content:url("data:image/svg+xml;utf8,")}' ); test( - 'should not mangle data urls (2)', - passthroughCSS, - '.has-svg:before{content:url("DATA:image/svg+xml;utf8,")}', + 'should not mangle data urls (2)', + passthroughCSS, + '.has-svg:before{content:url("DATA:image/svg+xml;utf8,")}' ); test( - 'should not mangle empty data urls', - passthroughCSS, - '.has-svg:before{content:url(data:,Hello%2C%20World!)}', + 'should not mangle empty data urls', + passthroughCSS, + '.has-svg:before{content:url(data:,Hello%2C%20World!)}' ); test( - 'should not mangle plain data urls', - passthroughCSS, - '.has-svg:before{content:url(data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D)}', + 'should not mangle plain data urls', + passthroughCSS, + '.has-svg:before{content:url(data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D)}' ); test( - 'should not mangle text/html data urls', - passthroughCSS, - '.has-svg:before{content:url(data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E)}', + 'should not mangle text/html data urls', + passthroughCSS, + '.has-svg:before{content:url(data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E)}' ); test( - 'should not mangle text/html data urls (2)', - passthroughCSS, - '.has-svg:before{content:url("data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E")}', + 'should not mangle text/html data urls (2)', + passthroughCSS, + '.has-svg:before{content:url("data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E")}' ); test( - 'should not mangle embedded fonts', - passthroughCSS, - '.font:before{src:url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SAscAAAC8AAAAYGNtYXAaVsyNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZryFoPwAAAF4AAAB3GhlYWQG2Pc9AAADVAAAADZoaGVhCB4EXgAAA4wAAAAkaG10eCC6AcMAAAOwAAAALGxvY2EBvgJWAAAD3AAAABhtYXhwABEAMgAAA/QAAAAgbmFtZVxlIn0AAAQUAAABknBvc3QAAwAAAAAFqAAAACAAAwOXAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADmBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg5gb//f//AAAAAAAg5gD//f//AAH/4xoEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABABAAggP6AukABgAAEwkBJwkBBxAB9QH1j/6a/pqPAmD+IgHeif6qAVaJAAEA0f/PAzgDuAAGAAAJAjcJAScCsP4hAd+I/qsBVYgDuP4L/gyPAWUBZo8AAQDR/8oDOAOzAAYAAAUJAQcJARcBWgHe/iKJAVb+qok2AfUB9I/+m/6ajwABAA0AggP2AukABgAACQIXCQE3A/b+DP4LjwFmAWWPAQsB3v4iiQFW/qqJAAUAAP/ABAADwAAEAAgAFQAiAC8AABMzESMRAwkBIQEUBiMiJjU0NjMyFhURFAYjIiY1NDYzMhYVERQGIyImNTQ2MzIWFeyenuwBOwE7/YoEAEUxMUVFMTFFRTExRUUxMUVFMTFFRTExRQPA/OwDFP2K/nYBigIAMUVFMTFFRTH87DFFRTExRUUxAYoxRUUxMUVFMQABAAf/zgRfA7IABgAACQI3FwEXBF/9LP58ffgCSpkDLvygAX2J7wLNhAAAAAH//f/mAhMDmgAHAAAJAjcBFQEnAb3+QAHAVv5nAZlWA5r+Jv4mUQGyUgGyUQAAAAEAAAABAABWiO5BXw889QALBAAAAAAA0a7ZYAAAAADRrtlg//3/wARfA8AAAAAIAAIAAAAAAAAAAQAAA8D/wAAABJL//QAABF8AAQAAAAAAAAAAAAAAAAAAAAsEAAAAAAAAAAAAAAACAAAABAAAEAQAANEEAADRBAAADQQAAAAEkgAHAif//QAAAAAACgAUAB4ANABKAGAAdgDAANYA7gABAAAACwAwAAUAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEACAAAAAEAAAAAAAIABwBpAAEAAAAAAAMACAA5AAEAAAAAAAQACAB+AAEAAAAAAAUACwAYAAEAAAAAAAYACABRAAEAAAAAAAoAGgCWAAMAAQQJAAEAEAAIAAMAAQQJAAIADgBwAAMAAQQJAAMAEABBAAMAAQQJAAQAEACGAAMAAQQJAAUAFgAjAAMAAQQJAAYAEABZAAMAAQQJAAoANACwYmV0MzY1VUkAYgBlAHQAMwA2ADUAVQBJVmVyc2lvbiAxLjAAVgBlAHIAcwBpAG8AbgAgADEALgAwYmV0MzY1VUkAYgBlAHQAMwA2ADUAVQBJYmV0MzY1VUkAYgBlAHQAMwA2ADUAVQBJUmVndWxhcgBSAGUAZwB1AGwAYQByYmV0MzY1VUkAYgBlAHQAMwA2ADUAVQBJRm9udCBnZW5lcmF0ZWQgYnkgSWNvTW9vbi4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==) format(\'truetype\')}', + 'should not mangle embedded fonts', + passthroughCSS, + ".font:before{src:url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SAscAAAC8AAAAYGNtYXAaVsyNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZryFoPwAAAF4AAAB3GhlYWQG2Pc9AAADVAAAADZoaGVhCB4EXgAAA4wAAAAkaG10eCC6AcMAAAOwAAAALGxvY2EBvgJWAAAD3AAAABhtYXhwABEAMgAAA/QAAAAgbmFtZVxlIn0AAAQUAAABknBvc3QAAwAAAAAFqAAAACAAAwOXAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADmBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg5gb//f//AAAAAAAg5gD//f//AAH/4xoEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABABAAggP6AukABgAAEwkBJwkBBxAB9QH1j/6a/pqPAmD+IgHeif6qAVaJAAEA0f/PAzgDuAAGAAAJAjcJAScCsP4hAd+I/qsBVYgDuP4L/gyPAWUBZo8AAQDR/8oDOAOzAAYAAAUJAQcJARcBWgHe/iKJAVb+qok2AfUB9I/+m/6ajwABAA0AggP2AukABgAACQIXCQE3A/b+DP4LjwFmAWWPAQsB3v4iiQFW/qqJAAUAAP/ABAADwAAEAAgAFQAiAC8AABMzESMRAwkBIQEUBiMiJjU0NjMyFhURFAYjIiY1NDYzMhYVERQGIyImNTQ2MzIWFeyenuwBOwE7/YoEAEUxMUVFMTFFRTExRUUxMUVFMTFFRTExRQPA/OwDFP2K/nYBigIAMUVFMTFFRTH87DFFRTExRUUxAYoxRUUxMUVFMQABAAf/zgRfA7IABgAACQI3FwEXBF/9LP58ffgCSpkDLvygAX2J7wLNhAAAAAH//f/mAhMDmgAHAAAJAjcBFQEnAb3+QAHAVv5nAZlWA5r+Jv4mUQGyUgGyUQAAAAEAAAABAABWiO5BXw889QALBAAAAAAA0a7ZYAAAAADRrtlg//3/wARfA8AAAAAIAAIAAAAAAAAAAQAAA8D/wAAABJL//QAABF8AAQAAAAAAAAAAAAAAAAAAAAsEAAAAAAAAAAAAAAACAAAABAAAEAQAANEEAADRBAAADQQAAAAEkgAHAif//QAAAAAACgAUAB4ANABKAGAAdgDAANYA7gABAAAACwAwAAUAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEACAAAAAEAAAAAAAIABwBpAAEAAAAAAAMACAA5AAEAAAAAAAQACAB+AAEAAAAAAAUACwAYAAEAAAAAAAYACABRAAEAAAAAAAoAGgCWAAMAAQQJAAEAEAAIAAMAAQQJAAIADgBwAAMAAQQJAAMAEABBAAMAAQQJAAQAEACGAAMAAQQJAAUAFgAjAAMAAQQJAAYAEABZAAMAAQQJAAoANACwYmV0MzY1VUkAYgBlAHQAMwA2ADUAVQBJVmVyc2lvbiAxLjAAVgBlAHIAcwBpAG8AbgAgADEALgAwYmV0MzY1VUkAYgBlAHQAMwA2ADUAVQBJYmV0MzY1VUkAYgBlAHQAMwA2ADUAVQBJUmVndWxhcgBSAGUAZwB1AGwAYQByYmV0MzY1VUkAYgBlAHQAMwA2ADUAVQBJRm9udCBnZW5lcmF0ZWQgYnkgSWNvTW9vbi4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==) format('truetype')}" ); test( - 'should not mangle embedded fonts (2)', - passthroughCSS, - '.font:before{src:url(data:font/truetype;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8RC0oAAAC8AAAAYGNtYXAAMwCZAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5ZgMDpbEAAAFwAAAAPGhlYWQErmD9AAABrAAAADZoaGVhA8IDxQAAAeQAAAAkaG10eAYAAAAAAAIIAAAAEGxvY2EAKAAUAAACGAAAAAptYXhwAAYABQAAAiQAAAAgbmFtZZlKCfsAAAJEAAABhnBvc3QAAwAAAAADzAAAACAAAwIAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAABAAAAAIAPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAMAAAAAgACAACAAAAAQAg//3//wAAAAAAIP/9//8AAf/jAAMAAQAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAEAAMnP4PlfDzz1AAsEAAAAAADSyBAAAAAAANLIEAAAAAAAAAAAAAAAAAgAAgAAAAAAAAABAAADwP/AAAAEAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAABAQAAAAAAAAAAAAAAAIAAAAAAAAAAAoAFAAeAAAAAQAAAAQAAwABAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGljb21vb24AaQBjAG8AbQBvAG8AblZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGljb21vb24AaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AblJlZ3VsYXIAUgBlAGcAdQBsAGEAcmljb21vb24AaQBjAG8AbQBvAG8AbkZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\') format(\'truetype\')}' + 'should not mangle embedded fonts (2)', + passthroughCSS, + ".font:before{src:url(data:font/truetype;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8RC0oAAAC8AAAAYGNtYXAAMwCZAAABHAAAAExnYXNwAAAAEAAAAWgAAAAIZ2x5ZgMDpbEAAAFwAAAAPGhlYWQErmD9AAABrAAAADZoaGVhA8IDxQAAAeQAAAAkaG10eAYAAAAAAAIIAAAAEGxvY2EAKAAUAAACGAAAAAptYXhwAAYABQAAAiQAAAAgbmFtZZlKCfsAAAJEAAABhnBvc3QAAwAAAAADzAAAACAAAwIAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAABAAAAAIAPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAMAAAAAgACAACAAAAAQAg//3//wAAAAAAIP/9//8AAf/jAAMAAQAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAEAAMnP4PlfDzz1AAsEAAAAAADSyBAAAAAAANLIEAAAAAAAAAAAAAAAAAgAAgAAAAAAAAABAAADwP/AAAAEAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAABAQAAAAAAAAAAAAAAAIAAAAAAAAAAAoAFAAeAAAAAQAAAAQAAwABAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGljb21vb24AaQBjAG8AbQBvAG8AblZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGljb21vb24AaQBjAG8AbQBvAG8Abmljb21vb24AaQBjAG8AbQBvAG8AblJlZ3VsYXIAUgBlAGcAdQBsAGEAcmljb21vb24AaQBjAG8AbQBvAG8AbkZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=') format('truetype')}" ); test( - 'should optimise @namespace urls', - processCSS, - '@namespace islands url(" http://bar.yandex.ru/ui/islands");', - '@namespace islands "http://bar.yandex.ru/ui/islands";' + 'should optimise @namespace urls', + processCSS, + '@namespace islands url(" http://bar.yandex.ru/ui/islands");', + '@namespace islands "http://bar.yandex.ru/ui/islands";' ); test( - 'should optimise @namespace urls (2)', - processCSS, - '@namespace islands url(http://bar.yandex.ru/ui/islands );', - '@namespace islands "http://bar.yandex.ru/ui/islands";' + 'should optimise @namespace urls (2)', + processCSS, + '@namespace islands url(http://bar.yandex.ru/ui/islands );', + '@namespace islands "http://bar.yandex.ru/ui/islands";' ); test( - 'should optimise @namespace urls (3)', - processCSS, - '@namespace islands " http://bar.yandex.ru/ui/islands ";', - '@namespace islands "http://bar.yandex.ru/ui/islands";' + 'should optimise @namespace urls (3)', + processCSS, + '@namespace islands " http://bar.yandex.ru/ui/islands ";', + '@namespace islands "http://bar.yandex.ru/ui/islands";' ); test( - 'should optimise @namespace urls (4)', - processCSS, - '@NAMESPACE islands " http://bar.yandex.ru/ui/islands ";', - '@NAMESPACE islands "http://bar.yandex.ru/ui/islands";' + 'should optimise @namespace urls (4)', + processCSS, + '@NAMESPACE islands " http://bar.yandex.ru/ui/islands ";', + '@NAMESPACE islands "http://bar.yandex.ru/ui/islands";' ); test( - 'should not normalize @document urls', - passthroughCSS, - '@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/){body{font-size:2em}}' + 'should not normalize @document urls', + passthroughCSS, + '@document url(http://www.w3.org/),url-prefix(http://www.w3.org/Style/){body{font-size:2em}}' ); test( - 'should handle protocol relative urls', - processCSS, - 'h1{background:url(//website.com:80/image.png)}', - 'h1{background:url(//website.com/image.png)}' + 'should handle protocol relative urls', + processCSS, + 'h1{background:url(//website.com:80/image.png)}', + 'h1{background:url(//website.com/image.png)}' ); test( - 'should pass through when it doesn\'t find a url function', - passthroughCSS, - 'h1{color:black;font-weight:bold}' + "should pass through when it doesn't find a url function", + passthroughCSS, + 'h1{color:black;font-weight:bold}' ); test( - 'should pass through non-url empty functions', - passthroughCSS, - 'h1{shape-outside:circle()}' + 'should pass through non-url empty functions', + passthroughCSS, + 'h1{shape-outside:circle()}' ); -test( - 'should pass through empty url', - passthroughCSS, - 'h1{background:url()}' -); +test('should pass through empty url', passthroughCSS, 'h1{background:url()}'); test( - 'should pass through invalid url', - passthroughCSS, - 'h1{background:url(http://)}' + 'should pass through invalid url', + passthroughCSS, + 'h1{background:url(http://)}' ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-normalize-url/src/index.js b/packages/postcss-normalize-url/src/index.js index 6b2bd8c13..98d3c2f88 100644 --- a/packages/postcss-normalize-url/src/index.js +++ b/packages/postcss-normalize-url/src/index.js @@ -5,99 +5,115 @@ import normalize from 'normalize-url'; import isAbsolute from 'is-absolute-url'; const multiline = /\\[\r\n]/; +// eslint-disable-next-line no-useless-escape const escapeChars = /([\s\(\)"'])/g; -function convert (url, options) { - if (isAbsolute(url) || url.startsWith('//')) { - let normalizedURL = null; +function convert(url, options) { + if (isAbsolute(url) || url.startsWith('//')) { + let normalizedURL = null; - try { - normalizedURL = normalize(url, options); - } catch (e) { - normalizedURL = url; - } - - return normalizedURL; + try { + normalizedURL = normalize(url, options); + } catch (e) { + normalizedURL = url; } - // `path.normalize` always returns backslashes on Windows, need replace in `/` - return path.normalize(url).replace(new RegExp('\\' + path.sep, 'g'), '/'); + return normalizedURL; + } + + // `path.normalize` always returns backslashes on Windows, need replace in `/` + return path.normalize(url).replace(new RegExp('\\' + path.sep, 'g'), '/'); } -function transformNamespace (rule) { - rule.params = valueParser(rule.params).walk(node => { - if (node.type === 'function' && node.value.toLowerCase() === 'url' && node.nodes.length) { - node.type = 'string'; - node.quote = node.nodes[0].quote || '"'; - node.value = node.nodes[0].value; - } - if (node.type === 'string') { - node.value = node.value.trim(); - } - return false; - }).toString(); +function transformNamespace(rule) { + rule.params = valueParser(rule.params) + .walk((node) => { + if ( + node.type === 'function' && + node.value.toLowerCase() === 'url' && + node.nodes.length + ) { + node.type = 'string'; + node.quote = node.nodes[0].quote || '"'; + node.value = node.nodes[0].value; + } + if (node.type === 'string') { + node.value = node.value.trim(); + } + return false; + }) + .toString(); } -function transformDecl (decl, opts) { - decl.value = valueParser(decl.value).walk(node => { - if ( - node.type !== 'function' || - node.value.toLowerCase() !== 'url' || - !node.nodes.length - ) { - return false; - } +function transformDecl(decl, opts) { + decl.value = valueParser(decl.value) + .walk((node) => { + if ( + node.type !== 'function' || + node.value.toLowerCase() !== 'url' || + !node.nodes.length + ) { + return false; + } - let url = node.nodes[0]; - let escaped; + let url = node.nodes[0]; + let escaped; - node.before = node.after = ''; - url.value = url.value.trim().replace(multiline, ''); + node.before = node.after = ''; + url.value = url.value.trim().replace(multiline, ''); - // Skip empty URLs - // Empty URL function equals request to current stylesheet where it is declared - if (url.value.length === 0) { - url.quote = ''; + // Skip empty URLs + // Empty URL function equals request to current stylesheet where it is declared + if (url.value.length === 0) { + url.quote = ''; - return false; - } + return false; + } - if (/^data:(.*)?,/i.test(url.value)) { - return false; - } + if (/^data:(.*)?,/i.test(url.value)) { + return false; + } - if (!(/^.+-extension:\//i.test(url.value))) { - url.value = convert(url.value, opts); - } + if (!/^.+-extension:\//i.test(url.value)) { + url.value = convert(url.value, opts); + } - if (escapeChars.test(url.value) && url.type === 'string') { - escaped = url.value.replace(escapeChars, '\\$1'); - if (escaped.length < url.value.length + 2) { - url.value = escaped; - url.type = 'word'; - } - } else { - url.type = 'word'; + if (escapeChars.test(url.value) && url.type === 'string') { + escaped = url.value.replace(escapeChars, '\\$1'); + if (escaped.length < url.value.length + 2) { + url.value = escaped; + url.type = 'word'; } + } else { + url.type = 'word'; + } - return false; - }).toString(); + return false; + }) + .toString(); } -export default postcss.plugin('postcss-normalize-url', opts => { - opts = Object.assign({}, { - normalizeProtocol: false, - stripFragment: false, - stripWWW: false, - }, opts); - - return css => { - css.walk(node => { - if (node.type === 'decl') { - return transformDecl(node, opts); - } else if (node.type === 'atrule' && node.name.toLowerCase() === 'namespace') { - return transformNamespace(node); - } - }); - }; +export default postcss.plugin('postcss-normalize-url', (opts) => { + opts = Object.assign( + {}, + { + normalizeProtocol: false, + stripFragment: false, + stripWWW: false, + }, + opts + ); + + return (css) => { + css.walk((node) => { + if (node.type === 'decl') { + return transformDecl(node, opts); + } else if ( + node.type === 'atrule' && + node.name.toLowerCase() === 'namespace' + ) { + return transformNamespace(node); + } + }); + }; }); diff --git a/packages/postcss-normalize-whitespace/src/__tests__/index.js b/packages/postcss-normalize-whitespace/src/__tests__/index.js index dddf68db6..c6a851f63 100644 --- a/packages/postcss-normalize-whitespace/src/__tests__/index.js +++ b/packages/postcss-normalize-whitespace/src/__tests__/index.js @@ -1,30 +1,32 @@ import test from 'ava'; import plugin from '..'; import { - processCSSFactory, - processCSSWithPresetFactory, + processCSSFactory, + processCSSWithPresetFactory, } from '../../../../util/testHelpers'; -const {processCSS} = processCSSFactory(plugin); -const {processCSS: withDefaultPreset} = processCSSWithPresetFactory('default'); +const { processCSS } = processCSSFactory(plugin); +const { processCSS: withDefaultPreset } = processCSSWithPresetFactory( + 'default' +); test( - 'should trim whitespace from nested functions', - processCSS, - 'h1{width:calc(10px - ( 100px / var(--test) ))}', - 'h1{width:calc(10px - (100px / var(--test)))}' + 'should trim whitespace from nested functions', + processCSS, + 'h1{width:calc(10px - ( 100px / var(--test) ))}', + 'h1{width:calc(10px - (100px / var(--test)))}' ); test( - 'should trim whitespace from nested functions (uppercase "calc")', - processCSS, - 'h1{width:CALC(10px - ( 100px / var(--test) ))}', - 'h1{width:CALC(10px - (100px / var(--test)))}' + 'should trim whitespace from nested functions (uppercase "calc")', + processCSS, + 'h1{width:CALC(10px - ( 100px / var(--test) ))}', + 'h1{width:CALC(10px - (100px / var(--test)))}' ); test( - 'should trim whitespace from nested functions (preset)', - withDefaultPreset, - 'h1{width:calc(10px - ( 100px / var(--test) ))}', - 'h1{width:calc(10px - 100px/var(--test))}', + 'should trim whitespace from nested functions (preset)', + withDefaultPreset, + 'h1{width:calc(10px - ( 100px / var(--test) ))}', + 'h1{width:calc(10px - 100px/var(--test))}' ); diff --git a/packages/postcss-normalize-whitespace/src/index.js b/packages/postcss-normalize-whitespace/src/index.js index 2d2a38b19..68a4f7524 100644 --- a/packages/postcss-normalize-whitespace/src/index.js +++ b/packages/postcss-normalize-whitespace/src/index.js @@ -1,84 +1,84 @@ -import {plugin} from "postcss"; -import valueParser from "postcss-value-parser"; - -const atrule = "atrule"; -const decl = "decl"; -const rule = "rule"; - -function reduceCalcWhitespaces (node) { - if (node.type === "space") { - node.value = " "; - } else if (node.type === "function") { - node.before = node.after = ""; - } +import { plugin } from 'postcss'; +import valueParser from 'postcss-value-parser'; + +const atrule = 'atrule'; +const decl = 'decl'; +const rule = 'rule'; + +function reduceCalcWhitespaces(node) { + if (node.type === 'space') { + node.value = ' '; + } else if (node.type === 'function') { + node.before = node.after = ''; + } } -function reduceWhitespaces (node) { - if (node.type === "space") { - node.value = " "; - } else if (node.type === "div") { - node.before = node.after = ""; - } else if (node.type === "function") { - node.before = node.after = ""; - - if (node.value.toLowerCase() === "calc") { - valueParser.walk(node.nodes, reduceCalcWhitespaces); - return false; - } +function reduceWhitespaces(node) { + if (node.type === 'space') { + node.value = ' '; + } else if (node.type === 'div') { + node.before = node.after = ''; + } else if (node.type === 'function') { + node.before = node.after = ''; + + if (node.value.toLowerCase() === 'calc') { + valueParser.walk(node.nodes, reduceCalcWhitespaces); + return false; } + } } -export default plugin("postcss-normalize-whitespace", () => { - return css => { - const cache = {}; - - css.walk(node => { - const {type} = node; - - if (~[decl, rule, atrule].indexOf(type) && node.raws.before) { - node.raws.before = node.raws.before.replace(/\s/g, ""); - } - - if (type === decl) { - // Ensure that !important values do not have any excess whitespace - if (node.important) { - node.raws.important = "!important"; - } - - // Remove whitespaces around ie 9 hack - node.value = node.value.replace(/\s*(\\9)\s*/, "$1"); - - const value = node.value; - - if (cache[value]) { - node.value = cache[value]; - } else { - const parsed = valueParser(node.value); - const result = parsed.walk(reduceWhitespaces).toString(); - - // Trim whitespace inside functions & dividers - node.value = result; - cache[value] = result; - } - - // Remove extra semicolons and whitespace before the declaration - if (node.raws.before) { - const prev = node.prev(); - - if (prev && prev.type !== rule) { - node.raws.before = node.raws.before.replace(/;/g, ""); - } - } - - node.raws.between = ":"; - node.raws.semicolon = false; - } else if (type === rule || type === atrule) { - node.raws.between = node.raws.after = ""; - node.raws.semicolon = false; - } - }); - - // Remove final newline - css.raws.after = ""; - }; +export default plugin('postcss-normalize-whitespace', () => { + return (css) => { + const cache = {}; + + css.walk((node) => { + const { type } = node; + + if (~[decl, rule, atrule].indexOf(type) && node.raws.before) { + node.raws.before = node.raws.before.replace(/\s/g, ''); + } + + if (type === decl) { + // Ensure that !important values do not have any excess whitespace + if (node.important) { + node.raws.important = '!important'; + } + + // Remove whitespaces around ie 9 hack + node.value = node.value.replace(/\s*(\\9)\s*/, '$1'); + + const value = node.value; + + if (cache[value]) { + node.value = cache[value]; + } else { + const parsed = valueParser(node.value); + const result = parsed.walk(reduceWhitespaces).toString(); + + // Trim whitespace inside functions & dividers + node.value = result; + cache[value] = result; + } + + // Remove extra semicolons and whitespace before the declaration + if (node.raws.before) { + const prev = node.prev(); + + if (prev && prev.type !== rule) { + node.raws.before = node.raws.before.replace(/;/g, ''); + } + } + + node.raws.between = ':'; + node.raws.semicolon = false; + } else if (type === rule || type === atrule) { + node.raws.between = node.raws.after = ''; + node.raws.semicolon = false; + } + }); + + // Remove final newline + css.raws.after = ''; + }; }); diff --git a/packages/postcss-ordered-values/src/__tests__/index.js b/packages/postcss-ordered-values/src/__tests__/index.js index ba0c2c5a2..57627132d 100644 --- a/packages/postcss-ordered-values/src/__tests__/index.js +++ b/packages/postcss-ordered-values/src/__tests__/index.js @@ -1,611 +1,582 @@ import test from 'ava'; import plugin from '..'; -import {usePostCSSPlugin, processCSSFactory} from '../../../../util/testHelpers'; +import { + usePostCSSPlugin, + processCSSFactory, +} from '../../../../util/testHelpers'; -const {processCSS, passthroughCSS} = processCSSFactory(plugin); +const { processCSS, passthroughCSS } = processCSSFactory(plugin); test( - 'should order border consistently', - processCSS, - 'h1{border:1px solid red;border:1px red solid;border:solid 1px red;border:solid red 1px;border:red solid 1px;border:red 1px solid}', - 'h1{border:1px solid red;border:1px solid red;border:1px solid red;border:1px solid red;border:1px solid red;border:1px solid red}' + 'should order border consistently', + processCSS, + 'h1{border:1px solid red;border:1px red solid;border:solid 1px red;border:solid red 1px;border:red solid 1px;border:red 1px solid}', + 'h1{border:1px solid red;border:1px solid red;border:1px solid red;border:1px solid red;border:1px solid red;border:1px solid red}' ); test( - 'should order border consistently (uppercase property and value)', - processCSS, - 'h1{BORDER:1PX SOLID RED;BORDER:1PX RED SOLID;BORDER:SOLID 1PX RED;BORDER:SOLID RED 1PX;BORDER:RED SOLID 1PX;BORDER:RED 1PX SOLID}', - 'h1{BORDER:1PX SOLID RED;BORDER:1PX SOLID RED;BORDER:1PX SOLID RED;BORDER:1PX SOLID RED;BORDER:1PX SOLID RED;BORDER:1PX SOLID RED}' + 'should order border consistently (uppercase property and value)', + processCSS, + 'h1{BORDER:1PX SOLID RED;BORDER:1PX RED SOLID;BORDER:SOLID 1PX RED;BORDER:SOLID RED 1PX;BORDER:RED SOLID 1PX;BORDER:RED 1PX SOLID}', + 'h1{BORDER:1PX SOLID RED;BORDER:1PX SOLID RED;BORDER:1PX SOLID RED;BORDER:1PX SOLID RED;BORDER:1PX SOLID RED;BORDER:1PX SOLID RED}' ); test( - 'should order border with two properties', - processCSS, - 'h1{border:solid 1px}', - 'h1{border:1px solid}' + 'should order border with two properties', + processCSS, + 'h1{border:solid 1px}', + 'h1{border:1px solid}' ); test( - 'invalid border property should remain invalid', - processCSS, - 'h1{border: 0 0 7px 7px solid black}', - 'h1{border: 0 0 7px 7px solid black}' + 'invalid border property should remain invalid', + processCSS, + 'h1{border: 0 0 7px 7px solid black}', + 'h1{border: 0 0 7px 7px solid black}' ); test( - 'should order border with color functions', - processCSS, - 'h1{border:rgba(255,255,255,0.5) dashed thick}', - 'h1{border:thick dashed rgba(255,255,255,0.5)}' + 'should order border with color functions', + processCSS, + 'h1{border:rgba(255,255,255,0.5) dashed thick}', + 'h1{border:thick dashed rgba(255,255,255,0.5)}' ); test( - 'should order border longhand', - processCSS, - 'h1{border-left:solid 2px red;border-right:#fff 3px dashed;border-top:dotted #000 1px;border-bottom:4px navy groove}', - 'h1{border-left:2px solid red;border-right:3px dashed #fff;border-top:1px dotted #000;border-bottom:4px groove navy}' + 'should order border longhand', + processCSS, + 'h1{border-left:solid 2px red;border-right:#fff 3px dashed;border-top:dotted #000 1px;border-bottom:4px navy groove}', + 'h1{border-left:2px solid red;border-right:3px dashed #fff;border-top:1px dotted #000;border-bottom:4px groove navy}' ); test( - 'should order width currentColor', - processCSS, - 'h1{border:solid 2vmin currentColor}', - 'h1{border:2vmin solid currentColor}' + 'should order width currentColor', + processCSS, + 'h1{border:solid 2vmin currentColor}', + 'h1{border:2vmin solid currentColor}' ); -test( - 'should skip border:inherit', - passthroughCSS, - 'h1{border:inherit}' -); +test('should skip border:inherit', passthroughCSS, 'h1{border:inherit}'); -test( - 'should skip border:initial', - passthroughCSS, - 'h1{border:initial}' -); +test('should skip border:initial', passthroughCSS, 'h1{border:initial}'); -test( - 'should skip border:unset', - passthroughCSS, - 'h1{border:unset}' -); +test('should skip border:unset', passthroughCSS, 'h1{border:unset}'); test( - 'should order outline consistently', - processCSS, - 'h1{outline:solid red .6em}', - 'h1{outline:.6em solid red}' + 'should order outline consistently', + processCSS, + 'h1{outline:solid red .6em}', + 'h1{outline:.6em solid red}' ); test( - 'should order outline(outline-color is invert)', - processCSS, - 'h1{outline:solid invert 1px}', - 'h1{outline:1px solid invert}' + 'should order outline(outline-color is invert)', + processCSS, + 'h1{outline:solid invert 1px}', + 'h1{outline:1px solid invert}' ); test( - 'should handle -webkit-focus-ring & auto', - processCSS, - 'h1{outline:-webkit-focus-ring-color 5px auto}', - 'h1{outline:5px auto -webkit-focus-ring-color}' + 'should handle -webkit-focus-ring & auto', + processCSS, + 'h1{outline:-webkit-focus-ring-color 5px auto}', + 'h1{outline:5px auto -webkit-focus-ring-color}' ); test( - 'should order flex-flow', - processCSS, - 'h1{flex-flow: wrap column}', - 'h1{flex-flow: column wrap}' + 'should order flex-flow', + processCSS, + 'h1{flex-flow: wrap column}', + 'h1{flex-flow: column wrap}' ); test( - 'should order flex-flow (uppercase property and value)', - processCSS, - 'h1{FLEX-FLOW: WRAP COLUMN}', - 'h1{FLEX-FLOW: COLUMN WRAP}' + 'should order flex-flow (uppercase property and value)', + processCSS, + 'h1{FLEX-FLOW: WRAP COLUMN}', + 'h1{FLEX-FLOW: COLUMN WRAP}' ); test( - 'should order flex-flow', - processCSS, - 'h1{flex-flow: row-reverse wrap-reverse}', - 'h1{flex-flow: row-reverse wrap-reverse}' + 'should order flex-flow', + processCSS, + 'h1{flex-flow: row-reverse wrap-reverse}', + 'h1{flex-flow: row-reverse wrap-reverse}' ); -test( - 'should skip flex-flow:inherit', - passthroughCSS, - 'h1{flex-flow:inherit}' -); +test('should skip flex-flow:inherit', passthroughCSS, 'h1{flex-flow:inherit}'); -test( - 'should skip flex-flow:unset', - passthroughCSS, - 'h1{flex-flow: unset}' -); +test('should skip flex-flow:unset', passthroughCSS, 'h1{flex-flow: unset}'); -test( - 'should skip flex: 1 0 auto', - passthroughCSS, - 'h1{flex: 1 0 auto;}' -); +test('should skip flex: 1 0 auto', passthroughCSS, 'h1{flex: 1 0 auto;}'); -test( - 'should skip flex: 0 1 auto', - passthroughCSS, - 'h1{flex: 0 1 auto;}' -); +test('should skip flex: 0 1 auto', passthroughCSS, 'h1{flex: 0 1 auto;}'); test( - 'should support calc width in borders', - processCSS, - 'h1 {border: solid red calc(20px - 10px);}', - 'h1 {border: calc(20px - 10px) solid red;}' + 'should support calc width in borders', + processCSS, + 'h1 {border: solid red calc(20px - 10px);}', + 'h1 {border: calc(20px - 10px) solid red;}' ); test( - 'should order box-shadow consistently (1)', - processCSS, - 'h1{box-shadow:2px 5px red}', - 'h1{box-shadow:2px 5px red}' + 'should order box-shadow consistently (1)', + processCSS, + 'h1{box-shadow:2px 5px red}', + 'h1{box-shadow:2px 5px red}' ); test( - 'should order box-shadow consistently (2)', - processCSS, - 'h1{box-shadow:red 2px 5px}', - 'h1{box-shadow:2px 5px red}' + 'should order box-shadow consistently (2)', + processCSS, + 'h1{box-shadow:red 2px 5px}', + 'h1{box-shadow:2px 5px red}' ); test( - 'should order box-shadow consistently (2) (uppercase property and value)', - processCSS, - 'h1{BOX-SHADOW:RED 2PX 5PX}', - 'h1{BOX-SHADOW:2PX 5PX RED}' + 'should order box-shadow consistently (2) (uppercase property and value)', + processCSS, + 'h1{BOX-SHADOW:RED 2PX 5PX}', + 'h1{BOX-SHADOW:2PX 5PX RED}' ); test( - 'should order box-shadow consistently (3)', - processCSS, - 'h1{box-shadow:2px 5px 10px red}', - 'h1{box-shadow:2px 5px 10px red}' + 'should order box-shadow consistently (3)', + processCSS, + 'h1{box-shadow:2px 5px 10px red}', + 'h1{box-shadow:2px 5px 10px red}' ); test( - 'should order box-shadow consistently (4)', - processCSS, - 'h1{box-shadow:red 2px 5px 10px}', - 'h1{box-shadow:2px 5px 10px red}' + 'should order box-shadow consistently (4)', + processCSS, + 'h1{box-shadow:red 2px 5px 10px}', + 'h1{box-shadow:2px 5px 10px red}' ); test( - 'should order box-shadow consistently (5)', - processCSS, - 'h1{box-shadow:inset red 2px 5px 10px}', - 'h1{box-shadow:inset 2px 5px 10px red}' + 'should order box-shadow consistently (5)', + processCSS, + 'h1{box-shadow:inset red 2px 5px 10px}', + 'h1{box-shadow:inset 2px 5px 10px red}' ); test( - 'should order box-shadow consistently (6)', - processCSS, - 'h1{box-shadow:red 2px 5px 10px inset}', - 'h1{box-shadow:inset 2px 5px 10px red}' + 'should order box-shadow consistently (6)', + processCSS, + 'h1{box-shadow:red 2px 5px 10px inset}', + 'h1{box-shadow:inset 2px 5px 10px red}' ); test( - 'should order box-shadow consistently (6) (uppercase "inset")', - processCSS, - 'h1{box-shadow:red 2px 5px 10px INSET}', - 'h1{box-shadow:INSET 2px 5px 10px red}' + 'should order box-shadow consistently (6) (uppercase "inset")', + processCSS, + 'h1{box-shadow:red 2px 5px 10px INSET}', + 'h1{box-shadow:INSET 2px 5px 10px red}' ); test( - 'should order box-shadow consistently (7)', - processCSS, - 'h1{box-shadow:2px 5px 10px red inset}', - 'h1{box-shadow:inset 2px 5px 10px red}' + 'should order box-shadow consistently (7)', + processCSS, + 'h1{box-shadow:2px 5px 10px red inset}', + 'h1{box-shadow:inset 2px 5px 10px red}' ); test( - 'should order box-shadow consistently (8)', - processCSS, - 'h1{box-shadow:red 2px 5px,blue 2px 5px}', - 'h1{box-shadow:2px 5px red,2px 5px blue}' + 'should order box-shadow consistently (8)', + processCSS, + 'h1{box-shadow:red 2px 5px,blue 2px 5px}', + 'h1{box-shadow:2px 5px red,2px 5px blue}' ); test( - 'should order box-shadow consistently (9)', - processCSS, - 'h1{box-shadow:red 2px 5px 10px inset,blue inset 2px 5px 10px}', - 'h1{box-shadow:inset 2px 5px 10px red,inset 2px 5px 10px blue}' + 'should order box-shadow consistently (9)', + processCSS, + 'h1{box-shadow:red 2px 5px 10px inset,blue inset 2px 5px 10px}', + 'h1{box-shadow:inset 2px 5px 10px red,inset 2px 5px 10px blue}' ); test( - 'should order box-shadow consistently (10)', - processCSS, - 'h1{box-shadow:red 2px 5px 10px inset,blue 2px 5px 10px inset}', - 'h1{box-shadow:inset 2px 5px 10px red,inset 2px 5px 10px blue}' + 'should order box-shadow consistently (10)', + processCSS, + 'h1{box-shadow:red 2px 5px 10px inset,blue 2px 5px 10px inset}', + 'h1{box-shadow:inset 2px 5px 10px red,inset 2px 5px 10px blue}' ); test( - 'should order box-shadow consistently (11)', - processCSS, - 'h1{box-shadow:rgba(255, 0, 0, 0.5) 2px 5px 10px inset}', - 'h1{box-shadow:inset 2px 5px 10px rgba(255, 0, 0, 0.5)}' + 'should order box-shadow consistently (11)', + processCSS, + 'h1{box-shadow:rgba(255, 0, 0, 0.5) 2px 5px 10px inset}', + 'h1{box-shadow:inset 2px 5px 10px rgba(255, 0, 0, 0.5)}' ); test( - 'should order box-shadow consistently (12)', - passthroughCSS, - 'h1{box-shadow:0 0 3px}' + 'should order box-shadow consistently (12)', + passthroughCSS, + 'h1{box-shadow:0 0 3px}' ); test( - 'should pass through box-shadow values that contain calc()', - passthroughCSS, - 'h1{box-shadow: inset 0 calc(1em + 1px) 0 1px red}' + 'should pass through box-shadow values that contain calc()', + passthroughCSS, + 'h1{box-shadow: inset 0 calc(1em + 1px) 0 1px red}' ); test( - 'should pass through box-shadow values that contain calc() (uppercase "calc")', - passthroughCSS, - 'h1{box-shadow: inset 0 CALC(1em + 1px) 0 1px red}' + 'should pass through box-shadow values that contain calc() (uppercase "calc")', + passthroughCSS, + 'h1{box-shadow: inset 0 CALC(1em + 1px) 0 1px red}' ); test( - 'should pass through box-shadow values that contain prefixed calc()', - passthroughCSS, - 'h1{box-shadow: inset 0 -webkit-calc(1em + 1px) 0 1px red}' + 'should pass through box-shadow values that contain prefixed calc()', + passthroughCSS, + 'h1{box-shadow: inset 0 -webkit-calc(1em + 1px) 0 1px red}' ); test( - 'should pass through invalid box-shadow values', - passthroughCSS, - 'h1{box-shadow:1px solid rgba(34,36,38,.15)}' + 'should pass through invalid box-shadow values', + passthroughCSS, + 'h1{box-shadow:1px solid rgba(34,36,38,.15)}' ); test( - 'should pass through important comments (border)', - passthroughCSS, - 'border: 1px /*!wow*/ red solid' + 'should pass through important comments (border)', + passthroughCSS, + 'border: 1px /*!wow*/ red solid' ); test( - 'should pass through important comments (box-shadow)', - passthroughCSS, - 'box-shadow: 0 1px 3px /*!wow*/ red' + 'should pass through important comments (box-shadow)', + passthroughCSS, + 'box-shadow: 0 1px 3px /*!wow*/ red' ); test( - 'should pass through important comments (flex-flow)', - passthroughCSS, - 'flex-flow: row-reverse /*!wow*/ wrap-reverse' + 'should pass through important comments (flex-flow)', + passthroughCSS, + 'flex-flow: row-reverse /*!wow*/ wrap-reverse' ); test( - 'should pass through important comments (transition)', - passthroughCSS, - 'transition: ease-out width /*!wow*/ .5s 2s' + 'should pass through important comments (transition)', + passthroughCSS, + 'transition: ease-out width /*!wow*/ .5s 2s' ); test( - 'should pass through important comments (animation)', - passthroughCSS, - 'animation: bounce /*!wow*/ 1s linear 2s 5 normal none running' + 'should pass through important comments (animation)', + passthroughCSS, + 'animation: bounce /*!wow*/ 1s linear 2s 5 normal none running' ); test( - 'should order transition consistently (1)', - passthroughCSS, - 'transition: width .5s ease-out 2s' + 'should order transition consistently (1)', + passthroughCSS, + 'transition: width .5s ease-out 2s' ); test( - 'should order transition consistently (2)', - processCSS, - 'transition: ease-out width .5s 2s', - 'transition: width .5s ease-out 2s' + 'should order transition consistently (2)', + processCSS, + 'transition: ease-out width .5s 2s', + 'transition: width .5s ease-out 2s' ); test( - 'should order transition consistently (2) (uppercase property and value)', - processCSS, - 'TRANSITION: EASE-OUT WIDTH .5S 2S', - 'TRANSITION: WIDTH .5S EASE-OUT 2S' + 'should order transition consistently (2) (uppercase property and value)', + processCSS, + 'TRANSITION: EASE-OUT WIDTH .5S 2S', + 'TRANSITION: WIDTH .5S EASE-OUT 2S' ); test( - 'should order transition consistently (3)', - processCSS, - 'transition: ease-out .5s width 2s', - 'transition: width .5s ease-out 2s' + 'should order transition consistently (3)', + processCSS, + 'transition: ease-out .5s width 2s', + 'transition: width .5s ease-out 2s' ); test( - 'should order transition consistently (4)', - processCSS, - 'transition: .5s 2s width ease-out', - 'transition: width .5s ease-out 2s' + 'should order transition consistently (4)', + processCSS, + 'transition: .5s 2s width ease-out', + 'transition: width .5s ease-out 2s' ); test( - 'should order transition consistently (5)', - processCSS, - 'transition: .5s 2s width steps(5, start)', - 'transition: width .5s steps(5, start) 2s' + 'should order transition consistently (5)', + processCSS, + 'transition: .5s 2s width steps(5, start)', + 'transition: width .5s steps(5, start) 2s' ); test( - 'should order transition consistently (6)', - processCSS, - 'transition: .5s 2s width cubic-bezier(0, 0.3, 0.6, 1)', - 'transition: width .5s cubic-bezier(0, 0.3, 0.6, 1) 2s' + 'should order transition consistently (6)', + processCSS, + 'transition: .5s 2s width cubic-bezier(0, 0.3, 0.6, 1)', + 'transition: width .5s cubic-bezier(0, 0.3, 0.6, 1) 2s' ); test( - 'should order transition consistently (6) (uppercase "cubic-bezier")', - processCSS, - 'transition: .5s 2s width CUBIC-BEZIER(0, 0.3, 0.6, 1)', - 'transition: width .5s CUBIC-BEZIER(0, 0.3, 0.6, 1) 2s' + 'should order transition consistently (6) (uppercase "cubic-bezier")', + processCSS, + 'transition: .5s 2s width CUBIC-BEZIER(0, 0.3, 0.6, 1)', + 'transition: width .5s CUBIC-BEZIER(0, 0.3, 0.6, 1) 2s' ); test( - 'should order transition consistently (7)', - processCSS, - 'transition: .5s 2s width ease-out,.8s 1s height ease', - 'transition: width .5s ease-out 2s,height .8s ease 1s' + 'should order transition consistently (7)', + processCSS, + 'transition: .5s 2s width ease-out,.8s 1s height ease', + 'transition: width .5s ease-out 2s,height .8s ease 1s' ); test( - 'should order transition consistently (8)', - processCSS, - '-webkit-transition: ease-out width .5s 2s', - '-webkit-transition: width .5s ease-out 2s' + 'should order transition consistently (8)', + processCSS, + '-webkit-transition: ease-out width .5s 2s', + '-webkit-transition: width .5s ease-out 2s' ); test( - 'should order animation consistently (1)', - passthroughCSS, - "animation: bounce 1s linear 2s 3 normal none running" + 'should order animation consistently (1)', + passthroughCSS, + 'animation: bounce 1s linear 2s 3 normal none running' ); test( - 'should order animation consistently (2)', - processCSS, - 'animation: running none normal 3 1s 2s linear bounce', - 'animation: bounce 1s linear 2s 3 normal none running' + 'should order animation consistently (2)', + processCSS, + 'animation: running none normal 3 1s 2s linear bounce', + 'animation: bounce 1s linear 2s 3 normal none running' ); test( - 'should order animation consistently (2) (uppercase property and value)', - processCSS, - 'ANIMATION: RUNNING NONE NORMAL 3 1S 2S LINEAR BOUNCE', - 'ANIMATION: BOUNCE 1S LINEAR 2S 3 NORMAL NONE RUNNING' + 'should order animation consistently (2) (uppercase property and value)', + processCSS, + 'ANIMATION: RUNNING NONE NORMAL 3 1S 2S LINEAR BOUNCE', + 'ANIMATION: BOUNCE 1S LINEAR 2S 3 NORMAL NONE RUNNING' ); test( - 'should order animation consistently (3) (timing function keywords)', - processCSS, - 'animation: ease-in-out 1s bounce', - 'animation: bounce 1s ease-in-out' + 'should order animation consistently (3) (timing function keywords)', + processCSS, + 'animation: ease-in-out 1s bounce', + 'animation: bounce 1s ease-in-out' ); test( - 'should order animation consistently (3) (steps timing function)', - processCSS, - 'animation: steps(3, start) 1s bounce', - 'animation: bounce 1s steps(3, start)' + 'should order animation consistently (3) (steps timing function)', + processCSS, + 'animation: steps(3, start) 1s bounce', + 'animation: bounce 1s steps(3, start)' ); test( - 'should order animation consistently (3) (cubic-bezier timing function)', - processCSS, - 'animation: cubic-bezier(0, 0.3, 0.6, 1) 1s bounce', - 'animation: bounce 1s cubic-bezier(0, 0.3, 0.6, 1)' + 'should order animation consistently (3) (cubic-bezier timing function)', + processCSS, + 'animation: cubic-bezier(0, 0.3, 0.6, 1) 1s bounce', + 'animation: bounce 1s cubic-bezier(0, 0.3, 0.6, 1)' ); test( - 'should order animation consistently (3) (frames timing function)', - processCSS, - 'animation: frames(3) 1s bounce', - 'animation: bounce 1s frames(3)' + 'should order animation consistently (3) (frames timing function)', + processCSS, + 'animation: frames(3) 1s bounce', + 'animation: bounce 1s frames(3)' ); test( - 'should order animation consistently (4) (iteration count as number)', - processCSS, - 'animation: 3 1s bounce', - 'animation: bounce 1s 3' + 'should order animation consistently (4) (iteration count as number)', + processCSS, + 'animation: 3 1s bounce', + 'animation: bounce 1s 3' ); test( - 'should order animation consistently (4) (iteration count as infinite)', - processCSS, - 'animation: infinite 1s bounce', - 'animation: bounce 1s infinite' + 'should order animation consistently (4) (iteration count as infinite)', + processCSS, + 'animation: infinite 1s bounce', + 'animation: bounce 1s infinite' ); test( - 'should order animation consistently (5) (do not reorder times)', - processCSS, - 'animation: 1s 2s bounce', - 'animation: bounce 1s 2s' + 'should order animation consistently (5) (do not reorder times)', + processCSS, + 'animation: 1s 2s bounce', + 'animation: bounce 1s 2s' ); test( - 'should order animation consistently (5) (do not reorder times)', - processCSS, - 'animation: 1s bounce 2s', - 'animation: bounce 1s 2s' + 'should order animation consistently (5) (do not reorder times)', + processCSS, + 'animation: 1s bounce 2s', + 'animation: bounce 1s 2s' ); test( - 'should order animation consistently (6) (direction "normal")', - processCSS, - 'animation: normal 1s bounce', - 'animation: bounce 1s normal' + 'should order animation consistently (6) (direction "normal")', + processCSS, + 'animation: normal 1s bounce', + 'animation: bounce 1s normal' ); test( - 'should order animation consistently (6) (direction "reverse")', - processCSS, - 'animation: reverse 1s bounce', - 'animation: bounce 1s reverse' + 'should order animation consistently (6) (direction "reverse")', + processCSS, + 'animation: reverse 1s bounce', + 'animation: bounce 1s reverse' ); test( - 'should order animation consistently (6) (direction "alternate")', - processCSS, - 'animation: alternate 1s bounce', - 'animation: bounce 1s alternate' + 'should order animation consistently (6) (direction "alternate")', + processCSS, + 'animation: alternate 1s bounce', + 'animation: bounce 1s alternate' ); test( - 'should order animation consistently (6) (direction "alternate-reverse")', - processCSS, - 'animation: alternate-reverse 1s bounce', - 'animation: bounce 1s alternate-reverse' + 'should order animation consistently (6) (direction "alternate-reverse")', + processCSS, + 'animation: alternate-reverse 1s bounce', + 'animation: bounce 1s alternate-reverse' ); test( - 'should order animation consistently (7) (fill mode "none")', - processCSS, - 'animation: none 1s bounce', - 'animation: bounce 1s none' + 'should order animation consistently (7) (fill mode "none")', + processCSS, + 'animation: none 1s bounce', + 'animation: bounce 1s none' ); test( - 'should order animation consistently (7) (fill mode "forwards")', - processCSS, - 'animation: forwards 1s bounce', - 'animation: bounce 1s forwards' + 'should order animation consistently (7) (fill mode "forwards")', + processCSS, + 'animation: forwards 1s bounce', + 'animation: bounce 1s forwards' ); test( - 'should order animation consistently (7) (fill mode "backwards")', - processCSS, - 'animation: backwards 1s bounce', - 'animation: bounce 1s backwards' + 'should order animation consistently (7) (fill mode "backwards")', + processCSS, + 'animation: backwards 1s bounce', + 'animation: bounce 1s backwards' ); test( - 'should order animation consistently (7) (fill mode "both")', - processCSS, - 'animation: both 1s bounce', - 'animation: bounce 1s both' + 'should order animation consistently (7) (fill mode "both")', + processCSS, + 'animation: both 1s bounce', + 'animation: bounce 1s both' ); test( - 'should order animation consistently (8) (play state "running")', - processCSS, - 'animation: running 1s bounce', - 'animation: bounce 1s running' + 'should order animation consistently (8) (play state "running")', + processCSS, + 'animation: running 1s bounce', + 'animation: bounce 1s running' ); test( - 'should order animation consistently (8) (play state "paused")', - processCSS, - 'animation: paused 1s bounce', - 'animation: bounce 1s paused' + 'should order animation consistently (8) (play state "paused")', + processCSS, + 'animation: paused 1s bounce', + 'animation: bounce 1s paused' ); test( - 'should order animation consistently (9) (assigns keyframe name last when it matches a keyword)', - processCSS, - 'animation: none 1s linear 2s both', - 'animation: both 1s linear 2s none' + 'should order animation consistently (9) (assigns keyframe name last when it matches a keyword)', + processCSS, + 'animation: none 1s linear 2s both', + 'animation: both 1s linear 2s none' ); test( - 'should order animation consistently (9) (assigns keyframe name last when it matches a keyword)', - processCSS, - 'animation: ease 1s linear', - 'animation: linear 1s ease' + 'should order animation consistently (9) (assigns keyframe name last when it matches a keyword)', + processCSS, + 'animation: ease 1s linear', + 'animation: linear 1s ease' ); test( - 'should order animation consistently (10) (handle multiple animation values)', - processCSS, - 'animation: 1s 2s bounce linear, 8s 1s shake ease', - 'animation: bounce 1s linear 2s,shake 8s ease 1s' + 'should order animation consistently (10) (handle multiple animation values)', + processCSS, + 'animation: 1s 2s bounce linear, 8s 1s shake ease', + 'animation: bounce 1s linear 2s,shake 8s ease 1s' ); test( - 'should order animation consistently (10) (process prefixed -webkit-animation)', - processCSS, - '-webkit-animation: linear bounce 1s 2s', - '-webkit-animation: bounce 1s linear 2s' + 'should order animation consistently (10) (process prefixed -webkit-animation)', + processCSS, + '-webkit-animation: linear bounce 1s 2s', + '-webkit-animation: bounce 1s linear 2s' ); test( - 'should abort ordering when a var is detected (animation)', - passthroughCSS, - 'animation: bounce /*!wow*/ 1s var(--linear) 2s 5 normal none running' + 'should abort ordering when a var is detected (animation)', + passthroughCSS, + 'animation: bounce /*!wow*/ 1s var(--linear) 2s 5 normal none running' ); test( - 'should abort ordering when a var is detected (animation) (uppercase "var")', - passthroughCSS, - 'animation: bounce /*!wow*/ 1s var(--linear) 2s 5 normal none running' + 'should abort ordering when a var is detected (animation) (uppercase "var")', + passthroughCSS, + 'animation: bounce /*!wow*/ 1s var(--linear) 2s 5 normal none running' ); test( - 'should abort ordering when a var is detected (transition)', - passthroughCSS, - 'transition: .5s 2s width var(--ease)' + 'should abort ordering when a var is detected (transition)', + passthroughCSS, + 'transition: .5s 2s width var(--ease)' ); test( - 'should abort ordering when a var is detected (transition) (uppercase "var")', - passthroughCSS, - 'transition: .5s 2s width VAR(--ease)' + 'should abort ordering when a var is detected (transition) (uppercase "var")', + passthroughCSS, + 'transition: .5s 2s width VAR(--ease)' ); test( - 'should abort ordering when a var is detected (flex-flow)', - passthroughCSS, - 'flex-flow: wrap var(--column)' + 'should abort ordering when a var is detected (flex-flow)', + passthroughCSS, + 'flex-flow: wrap var(--column)' ); test( - 'should abort ordering when a var is detected (flex-flow) (uppercase "var")', - passthroughCSS, - 'flex-flow: wrap VAR(--column)' + 'should abort ordering when a var is detected (flex-flow) (uppercase "var")', + passthroughCSS, + 'flex-flow: wrap VAR(--column)' ); test( - 'should abort ordering when a var is detected (box-shadow)', - passthroughCSS, - 'box-shadow: 0 1px 3px var(--red)' + 'should abort ordering when a var is detected (box-shadow)', + passthroughCSS, + 'box-shadow: 0 1px 3px var(--red)' ); test( - 'should abort ordering when a var is detected (box-shadow) (uppercase "var")', - passthroughCSS, - 'box-shadow: 0 1px 3px VAR(--red)' + 'should abort ordering when a var is detected (box-shadow) (uppercase "var")', + passthroughCSS, + 'box-shadow: 0 1px 3px VAR(--red)' ); test( - 'should abort ordering when a var is detected (border)', - passthroughCSS, - 'border: solid 1px var(--red)' + 'should abort ordering when a var is detected (border)', + passthroughCSS, + 'border: solid 1px var(--red)' ); test( - 'should abort ordering when a var is detected (border) (uppercase "var")', - passthroughCSS, - 'border: solid 1px VAR(--red)' + 'should abort ordering when a var is detected (border) (uppercase "var")', + passthroughCSS, + 'border: solid 1px VAR(--red)' ); test( - 'should abort when consumed via css loader', - passthroughCSS, - 'border: ___CSS_LOADER_IMPORT___0___ solid ___CSS_LOADER_IMPORT___1___;' + 'should abort when consumed via css loader', + passthroughCSS, + 'border: ___CSS_LOADER_IMPORT___0___ solid ___CSS_LOADER_IMPORT___1___;' ); -test( - 'should use the postcss plugin api', - usePostCSSPlugin, - plugin() -); +test('should use the postcss plugin api', usePostCSSPlugin, plugin()); diff --git a/packages/postcss-ordered-values/src/index.js b/packages/postcss-ordered-values/src/index.js index 0fbb6c3c7..0ad29511e 100644 --- a/packages/postcss-ordered-values/src/index.js +++ b/packages/postcss-ordered-values/src/index.js @@ -1,5 +1,5 @@ import postcss from 'postcss'; -import valueParser from "postcss-value-parser"; +import valueParser from 'postcss-value-parser'; // rules import animation from './rules/animation'; @@ -11,82 +11,82 @@ import transition from './rules/transition'; /* eslint-disable quote-props */ const rules = { - 'animation': animation, - '-webkit-animation': animation, - 'border': border, - 'border-top': border, - 'border-right': border, - 'border-bottom': border, - 'border-left': border, - 'outline': border, - 'box-shadow': boxShadow, - 'flex-flow': flexFlow, - 'transition': transition, - '-webkit-transition': transition, + animation: animation, + '-webkit-animation': animation, + border: border, + 'border-top': border, + 'border-right': border, + 'border-bottom': border, + 'border-left': border, + outline: border, + 'box-shadow': boxShadow, + 'flex-flow': flexFlow, + transition: transition, + '-webkit-transition': transition, }; /* eslint-enable */ -function shouldAbort (parsed) { - let abort = false; +function shouldAbort(parsed) { + let abort = false; - parsed.walk(({type, value}) => { - if ( - type === 'comment' || - type === 'function' && value.toLowerCase() === 'var' || - type === 'word' && ~value.indexOf(`___CSS_LOADER_IMPORT___`) - ) { - abort = true; + parsed.walk(({ type, value }) => { + if ( + type === 'comment' || + (type === 'function' && value.toLowerCase() === 'var') || + (type === 'word' && ~value.indexOf(`___CSS_LOADER_IMPORT___`)) + ) { + abort = true; - return false; - } - }); + return false; + } + }); - return abort; + return abort; } -function getValue (decl) { - let {value, raws} = decl; +function getValue(decl) { + let { value, raws } = decl; - if (raws && raws.value && raws.value.raw) { - value = raws.value.raw; - } + if (raws && raws.value && raws.value.raw) { + value = raws.value.raw; + } - return value; + return value; } export default postcss.plugin('postcss-ordered-values', () => { - return css => { - const cache = {}; + return (css) => { + const cache = {}; - css.walkDecls(decl => { - const lowerCasedProp = decl.prop.toLowerCase(); - const processor = rules[lowerCasedProp]; + css.walkDecls((decl) => { + const lowerCasedProp = decl.prop.toLowerCase(); + const processor = rules[lowerCasedProp]; - if (!processor) { - return; - } + if (!processor) { + return; + } - const value = getValue(decl); + const value = getValue(decl); - if (cache[value]) { - decl.value = cache[value]; + if (cache[value]) { + decl.value = cache[value]; - return; - } + return; + } - const parsed = valueParser(value); + const parsed = valueParser(value); - if (parsed.nodes.length < 2 || shouldAbort(parsed)) { - cache[value] = value; + if (parsed.nodes.length < 2 || shouldAbort(parsed)) { + cache[value] = value; - return; - } + return; + } - const result = processor(parsed); + const result = processor(parsed); - decl.value = result; - cache[value] = result; - }); - }; + decl.value = result; + cache[value] = result; + }); + }; }); diff --git a/packages/postcss-ordered-values/src/lib/addSpace.js b/packages/postcss-ordered-values/src/lib/addSpace.js index f4cc0f0ba..6fc11e41b 100644 --- a/packages/postcss-ordered-values/src/lib/addSpace.js +++ b/packages/postcss-ordered-values/src/lib/addSpace.js @@ -1,3 +1,3 @@ -export default function addSpace () { - return {type: 'space', value: ' '}; +export default function addSpace() { + return { type: 'space', value: ' ' }; } diff --git a/packages/postcss-ordered-values/src/lib/getValue.js b/packages/postcss-ordered-values/src/lib/getValue.js index ab0feddd7..c58382666 100644 --- a/packages/postcss-ordered-values/src/lib/getValue.js +++ b/packages/postcss-ordered-values/src/lib/getValue.js @@ -1,25 +1,25 @@ -import {stringify} from 'postcss-value-parser'; +import { stringify } from 'postcss-value-parser'; -export default function getValue (values) { - return stringify({ - nodes: values.reduce((nodes, arg, index) => { - arg.forEach((val, idx) => { - if ( - idx === arg.length - 1 && - index === values.length - 1 && - val.type === 'space' - ) { - return; - } - nodes.push(val); - }); +export default function getValue(values) { + return stringify({ + nodes: values.reduce((nodes, arg, index) => { + arg.forEach((val, idx) => { + if ( + idx === arg.length - 1 && + index === values.length - 1 && + val.type === 'space' + ) { + return; + } + nodes.push(val); + }); - if (index !== values.length - 1) { - nodes[nodes.length - 1].type = 'div'; - nodes[nodes.length - 1].value = ','; - } + if (index !== values.length - 1) { + nodes[nodes.length - 1].type = 'div'; + nodes[nodes.length - 1].value = ','; + } - return nodes; - }, []), - }); + return nodes; + }, []), + }); } diff --git a/packages/postcss-ordered-values/src/rules/animation.js b/packages/postcss-ordered-values/src/rules/animation.js index 9e848884d..8b5a20fdb 100644 --- a/packages/postcss-ordered-values/src/rules/animation.js +++ b/packages/postcss-ordered-values/src/rules/animation.js @@ -1,4 +1,4 @@ -import {unit} from 'postcss-value-parser'; +import { unit } from 'postcss-value-parser'; import getArguments from 'lerna:cssnano-util-get-arguments'; import addSpace from '../lib/addSpace'; import getValue from '../lib/getValue'; @@ -6,94 +6,107 @@ import getValue from '../lib/getValue'; // animation: [ none | ] ||