diff --git a/packages/shadcn/src/utils/add-components.ts b/packages/shadcn/src/utils/add-components.ts index 795b939280c..46c18803e9d 100644 --- a/packages/shadcn/src/utils/add-components.ts +++ b/packages/shadcn/src/utils/add-components.ts @@ -2,6 +2,7 @@ import path from "path" import { getRegistryItems } from "@/src/registry/api" import { configWithDefaults } from "@/src/registry/config" import { resolveRegistryTree } from "@/src/registry/resolver" +import { isUrl } from "@/src/registry/utils" import { configSchema, registryItemFileSchema, @@ -126,10 +127,14 @@ async function addProjectComponents( await updateDependencies(tree.dependencies, tree.devDependencies, config, { silent: options.silent, }) + + const isRemote = components.some((component) => isUrl(component)) + await updateFiles(tree.files, config, { overwrite: options.overwrite, silent: options.silent, path: options.path, + isRemote, }) if (tree.docs) { diff --git a/packages/shadcn/src/utils/transformers/transform-import.ts b/packages/shadcn/src/utils/transformers/transform-import.ts index 59177f9f78b..c79cb0ab9fb 100644 --- a/packages/shadcn/src/utils/transformers/transform-import.ts +++ b/packages/shadcn/src/utils/transformers/transform-import.ts @@ -62,7 +62,17 @@ function updateImportAliases( // This treats the remote as coming from a faux registry. if (isRemote && moduleSpecifier.startsWith("@/")) { - moduleSpecifier = moduleSpecifier.replace(/^@\//, `@/registry/new-york/`) + // Check if this is a known alias pattern that should be transformed + const isKnownPattern = + moduleSpecifier.match(/^@\/(components|ui|lib|hooks)(\/|$)/) !== null + + // Only transform known patterns, preserve others (like @/types, @/config, etc.) + if (isKnownPattern) { + moduleSpecifier = moduleSpecifier.replace(/^@\//, `@/registry/new-york/`) + } else { + // For non-standard paths like @/types, @/config, preserve them as-is + return moduleSpecifier + } } // Not a registry import. diff --git a/packages/shadcn/src/utils/updaters/update-files.ts b/packages/shadcn/src/utils/updaters/update-files.ts index 0e468e2e527..650a76b887a 100644 --- a/packages/shadcn/src/utils/updaters/update-files.ts +++ b/packages/shadcn/src/utils/updaters/update-files.ts @@ -544,6 +544,15 @@ async function resolveImports(filePaths: string[], config: Config) { continue } + // Preserve non-standard import paths (like @/config, @/types) that don't match + // known registry patterns. These should not be auto-resolved. + const isKnownRegistryPattern = + moduleSpecifier.match(/^@\/(components|ui|lib|hooks)(\/|$)/) !== null + if (!isKnownRegistryPattern) { + // Skip resolution for non-standard paths - preserve them as-is + continue + } + // Find the probable import file path. // This is where we expect to find the file on disk. const probableImportFilePath = await resolveImport( diff --git a/packages/shadcn/test/utils/__snapshots__/transform-import.test.ts.snap b/packages/shadcn/test/utils/__snapshots__/transform-import.test.ts.snap index 28e5c9a1a90..3917e51f5b1 100644 --- a/packages/shadcn/test/utils/__snapshots__/transform-import.test.ts.snap +++ b/packages/shadcn/test/utils/__snapshots__/transform-import.test.ts.snap @@ -1,5 +1,23 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`preserve @/config imports when isRemote is true 1`] = ` +"import type { DataTableConfig } from "@/config/data-table" + +import { Button } from "@/components/ui/button" + " +`; + +exports[`preserve @/types imports when isRemote is true 1`] = ` +"import type { + ExtendedColumnFilter, + FilterOperator, + JoinOperator, +} from "@/types/data-table" + +import { Button } from "@/components/ui/button" + " +`; + exports[`transform async/dynamic imports 1`] = ` "import * as React from "react" import { Button } from "@/components/ui/button" diff --git a/packages/shadcn/test/utils/transform-import.test.ts b/packages/shadcn/test/utils/transform-import.test.ts index 23efb64b338..d7f5258e2ec 100644 --- a/packages/shadcn/test/utils/transform-import.test.ts +++ b/packages/shadcn/test/utils/transform-import.test.ts @@ -2,8 +2,7 @@ import { expect, test } from "vitest" import { transform } from "../../src/utils/transformers" - -test('transform nested workspace folder for utils, website/src/utils', async () => { +test("transform nested workspace folder for utils, website/src/utils", async () => { expect( await transform({ filename: "test.ts", @@ -31,7 +30,6 @@ test('transform nested workspace folder for utils, website/src/utils', async () import { cn } from "website/src/utils" " `) - }) test("transform import", async () => { @@ -346,3 +344,47 @@ async function load() { }) ).toMatchSnapshot() }) + +test("preserve @/types imports when isRemote is true", async () => { + expect( + await transform({ + filename: "test.ts", + raw: `import type { + ExtendedColumnFilter, + FilterOperator, + JoinOperator, +} from "@/types/data-table" + +import { Button } from "@/components/ui/button" + `, + config: { + tsx: true, + aliases: { + components: "@/components", + utils: "@/lib/utils", + }, + }, + isRemote: true, + }) + ).toMatchSnapshot() +}) + +test("preserve @/config imports when isRemote is true", async () => { + expect( + await transform({ + filename: "test.ts", + raw: `import type { DataTableConfig } from "@/config/data-table" + +import { Button } from "@/components/ui/button" + `, + config: { + tsx: true, + aliases: { + components: "@/components", + utils: "@/lib/utils", + }, + }, + isRemote: true, + }) + ).toMatchSnapshot() +})