diff --git a/.changeset/proud-donuts-know.md b/.changeset/proud-donuts-know.md new file mode 100644 index 0000000000..766b1adde9 --- /dev/null +++ b/.changeset/proud-donuts-know.md @@ -0,0 +1,5 @@ +--- +"shadcn": patch +--- + +Preserve quotes in className literals when applying RTL transforms. diff --git a/packages/shadcn/src/utils/transformers/transform-rtl.ts b/packages/shadcn/src/utils/transformers/transform-rtl.ts index 057c34d2ee..608bffbd85 100644 --- a/packages/shadcn/src/utils/transformers/transform-rtl.ts +++ b/packages/shadcn/src/utils/transformers/transform-rtl.ts @@ -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) { diff --git a/packages/shadcn/test/utils/transform-rtl.test.ts b/packages/shadcn/test/utils/transform-rtl.test.ts index 9a2a51e9ff..0456ebb226 100644 --- a/packages/shadcn/test/utils/transform-rtl.test.ts +++ b/packages/shadcn/test/utils/transform-rtl.test.ts @@ -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
+} +`, + 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",