mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-23 04:35:46 +00:00
* feat(cli): add support for custom Tailwind prefix * fix(cli): add tw prefix on classes applied in the css file * feat(cli): add support for custom tailwind prefix * chore: add changeset * style(shadcn-ui): code format --------- Co-authored-by: shadcn <m@shadcn.com>
202 lines
6.5 KiB
TypeScript
202 lines
6.5 KiB
TypeScript
import { Transformer } from "@/src/utils/transformers"
|
|
import { SyntaxKind } from "ts-morph"
|
|
|
|
import { splitClassName } from "./transform-css-vars"
|
|
|
|
export const transformTwPrefixes: Transformer = async ({
|
|
sourceFile,
|
|
config,
|
|
}) => {
|
|
if (!config.tailwind?.prefix) {
|
|
return sourceFile
|
|
}
|
|
|
|
// Find the cva function calls.
|
|
sourceFile
|
|
.getDescendantsOfKind(SyntaxKind.CallExpression)
|
|
.filter((node) => node.getExpression().getText() === "cva")
|
|
.forEach((node) => {
|
|
// cva(base, ...)
|
|
if (node.getArguments()[0]?.isKind(SyntaxKind.StringLiteral)) {
|
|
const defaultClassNames = node.getArguments()[0]
|
|
if (defaultClassNames) {
|
|
defaultClassNames.replaceWithText(
|
|
`"${applyPrefix(
|
|
defaultClassNames.getText()?.replace(/"/g, ""),
|
|
config.tailwind.prefix
|
|
)}"`
|
|
)
|
|
}
|
|
}
|
|
|
|
// cva(..., { variants: { ... } })
|
|
if (node.getArguments()[1]?.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
node
|
|
.getArguments()[1]
|
|
?.getDescendantsOfKind(SyntaxKind.PropertyAssignment)
|
|
.find((node) => node.getName() === "variants")
|
|
?.getDescendantsOfKind(SyntaxKind.PropertyAssignment)
|
|
.forEach((node) => {
|
|
node
|
|
.getDescendantsOfKind(SyntaxKind.PropertyAssignment)
|
|
.forEach((node) => {
|
|
const classNames = node.getInitializerIfKind(
|
|
SyntaxKind.StringLiteral
|
|
)
|
|
if (classNames) {
|
|
classNames?.replaceWithText(
|
|
`"${applyPrefix(
|
|
classNames.getText()?.replace(/"/g, ""),
|
|
config.tailwind.prefix
|
|
)}"`
|
|
)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
})
|
|
|
|
// Find all jsx attributes with the name className.
|
|
sourceFile.getDescendantsOfKind(SyntaxKind.JsxAttribute).forEach((node) => {
|
|
if (node.getName() === "className") {
|
|
// className="..."
|
|
if (node.getInitializer()?.isKind(SyntaxKind.StringLiteral)) {
|
|
const value = node.getInitializer()
|
|
if (value) {
|
|
value.replaceWithText(
|
|
`"${applyPrefix(
|
|
value.getText()?.replace(/"/g, ""),
|
|
config.tailwind.prefix
|
|
)}"`
|
|
)
|
|
}
|
|
}
|
|
|
|
// className={...}
|
|
if (node.getInitializer()?.isKind(SyntaxKind.JsxExpression)) {
|
|
// Check if it's a call to cn().
|
|
const callExpression = node
|
|
.getInitializer()
|
|
?.getDescendantsOfKind(SyntaxKind.CallExpression)
|
|
.find((node) => node.getExpression().getText() === "cn")
|
|
if (callExpression) {
|
|
// Loop through the arguments.
|
|
callExpression.getArguments().forEach((node) => {
|
|
if (
|
|
node.isKind(SyntaxKind.ConditionalExpression) ||
|
|
node.isKind(SyntaxKind.BinaryExpression)
|
|
) {
|
|
node
|
|
.getChildrenOfKind(SyntaxKind.StringLiteral)
|
|
.forEach((node) => {
|
|
node.replaceWithText(
|
|
`"${applyPrefix(
|
|
node.getText()?.replace(/"/g, ""),
|
|
config.tailwind.prefix
|
|
)}"`
|
|
)
|
|
})
|
|
}
|
|
|
|
if (node.isKind(SyntaxKind.StringLiteral)) {
|
|
node.replaceWithText(
|
|
`"${applyPrefix(
|
|
node.getText()?.replace(/"/g, ""),
|
|
config.tailwind.prefix
|
|
)}"`
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// classNames={...}
|
|
if (node.getName() === "classNames") {
|
|
if (node.getInitializer()?.isKind(SyntaxKind.JsxExpression)) {
|
|
node
|
|
.getDescendantsOfKind(SyntaxKind.PropertyAssignment)
|
|
.forEach((node) => {
|
|
if (node.getInitializer()?.isKind(SyntaxKind.CallExpression)) {
|
|
const callExpression = node.getInitializerIfKind(
|
|
SyntaxKind.CallExpression
|
|
)
|
|
if (callExpression) {
|
|
// Loop through the arguments.
|
|
callExpression.getArguments().forEach((arg) => {
|
|
if (arg.isKind(SyntaxKind.ConditionalExpression)) {
|
|
arg
|
|
.getChildrenOfKind(SyntaxKind.StringLiteral)
|
|
.forEach((node) => {
|
|
node.replaceWithText(
|
|
`"${applyPrefix(
|
|
node.getText()?.replace(/"/g, ""),
|
|
config.tailwind.prefix
|
|
)}"`
|
|
)
|
|
})
|
|
}
|
|
|
|
if (arg.isKind(SyntaxKind.StringLiteral)) {
|
|
arg.replaceWithText(
|
|
`"${applyPrefix(
|
|
arg.getText()?.replace(/"/g, ""),
|
|
config.tailwind.prefix
|
|
)}"`
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
if (node.getInitializer()?.isKind(SyntaxKind.StringLiteral)) {
|
|
if (node.getName() !== "variant") {
|
|
const classNames = node.getInitializer()
|
|
if (classNames) {
|
|
classNames.replaceWithText(
|
|
`"${applyPrefix(
|
|
classNames.getText()?.replace(/"/g, ""),
|
|
config.tailwind.prefix
|
|
)}"`
|
|
)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
})
|
|
|
|
return sourceFile
|
|
}
|
|
|
|
export function applyPrefix(input: string, prefix: string = "") {
|
|
const classNames = input.split(" ")
|
|
const prefixed: string[] = []
|
|
for (let className of classNames) {
|
|
const [variant, value, modifier] = splitClassName(className)
|
|
if (variant) {
|
|
modifier
|
|
? prefixed.push(`${variant}:${prefix}${value}/${modifier}`)
|
|
: prefixed.push(`${variant}:${prefix}${value}`)
|
|
} else {
|
|
modifier
|
|
? prefixed.push(`${prefix}${value}/${modifier}`)
|
|
: prefixed.push(`${prefix}${value}`)
|
|
}
|
|
}
|
|
return prefixed.join(" ")
|
|
}
|
|
|
|
export function applyPrefixesCss(css: string, prefix: string) {
|
|
const lines = css.split("\n")
|
|
for (let line of lines) {
|
|
if (line.includes("@apply")) {
|
|
const originalTWCls = line.replace("@apply", "").trim()
|
|
const prefixedTwCls = applyPrefix(originalTWCls, prefix)
|
|
css = css.replace(originalTWCls, prefixedTwCls)
|
|
}
|
|
}
|
|
return css
|
|
}
|