Files
zeekay dacabef7d6 Fix finance component registry exports for React.lazy
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.
2025-11-10 10:02:11 -08:00

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