@mintlify/common
Version:
Commonly shared code within Mintlify
124 lines (123 loc) • 5.81 kB
JavaScript
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(' ');
};