Skip to content

Instantly share code, notes, and snippets.

@LionsAd
Created April 29, 2021 22:43
Show Gist options
  • Save LionsAd/b794e5f9d115bc985916bcf7fa944f46 to your computer and use it in GitHub Desktop.
Save LionsAd/b794e5f9d115bc985916bcf7fa944f46 to your computer and use it in GitHub Desktop.
Tailwind.css-to-JSON
const fs = require("fs")
const postcss = require("postcss")
const selectorParser = require("postcss-selector-parser")
const {
promisify
} = require("util")
const {
createHash,
} = require('crypto')
const asyncFs = {
access: promisify(fs.access),
readFile: promisify(fs.readFile),
};
const ALWAYS = 'keep:always';
function isInPseudoClass(selector) {
return (
(selector.parent &&
selector.parent.type === "pseudo" &&
selector.parent.value.startsWith(":")) ||
false
);
}
function findSelectors(selector) {
// ignore the selector if it is inside a pseudo class
if (isInPseudoClass(selector)) {
return [];
}
let matches = [];
for (const selectorNode of selector.nodes) {
switch (selectorNode.type) {
case "attribute":
break;
case "class":
case "id":
case "tag":
matches.push(selectorNode.toString())
break;
default:
continue;
}
}
return matches;
}
async function process() {
let rules = {};
let selectors = {};
let content = await asyncFs.readFile("css/utils.css", "utf-8")
let root = postcss.parse(content)
let atrule = '';
root.walk((node) => {
// Store atrule for later use.
if (node.type == 'atrule') {
let nodes = node.nodes;
node.nodes = [];
atrule = node.toString();
node.nodes = nodes;
}
// exit if it is not a rule
if (node.type != "rule" || !node.selector) {
return;
}
let rule = node.toString();
let ruleHash = createHash('sha1').update(rule).digest('hex');
let item = {};
item['rule'] = rule;
// Check again that this has an atrule.
if (node.parent && node.parent.type == 'atrule') {
item['atrule'] = atrule;
}
rules[ruleHash] = item;
selectorParser((selectorsParsed) => {
selectorsParsed.walk((selector) => {
if (selector.type !== "selector") {
return;
}
if (selector.toString() == '[hidden]') {
return;
}
// Find selector matches
let matches = findSelectors(selector);
if (matches.length == 0) {
matches.push(ALWAYS);
}
delete item['and'];
if (matches.length > 1) {
item['and'] = matches;
matches = [matches[0]];
}
for (key in matches) {
let match = matches[key]
if (!(match in selectors)) {
selectors[match] = [];
}
selectors[match].push(item);
}
});
}).processSync(node.selector);
});
console.log(JSON.stringify(selectors));
}
process();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment