Skip to content
Merged
23 changes: 22 additions & 1 deletion packages/cli/src/commands/registry/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ async function runBuild(options: BuildOptions) {

const dependencies = new Set(item.dependencies);
const devDependencies = new Set(item.devDependencies);
const registryDependencies = new Set(item.registryDependencies);

const registryDependencies = new Set(item.registryDependencies.map(transformLocal));

const predefinedDeps = dependencies.size > 0 && devDependencies.size > 0;
if (!predefinedDeps) {
Expand Down Expand Up @@ -210,3 +211,23 @@ async function runBuild(options: BuildOptions) {

await p.tasks(tasks);
}

/**
* Transforms registryDependencies that start with `local:` into a path
* relative to the current registry-item's json file.
*
* ```
* "local:stepper"
*```
* transforms into:
* ```
* "./stepper.json"
* ```
*/
function transformLocal(registryDep: string) {
if (registryDep.startsWith("local:")) {
const LOCAL_REGEX = /^local:(.*)/;
return registryDep.replace(LOCAL_REGEX, "./$1.json");
}
return registryDep;
}
1 change: 0 additions & 1 deletion packages/cli/src/commands/update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ async function runUpdate(cwd: string, config: cliConfig.Config, options: UpdateO
const tasks: p.Task[] = [];

const resolvedItems = await registry.resolveRegistryItems({
baseUrl: registryUrl,
registryIndex: registryIndex,
items: selectedComponents.map((comp) => comp.name),
});
Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/utils/add-registry-items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export async function addRegistryItems(opts: AddRegistryItemsProps) {

const registryIndex = await registry.getRegistryIndex(registryUrl);
const resolvedItems = await registry.resolveRegistryItems({
baseUrl: registryUrl,
items: Array.from(selectedItems),
registryIndex,
});
Expand Down
30 changes: 17 additions & 13 deletions packages/cli/src/utils/registry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,46 +47,50 @@ export async function getRegistryBaseColor(baseUrl: string, baseColor: string) {
}

type ResolveRegistryItemsProps = {
baseUrl: string;
registryIndex: schemas.RegistryIndex;
items: string[];
includeRegDeps?: boolean;
parentUrl?: URL;
};

type ResolvedRegistryItem = schemas.RegistryItem | schemas.RegistryIndexItem;
export async function resolveRegistryItems({
registryIndex,
baseUrl,
items,
includeRegDeps = true,
parentUrl,
}: ResolveRegistryItemsProps): Promise<ResolvedRegistryItem[]> {
const resolvedItems: ResolvedRegistryItem[] = [];

for (const item of items) {
let remoteUrl: URL | undefined;
let resolvedItem: ResolvedRegistryItem | undefined = registryIndex.find(
(entry) => entry.name === item
);

// the `item` doesn't exist in the `index`, so it _must_ be a remote item (in other words, it's a URL)
/**
* The `item` doesn't exist in the registry's `index`, so it can be one of two things:
* 1. a remote registry item (URL)
* 2. a `local:registryDep` of a _remote_ item (relative path from that item to the dep)
*/
if (!resolvedItem) {
const url = item;
if (!isUrl(url)) {
const isRelative = item.startsWith("./") || item.startsWith("../");
if (isUrl(item) || (parentUrl && isRelative)) {
remoteUrl = new URL(item, parentUrl);
const [result] = await fetchRegistry([remoteUrl]);
resolvedItem = schemas.registryItemSchema.parse(result);
} else {
throw error(
`Component item '${item}' does not exist in the registry, nor is it a valid URL.`
`Registry item '${item}' does not exist in the registry, nor is it a valid URL or a relative path to a registry dependency.`
);
}

const [result] = await fetchRegistry([url]);
resolvedItem = schemas.registryItemSchema.parse(result);
}

resolvedItems.push(resolvedItem);

if (includeRegDeps && resolvedItem.registryDependencies?.length) {
if (resolvedItem.registryDependencies?.length) {
const registryDeps = await resolveRegistryItems({
baseUrl,
registryIndex: registryIndex,
items: resolvedItem.registryDependencies,
parentUrl: remoteUrl,
});
resolvedItems.push(...registryDeps);
}
Expand Down
8 changes: 4 additions & 4 deletions registry-template/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"registryDependencies": ["button", "input", "label", "textarea", "card"],
"files": [
{
"path": "registry/blocks/example-form/example-form.svelte",
"path": "src/lib/registry/blocks/example-form/example-form.svelte",
"type": "registry:component"
}
]
Expand Down Expand Up @@ -84,15 +84,15 @@
"files": [
{
"path": "src/lib/registry/ui/stepper/stepper.svelte",
"type": "registry:component"
"type": "registry:file"
},
{
"path": "src/lib/registry/ui/stepper/stepper-item.svelte",
"type": "registry:component"
"type": "registry:file"
},
{
"path": "src/lib/registry/ui/stepper/index.ts",
"type": "registry:lib"
"type": "registry:file"
}
]
},
Expand Down
57 changes: 48 additions & 9 deletions sites/docs/src/content/registry/registry-item-json.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,23 +136,62 @@ Use `@version` to specify the version of your registry item.

### registryDependencies

Used for registry dependencies. Can be names or URLs. Use the name of the item to reference shadcn/ui components and urls to reference other registries.
Defines other registry items that this item depends on.

- For `shadcn-svelte` registry items such as `button`, `input`, `select`, etc use the name eg. `['button', 'input', 'select']`.
- For custom registry items use the URL of the registry item eg. `['https://example.com/r/hello-world.json']`.
Each entry may be one of the following:

#### shadcn-svelte Registry Item

The name of a shadcn-svelte registry item (e.g., `'button'`, `'input'`, `'select'`), which will resolve to that item in the shadcn-svelte registry.

```json title="registry-item.json" showLineNumbers
{
"registryDependencies": [
"button",
"input",
"select",
"https://example.com/r/editor.json"
"registryDependencies": ["button", "input", "select"]
}
```

#### Remote URL

A full URL to a custom registry item (e.g. `https://example.com/r/hello-world.json`)

```json title="registry-item.json" showLineNumbers
{
"registryDependencies": ["https://example.com/r/hello-world.json"]
}
```

#### Local alias (when building with the CLI)

If you're defining the item in `registry.json` and using the CLI to build the registry, you can use a name prefixed with `local:` (e.g. `local:stepper`) to reference an item in the current registry. The CLI will convert this to a relative path (e.g. `./stepper.json`) in the output `registry-item.json` file.

```json title="registry.json" showLineNumbers
{
"items": [
{
"name": "hello-world",
"registryDependencies": ["local:stepper"]
}
]
}
```

Note: The CLI will automatically resolve remote registry dependencies.
Which the CLI will convert to the following in the output `registry-item.json` file:

```json title="registry-item.json" showLineNumbers
{
"registryDependencies": ["./stepper.json"]
}
```

#### Relative Path

If you're not using the CLI and defining the item directly in its `registry-item.json` file, you can specify a relative path, which is relative to the current item, to reference another item in the registry (e.g. `./stepper.json`).

```json title="registry-item.json" showLineNumbers
{
"registryDependencies": ["./stepper.json"]
}
```

### files

Expand Down
4 changes: 2 additions & 2 deletions sites/docs/src/content/registry/registry-json.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The `registry.json` schema is used to define your custom component registry.
"description": "A simple hello world component.",
"files": [
{
"path": "registry/hello-world/hello-world.svelte",
"path": "src/lib/registry/blocks/hello-world/hello-world.svelte",
"type": "registry:component"
}
]
Expand Down Expand Up @@ -79,7 +79,7 @@ The `items` in your registry. Each item must implement the [registry-item schema
"description": "A simple hello world component.",
"files": [
{
"path": "registry/hello-world/hello-world.svelte",
"path": "src/lib/registry/blocks/hello-world/hello-world.svelte",
"type": "registry:component"
}
]
Expand Down