Files
shadcn-ui/apps/v4/scripts/build-registry.mts
shadcn 84bd724d97 feat: refactor registry (#8598)
* feat: refactor registry

* fix: remove components

* refactor: getActiveStyle

* fix: prettier in build-registry

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* fix

* Update apps/v4/scripts/build-registry.mts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix

* Update apps/v4/scripts/build-registry.mts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update apps/v4/components/block-viewer.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-29 15:07:56 +04:00

233 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { exec, execFile } from "child_process"
import { existsSync, promises as fs } from "fs"
import path from "path"
import { rimraf } from "rimraf"
import { registrySchema } from "shadcn/schema"
import { getAllBlocks } from "@/lib/blocks"
import { STYLES, type Style } from "@/registry/styles"
async function buildRegistryIndex(styles: Style[]) {
let index = `/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-explicit-any */
// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"
export const Index: Record<string, Record<string, any>> = {`
for (const style of styles) {
// Dynamically import the registry for this style.
const { registry: importedRegistry } = await import(
`../registry/${style.name}/registry.ts`
)
// Validate the registry schema.
const parseResult = registrySchema.safeParse(importedRegistry)
if (!parseResult.success) {
console.error(`❌ Registry validation failed for ${style.name}:`)
console.error(parseResult.error.format())
throw new Error(`Invalid registry schema for ${style.name}`)
}
const registry = parseResult.data
index += `
"${style.name}": {`
for (const item of registry.items) {
const files =
item.files?.map((file) => ({
path: typeof file === "string" ? file : file.path,
type: typeof file === "string" ? item.type : file.type,
target: typeof file === "string" ? undefined : file.target,
})) ?? []
if (files.length === 0) {
continue
}
const componentPath = item.files?.[0]?.path
? `@/registry/${style.name}/${item.files[0].path}`
: ""
index += `
"${item.name}": {
name: "${item.name}",
description: "${item.description ?? ""}",
type: "${item.type}",
registryDependencies: ${JSON.stringify(item.registryDependencies)},
files: [${files.map((file) => {
const filePath = `registry/${style.name}/${file.path}`
return `{
path: "${filePath}",
type: "${file.type}",
target: "${file.target ?? ""}"
}`
})}],
component: ${
componentPath
? `React.lazy(async () => {
const mod = await import("${componentPath}")
const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') || item.name
return { default: mod.default || mod[exportName] }
})`
: "null"
},
categories: ${JSON.stringify(item.categories)},
meta: ${JSON.stringify(item.meta)},
},`
}
index += `
},`
}
index += `
}`
console.log(
`#️⃣ Built multi-style index with ${styles.length} styles: ${styles.map((s) => s.name).join(", ")}`
)
// Write unified index.
rimraf.sync(path.join(process.cwd(), "registry/__index__.tsx"))
await fs.writeFile(path.join(process.cwd(), "registry/__index__.tsx"), index)
}
async function buildRegistryJsonFile(styleName: string) {
// 1. Import the registry for this style.
const { registry: importedRegistry } = await import(
`../registry/${styleName}/registry.ts`
)
// 2. Validate the registry schema.
const parseResult = registrySchema.safeParse(importedRegistry)
if (!parseResult.success) {
console.error(`❌ Registry validation failed for ${styleName}:`)
console.error(parseResult.error.format())
throw new Error(`Invalid registry schema for ${styleName}`)
}
const registry = parseResult.data
// 3. Fix the path for registry items.
const fixedRegistry = {
...registry,
items: registry.items.map((item) => {
const files = item.files?.map((file) => {
return {
...file,
path: `registry/${styleName}/${file.path}`,
}
})
return {
...item,
files,
}
}),
}
// 3. Create the output directory and write registry.json.
const outputDir = path.join(
process.cwd(),
styleName === "new-york-v4" ? `public/r/styles/${styleName}` : `public/r/${styleName}`
)
await fs.mkdir(outputDir, { recursive: true })
// 4. Write registry.json to output directory and format it.
const registryJsonPath = path.join(outputDir, "registry.json")
await fs.writeFile(registryJsonPath, JSON.stringify(fixedRegistry, null, 2))
await new Promise<void>((resolve, reject) => {
execFile('prettier', ['--write', registryJsonPath], (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
})
// 5. Write temporary registry file needed by shadcn build.
const tempRegistryPath = path.join(process.cwd(), `registry-${styleName}.json`)
await fs.writeFile(tempRegistryPath, JSON.stringify(fixedRegistry, null, 2))
}
async function buildRegistry(styleName: string) {
return new Promise((resolve, reject) => {
// Use local shadcn copy.
const outputPath =
styleName === "new-york-v4" ? `public/r/styles/${styleName}` : `public/r/${styleName}`
const process = exec(
`node ../../packages/shadcn/dist/index.js build registry-${styleName}.json --output ${outputPath}`
)
// exec(
// `pnpm dlx shadcn build registry-${styleName}.json --output public/r/styles/${styleName}`
// )
process.on("exit", (code) => {
if (code === 0) {
resolve(undefined)
} else {
reject(new Error(`Process exited with code ${code}`))
}
})
})
}
async function buildBlocksIndex() {
const blocks = await getAllBlocks(["registry:block"])
const payload = blocks.map((block) => ({
name: block.name,
description: block.description,
categories: block.categories,
}))
rimraf.sync(path.join(process.cwd(), "registry/__blocks__.json"))
await fs.writeFile(
path.join(process.cwd(), "registry/__blocks__.json"),
JSON.stringify(payload, null, 2)
)
await exec(`prettier --write registry/__blocks__.json`)
}
try {
const styles = Array.from(STYLES)
console.log(`🎨 Found ${styles.length} styles: ${styles.map((s) => s.name).join(", ")}`)
// Build unified multi-style index.
console.log("\n🗂 Building unified multi-style registry/__index__.tsx...")
await buildRegistryIndex(styles)
for (const style of styles) {
console.log(`\n📦 Processing style: ${style.name}`)
console.log(`💅 Building registry-${style.name}.json...`)
await buildRegistryJsonFile(style.name)
console.log(`🏗️ Building registry for ${style.name}...`)
await buildRegistry(style.name)
}
console.log("\n🗂 Building registry/__blocks__.json...")
await buildBlocksIndex()
// Clean up intermediate files.
console.log("\n🧹 Cleaning up intermediate files...")
for (const style of styles) {
if (existsSync(path.join(process.cwd(), `registry-${style.name}.json`))) {
await fs.unlink(path.join(process.cwd(), `registry-${style.name}.json`))
}
}
console.log("\n✅ Build complete!")
} catch (error) {
console.error(error)
process.exit(1)
}