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
5 changes: 5 additions & 0 deletions packages/shadcn/src/utils/add-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down
12 changes: 11 additions & 1 deletion packages/shadcn/src/utils/transformers/transform-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 9 additions & 0 deletions packages/shadcn/src/utils/updaters/update-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
48 changes: 45 additions & 3 deletions packages/shadcn/test/utils/transform-import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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()
})