mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-24 13:44:54 +00:00
240 lines
7.1 KiB
TypeScript
240 lines
7.1 KiB
TypeScript
import { ComponentType } from 'react'
|
|
import * as PhosphorIcons from '@phosphor-icons/react'
|
|
import jsonComponentsRegistry from '../../../json-components-registry.json'
|
|
|
|
export interface UIComponentRegistry {
|
|
[key: string]: ComponentType<any>
|
|
}
|
|
|
|
interface JsonRegistryEntry {
|
|
name?: string
|
|
type?: string
|
|
export?: string
|
|
source?: string
|
|
status?: string
|
|
wrapperRequired?: boolean
|
|
wrapperComponent?: string
|
|
wrapperFor?: string
|
|
load?: {
|
|
path?: string
|
|
export?: string
|
|
}
|
|
deprecated?: DeprecatedComponentInfo
|
|
}
|
|
|
|
interface JsonComponentRegistry {
|
|
components?: JsonRegistryEntry[]
|
|
sourceRoots?: Record<string, string[]>
|
|
}
|
|
|
|
export interface DeprecatedComponentInfo {
|
|
replacedBy?: string
|
|
message?: string
|
|
}
|
|
|
|
const jsonRegistry = jsonComponentsRegistry as JsonComponentRegistry
|
|
const sourceRoots = jsonRegistry.sourceRoots ?? {}
|
|
const moduleMapsBySource = Object.fromEntries(
|
|
Object.entries(sourceRoots).map(([source, patterns]) => {
|
|
if (!patterns || patterns.length === 0) {
|
|
return [source, {}]
|
|
}
|
|
return [source, import.meta.glob(patterns, { eager: true })]
|
|
})
|
|
) as Record<string, Record<string, unknown>>
|
|
|
|
const getRegistryEntryKey = (entry: JsonRegistryEntry): string | undefined =>
|
|
entry.name ?? entry.type
|
|
|
|
const getRegistryEntryExportName = (entry: JsonRegistryEntry): string | undefined =>
|
|
entry.load?.export ?? entry.export ?? getRegistryEntryKey(entry)
|
|
|
|
const jsonRegistryEntries = jsonRegistry.components ?? []
|
|
const registryEntryByType = new Map(
|
|
jsonRegistryEntries
|
|
.map((entry) => {
|
|
const entryKey = getRegistryEntryKey(entry)
|
|
return entryKey ? [entryKey, entry] : null
|
|
})
|
|
.filter((entry): entry is [string, JsonRegistryEntry] => Boolean(entry))
|
|
)
|
|
const deprecatedComponentInfo = jsonRegistryEntries.reduce<Record<string, DeprecatedComponentInfo>>(
|
|
(acc, entry) => {
|
|
const entryKey = getRegistryEntryKey(entry)
|
|
if (!entryKey) {
|
|
return acc
|
|
}
|
|
if (entry.status === 'deprecated' || entry.deprecated) {
|
|
acc[entryKey] = entry.deprecated ?? {}
|
|
}
|
|
return acc
|
|
},
|
|
{}
|
|
)
|
|
|
|
const buildComponentMapFromExports = (
|
|
exports: Record<string, unknown>
|
|
): Record<string, ComponentType<any>> => {
|
|
return Object.entries(exports).reduce<Record<string, ComponentType<any>>>((acc, [key, value]) => {
|
|
if (value && (typeof value === 'function' || typeof value === 'object')) {
|
|
acc[key] = value as ComponentType<any>
|
|
}
|
|
return acc
|
|
}, {})
|
|
}
|
|
|
|
const buildComponentMapFromModules = (
|
|
modules: Record<string, unknown>
|
|
): Record<string, ComponentType<any>> => {
|
|
return Object.values(modules).reduce<Record<string, ComponentType<any>>>((acc, moduleExports) => {
|
|
if (!moduleExports || typeof moduleExports !== 'object') {
|
|
return acc
|
|
}
|
|
Object.entries(buildComponentMapFromExports(moduleExports as Record<string, unknown>)).forEach(
|
|
([key, component]) => {
|
|
acc[key] = component
|
|
}
|
|
)
|
|
return acc
|
|
}, {})
|
|
}
|
|
|
|
const atomModules = import.meta.glob('@/components/atoms/*.tsx', { eager: true })
|
|
const moleculeModules = import.meta.glob('@/components/molecules/*.tsx', { eager: true })
|
|
const organismModules = import.meta.glob('@/components/organisms/*.tsx', { eager: true })
|
|
const uiModules = import.meta.glob('@/components/ui/**/*.{ts,tsx}', { eager: true })
|
|
const wrapperModules = import.meta.glob('@/lib/json-ui/wrappers/*.tsx', { eager: true })
|
|
const explicitModules = import.meta.glob(
|
|
['@/components/**/*.tsx', '@/lib/json-ui/wrappers/**/*.tsx'],
|
|
{ eager: true }
|
|
)
|
|
|
|
const atomComponentMap = buildComponentMapFromModules(atomModules)
|
|
const moleculeComponentMap = buildComponentMapFromModules(moleculeModules)
|
|
const organismComponentMap = buildComponentMapFromModules(organismModules)
|
|
const uiComponentMap = buildComponentMapFromModules(uiModules)
|
|
const wrapperComponentMap = buildComponentMapFromModules(wrapperModules)
|
|
const iconComponentMap = buildComponentMapFromExports(PhosphorIcons)
|
|
|
|
const resolveComponentFromExplicitPath = (
|
|
entry: JsonRegistryEntry,
|
|
entryExportName: string
|
|
): ComponentType<any> | undefined => {
|
|
if (!entry.load?.path) {
|
|
return undefined
|
|
}
|
|
const moduleExports = explicitModules[entry.load.path]
|
|
if (!moduleExports || typeof moduleExports !== 'object') {
|
|
return undefined
|
|
}
|
|
const explicitComponents = buildComponentMapFromExports(
|
|
moduleExports as Record<string, unknown>
|
|
)
|
|
return explicitComponents[entryExportName]
|
|
}
|
|
|
|
const buildRegistryFromEntries = (
|
|
source: string,
|
|
componentMap: Record<string, ComponentType<any>>
|
|
): UIComponentRegistry => {
|
|
return jsonRegistryEntries
|
|
.filter((entry) => entry.source === source)
|
|
.reduce<UIComponentRegistry>((registry, entry) => {
|
|
const entryKey = getRegistryEntryKey(entry)
|
|
const entryExportName = getRegistryEntryExportName(entry)
|
|
if (!entryKey || !entryExportName) {
|
|
return registry
|
|
}
|
|
const component =
|
|
resolveComponentFromExplicitPath(entry, entryExportName) ??
|
|
componentMap[entryExportName]
|
|
if (component) {
|
|
registry[entryKey] = component
|
|
}
|
|
return registry
|
|
}, {})
|
|
}
|
|
|
|
export const primitiveComponents: UIComponentRegistry = {
|
|
div: 'div' as any,
|
|
span: 'span' as any,
|
|
p: 'p' as any,
|
|
h1: 'h1' as any,
|
|
h2: 'h2' as any,
|
|
h3: 'h3' as any,
|
|
h4: 'h4' as any,
|
|
h5: 'h5' as any,
|
|
h6: 'h6' as any,
|
|
section: 'section' as any,
|
|
article: 'article' as any,
|
|
header: 'header' as any,
|
|
footer: 'footer' as any,
|
|
main: 'main' as any,
|
|
aside: 'aside' as any,
|
|
nav: 'nav' as any,
|
|
}
|
|
|
|
export const shadcnComponents: UIComponentRegistry = buildRegistryFromEntries(
|
|
'ui',
|
|
uiComponentMap
|
|
)
|
|
|
|
export const atomComponents: UIComponentRegistry = buildRegistryFromEntries(
|
|
'atoms',
|
|
atomComponentMap
|
|
)
|
|
|
|
export const moleculeComponents: UIComponentRegistry = buildRegistryFromEntries(
|
|
'molecules',
|
|
moleculeComponentMap
|
|
)
|
|
|
|
export const organismComponents: UIComponentRegistry = buildRegistryFromEntries(
|
|
'organisms',
|
|
organismComponentMap
|
|
)
|
|
|
|
export const jsonWrapperComponents: UIComponentRegistry = buildRegistryFromEntries(
|
|
'wrappers',
|
|
wrapperComponentMap
|
|
)
|
|
|
|
export const iconComponents: UIComponentRegistry = buildRegistryFromEntries(
|
|
'icons',
|
|
iconComponentMap
|
|
)
|
|
|
|
export const uiComponentRegistry: UIComponentRegistry = {
|
|
...primitiveComponents,
|
|
...shadcnComponents,
|
|
...atomComponents,
|
|
...moleculeComponents,
|
|
...organismComponents,
|
|
...jsonWrapperComponents,
|
|
...iconComponents,
|
|
}
|
|
|
|
export function registerComponent(name: string, component: ComponentType<any>) {
|
|
uiComponentRegistry[name] = component
|
|
}
|
|
|
|
const resolveWrapperComponent = (type: string): ComponentType<any> | null => {
|
|
const entry = registryEntryByType.get(type)
|
|
if (entry?.wrapperRequired && entry.wrapperComponent) {
|
|
return uiComponentRegistry[entry.wrapperComponent] || null
|
|
}
|
|
return null
|
|
}
|
|
|
|
export function getUIComponent(type: string): ComponentType<any> | string | null {
|
|
return resolveWrapperComponent(type) ?? uiComponentRegistry[type] ?? null
|
|
}
|
|
|
|
export function hasComponent(type: string): boolean {
|
|
return Boolean(resolveWrapperComponent(type) ?? uiComponentRegistry[type])
|
|
}
|
|
|
|
export function getDeprecatedComponentInfo(type: string): DeprecatedComponentInfo | null {
|
|
return deprecatedComponentInfo[type] ?? null
|
|
}
|