diff --git a/package.json b/package.json index 5ca5b05f..fa9c4344 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "another-json": "^0.2.0", "base64-arraybuffer": "^0.2.0", "dompurify": "^2.3.0", - "off-color": "^2.0.0" + "off-color": "^2.0.0", + "postcss-value-parser": "^4.2.0" } } diff --git a/postcss/css-compile-variables.js b/postcss/css-compile-variables.js index 8d4b0fd9..bd952ac8 100644 --- a/postcss/css-compile-variables.js +++ b/postcss/css-compile-variables.js @@ -1,4 +1,5 @@ const offColor = require("off-color").offColor; +const valueParser = require("postcss-value-parser"); let aliasMap; let resolvedMap; @@ -9,28 +10,45 @@ function getValueFromAlias(alias) { return resolvedMap.get(derivedVariable); // what if we haven't resolved this variable yet? } -function resolveDerivedVariable(decl, variables) { - const matches = decl.value.match(RE_VARIABLE_VALUE); - if (matches) { - const [, wholeVariable, baseVariable, operation, argument] = matches; - if (!variables[baseVariable]) { - // hmm.. baseVariable should be in config..., maybe this is an alias? - if (!aliasMap.get(`--${baseVariable}`)) { - throw new Error(`Cannot derive from ${baseVariable} because it is neither defined in config nor is it an alias!`); - } +function parseDeclarationValue(value) { + const parsed = valueParser(value); + const variables = []; + parsed.walk(node => { + if (node.type !== "function" && node.value !== "var") { + return; } - switch (operation) { - case "darker": { - const colorString = variables[baseVariable] ?? getValueFromAlias(baseVariable); - const newColorString = offColor(colorString).darken(argument / 100).hex(); - resolvedMap.set(wholeVariable, newColorString); - break; + const variable = node.nodes[0]; + variables.push(variable.value); + }); + return variables; +} + +function resolveDerivedVariable(decl, variables) { + const RE_VARIABLE_VALUE = /--(.+)--(.+)-(.+)/; + const variableCollection = parseDeclarationValue(decl.value); + for (const variable of variableCollection) { + const matches = variable.match(RE_VARIABLE_VALUE); + if (matches) { + const [wholeVariable, baseVariable, operation, argument] = matches; + if (!variables[baseVariable]) { + // hmm.. baseVariable should be in config..., maybe this is an alias? + if (!aliasMap.get(`--${baseVariable}`)) { + throw new Error(`Cannot derive from ${baseVariable} because it is neither defined in config nor is it an alias!`); + } } - case "lighter": { - const colorString = variables[baseVariable] ?? getValueFromAlias(baseVariable); - const newColorString = offColor(colorString).lighten(argument / 100).hex(); - resolvedMap.set(wholeVariable, newColorString); - break; + switch (operation) { + case "darker": { + const colorString = variables[baseVariable] ?? getValueFromAlias(baseVariable); + const newColorString = offColor(colorString).darken(argument / 100).hex(); + resolvedMap.set(wholeVariable, newColorString); + break; + } + case "lighter": { + const colorString = variables[baseVariable] ?? getValueFromAlias(baseVariable); + const newColorString = offColor(colorString).lighten(argument / 100).hex(); + resolvedMap.set(wholeVariable, newColorString); + break; + } } } } diff --git a/postcss/test.js b/postcss/test.js index 104696aa..e67016b6 100644 --- a/postcss/test.js +++ b/postcss/test.js @@ -61,6 +61,29 @@ module.exports.tests = function tests() { color: var(--icon-color--darker-20); }`; assert.rejects(async () => await postcss([plugin({ variables: {} })]).process(css, { from: undefined, })); + }, + + "multiple derived variable in single declaration is parsed correctly": async (assert) => { + const inputCSS = `div { + background-color: linear-gradient(var(--foo-color--lighter-50), var(--foo-color--darker-20)); + }`; + const transformedColor1 = offColor("#ff0").lighten(0.5); + const transformedColor2 = offColor("#ff0").darken(0.2); + const outputCSS = + inputCSS + + ` + :root { + --foo-color: #ff0; + --foo-color--lighter-50: ${transformedColor1.hex()}; + --foo-color--darker-20: ${transformedColor2.hex()}; + } + `; + await run( + inputCSS, + outputCSS, + { variables: { "foo-color": "#ff0" } }, + assert + ); } }; }; diff --git a/yarn.lock b/yarn.lock index 5836b2af..7bcefdd4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1222,6 +1222,11 @@ postcss-flexbugs-fixes@^5.0.2: resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz#2028e145313074fc9abe276cb7ca14e5401eb49d" integrity sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ== +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + postcss@^8.3.8: version "8.3.9" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.9.tgz#98754caa06c4ee9eb59cc48bd073bb6bd3437c31"