Skip to content

Instantly share code, notes, and snippets.

@studentIvan
Created June 17, 2021 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save studentIvan/eaffd0cffc10e2ec0701a34c83b48cf5 to your computer and use it in GitHub Desktop.
Save studentIvan/eaffd0cffc10e2ec0701a34c83b48cf5 to your computer and use it in GitHub Desktop.
Postcss plugin variables rename
/* eslint-disable no-plusplus */
/* eslint-disable func-names */
/* eslint-disable no-restricted-syntax */
const fs = require('fs');
const path = require('path');
/** load the existing json map with access to write and read */
const variablesMapJSON = path.join(__dirname, '/variables-map.json');
/**
* alphabetGetter is something easy to understand for the human mind
* just use alphabetGetter.next() to receive the next variable name
*/
const alphabetGetter = {
get alphabetEnglish() {
return Array.from({ length: 26 }, (_, i) => String.fromCharCode('a'.charCodeAt(0) + i));
},
/**
* Provide the existing giving names map to the alphabetGetter so it won't overwrite them
* @param {Object<string, string>} variablesDeclaredToUse
*/
init(variablesDeclaredToUse) {
this.alphabet = this.alphabetEnglish;
/** five levels is a lot */
for (const rangeOrder of ['1', '2', '3', '4', '5']) {
this.alphabet = this.alphabet.concat(this.alphabetEnglish.map(l => `${ l }${ rangeOrder }`));
}
const usingChars = Object.values(variablesDeclaredToUse).map(l => l.replace('--', '')).sort();
this.index = !usingChars.length ? -1 : this.alphabet.indexOf(usingChars[usingChars.length - 1]);
},
/**
* @returns {string} the next variable short name with --
*/
next() {
this.index += 1;
if (!this.alphabet[this.index]) {
throw new Error('alphabet is limited');
}
return `--${ this.alphabet[this.index] }`;
},
};
/** rename css variables declaration in the css */
module.exports = () => {
return {
postcssPlugin: 'rename-css-variables',
/** support the postcss modern syntax */
Once(css) {
/** we don't want to spend the developer machine resources - be eco bitch */
const PRODUCTION_MODE = process.env.NODE_ENV !== 'development' && process.env.NODE_ENV !== 'test';
if (!PRODUCTION_MODE) return;
/** @type {Object<string, string>} */
const variablesDeclaredToUse = JSON.parse(fs.readFileSync(variablesMapJSON, { encoding: 'utf8' })) || {};
/** no try/catch here, because it bad for performance... */
alphabetGetter.init(variablesDeclaredToUse);
/** Have the variables been updated or not? */
let variablesMapWereChanged = false;
const indexVariable = (variableName) => {
if (!(variableName in variablesDeclaredToUse)) {
variablesDeclaredToUse[variableName] = alphabetGetter.next();
variablesMapWereChanged = true;
}
};
const optimizeVariable = (variableName) => {
return variableName in variablesDeclaredToUse ? variablesDeclaredToUse[variableName] : variableName;
};
/** walking first time - indexing... */
css.walk((rule) => {
if (rule.nodes === undefined) return;
rule.nodes.filter(node => node.type === 'decl').forEach((node) => {
if (/^--[a-z0-9-_]{4,}$/i.test(node.prop)) {
indexVariable(node.prop);
}
if (node.value) {
[...node.value.matchAll(/var\((--[^)]{4,})\)/g)].map(v => v[1]).forEach(indexVariable);
}
});
});
/** walking second time - patching... */
css.walk((rule) => {
if (rule.nodes === undefined) return;
rule.nodes.filter(node => node.type === 'decl').forEach((node) => {
if (/^--[a-z0-9-_]{4,}$/i.test(node.prop)) {
node.prop = optimizeVariable(node.prop);
}
if (node.value) {
const usingVariables = [...node.value.matchAll(/var\((--[^)]{4,})\)/g)].map(v => v[1]);
if (usingVariables.length) {
usingVariables.forEach((usingVariable) => {
node.value = node.value.replace(usingVariable, optimizeVariable(usingVariable));
});
}
}
});
});
/** if something has changed - save the map... */
if (variablesMapWereChanged) {
fs.writeFileSync(variablesMapJSON, JSON.stringify(variablesDeclaredToUse, null, 2), { encoding: 'utf8' });
}
},
};
};
module.exports.postcss = true;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment