Extract base variables from css

This commit is contained in:
RMidhunSuresh 2022-03-14 23:26:37 +05:30
parent bca1648df6
commit 19a6d669a9
2 changed files with 43 additions and 42 deletions

View file

@ -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});
},
};
};

View file

@ -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);
}
};
};