Skip to content

Commit 7919ae9

Browse files
committed
feat: add support for a template registry
1 parent dde3428 commit 7919ae9

File tree

3 files changed

+96
-0
lines changed

3 files changed

+96
-0
lines changed

src/registry/registry.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Registry, validateRegistry } from './validate-registry'
2+
3+
export const defaultIndex = `https://raw.githubusercontent.com/solana-developers/solana-templates/refs/heads/main/index.json`
4+
export const solanaTemplatesIndex = process.env.SOLANA_TEMPLATES_INDEX ?? defaultIndex
5+
6+
export async function getRegistry(): Promise<Registry> {
7+
const result = await fetch(solanaTemplatesIndex)
8+
9+
if (!result.ok) {
10+
throw new Error(`Failed to fetch registry index file: ${solanaTemplatesIndex}`)
11+
}
12+
13+
return validateRegistry(await result.json())
14+
}

src/registry/validate-registry.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { z } from 'zod'
2+
3+
// Template schema
4+
const registryTemplateSchema = z.object({
5+
name: z.string(),
6+
description: z.string(),
7+
repository: z.string(),
8+
})
9+
10+
// Group schema
11+
const registryGroupSchema = z.object({
12+
id: z.string(),
13+
name: z.string(),
14+
description: z.string(),
15+
templates: z.array(registryTemplateSchema),
16+
})
17+
18+
// Section schema (default/legacy)
19+
const registrySectionSchema = z.object({
20+
id: z.string(),
21+
name: z.string(),
22+
groups: z.array(registryGroupSchema),
23+
})
24+
25+
// Main schema: requires 'default' and allows additional sections
26+
const registrySchema = z
27+
.object({
28+
default: registrySectionSchema, // 'default' is required
29+
})
30+
.catchall(registrySectionSchema) // Any other key is optional and must match registrySectionSchema
31+
32+
// Type inference
33+
export type Registry = z.infer<typeof registrySchema>
34+
export type RegistrySection = z.infer<typeof registrySectionSchema>
35+
export type RegistryGroup = z.infer<typeof registryGroupSchema>
36+
export type RegistryTemplate = z.infer<typeof registryTemplateSchema>
37+
38+
export async function validateRegistry(resultJson: unknown): Promise<Registry> {
39+
return registrySchema.parse(resultJson)
40+
}

src/utils/get-args.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { intro, log, outro } from '@clack/prompts'
22
import { program } from 'commander'
3+
import pico from 'picocolors'
4+
import { getRegistry } from '../registry/registry'
5+
import { Registry, RegistrySection } from '../registry/validate-registry'
36
import { findTemplate, listTemplates, Template } from '../templates/templates'
47
import { AppInfo } from './get-app-info'
58
import { GetArgsResult } from './get-args-result'
@@ -20,6 +23,7 @@ export async function getArgs(argv: string[], app: AppInfo, pm: PackageManager =
2023
.option('-d, --dry-run', help('Dry run (default: false)'))
2124
.option('-t, --template <template-name>', help('Use a template'))
2225
.option('--list-templates', help('List available templates'))
26+
.option('--list-registry', help('List the templates in the registry'))
2327
.option('--list-versions', help('Verify your versions of Anchor, AVM, Rust, and Solana'))
2428
.option('--skip-git', help('Skip git initialization'))
2529
.option('--skip-init', help('Skip running the init script'))
@@ -43,10 +47,16 @@ Examples:
4347
// Get the options from the command line
4448
const result = input.opts()
4549

50+
const registry = await getRegistry()
51+
4652
if (result.listVersions) {
4753
listVersions()
4854
process.exit(0)
4955
}
56+
if (result.listRegistry) {
57+
listRegistry(registry)
58+
process.exit(0)
59+
}
5060
if (result.listTemplates) {
5161
listTemplates()
5262
outro(
@@ -121,3 +131,35 @@ function help(text: string) {
121131
122132
${text}`
123133
}
134+
135+
function listRegistry(registry: Registry) {
136+
const { default: defaultRegistry, ...otherRegistries } = registry
137+
138+
printRegistrySection(defaultRegistry)
139+
140+
const others = Object.keys(otherRegistries)
141+
142+
for (const key of others) {
143+
printRegistrySection(otherRegistries[key])
144+
}
145+
}
146+
147+
function printRegistrySection(section: RegistrySection) {
148+
console.log(` === ${section.id} === `)
149+
console.log(`Name: ${section.name}`)
150+
// console.log(`Description: ${section.description}`) // TODO: add description to index.json and the validation
151+
console.log(`Groups: ${section.groups.length}`)
152+
153+
for (const group of section.groups) {
154+
console.log(` === ${group.id} === `)
155+
console.log(`Name: ${group.name}`)
156+
console.log(pico.gray(`Description: ${group.description}`))
157+
console.log(pico.gray(`Templates: ${group.templates.length}`))
158+
159+
for (const template of group.templates) {
160+
console.log(` ${template.name}`)
161+
console.log(pico.gray(` Description: ${template.description}`))
162+
console.log(pico.gray(` Repository: ${template.repository}`))
163+
}
164+
}
165+
}

0 commit comments

Comments
 (0)