diff --git a/scripts/postcss/css-compile-variables.js b/scripts/postcss/css-compile-variables.js index d7eca7f2..ea939780 100644 --- a/scripts/postcss/css-compile-variables.js +++ b/scripts/postcss/css-compile-variables.js @@ -18,10 +18,11 @@ const valueParser = require("postcss-value-parser"); let aliasMap; let resolvedMap; +let baseVariables; -function getValueFromAlias(alias, variables) { +function getValueFromAlias(alias) { const derivedVariable = aliasMap.get(`--${alias}`); - return variables[derivedVariable] ?? resolvedMap.get(`--${derivedVariable}`); + return baseVariables.get(derivedVariable) ?? resolvedMap.get(derivedVariable); } function parseDeclarationValue(value) { @@ -37,14 +38,14 @@ function parseDeclarationValue(value) { return variables; } -function resolveDerivedVariable(decl, {variables, derive}) { +function resolveDerivedVariable(decl, derive) { 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; - const value = variables[baseVariable] ?? getValueFromAlias(baseVariable, variables); + const value = baseVariables.get(`--${baseVariable}`) ?? getValueFromAlias(baseVariable); if (!value) { throw new Error(`Cannot derive from ${baseVariable} because it is neither defined in config nor is it an alias!`); } @@ -54,22 +55,21 @@ function resolveDerivedVariable(decl, {variables, derive}) { } } -function extractAlias(decl) { +function extract(decl) { if (decl.variable) { - const wholeVariable = decl.value.match(/var\(--(.+)\)/)?.[1]; + // see if right side is of form "var(--foo)" + const wholeVariable = decl.value.match(/var\((--.+)\)/)?.[1]; if (wholeVariable) { aliasMap.set(decl.prop, wholeVariable); + // Since this is an alias, we shouldn't store it in baseVariables + return; } + baseVariables.set(decl.prop, decl.value); } } -function addResolvedVariablesToRootSelector(root, variables, {Rule, Declaration}) { +function addResolvedVariablesToRootSelector(root, {Rule, Declaration}) { const newRule = new Rule({ selector: ":root", source: root.source }); - // Add base css variables to :root - for (const [key, value] of Object.entries(variables)) { - const declaration = new Declaration({prop: `--${key}`, value}); - newRule.append(declaration); - } // Add derived css variables to :root resolvedMap.forEach((value, key) => { const declaration = new Declaration({prop: key, value}); @@ -87,24 +87,23 @@ function addResolvedVariablesToRootSelector(root, variables, {Rule, Declaration} /** * * @param {Object} opts - Options for the plugin - * @param {Object} opts.variables - An object of the form: {base_variable_name_1: value, base_variable_name_2: value, ...} * @param {derive} opts.derive - The callback which contains the logic for resolving derived variables */ module.exports = (opts = {}) => { aliasMap = new Map(); resolvedMap = new Map(); + baseVariables = new Map(); return { postcssPlugin: "postcss-compile-variables", Once(root, {Rule, Declaration}) { /* - Go through the CSS file once to extract all aliases. - We use the extracted alias when resolving derived variables - later. + Go through the CSS file once to extract all aliases and base variables. + We use these when resolving derived variables later. */ - root.walkDecls(decl => extractAlias(decl)); - root.walkDecls(decl => resolveDerivedVariable(decl, opts)); - addResolvedVariablesToRootSelector(root, opts.variables, {Rule, Declaration}); + root.walkDecls(decl => extract(decl)); + root.walkDecls(decl => resolveDerivedVariable(decl, opts.derive)); + addResolvedVariablesToRootSelector(root, {Rule, Declaration}); }, }; }; diff --git a/scripts/postcss/test.js b/scripts/postcss/test.js index 4ed74c67..36ff9282 100644 --- a/scripts/postcss/test.js +++ b/scripts/postcss/test.js @@ -31,7 +31,11 @@ async function run(input, output, opts = {}, assert) { module.exports.tests = function tests() { return { "derived variables are resolved": async (assert) => { - const inputCSS = `div { + const inputCSS = ` + :root { + --foo-color: #ff0; + } + div { background-color: var(--foo-color--lighter-50); }`; const transformedColor = offColor("#ff0").lighten(0.5); @@ -39,38 +43,30 @@ module.exports.tests = function tests() { inputCSS + ` :root { - --foo-color: #ff0; --foo-color--lighter-50: ${transformedColor.hex()}; } `; - await run( - inputCSS, - outputCSS, - { variables: { "foo-color": "#ff0" } }, - assert - ); + await run( inputCSS, outputCSS, {}, assert); }, "derived variables work with alias": async (assert) => { - const inputCSS = `div { + const inputCSS = ` + :root { + --icon-color: #fff; + } + div { background: var(--icon-color--darker-20); --my-alias: var(--icon-color--darker-20); color: var(--my-alias--lighter-15); }`; const colorDarker = offColor("#fff").darken(0.2).hex(); const aliasLighter = offColor(colorDarker).lighten(0.15).hex(); - const outputCSS = `div { - background: var(--icon-color--darker-20); - --my-alias: var(--icon-color--darker-20); - color: var(--my-alias--lighter-15); - } - :root { - --icon-color: #fff; + const outputCSS = inputCSS + `:root { --icon-color--darker-20: ${colorDarker}; --my-alias--lighter-15: ${aliasLighter}; } `; - await run(inputCSS, outputCSS, { variables: { "icon-color": "#fff" }, }, assert); + await run(inputCSS, outputCSS, { }, assert); }, "derived variable throws if base not present in config": async (assert) => { @@ -81,7 +77,11 @@ module.exports.tests = function tests() { }, "multiple derived variable in single declaration is parsed correctly": async (assert) => { - const inputCSS = `div { + const inputCSS = ` + :root { + --foo-color: #ff0; + } + div { background-color: linear-gradient(var(--foo-color--lighter-50), var(--foo-color--darker-20)); }`; const transformedColor1 = offColor("#ff0").lighten(0.5); @@ -90,15 +90,18 @@ module.exports.tests = function tests() { 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); + await run( inputCSS, outputCSS, { }, assert); }, "multiple aliased-derived variable in single declaration is parsed correctly": async (assert) => { - const inputCSS = `div { + const inputCSS = ` + :root { + --foo-color: #ff0; + } + div { --my-alias: var(--foo-color); background-color: linear-gradient(var(--my-alias--lighter-50), var(--my-alias--darker-20)); }`; @@ -108,12 +111,11 @@ module.exports.tests = function tests() { inputCSS + ` :root { - --foo-color: #ff0; --my-alias--lighter-50: ${transformedColor1.hex()}; --my-alias--darker-20: ${transformedColor2.hex()}; } `; - await run( inputCSS, outputCSS, { variables: { "foo-color": "#ff0" } }, assert); + await run( inputCSS, outputCSS, { }, assert); } }; };