fix(transform-rtl): preserve quotes in transformed className literals (#10495)

* fix(transform-rtl): preserve string literal escapes

* chore(changeset): add rtl quote preservation note

---------

Co-authored-by: shadcn <m@shadcn.com>
This commit is contained in:
Artyom Konoplyov
2026-05-27 20:29:44 +02:00
committed by GitHub
parent e2fa0101e3
commit 360e8a19c3
3 changed files with 51 additions and 6 deletions

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
Preserve quotes in className literals when applying RTL transforms.

View File

@@ -1,5 +1,6 @@
import { Transformer } from "@/src/utils/transformers"
import { Project, ScriptKind, SourceFile, SyntaxKind } from "ts-morph"
import type { StringLiteral } from "ts-morph"
import { splitClassName } from "./transform-css-vars"
@@ -130,12 +131,9 @@ function stripQuotes(str: string) {
}
// Transforms a string literal node by applying RTL mappings.
function transformStringLiteralNode(node: {
getText(): string
replaceWithText(text: string): void
}) {
const text = stripQuotes(node.getText() ?? "")
node.replaceWithText(`"${applyRtlMapping(text)}"`)
function transformStringLiteralNode(node: StringLiteral) {
const text = node.getLiteralText()
node.setLiteralValue(applyRtlMapping(text))
}
export function applyRtlMapping(input: string) {

View File

@@ -1,4 +1,5 @@
import { describe, expect, test } from "vitest"
import ts from "typescript"
import { transform } from "../../src/utils/transformers"
import { applyRtlMapping } from "../../src/utils/transformers/transform-rtl"
@@ -349,6 +350,47 @@ export function Foo() {
expect(result).toContain("text-start")
})
test("escapes transformed string literals that contain double quotes", async () => {
const result = await transform({
filename: "test.tsx",
raw: String.raw`import * as React from "react"
export function Foo() {
return <div className='ml-1 after:content-["\""]' />
}
`,
config: {
rtl: true,
tailwind: {
baseColor: "neutral",
},
aliases: {
components: "@/components",
utils: "@/lib/utils",
},
},
})
const sourceFile = ts.createSourceFile(
"test.tsx",
result,
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TSX
)
const stringLiterals: string[] = []
function visit(node: ts.Node) {
if (ts.isStringLiteral(node)) {
stringLiterals.push(node.text)
}
ts.forEachChild(node, visit)
}
visit(sourceFile)
expect(sourceFile.parseDiagnostics).toHaveLength(0)
expect(stringLiterals).toContain('ms-1 after:content-["\\""]')
})
test("does not transform when rtl is false", async () => {
const result = await transform({
filename: "test.tsx",