This repository has been archived on 2022-08-19. You can view files and clone it, but cannot push or open issues or pull requests.
hydrogen-web/postcss/css-compile-variables.js

103 lines
3.7 KiB
JavaScript

const offColor = require("off-color").offColor;
const valueParser = require("postcss-value-parser");
let aliasMap;
let resolvedMap;
const RE_VARIABLE_VALUE = /var\((--(.+)--(.+)-(.+))\)/;
function getValueFromAlias(alias) {
const derivedVariable = aliasMap.get(`--${alias}`);
return resolvedMap.get(derivedVariable); // what if we haven't resolved this variable yet?
}
function parseDeclarationValue(value) {
const parsed = valueParser(value);
const variables = [];
parsed.walk(node => {
if (node.type !== "function" && node.value !== "var") {
return;
}
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!`);
}
}
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;
}
}
}
}
}
function extractAlias(decl) {
const wholeVariable = decl.value.match(RE_VARIABLE_VALUE)?.[1];
if (decl.prop.startsWith("--") && wholeVariable) {
aliasMap.set(decl.prop, wholeVariable);
}
}
function addResolvedVariablesToRootSelector(root, variables, { 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 });
newRule.append(declaration);
});
root.append(newRule);
}
/* *
* @type {import('postcss').PluginCreator}
*/
module.exports = (opts = {}) => {
aliasMap = new Map();
resolvedMap = new Map();
const { variables } = opts;
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.
*/
root.walkDecls(decl => extractAlias(decl));
root.walkDecls(decl => resolveDerivedVariable(decl, variables));
addResolvedVariablesToRootSelector(root, variables, { Rule, Declaration });
},
};
};
module.exports.postcss = true;