meno-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.ts +281 -0
- package/build-static.ts +298 -0
- package/bunfig.toml +39 -0
- package/entries/client-router.tsx +111 -0
- package/entries/server-router.tsx +71 -0
- package/lib/client/ClientInitializer.test.ts +9 -0
- package/lib/client/ClientInitializer.test.ts.skip +92 -0
- package/lib/client/ClientInitializer.ts +60 -0
- package/lib/client/ErrorBoundary.test.tsx +595 -0
- package/lib/client/ErrorBoundary.tsx +230 -0
- package/lib/client/componentRegistry.test.ts +165 -0
- package/lib/client/componentRegistry.ts +18 -0
- package/lib/client/contexts/ThemeContext.tsx +73 -0
- package/lib/client/core/ComponentBuilder.test.ts +677 -0
- package/lib/client/core/ComponentBuilder.ts +660 -0
- package/lib/client/core/ComponentRenderer.test.tsx +176 -0
- package/lib/client/core/ComponentRenderer.tsx +83 -0
- package/lib/client/core/cmsTemplateProcessor.ts +129 -0
- package/lib/client/elementRegistry.ts +81 -0
- package/lib/client/hmr/HMRManager.tsx +179 -0
- package/lib/client/hmr/index.ts +5 -0
- package/lib/client/hmrWebSocket.test.ts +9 -0
- package/lib/client/hmrWebSocket.ts +250 -0
- package/lib/client/hooks/useColorVariables.test.ts +166 -0
- package/lib/client/hooks/useColorVariables.ts +249 -0
- package/lib/client/hooks/usePropertyAutocomplete.test.ts +9 -0
- package/lib/client/hooks/usePropertyAutocomplete.ts +40 -0
- package/lib/client/hydration/HydrationUtils.test.ts +154 -0
- package/lib/client/hydration/HydrationUtils.ts +35 -0
- package/lib/client/i18nConfigService.test.ts +74 -0
- package/lib/client/i18nConfigService.ts +78 -0
- package/lib/client/index.ts +56 -0
- package/lib/client/navigation.test.ts +441 -0
- package/lib/client/navigation.ts +23 -0
- package/lib/client/responsiveStyleResolver.test.ts +491 -0
- package/lib/client/responsiveStyleResolver.ts +184 -0
- package/lib/client/routing/RouteLoader.test.ts +635 -0
- package/lib/client/routing/RouteLoader.ts +347 -0
- package/lib/client/routing/Router.tsx +382 -0
- package/lib/client/scripts/ScriptExecutor.test.ts +489 -0
- package/lib/client/scripts/ScriptExecutor.ts +171 -0
- package/lib/client/scripts/formHandler.ts +103 -0
- package/lib/client/styleProcessor.test.ts +126 -0
- package/lib/client/styleProcessor.ts +92 -0
- package/lib/client/styles/StyleInjector.test.ts +354 -0
- package/lib/client/styles/StyleInjector.ts +154 -0
- package/lib/client/templateEngine.test.ts +660 -0
- package/lib/client/templateEngine.ts +667 -0
- package/lib/client/theme.test.ts +173 -0
- package/lib/client/theme.ts +159 -0
- package/lib/client/utils/toast.ts +46 -0
- package/lib/server/createServer.ts +170 -0
- package/lib/server/cssGenerator.test.ts +172 -0
- package/lib/server/cssGenerator.ts +58 -0
- package/lib/server/fileWatcher.ts +134 -0
- package/lib/server/index.ts +55 -0
- package/lib/server/jsonLoader.test.ts +103 -0
- package/lib/server/jsonLoader.ts +350 -0
- package/lib/server/middleware/cors.test.ts +177 -0
- package/lib/server/middleware/cors.ts +69 -0
- package/lib/server/middleware/errorHandler.test.ts +208 -0
- package/lib/server/middleware/errorHandler.ts +63 -0
- package/lib/server/middleware/index.ts +9 -0
- package/lib/server/middleware/logger.test.ts +233 -0
- package/lib/server/middleware/logger.ts +99 -0
- package/lib/server/pageCache.test.ts +167 -0
- package/lib/server/pageCache.ts +97 -0
- package/lib/server/projectContext.ts +51 -0
- package/lib/server/providers/fileSystemCMSProvider.test.ts +292 -0
- package/lib/server/providers/fileSystemCMSProvider.ts +227 -0
- package/lib/server/providers/fileSystemPageProvider.ts +83 -0
- package/lib/server/routes/api/cms.test.ts +177 -0
- package/lib/server/routes/api/cms.ts +82 -0
- package/lib/server/routes/api/colors.ts +59 -0
- package/lib/server/routes/api/components.ts +70 -0
- package/lib/server/routes/api/config.test.ts +9 -0
- package/lib/server/routes/api/config.ts +28 -0
- package/lib/server/routes/api/core-routes.ts +182 -0
- package/lib/server/routes/api/functions.ts +170 -0
- package/lib/server/routes/api/index.ts +69 -0
- package/lib/server/routes/api/pages.ts +95 -0
- package/lib/server/routes/api/shared.test.ts +81 -0
- package/lib/server/routes/api/shared.ts +31 -0
- package/lib/server/routes/editor.test.ts +9 -0
- package/lib/server/routes/index.ts +104 -0
- package/lib/server/routes/pages.ts +161 -0
- package/lib/server/routes/static.ts +107 -0
- package/lib/server/services/ColorService.ts +193 -0
- package/lib/server/services/cmsService.test.ts +388 -0
- package/lib/server/services/cmsService.ts +296 -0
- package/lib/server/services/componentService.test.ts +276 -0
- package/lib/server/services/componentService.ts +346 -0
- package/lib/server/services/configService.ts +156 -0
- package/lib/server/services/fileWatcherService.ts +67 -0
- package/lib/server/services/index.ts +10 -0
- package/lib/server/services/pageService.test.ts +258 -0
- package/lib/server/services/pageService.ts +240 -0
- package/lib/server/ssrRenderer.test.ts +1005 -0
- package/lib/server/ssrRenderer.ts +878 -0
- package/lib/server/utilityClassGenerator.ts +11 -0
- package/lib/server/utils/index.ts +5 -0
- package/lib/server/utils/jsonLineMapper.test.ts +100 -0
- package/lib/server/utils/jsonLineMapper.ts +166 -0
- package/lib/server/validateStyleCoverage.test.ts +9 -0
- package/lib/server/validateStyleCoverage.ts +167 -0
- package/lib/server/websocketManager.test.ts +9 -0
- package/lib/server/websocketManager.ts +95 -0
- package/lib/shared/attributeNodeUtils.test.ts +152 -0
- package/lib/shared/attributeNodeUtils.ts +50 -0
- package/lib/shared/breakpoints.test.ts +166 -0
- package/lib/shared/breakpoints.ts +65 -0
- package/lib/shared/colorProperties.test.ts +111 -0
- package/lib/shared/colorProperties.ts +40 -0
- package/lib/shared/colorVariableUtils.test.ts +319 -0
- package/lib/shared/colorVariableUtils.ts +97 -0
- package/lib/shared/constants.test.ts +175 -0
- package/lib/shared/constants.ts +116 -0
- package/lib/shared/cssGeneration.ts +481 -0
- package/lib/shared/cssProperties.test.ts +252 -0
- package/lib/shared/cssProperties.ts +338 -0
- package/lib/shared/elementUtils.test.ts +245 -0
- package/lib/shared/elementUtils.ts +90 -0
- package/lib/shared/fontLoader.ts +97 -0
- package/lib/shared/i18n.test.ts +313 -0
- package/lib/shared/i18n.ts +286 -0
- package/lib/shared/index.ts +50 -0
- package/lib/shared/interfaces/contentProvider.test.ts +9 -0
- package/lib/shared/interfaces/contentProvider.ts +121 -0
- package/lib/shared/nodeUtils.test.ts +320 -0
- package/lib/shared/nodeUtils.ts +220 -0
- package/lib/shared/pathArrayUtils.test.ts +315 -0
- package/lib/shared/pathArrayUtils.ts +17 -0
- package/lib/shared/pathUtils.test.ts +260 -0
- package/lib/shared/pathUtils.ts +244 -0
- package/lib/shared/paths/Path.test.ts +74 -0
- package/lib/shared/paths/Path.ts +23 -0
- package/lib/shared/paths/PathConverter.test.ts +232 -0
- package/lib/shared/paths/PathConverter.ts +141 -0
- package/lib/shared/paths/PathUtils.ts +290 -0
- package/lib/shared/paths/PathValidator.test.ts +193 -0
- package/lib/shared/paths/PathValidator.ts +53 -0
- package/lib/shared/paths/index.ts +48 -0
- package/lib/shared/propResolver.test.ts +639 -0
- package/lib/shared/propResolver.ts +124 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +190 -0
- package/lib/shared/registry/BaseNodeTypeRegistry.ts +200 -0
- package/lib/shared/registry/ClientNodeTypeRegistry.ts +34 -0
- package/lib/shared/registry/ClientRegistry.test.ts +26 -0
- package/lib/shared/registry/ClientRegistry.ts +15 -0
- package/lib/shared/registry/ComponentRegistry.test.ts +293 -0
- package/lib/shared/registry/ComponentRegistry.ts +100 -0
- package/lib/shared/registry/NodeTypeDefinition.ts +198 -0
- package/lib/shared/registry/NodeTypeManager.ts +94 -0
- package/lib/shared/registry/RegistryManager.test.ts +58 -0
- package/lib/shared/registry/RegistryManager.ts +60 -0
- package/lib/shared/registry/SSRNodeTypeRegistry.ts +33 -0
- package/lib/shared/registry/SSRRegistry.test.ts +26 -0
- package/lib/shared/registry/SSRRegistry.ts +15 -0
- package/lib/shared/registry/createNodeType.ts +175 -0
- package/lib/shared/registry/defineNodeType.ts +73 -0
- package/lib/shared/registry/fieldPresets.ts +109 -0
- package/lib/shared/registry/index.ts +50 -0
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +71 -0
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +61 -0
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +88 -0
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +66 -0
- package/lib/shared/registry/nodeTypes/ObjectLinkNodeType.ts +75 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +49 -0
- package/lib/shared/registry/nodeTypes/TextNodeType.ts +52 -0
- package/lib/shared/registry/nodeTypes/index.ts +75 -0
- package/lib/shared/responsiveScaling.test.ts +268 -0
- package/lib/shared/responsiveScaling.ts +194 -0
- package/lib/shared/responsiveStyleUtils.test.ts +300 -0
- package/lib/shared/responsiveStyleUtils.ts +139 -0
- package/lib/shared/slugTranslator.test.ts +325 -0
- package/lib/shared/slugTranslator.ts +177 -0
- package/lib/shared/styleNodeUtils.test.ts +132 -0
- package/lib/shared/styleNodeUtils.ts +102 -0
- package/lib/shared/styleUtils.test.ts +238 -0
- package/lib/shared/styleUtils.ts +63 -0
- package/lib/shared/themeDefaults.test.ts +113 -0
- package/lib/shared/themeDefaults.ts +103 -0
- package/lib/shared/tree/PathBuilder.ts +383 -0
- package/lib/shared/treePathUtils.test.ts +539 -0
- package/lib/shared/treePathUtils.ts +339 -0
- package/lib/shared/types/api.ts +58 -0
- package/lib/shared/types/cms.ts +95 -0
- package/lib/shared/types/colors.ts +45 -0
- package/lib/shared/types/components.ts +121 -0
- package/lib/shared/types/errors.test.ts +103 -0
- package/lib/shared/types/errors.ts +69 -0
- package/lib/shared/types/index.ts +96 -0
- package/lib/shared/types/nodes.ts +20 -0
- package/lib/shared/types/rendering.ts +61 -0
- package/lib/shared/types/styles.ts +38 -0
- package/lib/shared/types.ts +11 -0
- package/lib/shared/utilityClassConfig.ts +287 -0
- package/lib/shared/utilityClassMapper.test.ts +140 -0
- package/lib/shared/utilityClassMapper.ts +229 -0
- package/lib/shared/utils/fileUtils.test.ts +99 -0
- package/lib/shared/utils/fileUtils.ts +56 -0
- package/lib/shared/utils.test.ts +261 -0
- package/lib/shared/utils.ts +84 -0
- package/lib/shared/validation/index.ts +7 -0
- package/lib/shared/validation/propValidator.test.ts +178 -0
- package/lib/shared/validation/propValidator.ts +238 -0
- package/lib/shared/validation/schemas.test.ts +177 -0
- package/lib/shared/validation/schemas.ts +401 -0
- package/lib/shared/validation/validators.test.ts +109 -0
- package/lib/shared/validation/validators.ts +304 -0
- package/lib/test-utils/dom-setup.ts +55 -0
- package/lib/test-utils/factories/ConsoleMockFactory.ts +200 -0
- package/lib/test-utils/factories/DomMockFactory.ts +487 -0
- package/lib/test-utils/factories/EventMockFactory.ts +244 -0
- package/lib/test-utils/factories/FetchMockFactory.ts +210 -0
- package/lib/test-utils/factories/ServerMockFactory.ts +223 -0
- package/lib/test-utils/factories/StoreMockFactory.ts +370 -0
- package/lib/test-utils/factories/index.ts +11 -0
- package/lib/test-utils/fixtures.ts +134 -0
- package/lib/test-utils/helpers/asyncHelpers.test.ts +112 -0
- package/lib/test-utils/helpers/asyncHelpers.ts +196 -0
- package/lib/test-utils/helpers/index.ts +6 -0
- package/lib/test-utils/helpers.test.ts +73 -0
- package/lib/test-utils/helpers.ts +90 -0
- package/lib/test-utils/index.ts +17 -0
- package/lib/test-utils/mockFactories.ts +92 -0
- package/lib/test-utils/mocks.ts +341 -0
- package/package.json +38 -0
- package/templates/index-router.html +34 -0
- package/tsconfig.json +14 -0
- package/vite.config.ts +43 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route Registration
|
|
3
|
+
* Centralized route handling with middleware
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PageService } from '../services/pageService';
|
|
7
|
+
import type { ComponentService } from '../services/componentService';
|
|
8
|
+
import type { CMSService } from '../services/cmsService';
|
|
9
|
+
import type { CMSProvider } from '../../shared/interfaces/contentProvider';
|
|
10
|
+
import { HMR_ROUTE } from '../../shared/constants';
|
|
11
|
+
import { handleCorsPreflight } from '../middleware/cors';
|
|
12
|
+
import { handleRouteError } from '../middleware/errorHandler';
|
|
13
|
+
import { logRequest, logResponseTime } from '../middleware/logger';
|
|
14
|
+
import { handleApiRoutes, type CMSRouteContext } from './api/index';
|
|
15
|
+
import { handlePageRoute } from './pages';
|
|
16
|
+
import { handleStaticRoute } from './static';
|
|
17
|
+
|
|
18
|
+
export interface RouteContext {
|
|
19
|
+
pageService: PageService;
|
|
20
|
+
componentService: ComponentService;
|
|
21
|
+
cmsService?: CMSService;
|
|
22
|
+
cmsProvider?: CMSProvider;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Handle all routes with middleware
|
|
27
|
+
*/
|
|
28
|
+
export async function handleRoutes(
|
|
29
|
+
req: Request,
|
|
30
|
+
url: URL,
|
|
31
|
+
server: any,
|
|
32
|
+
context: RouteContext
|
|
33
|
+
): Promise<Response | undefined> {
|
|
34
|
+
const startTime = Date.now();
|
|
35
|
+
const { pageService, componentService, cmsService, cmsProvider } = context;
|
|
36
|
+
|
|
37
|
+
// Log request
|
|
38
|
+
logRequest(req);
|
|
39
|
+
|
|
40
|
+
// Handle CORS preflight requests
|
|
41
|
+
const corsResponse = handleCorsPreflight(req);
|
|
42
|
+
if (corsResponse) {
|
|
43
|
+
logResponseTime(startTime, req);
|
|
44
|
+
return corsResponse;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// WebSocket upgrade for custom YAML reload notifications
|
|
49
|
+
if (url.pathname === HMR_ROUTE) {
|
|
50
|
+
const success = server.upgrade(req);
|
|
51
|
+
logResponseTime(startTime, req);
|
|
52
|
+
return success
|
|
53
|
+
? undefined
|
|
54
|
+
: new Response('WebSocket upgrade failed', { status: 500 });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Build CMS context if CMS service and provider are available
|
|
58
|
+
const cmsContext: CMSRouteContext | undefined =
|
|
59
|
+
cmsService && cmsProvider
|
|
60
|
+
? { cmsService, cmsProvider }
|
|
61
|
+
: undefined;
|
|
62
|
+
|
|
63
|
+
// API Routes
|
|
64
|
+
const apiResponse = await handleApiRoutes(
|
|
65
|
+
req,
|
|
66
|
+
url,
|
|
67
|
+
pageService,
|
|
68
|
+
componentService,
|
|
69
|
+
cmsContext
|
|
70
|
+
);
|
|
71
|
+
if (apiResponse) {
|
|
72
|
+
logResponseTime(startTime, req);
|
|
73
|
+
return apiResponse;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Static asset directories - serve before page routes
|
|
77
|
+
if (url.pathname.startsWith('/images/') || url.pathname.startsWith('/videos/') || url.pathname.startsWith('/assets/') || url.pathname.startsWith('/public/') || url.pathname.startsWith('/icons/')) {
|
|
78
|
+
const response = await handleStaticRoute(url);
|
|
79
|
+
if (response) {
|
|
80
|
+
logResponseTime(startTime, req);
|
|
81
|
+
return response;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Page routes (SSR)
|
|
86
|
+
if (url.pathname === '/' || (url.pathname.startsWith('/') && !url.pathname.includes('.'))) {
|
|
87
|
+
const response = await handlePageRoute(url, context);
|
|
88
|
+
logResponseTime(startTime, req);
|
|
89
|
+
return response;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Static files
|
|
93
|
+
const response = await handleStaticRoute(url);
|
|
94
|
+
logResponseTime(startTime, req);
|
|
95
|
+
return response;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
logResponseTime(startTime, req);
|
|
98
|
+
return handleRouteError(
|
|
99
|
+
() => Promise.reject(error),
|
|
100
|
+
`Error handling route: ${url.pathname}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Route Handler
|
|
3
|
+
* Handles page route requests with SSR, i18n, and CMS support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RouteContext } from './index';
|
|
7
|
+
import { generateSSRHTML } from '../ssrRenderer';
|
|
8
|
+
import { getStaticFilePath } from '../../shared/pathUtils';
|
|
9
|
+
import { parseJSON, loadI18nConfig } from '../jsonLoader';
|
|
10
|
+
import { packagePaths } from '../projectContext';
|
|
11
|
+
import { parseLocaleFromPath } from '../../shared/i18n';
|
|
12
|
+
import { buildSlugIndex, resolveSlugToPageId } from '../../shared/slugTranslator';
|
|
13
|
+
import type { CMSItem } from '../../shared/types';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Handle page route requests
|
|
17
|
+
*/
|
|
18
|
+
export async function handlePageRoute(
|
|
19
|
+
url: URL,
|
|
20
|
+
context: RouteContext
|
|
21
|
+
): Promise<Response | undefined> {
|
|
22
|
+
const { pageService, componentService, cmsService } = context;
|
|
23
|
+
const pagePath = url.pathname;
|
|
24
|
+
|
|
25
|
+
// Load i18n config for locale extraction
|
|
26
|
+
const i18nConfig = await loadI18nConfig();
|
|
27
|
+
|
|
28
|
+
// Parse locale from URL path (e.g., /pl/o-nas -> locale: 'pl', path: '/o-nas')
|
|
29
|
+
const { locale, pathWithoutLocale } = parseLocaleFromPath(pagePath, i18nConfig);
|
|
30
|
+
|
|
31
|
+
// Build slug index for translation lookup
|
|
32
|
+
const slugMappings = pageService.getSlugMappings();
|
|
33
|
+
const slugIndex = buildSlugIndex(slugMappings);
|
|
34
|
+
|
|
35
|
+
// 1. Try CMS route match first
|
|
36
|
+
if (cmsService) {
|
|
37
|
+
const cmsMatch = await cmsService.matchRoute(pathWithoutLocale);
|
|
38
|
+
|
|
39
|
+
if (cmsMatch) {
|
|
40
|
+
// Load template page content by file path
|
|
41
|
+
const templatePageContent = await loadPageByFilePath(cmsMatch.pagePath);
|
|
42
|
+
|
|
43
|
+
if (templatePageContent) {
|
|
44
|
+
try {
|
|
45
|
+
// Parse page JSON
|
|
46
|
+
const pageData = parseJSON(templatePageContent);
|
|
47
|
+
|
|
48
|
+
// Convert global components Map to Record for SSR
|
|
49
|
+
const globalComponentsRecord = componentService.getAllComponents();
|
|
50
|
+
|
|
51
|
+
// Generate SSR HTML with CMS context
|
|
52
|
+
const baseUrl = `${url.protocol}//${url.host}`;
|
|
53
|
+
const ssrHTML = await generateSSRHTML(
|
|
54
|
+
pageData,
|
|
55
|
+
globalComponentsRecord,
|
|
56
|
+
pagePath,
|
|
57
|
+
baseUrl,
|
|
58
|
+
false,
|
|
59
|
+
locale,
|
|
60
|
+
slugMappings,
|
|
61
|
+
{ cms: cmsMatch.item } // Pass CMS item as context
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return new Response(ssrHTML, {
|
|
65
|
+
headers: {
|
|
66
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
67
|
+
'Cache-Control': 'no-store, max-age=0',
|
|
68
|
+
'Pragma': 'no-cache',
|
|
69
|
+
'Expires': '0',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error('Error rendering CMS page:', error);
|
|
74
|
+
return new Response(Bun.file(packagePaths.indexHtml()));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 2. Fall back to static page routing
|
|
81
|
+
|
|
82
|
+
// Extract slug from path (remove leading slash)
|
|
83
|
+
const slug = pathWithoutLocale === '/' ? '' : pathWithoutLocale.substring(1);
|
|
84
|
+
|
|
85
|
+
// Try to resolve translated slug to pageId (e.g., "o-nas" + "pl" -> "about")
|
|
86
|
+
const pageId = resolveSlugToPageId(slug, locale, slugIndex);
|
|
87
|
+
|
|
88
|
+
// Determine lookup path: use resolved pageId or fall back to original path
|
|
89
|
+
let lookupPath: string;
|
|
90
|
+
if (pageId) {
|
|
91
|
+
lookupPath = pageId === 'index' ? '/' : `/${pageId}`;
|
|
92
|
+
} else {
|
|
93
|
+
// No translation found, use original path without locale
|
|
94
|
+
lookupPath = pathWithoutLocale;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Skip static files in development mode - always use fresh SSR from JSON files
|
|
98
|
+
const isDevelopment = true; // Always true when running dev server
|
|
99
|
+
|
|
100
|
+
if (!isDevelopment) {
|
|
101
|
+
// Production mode: Check for pre-built static file first
|
|
102
|
+
const staticFilePath = getStaticFilePath(pagePath);
|
|
103
|
+
const staticFile = Bun.file(staticFilePath);
|
|
104
|
+
|
|
105
|
+
if (await staticFile.exists()) {
|
|
106
|
+
return new Response(staticFile, {
|
|
107
|
+
headers: {
|
|
108
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
109
|
+
'Cache-Control': 'public, max-age=31536000', // 1 year cache for static files
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Development mode: Always use fresh SSR from JSON files (no caching)
|
|
116
|
+
const pageContent = pageService.getPage(lookupPath);
|
|
117
|
+
|
|
118
|
+
if (pageContent) {
|
|
119
|
+
try {
|
|
120
|
+
// Parse page JSON
|
|
121
|
+
const pageData = parseJSON(pageContent);
|
|
122
|
+
|
|
123
|
+
// Convert global components Map to Record for SSR
|
|
124
|
+
const globalComponentsRecord = componentService.getAllComponents();
|
|
125
|
+
|
|
126
|
+
// Generate SSR HTML with pre-rendered content and locale
|
|
127
|
+
const baseUrl = `${url.protocol}//${url.host}`;
|
|
128
|
+
const ssrHTML = await generateSSRHTML(pageData, globalComponentsRecord, pagePath, baseUrl, false, locale, slugMappings);
|
|
129
|
+
|
|
130
|
+
return new Response(ssrHTML, {
|
|
131
|
+
headers: {
|
|
132
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
133
|
+
'Cache-Control': 'no-store, max-age=0',
|
|
134
|
+
'Pragma': 'no-cache',
|
|
135
|
+
'Expires': '0',
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
} catch (error) {
|
|
139
|
+
// Fallback to client-side rendering on error
|
|
140
|
+
return new Response(Bun.file(packagePaths.indexHtml()));
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
// Page not found - still return HTML shell for client-side 404 handling
|
|
144
|
+
return new Response(Bun.file(packagePaths.indexHtml()));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Load page content by file path (for CMS template pages)
|
|
150
|
+
*/
|
|
151
|
+
async function loadPageByFilePath(filePath: string): Promise<string | null> {
|
|
152
|
+
try {
|
|
153
|
+
const file = Bun.file(filePath);
|
|
154
|
+
if (await file.exists()) {
|
|
155
|
+
return await file.text();
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static File Route Handler
|
|
3
|
+
* Serves static files and handles TypeScript/TSX file requests
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { resolveProjectPath, resolvePackagePath, packagePaths } from '../projectContext';
|
|
8
|
+
|
|
9
|
+
export async function handleStaticRoute(url: URL): Promise<Response | undefined> {
|
|
10
|
+
// Handle client-router requests (both .js and .tsx)
|
|
11
|
+
if (url.pathname === '/client-router.js' || url.pathname === '/client-router.tsx') {
|
|
12
|
+
const result = await Bun.build({
|
|
13
|
+
entrypoints: [packagePaths.clientRouter()],
|
|
14
|
+
target: 'browser',
|
|
15
|
+
format: 'esm',
|
|
16
|
+
minify: false,
|
|
17
|
+
sourcemap: 'inline',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (result.success && result.outputs[0]) {
|
|
21
|
+
return new Response(result.outputs[0], {
|
|
22
|
+
headers: {
|
|
23
|
+
'Content-Type': 'application/javascript',
|
|
24
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Serve client bundle with HMR enabled (editor TypeScript files)
|
|
31
|
+
if (url.pathname.endsWith('.tsx') || url.pathname.endsWith('.ts')) {
|
|
32
|
+
const entrypoint = resolvePackagePath(url.pathname.slice(1));
|
|
33
|
+
const result = await Bun.build({
|
|
34
|
+
entrypoints: [entrypoint],
|
|
35
|
+
target: 'browser',
|
|
36
|
+
format: 'esm',
|
|
37
|
+
minify: false,
|
|
38
|
+
sourcemap: 'inline',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (result.success && result.outputs[0]) {
|
|
42
|
+
return new Response(result.outputs[0], {
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/javascript',
|
|
45
|
+
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Serve static files (including fonts and images)
|
|
52
|
+
const decodedPathname = decodeURIComponent(url.pathname);
|
|
53
|
+
|
|
54
|
+
// For fonts, images, and icons, resolve relative to project root
|
|
55
|
+
// Other static files (editor assets) resolve relative to package root
|
|
56
|
+
let filePath: string;
|
|
57
|
+
if (decodedPathname.startsWith('/fonts/') || decodedPathname.startsWith('/images/') || decodedPathname.startsWith('/icons/')) {
|
|
58
|
+
filePath = resolveProjectPath(decodedPathname.slice(1)); // Remove leading /
|
|
59
|
+
} else {
|
|
60
|
+
filePath = resolvePackagePath(decodedPathname.slice(1)); // Remove leading /
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const file = Bun.file(filePath);
|
|
64
|
+
if (await file.exists()) {
|
|
65
|
+
const pathname = decodedPathname.toLowerCase();
|
|
66
|
+
let contentType = 'application/octet-stream';
|
|
67
|
+
|
|
68
|
+
// Set proper MIME types for font files
|
|
69
|
+
if (pathname.endsWith('.woff2')) {
|
|
70
|
+
contentType = 'font/woff2';
|
|
71
|
+
} else if (pathname.endsWith('.woff')) {
|
|
72
|
+
contentType = 'font/woff';
|
|
73
|
+
} else if (pathname.endsWith('.ttf')) {
|
|
74
|
+
contentType = 'font/ttf';
|
|
75
|
+
} else if (pathname.endsWith('.otf')) {
|
|
76
|
+
contentType = 'font/otf';
|
|
77
|
+
}
|
|
78
|
+
// Set proper MIME types for image files
|
|
79
|
+
else if (pathname.endsWith('.jpg') || pathname.endsWith('.jpeg')) {
|
|
80
|
+
contentType = 'image/jpeg';
|
|
81
|
+
} else if (pathname.endsWith('.png')) {
|
|
82
|
+
contentType = 'image/png';
|
|
83
|
+
} else if (pathname.endsWith('.gif')) {
|
|
84
|
+
contentType = 'image/gif';
|
|
85
|
+
} else if (pathname.endsWith('.webp')) {
|
|
86
|
+
contentType = 'image/webp';
|
|
87
|
+
} else if (pathname.endsWith('.svg')) {
|
|
88
|
+
contentType = 'image/svg+xml';
|
|
89
|
+
} else if (pathname.endsWith('.ico')) {
|
|
90
|
+
contentType = 'image/x-icon';
|
|
91
|
+
} else if (pathname.endsWith('.bmp')) {
|
|
92
|
+
contentType = 'image/bmp';
|
|
93
|
+
} else if (pathname.endsWith('.tiff') || pathname.endsWith('.tif')) {
|
|
94
|
+
contentType = 'image/tiff';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return new Response(file, {
|
|
98
|
+
headers: {
|
|
99
|
+
'Content-Type': contentType,
|
|
100
|
+
'Cache-Control': 'public, max-age=31536000, immutable', // Cache static files for 1 year
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Service
|
|
3
|
+
* Manages loading and caching of color variables and themes from colors.json
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { loadJSONFile, parseJSON } from '../jsonLoader';
|
|
7
|
+
import { projectPaths } from '../projectContext';
|
|
8
|
+
import type { ColorVariables, ColorVariableEntry, ThemeConfig, ThemeEntry } from '../../shared/types';
|
|
9
|
+
|
|
10
|
+
export class ColorService {
|
|
11
|
+
private themeConfig: ThemeConfig | null = null;
|
|
12
|
+
private loadingPromise: Promise<ThemeConfig> | null = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Load theme configuration from colors.json
|
|
16
|
+
*/
|
|
17
|
+
async loadThemeConfig(): Promise<ThemeConfig> {
|
|
18
|
+
// Return cached config if already loaded
|
|
19
|
+
if (this.themeConfig) {
|
|
20
|
+
return this.themeConfig;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Return existing loading promise if already in progress
|
|
24
|
+
if (this.loadingPromise) {
|
|
25
|
+
return this.loadingPromise;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Start loading
|
|
29
|
+
this.loadingPromise = this.performLoad();
|
|
30
|
+
this.themeConfig = await this.loadingPromise;
|
|
31
|
+
this.loadingPromise = null;
|
|
32
|
+
|
|
33
|
+
return this.themeConfig;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Perform the actual loading from file
|
|
38
|
+
*/
|
|
39
|
+
private async performLoad(): Promise<ThemeConfig> {
|
|
40
|
+
try {
|
|
41
|
+
const content = await loadJSONFile(projectPaths.colors());
|
|
42
|
+
if (content) {
|
|
43
|
+
const data = parseJSON(content);
|
|
44
|
+
if (data && typeof data === 'object' && 'themes' in data && 'default' in data) {
|
|
45
|
+
return data as ThemeConfig;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.warn('Failed to load colors.json:', error);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Return default theme config if file not found or invalid
|
|
53
|
+
return this.getDefaultThemeConfig();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get default theme configuration
|
|
58
|
+
*/
|
|
59
|
+
private getDefaultThemeConfig(): ThemeConfig {
|
|
60
|
+
return {
|
|
61
|
+
default: 'dark',
|
|
62
|
+
themes: {
|
|
63
|
+
dark: {
|
|
64
|
+
label: 'Dark',
|
|
65
|
+
colors: {
|
|
66
|
+
'primary': '#007acc',
|
|
67
|
+
'primary-light': '#0099ff',
|
|
68
|
+
'primary-dark': '#005a99',
|
|
69
|
+
'secondary': '#6c757d',
|
|
70
|
+
'success': '#28a745',
|
|
71
|
+
'warning': '#ffc107',
|
|
72
|
+
'danger': '#dc3545',
|
|
73
|
+
'error': '#d32f2f',
|
|
74
|
+
'info': '#17a2b8',
|
|
75
|
+
'light': '#f8f9fa',
|
|
76
|
+
'dark': '#343a40',
|
|
77
|
+
'text': '#cccccc',
|
|
78
|
+
'text-dark': '#1e1e1e',
|
|
79
|
+
'background': '#1e1e1e',
|
|
80
|
+
'background-light': '#252526',
|
|
81
|
+
'border': '#444444',
|
|
82
|
+
'border-light': '#d0d0d0',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
light: {
|
|
86
|
+
label: 'Light',
|
|
87
|
+
colors: {
|
|
88
|
+
'primary': '#0066ff',
|
|
89
|
+
'primary-light': '#3385ff',
|
|
90
|
+
'primary-dark': '#0052cc',
|
|
91
|
+
'secondary': '#888888',
|
|
92
|
+
'success': '#22c55e',
|
|
93
|
+
'warning': '#f59e0b',
|
|
94
|
+
'danger': '#ef4444',
|
|
95
|
+
'error': '#dc2626',
|
|
96
|
+
'info': '#06b6d4',
|
|
97
|
+
'light': '#f8f9fa',
|
|
98
|
+
'dark': '#1f2937',
|
|
99
|
+
'text': '#1f2937',
|
|
100
|
+
'text-dark': '#ffffff',
|
|
101
|
+
'background': '#ffffff',
|
|
102
|
+
'background-light': '#f3f4f6',
|
|
103
|
+
'border': '#d1d5db',
|
|
104
|
+
'border-light': '#e5e7eb',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get colors for a specific theme (or default if not found)
|
|
113
|
+
*/
|
|
114
|
+
async getThemeColors(themeName?: string): Promise<ColorVariables> {
|
|
115
|
+
const config = await this.loadThemeConfig();
|
|
116
|
+
const theme = themeName && config.themes[themeName] ? config.themes[themeName] : config.themes[config.default];
|
|
117
|
+
return { colors: theme.colors };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Load color variables from colors.json (for backward compatibility)
|
|
122
|
+
*/
|
|
123
|
+
async loadColors(): Promise<ColorVariables> {
|
|
124
|
+
return this.getThemeColors();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get all available themes
|
|
129
|
+
*/
|
|
130
|
+
async getAvailableThemes(): Promise<ThemeEntry[]> {
|
|
131
|
+
const config = await this.loadThemeConfig();
|
|
132
|
+
return Object.entries(config.themes).map(([name, theme]) => ({
|
|
133
|
+
name,
|
|
134
|
+
label: theme.label,
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get default theme name
|
|
140
|
+
*/
|
|
141
|
+
async getDefaultTheme(): Promise<string> {
|
|
142
|
+
const config = await this.loadThemeConfig();
|
|
143
|
+
return config.default;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get all color variables as entries for a theme
|
|
148
|
+
*/
|
|
149
|
+
async getColorEntries(themeName?: string): Promise<ColorVariableEntry[]> {
|
|
150
|
+
const colors = await this.getThemeColors(themeName);
|
|
151
|
+
return Object.entries(colors.colors).map(([name, value]) => ({
|
|
152
|
+
name,
|
|
153
|
+
value,
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get a specific color by name from a theme
|
|
159
|
+
*/
|
|
160
|
+
async getColor(name: string, themeName?: string): Promise<string | null> {
|
|
161
|
+
const colors = await this.getThemeColors(themeName);
|
|
162
|
+
return colors.colors[name] || null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Clear cache (used for hot reload)
|
|
167
|
+
*/
|
|
168
|
+
clearCache(): void {
|
|
169
|
+
this.themeConfig = null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get the full theme configuration (for editor)
|
|
174
|
+
*/
|
|
175
|
+
async getFullConfig(): Promise<ThemeConfig> {
|
|
176
|
+
return this.loadThemeConfig();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Save theme configuration to colors.json
|
|
181
|
+
*/
|
|
182
|
+
async saveThemeConfig(config: ThemeConfig): Promise<void> {
|
|
183
|
+
const colorsPath = projectPaths.colors();
|
|
184
|
+
const content = JSON.stringify(config, null, 2);
|
|
185
|
+
await Bun.write(colorsPath, content);
|
|
186
|
+
|
|
187
|
+
// Update cache with new config
|
|
188
|
+
this.themeConfig = config;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Export singleton instance
|
|
193
|
+
export const colorService = new ColorService();
|