flexcss-cli
Version:
The Flex CSS, what if we dont follow CSS's rules? No Identation.
128 lines (106 loc) • 4.38 kB
JavaScript
import { watchFile, readFileSync, writeFileSync } from "fs";
const staticKeywords = {
"ghost": "background: #eee",
"p-4": "font-size: 4rem",
"fusion-gradient": "background: linear-gradient(135deg, #4776E6, #8E54E9)",
"shadow-xl": "box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04)",
"rounded": "border-radius: 50px" // So Cubes are not converted into Ovals.
};
function convertPadding(n) {
const map = {
"1": "0.25rem",
"2": "0.5rem",
"3": "0.75rem",
"4": "1rem"
};
return map[n] || `${n}rem`;
}
function convertBlockContent(blockContent) {
for (const key in staticKeywords) {
const regex = new RegExp(`\\b${key}\\b`, 'g');
blockContent = blockContent.replace(regex, staticKeywords[key]);
}
blockContent = blockContent.replace(/\btxt-([0-9a-fA-F]{3,6})\b/g, (_, hex) => {
return `color: #${hex}`;
});
blockContent = blockContent.replace(/\bbg-([0-9a-fA-F]{3,6})\b/g, (_, hex) => {
return `background-color: #${hex}`;
});
blockContent = blockContent.replace(/\b([pm])-(\d+)\b/g, (_, type, num) => {
const val = type === "p" ? convertPadding(num) : `${num}rem`;
const prop = type === "p" ? "padding" : "margin";
return `${prop}: ${val}`;
});
blockContent = blockContent.replace(/\b([pm][lrud])-(\d+)\b/g, (_, prop, num) => {
const type = prop.startsWith("p") ? "padding" : "margin";
const dirMap = { l: "left", r: "right", u: "top", d: "bottom" };
const dir = dirMap[prop[1]];
return `${type}-${dir}: ${num}rem`;
});
blockContent = blockContent.replace(/\b(hi|wi)-(\d+)([phwr])\b/g, (_, type, val, unit) => {
const prop = type === "hi" ? "height" : "width";
const unitMap = { p: "px", h: "vh", w: "vw", r: "rem" };
return `${prop}: ${val}${unitMap[unit]}`;
});
blockContent = blockContent.replace(/\bmax-(w|h)-(\d+)-([phwr])\b/g, (_, dim, val, unit) => {
const prop = `max-${dim === "w" ? "width" : "height"}`;
const unitMap = { p: "px", h: "vh", w: "vw", r: "rem" };
return `${prop}: ${val}${unitMap[unit]}`;
});
return blockContent;
}
function autoSemicolonFix(content) {
return content.replace(/([^\s;{}\n])\n/g, "$1;\n");
}
function transpill(input, output) {
let source = readFileSync(input, "utf-8");
let prepared = source.replaceAll(",", ";\n ");
let transformed = prepared.replace(/([^{]+){([^}]+)}/g, (match, selector, block) => {
const cleanedSelector = selector.trim(); // like #ghost-btn
let blockContent = block.trim(); // like ghost; txt-fff; ...
blockContent = convertBlockContent(blockContent);
blockContent = autoSemicolonFix(blockContent);
return `${cleanedSelector} {\n ${blockContent}\n}`;
});
const result = `/* Compiled from ${input} */\n` + transformed;
writeFileSync(output, result);
console.log("✅ Done.");
}
////////////////////////////
// C L I //
// Command Line Interface //
// Don't touch me! //
////////////////////////////
const args = process.argv.slice(2);
const command = args[0];
if (command === "transpill") {
const inputFile = args[1];
const outputIndex = args.indexOf("-o");
if (outputIndex === -1 || !args[outputIndex + 1]) {
console.error("Error: Output file not specified with -o");
process.exit(1);
}
const outputFile = args[outputIndex + 1];
console.log("Transpilling...");
console.log("Input:", inputFile);
console.log("Output:", outputFile);
transpill(inputFile, outputFile);
} else if (command === "watch") {
const inputFile = args[1];
const outputIndex = args.indexOf("-o");
if (outputIndex === -1 || !args[outputIndex + 1]) {
console.error("Error: Output file not specified with -o");
process.exit(1);
}
const outputFile = args[outputIndex + 1];
console.log(`Watching ${inputFile} for changes...`);
transpill(inputFile, outputFile); // Initial run
watchFile(inputFile, { interval: 500 }, () => {
console.log(`[FlexCSS] Change detected. Re-transpilling...`);
transpill(inputFile, outputFile);
});
} else {
console.error("Unknown command:", command);
process.exit(1);
}