UNPKG

@twstyled/core

Version:

twstyled -- the full-featured Tailwind CSS + CSS in JS solution with blazing fast build times and no runtime overhead

232 lines (229 loc) 7.12 kB
import * as fs from 'fs'; import * as path from 'path'; import tailwindData, { createTwClassDictionary, resolveConfig } from '@xwind/core'; import DESCRIPTIONS, { VARIANTS } from './descriptions'; const FractionRegExp = /^[0-9]+\/[0-9]+$/; const twConfig = resolveConfig({}); const { utilitiesRoot, componentsRoot, screens, variants } = tailwindData(twConfig); const twObjectMap = createTwClassDictionary(utilitiesRoot, componentsRoot); const TW_COLORS = /^(divide|bg|from|via|to|border|placeholder|ring-offset|ring|text)\-/; const twClassList = []; const jsx = Object.keys(twObjectMap) .filter((key) => !key.startsWith('XWIND')) .sort() .reduce((accum, twClass) => { const jsxClass = withSuffix(twClass); const baseClass = removePrefix(twClass); twClassList.push(baseClass); const description = DESCRIPTIONS[baseClass] || DESCRIPTIONS[Object.keys(DESCRIPTIONS).find((d) => baseClass.startsWith(d))]; if (!description) { console.log(`${baseClass} MISSING DESCRIPTION`); } const cssCode = `/** * Tailwind CSS **\`${baseClass}\`** * * *${description}* * \`\`\` css * ${twObjectMap[twClass] .toString() .replace(/[^{]*\{/, '{') .trim() .replace(/\\./g, '.') .replace(/\//g, '\\/') .replace(/\n\s*/g, ' ')} * \`\`\` * Docs: [tailwindcss](https://tailwindcss.com/docs/container) Source: [twstyled](https://github/twstyled/twstyled) **/\r\n`; let twClassPrefix, twClassValue; if (baseClass in DESCRIPTIONS) { twClassPrefix = baseClass; twClassValue = true; } else if (TW_COLORS.test(baseClass)) { const twClassParts = splitAtLastTwoOccurences(twClass, '-'); if (twClassParts[0]) { twClassPrefix = twClassParts[0]; twClassValue = `${twClassParts[1]}-${twClassParts[2]}`; } else { twClassPrefix = twClassParts[1]; twClassValue = toNumberOrString(twClassParts[2]); } } else { const twClassParts = splitAtLastOccurence(twClass, '-'); if (twClassParts[0]) { twClassPrefix = twClassParts[0]; twClassValue = toNumberOrString(twClassParts[1]); } else { twClassPrefix = twClassParts[1]; twClassValue = true; } } const jsxClassPrefix = withSuffixPartial(twClassPrefix); const jsxClassValue = twClassPrefix.startsWith('-') ? toNegative(twClassValue) : twClassValue; const categoryCode = `/** * Tailwind CSS Prefix **\`${jsxClassPrefix}\`** * * *${description}* **/\r\n`; accum[jsxClass] = accum[jsxClass] || { description: cssCode, values: [] }; if (accum[jsxClass].values.indexOf(true) === -1) { accum[jsxClass].values.push(true); } if (jsxClassValue !== null && jsxClassValue !== true) { accum[jsxClassPrefix] = accum[jsxClassPrefix] || { description: categoryCode, values: [] }; accum[jsxClassPrefix].values.push(jsxClassValue); } return accum; }, {}); Object.keys(jsx).forEach((jsxClass) => { if (jsx[jsxClass].values.length > 1 || jsx[jsxClass].values[0] !== true) { const additionalDescription = jsx[jsxClass].values .sort(sortNumberOrString) .map((suffix) => { const twClass = toTwclass(jsxClass, suffix); const css = twObjectMap[twClass] .toString() .replace(/[^{]*\{/, '{') .trim() .replace(/\\./g, '.') .replace(/\//g, '\\/') .replace(/\n\s*/g, ' '); return `* ${suffix !== true ? `${suffix}` : jsxClass}: ${css}`; }) .join('\r\n'); jsx[jsxClass].description = jsx[jsxClass].description.replace(/\*\*\/\r\n$/, ` \`\`\` css ${additionalDescription} \`\`\` **/\r\n`); } }); const result = `/* eslint-disable max-lines */ /* eslint-disable prettier/prettier */ type TailwindClasses = | ${twClassList .map((twClass) => { return `'${twClass}'`; }) .join('| \r\n')}; export interface TailwindAttributes { ${Object.keys(jsx) .sort() .map((jsxClass) => { return `${jsx[jsxClass].description} ${JSON.stringify(jsxClass)}: ${jsx[jsxClass].values .sort(sortNumberOrString) .map((v) => (typeof v === 'string' ? `'${v}'` : `${v}`)) .join(' | ')}`; }) .join('\r\n')} ${[] .concat(...screens, ...variants, 'tw') .map((variant) => { return ` /** * Tailwind ${variant === 'tw' ? 'Styles' : `Variant **\`${variant}:\`**`} * * ${VARIANTS[variant] || ''} * * Docs: [tailwindcss](https://tailwindcss.com/docs/hover-focus-and-other-states#${variant}) Source: [twstyled](https://github/twstyled/twstyled) **/ "${variant}--": Array<TailwindClasses>`; }) .join('\r\n')} }`; fs.writeFileSync(path.resolve(__dirname, '../../../src/types-tw.ts'), result); /** HELPER FUNCTIONS */ function splitAtLastOccurence(src, char) { const pos = src.lastIndexOf(char); return [src.substring(0, pos), src.substring(pos + 1)]; } function splitAtLastTwoOccurences(src, char) { const pos2 = src.lastIndexOf(char); const pos1 = src.substring(0, pos2 - 1).lastIndexOf(char); return [ src.substring(0, pos1), src.substring(pos1 + 1, pos2), src.substring(pos2 + 1) ]; } function toNumberOrString(src) { if (FractionRegExp.test(src)) { //const split = src.split('/') return `${src}`; // `parseInt(split[0], 10) / parseInt(split[1], 10)} | '${src}'` } const asNumber = Number(src); if (Number.isNaN(asNumber)) { return `${src}`; } else { return asNumber; } } function sortNumberOrString(a, b) { if (typeof a === 'string') { if (typeof b === 'string') { return b > a ? 1 : b === a ? 0 : -1; } else { return -1; } } else { if (typeof b === 'string') { return 1; } else if (typeof a === 'boolean' || typeof b === 'boolean') { return 0; } else { return a - b; } } } function withSuffix(key) { if (key.startsWith('-')) { return `${key.substr(1)}-`; } return `${key}-`; } function withSuffixPartial(key) { if (key.startsWith('-')) { return `${key.substr(1)}-`; } return `${key}-`; } function removeSuffix(key) { return key.substr(0, key.length - 1); } function removePrefix(key) { if (key.startsWith('-')) { return key.substr(1); } return key; } function toTwclass(jsxPrefix, suffix) { const twPrefix = removeSuffix(jsxPrefix); if (suffix === true) { return twPrefix; } else if (typeof suffix === 'string' || suffix >= 0) { return `${twPrefix}-${suffix}`; } else { return `-${twPrefix}${suffix}`; } } function toNegative(key) { if (typeof key === 'string' || key === 0) { return null; } return -1 * key; }