mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-28 07:04:20 +00:00
* 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>
233 lines
7.0 KiB
TypeScript
233 lines
7.0 KiB
TypeScript
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)
|
||
}
|