mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
feat: load UI pages from lua packages
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Metadata } from 'next'
|
||||
import { notFound } from 'next/navigation'
|
||||
import { UIPageRenderer } from '@/components/ui-page-renderer/UIPageRenderer'
|
||||
import { loadPageFromLuaPackages } from '@/lib/ui-pages/load-page-from-lua-packages'
|
||||
import { loadPageFromDB } from '@/lib/ui-pages/load-page-from-db'
|
||||
|
||||
interface PageProps {
|
||||
@@ -25,8 +26,8 @@ export default async function DynamicUIPage({ params }: PageProps) {
|
||||
const slug = resolvedParams.slug || []
|
||||
const path = '/' + slug.join('/')
|
||||
|
||||
// Load page from database
|
||||
const pageData = await loadPageFromDB(path)
|
||||
// Prefer Lua package-based UI pages, fallback to database-backed pages
|
||||
const pageData = (await loadPageFromLuaPackages(path)) ?? (await loadPageFromDB(path))
|
||||
|
||||
if (!pageData) {
|
||||
notFound()
|
||||
@@ -46,7 +47,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
|
||||
const slug = resolvedParams.slug || []
|
||||
const path = '/' + slug.join('/')
|
||||
|
||||
const pageData = await loadPageFromDB(path)
|
||||
const pageData = (await loadPageFromLuaPackages(path)) ?? (await loadPageFromDB(path))
|
||||
|
||||
if (!pageData) {
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, expect, it } from 'bun:test'
|
||||
|
||||
import { loadPageFromLuaPackages } from './load-page-from-lua-packages'
|
||||
|
||||
describe('loadPageFromLuaPackages', () => {
|
||||
it('loads a page definition from Lua UI packages', async () => {
|
||||
const page = await loadPageFromLuaPackages('/example-form')
|
||||
|
||||
expect(page).not.toBeNull()
|
||||
expect(page?.title).toBe('Example Form')
|
||||
expect(page?.layout).toBeDefined()
|
||||
expect(Object.keys(page?.actions ?? {})).toContain('handleFormSubmit')
|
||||
})
|
||||
|
||||
it('returns null for paths that are not in Lua packages', async () => {
|
||||
const page = await loadPageFromLuaPackages('/does-not-exist')
|
||||
|
||||
expect(page).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,80 @@
|
||||
import { access, readFile, readdir } from 'fs/promises'
|
||||
import { constants } from 'fs'
|
||||
import { join } from 'path'
|
||||
|
||||
import type { JsonObject } from '@/types/utility-types'
|
||||
import { loadLuaUIPackage } from '@/lib/lua/ui/load-lua-ui-package'
|
||||
|
||||
import type { UIPageData } from './load-page-from-db'
|
||||
|
||||
const LUA_UI_PACKAGES_ROOT = join(process.cwd(), 'src/lib/packages/lua-ui')
|
||||
|
||||
/**
|
||||
* Load a UI page directly from Lua UI packages on disk.
|
||||
*
|
||||
* - Scans packages in src/lib/packages/lua-ui
|
||||
* - Uses manifest.json + Lua files to build the page definition
|
||||
* - Returns the same UIPageData shape used by database-backed pages
|
||||
*/
|
||||
export async function loadPageFromLuaPackages(path: string): Promise<UIPageData | null> {
|
||||
let packageEntries: Awaited<ReturnType<typeof readdir>>
|
||||
|
||||
try {
|
||||
packageEntries = await readdir(LUA_UI_PACKAGES_ROOT, { withFileTypes: true })
|
||||
} catch (error) {
|
||||
console.error('Failed to read Lua UI packages directory', error)
|
||||
return null
|
||||
}
|
||||
|
||||
for (const entry of packageEntries) {
|
||||
if (!entry.isDirectory()) {
|
||||
continue
|
||||
}
|
||||
|
||||
const packagePath = join(LUA_UI_PACKAGES_ROOT, entry.name)
|
||||
const manifestPath = join(packagePath, 'manifest.json')
|
||||
|
||||
try {
|
||||
await access(manifestPath, constants.F_OK)
|
||||
} catch (_error) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Quickly inspect manifest to see if this package provides the requested path
|
||||
try {
|
||||
const manifestContent = await readFile(manifestPath, 'utf-8')
|
||||
const manifest = JSON.parse(manifestContent) as { pages?: Array<{ path: string }> }
|
||||
|
||||
const hasMatchingPage = manifest.pages?.some(page => page.path === path)
|
||||
|
||||
if (!hasMatchingPage) {
|
||||
continue
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Skipping Lua UI package at ${packagePath}:`, error)
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
const uiPackage = await loadLuaUIPackage(packagePath)
|
||||
const page = uiPackage.pages.find(p => p.path === path)
|
||||
|
||||
if (page) {
|
||||
return {
|
||||
path: page.path,
|
||||
title: page.title,
|
||||
level: page.level,
|
||||
requireAuth: page.requiresAuth ?? false,
|
||||
requiredRole: page.requiredRole,
|
||||
layout: page.layout as JsonObject,
|
||||
actions: uiPackage.actions,
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Skip packages that fail to load so other packages can still resolve the page
|
||||
console.warn(`Skipping Lua UI package at ${packagePath}:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user