236 lines
5.8 KiB
JavaScript
236 lines
5.8 KiB
JavaScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { customRoutes } from '../src/router/routes/custom-routes.ts';
|
|
import { generatedRoutes } from '../src/router/elegant/routes.ts';
|
|
import zhCn from '../src/locales/langs/zh-cn.ts';
|
|
|
|
const currentFilePath = fileURLToPath(import.meta.url);
|
|
const currentDirPath = path.dirname(currentFilePath);
|
|
const rootDir = path.resolve(currentDirPath, '..');
|
|
const outputPath = path.resolve(rootDir, 'docs/frontend-page-resource-manifest.json');
|
|
const excludedPageResourceRouteNames = new Set(['exception_403', 'exception_404', 'exception_500']);
|
|
const excludedPageResourcePathPrefixes = ['/function/', '/plugin/'];
|
|
|
|
function isConstantRoute(route) {
|
|
return Boolean(route.meta?.constant);
|
|
}
|
|
|
|
function getRouteOrder(route) {
|
|
const order = Number(route.meta?.order);
|
|
|
|
if (Number.isFinite(order)) {
|
|
return order;
|
|
}
|
|
|
|
return Number.MAX_SAFE_INTEGER;
|
|
}
|
|
|
|
function sortRoutes(routes) {
|
|
return [...routes].sort((next, prev) => {
|
|
const orderDiff = getRouteOrder(next) - getRouteOrder(prev);
|
|
|
|
if (orderDiff !== 0) {
|
|
return orderDiff;
|
|
}
|
|
|
|
return String(next.path || '').localeCompare(String(prev.path || ''));
|
|
});
|
|
}
|
|
|
|
function getPageComponent(component) {
|
|
if (!component) {
|
|
return null;
|
|
}
|
|
|
|
if (component.startsWith('view.')) {
|
|
return component;
|
|
}
|
|
|
|
if (component.startsWith('layout.') && component.includes('$view.')) {
|
|
return component;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function getPageType(component) {
|
|
if (!component) {
|
|
return null;
|
|
}
|
|
|
|
if (component.startsWith('view.')) {
|
|
return 'leaf';
|
|
}
|
|
|
|
if (component.startsWith('layout.') && component.includes('$view.')) {
|
|
return 'single';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function shouldExcludePageResource(route) {
|
|
if (excludedPageResourceRouteNames.has(route.name)) {
|
|
return true;
|
|
}
|
|
|
|
return excludedPageResourcePathPrefixes.some(prefix => String(route.path || '').startsWith(prefix));
|
|
}
|
|
|
|
function getNumberMetaValue(value) {
|
|
const parsed = Number(value);
|
|
|
|
if (Number.isFinite(parsed)) {
|
|
return parsed;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function normalizeRouteProps(props) {
|
|
if (props === true) {
|
|
return true;
|
|
}
|
|
|
|
if (props && typeof props === 'object') {
|
|
return props;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function getRouteLabel(route) {
|
|
const routeLocales = zhCn.route || {};
|
|
const i18nKey = route.meta?.i18nKey;
|
|
|
|
if (typeof i18nKey === 'string' && i18nKey.startsWith('route.')) {
|
|
const localeKey = i18nKey.slice('route.'.length);
|
|
|
|
if (routeLocales[localeKey]) {
|
|
return routeLocales[localeKey];
|
|
}
|
|
}
|
|
|
|
if (routeLocales[route.name]) {
|
|
return routeLocales[route.name];
|
|
}
|
|
|
|
return route.meta?.title || route.name;
|
|
}
|
|
|
|
function getPageResourceMeta(route) {
|
|
const hideInMenu = Boolean(route.meta?.hideInMenu);
|
|
const order = getNumberMetaValue(route.meta?.order);
|
|
const fixedIndexInTab = getNumberMetaValue(route.meta?.fixedIndexInTab);
|
|
|
|
return {
|
|
title: getRouteLabel(route),
|
|
i18nKey: route.meta?.i18nKey || null,
|
|
icon: route.meta?.icon || null,
|
|
localIcon: route.meta?.localIcon || null,
|
|
order,
|
|
keepAlive: Boolean(route.meta?.keepAlive),
|
|
hideInMenu,
|
|
activeMenu: route.meta?.activeMenu || null,
|
|
multiTab: Boolean(route.meta?.multiTab),
|
|
fixedIndexInTab
|
|
};
|
|
}
|
|
|
|
function shouldCollectPageResource(route, pageComponent) {
|
|
if (!pageComponent) {
|
|
return false;
|
|
}
|
|
|
|
if (route.meta?.hideInMenu) {
|
|
return false;
|
|
}
|
|
|
|
return !shouldExcludePageResource(route);
|
|
}
|
|
|
|
function createPageResourceItem(route, options) {
|
|
const { pageComponent, parentName, source } = options;
|
|
const props = normalizeRouteProps(route.props);
|
|
const meta = getPageResourceMeta(route);
|
|
|
|
return {
|
|
name: route.name,
|
|
path: route.path,
|
|
component: pageComponent,
|
|
title: meta.title,
|
|
routeTitle: route.meta?.title || route.name,
|
|
i18nKey: meta.i18nKey,
|
|
icon: meta.icon,
|
|
localIcon: meta.localIcon,
|
|
order: meta.order,
|
|
hideInMenu: meta.hideInMenu,
|
|
keepAlive: meta.keepAlive,
|
|
activeMenu: meta.activeMenu,
|
|
multiTab: meta.multiTab,
|
|
fixedIndexInTab: meta.fixedIndexInTab,
|
|
redirect: route.redirect || null,
|
|
props,
|
|
meta,
|
|
parentName,
|
|
pageType: getPageType(pageComponent),
|
|
source
|
|
};
|
|
}
|
|
|
|
function collectPageResources(routes, options, items) {
|
|
const { parentName = null, source } = options;
|
|
const orderedRoutes = sortRoutes(routes);
|
|
|
|
for (const route of orderedRoutes) {
|
|
const pageComponent = getPageComponent(route.component);
|
|
|
|
// The backend page-node whitelist only contains menu-visible pages.
|
|
if (shouldCollectPageResource(route, pageComponent)) {
|
|
items.push(createPageResourceItem(route, { pageComponent, parentName, source }));
|
|
}
|
|
|
|
if (route.children?.length) {
|
|
collectPageResources(route.children, { parentName: route.name, source }, items);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getPageResources() {
|
|
const items = [];
|
|
|
|
collectPageResources(
|
|
customRoutes.filter(route => !isConstantRoute(route)),
|
|
{ source: 'custom' },
|
|
items
|
|
);
|
|
collectPageResources(
|
|
generatedRoutes.filter(route => !isConstantRoute(route)),
|
|
{ source: 'generated' },
|
|
items
|
|
);
|
|
|
|
const uniqueItems = Array.from(new Map(items.map(item => [item.name, item])).values());
|
|
|
|
return uniqueItems;
|
|
}
|
|
|
|
const pageResources = getPageResources();
|
|
|
|
const result = {
|
|
generatedAt: new Date().toISOString(),
|
|
description: 'Frontend visible page resource whitelist for backend route/menu configuration.',
|
|
rules: {
|
|
directoryComponent: 'layout.base',
|
|
pageComponentPattern: 'view.<routeName>',
|
|
singlePageComponentPattern: 'layout.<layoutName>$view.<routeName>'
|
|
},
|
|
total: pageResources.length,
|
|
items: pageResources
|
|
};
|
|
|
|
fs.writeFileSync(outputPath, `${JSON.stringify(result, null, 2)}\n`, 'utf8');
|
|
|
|
console.log(`Generated ${pageResources.length} page resources -> ${path.relative(rootDir, outputPath)}`);
|