UNPKG

@mintlify/common

Version:

Commonly shared code within Mintlify

124 lines (123 loc) 5.81 kB
import { walk } from 'estree-walker'; import { visit } from 'unist-util-visit'; export const MINTLIFY_TAILWIND_PREFIX = 'mint-'; export const rehypeDynamicTailwindCss = (tailwindSelectors) => (tree) => { visit(tree, (node) => { if ((node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') && node.name && typeof node.name === 'string' && // check for valid html tags (/^[a-z]/.test(node.name) || node.name === 'Heading') && !node.name.includes('.') && node.attributes.length > 0) { const className = node.attributes.find((attr) => attr.type === 'mdxJsxAttribute' && attr.name === 'className'); if (className && typeof className.value === 'string') { className.value = transformClassNames(className.value, tailwindSelectors); } } if (node.type === 'mdxjsEsm' && node.data && node.data.estree) { transformMdxJsEsmNode(node.data.estree, tailwindSelectors); } }); }; const transformMdxJsEsmNode = (estreeNode, tailwindSelectors) => { walk(estreeNode, { enter(node) { if (node.type === 'JSXAttribute' && node.name.type === 'JSXIdentifier' && (node.name.name === 'className' || node.name.name === 'class')) { if (node.value && node.value.type === 'JSXExpressionContainer') { const expression = node.value.expression; if (expression.type === 'TemplateLiteral') { // example: className={`bg-green-500`} expression.quasis.forEach((quasi) => { if (quasi.value.cooked) { const originalValue = quasi.value.cooked; const transformedValue = transformClassNames(originalValue, tailwindSelectors); if (originalValue !== transformedValue) { quasi.value.cooked = transformedValue; quasi.value.raw = transformedValue; } } }); // example: className={`${isActive ? "bg-red-500" : "bg-blue-500"}`} expression.expressions.forEach((expr) => { transformExpressionClassNames(expr, tailwindSelectors); }); } } // example: className="bg-green-500" if (node.value && node.value.type === 'Literal' && typeof node.value.value === 'string') { const originalValue = node.value.value; const transformedValue = transformClassNames(originalValue, tailwindSelectors); if (originalValue !== transformedValue) { node.value.value = transformedValue; if (typeof node.value.raw === 'string') { node.value.raw = JSON.stringify(transformedValue); } } } } }, }); }; const transformExpressionClassNames = (expr, tailwindSelectors) => { if (!expr) return; if (expr.type === 'Literal' && typeof expr.value === 'string') { const originalValue = expr.value; const transformedValue = transformClassNames(originalValue, tailwindSelectors); if (originalValue !== transformedValue) { expr.value = transformedValue; if (typeof expr.raw === 'string') { expr.raw = JSON.stringify(transformedValue); } } } else if (expr.type === 'ConditionalExpression') { // example: ${isActive ? "bg-red-500" : "bg-blue-500"} transformExpressionClassNames(expr.consequent, tailwindSelectors); transformExpressionClassNames(expr.alternate, tailwindSelectors); } else if (expr.type === 'LogicalExpression') { // example: ${isActive && "bg-red-500"} transformExpressionClassNames(expr.left, tailwindSelectors); transformExpressionClassNames(expr.right, tailwindSelectors); } else if (expr.type === 'BinaryExpression' && expr.operator === '+') { // example: ${"bg-red-500" + "bg-blue-500"} transformExpressionClassNames(expr.left, tailwindSelectors); transformExpressionClassNames(expr.right, tailwindSelectors); } }; const transformClassNames = (value, tailwindSelectors) => { if (!tailwindSelectors || !tailwindSelectors.length) return value; return value .split(/\s+/) .map((className) => { if (!className) return ''; if (className.startsWith('[') || className.startsWith(MINTLIFY_TAILWIND_PREFIX)) { return className; } const parts = className.split(':'); let baseClass = parts.pop(); const hasImportantModifier = baseClass.startsWith('!'); if (hasImportantModifier) { baseClass = baseClass.slice(1); } const variants = parts; if (tailwindSelectors.some((selector) => { // remove the leading dot and any escaping backslashes from the selector // for example, .bg-orange-400\\/50 -> bg-orange-400/50 const normalizedSelector = selector.replace(/^\.|\\+/g, ''); return normalizedSelector.includes(baseClass); })) { const transformedClass = `${hasImportantModifier ? '!' : ''}${MINTLIFY_TAILWIND_PREFIX}${baseClass}`; return [...variants, transformedClass].join(':'); } return className; }) .join(' '); };