diff --git a/.changeset/kind-candies-float.md b/.changeset/kind-candies-float.md new file mode 100644 index 0000000000..4f7b86391e --- /dev/null +++ b/.changeset/kind-candies-float.md @@ -0,0 +1,5 @@ +--- +"shadcn": patch +--- + +Fix: skip all transforms for universal registry items diff --git a/packages/shadcn/src/utils/updaters/update-files.ts b/packages/shadcn/src/utils/updaters/update-files.ts index f9c7133441..ddd00e6a43 100644 --- a/packages/shadcn/src/utils/updaters/update-files.ts +++ b/packages/shadcn/src/utils/updaters/update-files.ts @@ -124,30 +124,35 @@ export async function updateFiles( // Run our transformers. // Skip transformers for .env files to preserve exact content - const content = isEnvFile(filePath) - ? file.content - : await transform( - { - filename: file.path, - raw: file.content, - config, - baseColor, - transformJsx: !config.tsx, - isRemote: options.isRemote, - }, - [ - transformImport, - transformRsc, - transformCssVars, - transformTwPrefixes, - transformIcons, - transformMenu, - transformAsChild, - ...(_isNext16Middleware(filePath, projectInfo, config) - ? [transformNext] - : []), - ] - ) + // Skip transformers for universal item files (registry:file and registry:item) + // to preserve their original content as they're meant to be framework-agnostic + const isUniversalItemFile = + file.type === "registry:file" || file.type === "registry:item" + const content = + isEnvFile(filePath) || isUniversalItemFile + ? file.content + : await transform( + { + filename: file.path, + raw: file.content, + config, + baseColor, + transformJsx: !config.tsx, + isRemote: options.isRemote, + }, + [ + transformImport, + transformRsc, + transformCssVars, + transformTwPrefixes, + transformIcons, + transformMenu, + transformAsChild, + ...(_isNext16Middleware(filePath, projectInfo, config) + ? [transformNext] + : []), + ] + ) // Skip the file if it already exists and the content is the same. // Exception: Don't skip .env files as we merge content instead of replacing diff --git a/packages/shadcn/test/utils/updaters/update-files.test.ts b/packages/shadcn/test/utils/updaters/update-files.test.ts index b9ae549189..8f355f9c7a 100644 --- a/packages/shadcn/test/utils/updaters/update-files.test.ts +++ b/packages/shadcn/test/utils/updaters/update-files.test.ts @@ -1526,6 +1526,111 @@ DATABASE_URL=postgres://localhost:5432/mydb`, } `) }) + + test("should preserve 'use client' directive for universal item files (registry:file)", async () => { + const config = await getConfig( + path.resolve(__dirname, "../../fixtures/vite-with-tailwind") + ) + const result = await updateFiles( + [ + { + path: "custom-component.tsx", + type: "registry:file", + target: "~/custom-component.tsx", + content: `"use client" + +export function CustomComponent() { + return
Custom Component
+}`, + }, + ], + config, + { + overwrite: true, + silent: true, + } + ) + + // Verify that the file was created + expect(result.filesCreated).toContain("custom-component.tsx") + + // Read the written file and check if 'use client' is preserved + const writtenContent = (fs.writeFile as any).mock.calls.find((call: any) => + call[0].endsWith("custom-component.tsx") + )?.[1] + + expect(writtenContent).toContain('"use client"') + }) + + test("should preserve 'use client' directive for universal item files (registry:item)", async () => { + const config = await getConfig( + path.resolve(__dirname, "../../fixtures/vite-with-tailwind") + ) + const result = await updateFiles( + [ + { + path: "universal-widget.tsx", + type: "registry:item", + target: "~/universal-widget.tsx", + content: `'use client' + +export function UniversalWidget() { + return
Universal Widget
+}`, + }, + ], + config, + { + overwrite: true, + silent: true, + } + ) + + // Verify that the file was created + expect(result.filesCreated).toContain("universal-widget.tsx") + + // Read the written file and check if 'use client' is preserved + const writtenContent = (fs.writeFile as any).mock.calls.find((call: any) => + call[0].endsWith("universal-widget.tsx") + )?.[1] + + expect(writtenContent).toContain("'use client'") + }) + + test("should remove 'use client' directive for non-universal item files when rsc is false", async () => { + const config = await getConfig( + path.resolve(__dirname, "../../fixtures/vite-with-tailwind") + ) + const result = await updateFiles( + [ + { + path: "registry/default/ui/regular-component.tsx", + type: "registry:ui", + content: `"use client" + +export function RegularComponent() { + return
Regular Component
+}`, + }, + ], + config, + { + overwrite: true, + silent: true, + } + ) + + // Verify that the file was created (filesCreated contains relative paths) + expect(result.filesCreated.length).toBeGreaterThan(0) + + // Read the written file and check if 'use client' was removed + const writtenContent = (fs.writeFile as any).mock.calls.find((call: any) => + call[0].endsWith("regular-component.tsx") + )?.[1] + + // The 'use client' should be removed by the RSC transformer + expect(writtenContent).not.toContain('"use client"') + }) }) describe("resolveModuleByProbablePath", () => {