Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 27 additions & 22 deletions packages/shadcn/src/utils/updaters/update-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,28 +122,33 @@ 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,
...(_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,
...(_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
Expand Down
105 changes: 105 additions & 0 deletions packages/shadcn/test/utils/updaters/update-files.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <div>Custom Component</div>
}`,
},
],
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 <div>Universal Widget</div>
}`,
},
],
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 <div>Regular Component</div>
}`,
},
],
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", () => {
Expand Down