Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support remote registry urls in cli #4992

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
8 changes: 7 additions & 1 deletion packages/shadcn/src/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const addOptionsSchema = z.object({
cwd: z.string(),
all: z.boolean(),
path: z.string().optional(),
registry: z.string().optional(),
silent: z.boolean(),
srcDir: z.boolean().optional(),
})
Expand All @@ -40,6 +41,7 @@ export const add = new Command()
)
.option("-a, --all", "add all available components", false)
.option("-p, --path <path>", "the path to add the component to.")
.option("-r, --registry <registry>", "the registry to use.")
.option("-s, --silent", "mute output.", false)
.option(
"--src-dir",
Expand Down Expand Up @@ -80,6 +82,10 @@ export const add = new Command()
options.components = await promptForRegistryComponents(options)
}

if (options.registry) {
options.components = await promptForRegistryComponents(options)
}

let { errors, config } = await preFlightAdd(options)

// No components.json file. Prompt the user to run init.
Expand Down Expand Up @@ -161,7 +167,7 @@ export const add = new Command()
async function promptForRegistryComponents(
options: z.infer<typeof addOptionsSchema>
) {
const registryIndex = await getRegistryIndex()
const registryIndex = await getRegistryIndex(options.registry)
if (!registryIndex) {
logger.break()
handleError(new Error("Failed to fetch registry index."))
Expand Down
7 changes: 6 additions & 1 deletion packages/shadcn/src/utils/add-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export async function addComponents(
components: string[],
config: Config,
options: {
registry?: string
overwrite?: boolean
silent?: boolean
isNewProject?: boolean
Expand All @@ -27,7 +28,11 @@ export async function addComponents(
const registrySpinner = spinner(`Checking registry.`, {
silent: options.silent,
})?.start()
const tree = await registryResolveItemsTree(components, config)
const tree = await registryResolveItemsTree(
options.registry,
components,
config
)
if (!tree) {
registrySpinner?.fail()
return handleError(new Error("Failed to fetch components from registry."))
Expand Down
67 changes: 47 additions & 20 deletions packages/shadcn/src/utils/registry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ const agent = process.env.https_proxy
? new HttpsProxyAgent(process.env.https_proxy)
: undefined

export async function getRegistryIndex() {
export async function getRegistryIndex(registryUrl: string = REGISTRY_URL) {
try {
const [result] = await fetchRegistry(["index.json"])
const [result] = await fetchRegistry(registryUrl, ["index.json"])

return registryIndexSchema.parse(result)
} catch (error) {
Expand All @@ -34,9 +34,9 @@ export async function getRegistryIndex() {
}
}

export async function getRegistryStyles() {
export async function getRegistryStyles(registryUrl: string = REGISTRY_URL) {
try {
const [result] = await fetchRegistry(["styles/index.json"])
const [result] = await fetchRegistry(registryUrl, ["styles/index.json"])

return stylesSchema.parse(result)
} catch (error) {
Expand All @@ -46,9 +46,13 @@ export async function getRegistryStyles() {
}
}

export async function getRegistryItem(name: string, style: string) {
export async function getRegistryItem(
registryUrl: string = REGISTRY_URL,
name: string,
style: string
) {
try {
const [result] = await fetchRegistry([
const [result] = await fetchRegistry(registryUrl, [
isUrl(name) ? name : `styles/${style}/${name}.json`,
])

Expand Down Expand Up @@ -85,9 +89,14 @@ export async function getRegistryBaseColors() {
]
}

export async function getRegistryBaseColor(baseColor: string) {
export async function getRegistryBaseColor(
registryUrl: string = REGISTRY_URL,
baseColor: string
) {
try {
const [result] = await fetchRegistry([`colors/${baseColor}.json`])
const [result] = await fetchRegistry(registryUrl, [
`colors/${baseColor}.json`,
])

return registryBaseColorSchema.parse(result)
} catch (error) {
Expand All @@ -96,6 +105,7 @@ export async function getRegistryBaseColor(baseColor: string) {
}

export async function resolveTree(
registryUrl: string = REGISTRY_URL,
index: z.infer<typeof registryIndexSchema>,
names: string[]
) {
Expand All @@ -111,7 +121,11 @@ export async function resolveTree(
tree.push(entry)

if (entry.registryDependencies) {
const dependencies = await resolveTree(index, entry.registryDependencies)
const dependencies = await resolveTree(
registryUrl,
index,
entry.registryDependencies
)
tree.push(...dependencies)
}
}
Expand All @@ -123,12 +137,13 @@ export async function resolveTree(
}

export async function fetchTree(
registryUrl: string = REGISTRY_URL,
style: string,
tree: z.infer<typeof registryIndexSchema>
) {
try {
const paths = tree.map((item) => `styles/${style}/${item.name}.json`)
const result = await fetchRegistry(paths)
const result = await fetchRegistry(registryUrl, paths)
return registryIndexSchema.parse(result)
} catch (error) {
handleError(error)
Expand Down Expand Up @@ -159,11 +174,11 @@ export async function getItemTargetPath(
)
}

async function fetchRegistry(paths: string[]) {
async function fetchRegistry(registryUrl: string, paths: string[]) {
try {
const results = await Promise.all(
paths.map(async (path) => {
const url = getRegistryUrl(path)
const url = getRegistryUrl(registryUrl, path)
const response = await fetch(url, { agent })

if (!response.ok) {
Expand Down Expand Up @@ -256,11 +271,12 @@ export function getRegistryItemFileTargetPath(
}

export async function registryResolveItemsTree(
registryUrl: string = REGISTRY_URL,
names: z.infer<typeof registryItemSchema>["name"][],
config: Config
) {
try {
const index = await getRegistryIndex()
const index = await getRegistryIndex(registryUrl)
if (!index) {
return null
}
Expand All @@ -273,14 +289,15 @@ export async function registryResolveItemsTree(
let registryDependencies: string[] = []
for (const name of names) {
const itemRegistryDependencies = await resolveRegistryDependencies(
registryUrl,
name,
config
)
registryDependencies.push(...itemRegistryDependencies)
}

const uniqueRegistryDependencies = Array.from(new Set(registryDependencies))
let result = await fetchRegistry(uniqueRegistryDependencies)
let result = await fetchRegistry(registryUrl, uniqueRegistryDependencies)
const payload = z.array(registryItemSchema).parse(result)

if (!payload) {
Expand All @@ -293,7 +310,11 @@ export async function registryResolveItemsTree(
// Other components will ship with their theme tokens.
if (names.includes("index")) {
if (config.tailwind.baseColor) {
const theme = await registryGetTheme(config.tailwind.baseColor, config)
const theme = await registryGetTheme(
registryUrl,
config.tailwind.baseColor,
config
)
if (theme) {
payload.unshift(theme)
}
Expand Down Expand Up @@ -336,6 +357,7 @@ export async function registryResolveItemsTree(
}

async function resolveRegistryDependencies(
registryUrl: string = REGISTRY_URL,
url: string,
config: Config
): Promise<string[]> {
Expand All @@ -344,6 +366,7 @@ async function resolveRegistryDependencies(

async function resolveDependencies(itemUrl: string) {
const url = getRegistryUrl(
registryUrl,
isUrl(itemUrl) ? itemUrl : `styles/${config.style}/${itemUrl}.json`
)

Expand All @@ -354,7 +377,7 @@ async function resolveRegistryDependencies(
visited.add(url)

try {
const [result] = await fetchRegistry([url])
const [result] = await fetchRegistry(registryUrl, [url])
const item = registryItemSchema.parse(result)
payload.push(url)

Expand All @@ -375,8 +398,12 @@ async function resolveRegistryDependencies(
return Array.from(new Set(payload))
}

export async function registryGetTheme(name: string, config: Config) {
const baseColor = await getRegistryBaseColor(name)
export async function registryGetTheme(
registryUrl: string = REGISTRY_URL,
name: string,
config: Config
) {
const baseColor = await getRegistryBaseColor(registryUrl, name)
if (!baseColor) {
return null
}
Expand Down Expand Up @@ -427,7 +454,7 @@ export async function registryGetTheme(name: string, config: Config) {
return theme
}

function getRegistryUrl(path: string) {
function getRegistryUrl(registryUrl: string, path: string) {
if (isUrl(path)) {
// If the url contains /chat/b/, we assume it's the v0 registry.
// We need to add the /json suffix if it's missing.
Expand All @@ -439,7 +466,7 @@ function getRegistryUrl(path: string) {
return url.toString()
}

return `${REGISTRY_URL}/${path}`
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hard-coded dependency on registry url

return `${registryUrl}/${path}`
}

function isUrl(path: string) {
Expand Down
3 changes: 2 additions & 1 deletion packages/shadcn/src/utils/updaters/update-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export async function updateFiles(
files: RegistryItem["files"],
config: Config,
options: {
registry?: string
overwrite?: boolean
force?: boolean
silent?: boolean
Expand All @@ -54,7 +55,7 @@ export async function updateFiles(

const [projectInfo, baseColor] = await Promise.all([
getProjectInfo(config.resolvedPaths.cwd),
getRegistryBaseColor(config.tailwind.baseColor),
getRegistryBaseColor(options.registry, config.tailwind.baseColor),
])

const filesCreated = []
Expand Down