mirror of
https://github.com/hanzoai/shadcn.git
synced 2026-10-30 18:22:59 +00:00
React.lazy expects default exports, but all finance registry files were using named exports. This caused build failures with the error: "Element type is invalid: expected a string ... but got: undefined" Changes: - Updated all finance registry files to import and re-export as default - Removed non-existent mini-chart and symbol-overview MDX files - Updated sidebar navigation to remove mini-chart and symbol-overview - Fixed market-overview, ticker-tape, and trading-panel registry files The finance components now properly load in ComponentPreview during static site generation.
175 lines
4.7 KiB
TypeScript
175 lines
4.7 KiB
TypeScript
// Removed "use server" for static export compatibility
|
|
import { promises as fs } from "fs"
|
|
import { tmpdir } from "os"
|
|
import path from "path"
|
|
import { Index } from "@/__registry__"
|
|
import { Project, ScriptKind, SourceFile, SyntaxKind } from "ts-morph"
|
|
import { z } from "zod"
|
|
|
|
import { highlightCode } from "@/lib/highlight-code"
|
|
import { BlockChunk } from "@/registry/schema"
|
|
import { Style } from "@/registry/styles"
|
|
|
|
const DEFAULT_BLOCKS_STYLE = "default" satisfies Style["name"]
|
|
|
|
const project = new Project({
|
|
compilerOptions: {},
|
|
})
|
|
|
|
// Single theme system - no style parameter needed
|
|
export async function getAllBlockIds(types?: string[], categories?: string[]) {
|
|
const blocks = await getAllBlocks(
|
|
types ?? ["components:block"],
|
|
categories ?? []
|
|
)
|
|
|
|
return blocks.map((block) => block.name)
|
|
}
|
|
|
|
export async function getAllBlocks(
|
|
types: string[] = ["components:block"],
|
|
categories: string[] = []
|
|
) {
|
|
// Collect all blocks (single theme system)
|
|
const allBlocks: any[] = []
|
|
|
|
if (typeof Index === "object" && Index !== null) {
|
|
for (const itemName in Index) {
|
|
const item = Index[itemName]
|
|
allBlocks.push(item)
|
|
}
|
|
}
|
|
|
|
// Filter by type and categories (registry is generated by build script, no validation needed)
|
|
return allBlocks.filter((block) => {
|
|
const matchesType = types.includes(block.type)
|
|
|
|
// Extract category from block name (e.g., "sidebar-01" -> "sidebar")
|
|
const blockCategory = block.name.split("-")[0]
|
|
const matchesCategory =
|
|
categories.length === 0 || categories.includes(blockCategory)
|
|
|
|
return matchesType && matchesCategory
|
|
})
|
|
}
|
|
|
|
// Single theme system - no style parameter needed
|
|
export async function getBlock(name: string) {
|
|
const entry = Index[name]
|
|
|
|
// Return null if block doesn't exist
|
|
if (!entry) {
|
|
return null
|
|
}
|
|
|
|
// Skip validation - we control registry generation and know it's correct
|
|
const content = await _getBlockContent(name)
|
|
|
|
const chunks = await Promise.all(
|
|
entry.chunks?.map(async (chunk: BlockChunk) => {
|
|
const code = await readFile(chunk.file)
|
|
|
|
const tempFile = await createTempSourceFile(`${chunk.name}.tsx`)
|
|
const sourceFile = project.createSourceFile(tempFile, code, {
|
|
scriptKind: ScriptKind.TSX,
|
|
})
|
|
|
|
sourceFile
|
|
.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)
|
|
.filter((node) => {
|
|
return node.getAttribute("x-chunk") !== undefined
|
|
})
|
|
?.map((component) => {
|
|
component
|
|
.getAttribute("x-chunk")
|
|
?.asKind(SyntaxKind.JsxAttribute)
|
|
?.remove()
|
|
})
|
|
|
|
return {
|
|
...chunk,
|
|
code: sourceFile
|
|
.getText()
|
|
.replaceAll(`@/registry/default/`, "@/components/"),
|
|
}
|
|
}) ?? []
|
|
)
|
|
|
|
// Skip validation - return object directly
|
|
return {
|
|
style: "default", // Single theme system
|
|
highlightedCode: content.code ? await highlightCode(content.code) : "",
|
|
...entry,
|
|
...content,
|
|
chunks,
|
|
type: "components:block",
|
|
}
|
|
}
|
|
|
|
// Single theme system - no style parameter needed
|
|
async function _getBlockCode(name: string) {
|
|
const entry = Index[name]
|
|
|
|
if (!entry?.source) {
|
|
return ""
|
|
}
|
|
|
|
return await readFile(entry.source)
|
|
}
|
|
|
|
async function readFile(source: string) {
|
|
const filepath = path.join(process.cwd(), source)
|
|
return await fs.readFile(filepath, "utf-8")
|
|
}
|
|
|
|
async function createTempSourceFile(filename: string) {
|
|
const dir = await fs.mkdtemp(path.join(tmpdir(), "codex-"))
|
|
return path.join(dir, filename)
|
|
}
|
|
|
|
// Single theme system - no style parameter needed
|
|
async function _getBlockContent(name: string) {
|
|
const raw = await _getBlockCode(name)
|
|
|
|
const tempFile = await createTempSourceFile(`${name}.tsx`)
|
|
const sourceFile = project.createSourceFile(tempFile, raw, {
|
|
scriptKind: ScriptKind.TSX,
|
|
})
|
|
|
|
// Extract meta.
|
|
const description = _extractVariable(sourceFile, "description")
|
|
const iframeHeight = _extractVariable(sourceFile, "iframeHeight")
|
|
const containerClassName = _extractVariable(sourceFile, "containerClassName")
|
|
|
|
// Format the code.
|
|
let code = sourceFile.getText()
|
|
code = code.replaceAll(`@/registry/default/`, "@/components/")
|
|
code = code.replaceAll("export default", "export")
|
|
|
|
return {
|
|
description: description ?? undefined,
|
|
code,
|
|
container: {
|
|
height: iframeHeight ?? undefined,
|
|
className: containerClassName ?? undefined,
|
|
},
|
|
}
|
|
}
|
|
|
|
function _extractVariable(sourceFile: SourceFile, name: string) {
|
|
const variable = sourceFile.getVariableDeclaration(name)
|
|
if (!variable) {
|
|
return null
|
|
}
|
|
|
|
const value = variable
|
|
.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral)
|
|
.getLiteralValue()
|
|
|
|
variable.remove()
|
|
|
|
return value
|
|
}
|
|
|
|
// Force recompile
|