const fs = require("fs"); const path = require("path"); const glob = require("glob"); const PREFIX = "tw-"; const rootDir = "./src"; function replaceClassNames(classNamesString) { const classNames = classNamesString.split(" "); return classNames .map((className) => { const values = className.split(":"); const modifiers = values.length > 1 ? values.slice(0, values.length - 1) : []; const value = values[values.length - 1]; const isNegative = value.startsWith("-"); if (value.includes(PREFIX)) { return className; } return [ modifiers.length ? modifiers.join(":") + ":" : null, isNegative ? "-" : "", PREFIX, isNegative ? value.slice(1) : value, ] .filter((value) => !!value) .join(""); }) .join(" "); } glob("**/*.{js,jsx,ts,tsx,vue,css}", { cwd: rootDir }, (er, files) => { files.forEach((file) => { const filePath = path.join(rootDir, file); fs.readFile(filePath, "utf-8", (err, data) => { if (err) { console.error(err); return; } const result = data // Classes .replace(/class=["']([."'\w\s\/:\[\]-]+)["']/g, (match, p1) => { return `class="${replaceClassNames(p1)}"`; }) // @apply directives .replace(/@apply ([."'\w\s\/:\[\]-]+)/g, (match, p1) => { return `@apply ${replaceClassNames(p1)}`; }) // Conditionals // TODO: Handle multi-line conditionals .replace(/:class="(.*\?\s)["'](.*)["'](.*:\s)["'](.*)["']"/g, (match, p1, p2, p3, p4) => { return `:class="${p1}'${replaceClassNames(p2)}'${p3}'${replaceClassNames(p4)}'"`; }) .replace(/:class="(.*&&\s)["'](.*)["']"/g, (match, p1, p2) => { return `:class="${p1}'${replaceClassNames(p2)}'"`; }); fs.writeFile(filePath, result, "utf-8", (err) => { if (err) console.error(err); }); }); }); });