accounts/dashboard/cms/extensionRuntime.ts
Haitao Pan f3d0a7f931 fix(dashboard): resolve npm run build errors
- Fix module path in prebuild script (../../scripts → ../scripts)
- Update API routes to use Promise-based params for Next.js 16
- Install @types/sanitize-html and @types/js-yaml
- Fix component prop naming (loading → isLoading, saving → isSaving, etc.)
- Update VLESS config type definition with missing 'id' property
- Fix TypeScript type conflicts and export declarations

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-06 11:07:30 +08:00

55 lines
1.8 KiB
TypeScript

import { ComponentType, createElement, isValidElement, type ReactNode } from 'react'
import { cmsConfig, type ExtensionName, type ThemeName } from './config'
import type { CmsExtension, CmsTheme } from './types'
import { appShellExtension } from './extensions/appShell'
import { markdownSyncExtension } from './extensions/markdownSync'
import defaultTheme from './themes/default'
import { AppShellBypass } from '../lib/appShellBypass'
const themeRegistry: Record<ThemeName, CmsTheme> = {
default: defaultTheme,
}
const extensionRegistry: Record<ExtensionName, CmsExtension> = {
'app-shell': appShellExtension,
'markdown-sync': markdownSyncExtension,
}
export function getActiveTheme(): CmsTheme {
return themeRegistry[cmsConfig.theme]
}
export function getActiveExtensions(): CmsExtension[] {
return cmsConfig.extensions
.map((extensionName) => extensionRegistry[extensionName])
.filter(Boolean)
}
export function collectExtensionProviders(): ComponentType<{ children: ReactNode }>[] {
return getActiveExtensions().flatMap((extension) => extension.providers ?? [])
}
export function applyExtensionLayouts(children: ReactNode): ReactNode {
const { content, skipAppShell } = unwrapAppShellBypass(children)
return getActiveExtensions().reduceRight((acc, extension) => {
if (!extension.Layout) {
return acc
}
if (skipAppShell && extension.name === 'app-shell') {
return acc
}
const LayoutComponent = extension.Layout
return createElement(LayoutComponent, null, acc)
}, content)
}
function unwrapAppShellBypass(node: ReactNode): { content: ReactNode; skipAppShell: boolean } {
if (isValidElement(node) && (node.type as any) === AppShellBypass) {
return { content: (node.props as any).children, skipAppShell: true }
}
return { content: node, skipAppShell: false }
}