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,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route Loader
|
|
3
|
+
* Handles loading of pages, components, and manages race conditions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { API_ROUTES, NOT_FOUND_TIMEOUT_MS } from '../../shared/constants';
|
|
7
|
+
import type { ComponentRegistry } from '../componentRegistry';
|
|
8
|
+
import type { ComponentNode, PageData, I18nConfig } from '../../shared/types';
|
|
9
|
+
import { validatePageData } from '../../shared/validation/validators';
|
|
10
|
+
import { parseLocaleFromPath, DEFAULT_I18N_CONFIG } from '../../shared/i18n';
|
|
11
|
+
|
|
12
|
+
export interface RouteLoaderConfig {
|
|
13
|
+
componentRegistry: ComponentRegistry;
|
|
14
|
+
onLoadStart?: () => void;
|
|
15
|
+
onLoadComplete?: (tree: ComponentNode | ComponentNode[] | null) => void;
|
|
16
|
+
onLoadError?: (error: Error, errorTree?: ComponentNode) => void;
|
|
17
|
+
onNotFound?: () => void;
|
|
18
|
+
onPagesLoaded?: (pages: string[]) => void;
|
|
19
|
+
onLocaleDetected?: (locale: string) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class RouteLoader {
|
|
23
|
+
private abortController: AbortController | null = null;
|
|
24
|
+
private notFoundTimeout: NodeJS.Timeout | null = null;
|
|
25
|
+
private config: RouteLoaderConfig;
|
|
26
|
+
private isCancelled: boolean = false;
|
|
27
|
+
private i18nConfig: I18nConfig = DEFAULT_I18N_CONFIG;
|
|
28
|
+
|
|
29
|
+
constructor(config: RouteLoaderConfig) {
|
|
30
|
+
this.config = config;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Update i18n configuration
|
|
35
|
+
*/
|
|
36
|
+
setI18nConfig(config: I18nConfig): void {
|
|
37
|
+
this.i18nConfig = config;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Load global components (shared across all pages)
|
|
42
|
+
* @param signal Optional abort signal to cancel the request
|
|
43
|
+
*/
|
|
44
|
+
async loadGlobalComponents(signal?: AbortSignal): Promise<void> {
|
|
45
|
+
try {
|
|
46
|
+
const fetchOptions: RequestInit = { cache: 'no-store' };
|
|
47
|
+
if (signal) {
|
|
48
|
+
fetchOptions.signal = signal;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const response = await fetch(API_ROUTES.COMPONENTS, fetchOptions);
|
|
52
|
+
|
|
53
|
+
// Check if aborted after fetch
|
|
54
|
+
if (signal?.aborted) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const globalComps = await response.json();
|
|
59
|
+
|
|
60
|
+
// Check again if aborted after JSON parse
|
|
61
|
+
if (signal?.aborted) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (Object.keys(globalComps).length > 0) {
|
|
66
|
+
// Merge global components into registry (page-specific components will override)
|
|
67
|
+
this.config.componentRegistry.merge(globalComps);
|
|
68
|
+
// JavaScript will be executed via useEffect after componentTree renders
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// Ignore abort errors
|
|
72
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Continue anyway - components might still work with cached/previous registry state
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Load available pages list
|
|
81
|
+
*/
|
|
82
|
+
async loadPages(): Promise<string[]> {
|
|
83
|
+
try {
|
|
84
|
+
const response = await fetch(API_ROUTES.PAGES);
|
|
85
|
+
const data = await response.json();
|
|
86
|
+
const pages = data.pages || [];
|
|
87
|
+
|
|
88
|
+
if (this.config.onPagesLoaded) {
|
|
89
|
+
this.config.onPagesLoaded(pages);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return pages;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Load JSON components for current page
|
|
100
|
+
*/
|
|
101
|
+
async loadComponents(path: string): Promise<ComponentNode | ComponentNode[] | null> {
|
|
102
|
+
// Cancel any previous loading request
|
|
103
|
+
if (this.abortController) {
|
|
104
|
+
this.abortController.abort();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Clear any pending "not found" timeout
|
|
108
|
+
this.clearNotFoundTimeout();
|
|
109
|
+
|
|
110
|
+
// Reset cancelled flag
|
|
111
|
+
this.isCancelled = false;
|
|
112
|
+
|
|
113
|
+
// Create new abort controller for this request
|
|
114
|
+
const abortController = new AbortController();
|
|
115
|
+
this.abortController = abortController;
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
// Set loading state and reset showNotFound
|
|
119
|
+
if (this.config.onLoadStart && !this.isCancelled) {
|
|
120
|
+
this.config.onLoadStart();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Reload global components first (with abort signal)
|
|
124
|
+
await this.loadGlobalComponents(abortController.signal);
|
|
125
|
+
|
|
126
|
+
// Check if request was aborted
|
|
127
|
+
if (abortController.signal.aborted || this.isCancelled) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Parse locale from path (e.g., /pl/about -> locale: 'pl', path: '/about')
|
|
132
|
+
const { locale, pathWithoutLocale } = parseLocaleFromPath(path, this.i18nConfig);
|
|
133
|
+
|
|
134
|
+
// Notify about detected locale
|
|
135
|
+
if (this.config.onLocaleDetected && !this.isCancelled) {
|
|
136
|
+
this.config.onLocaleDetected(locale);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const response = await fetch(`${API_ROUTES.YAML}?page=${encodeURIComponent(pathWithoutLocale)}`, {
|
|
140
|
+
cache: 'no-store',
|
|
141
|
+
signal: abortController.signal,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Check again if aborted (after fetch completes)
|
|
145
|
+
if (abortController.signal.aborted || this.isCancelled) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
|
|
151
|
+
if (this.config.onLoadComplete && !this.isCancelled) {
|
|
152
|
+
this.config.onLoadComplete(null);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Delay showing "not found" to prevent flash
|
|
156
|
+
// Only set timeout if not cancelled
|
|
157
|
+
if (!this.isCancelled) {
|
|
158
|
+
this.notFoundTimeout = setTimeout(() => {
|
|
159
|
+
// Check again before calling callback (component might have unmounted)
|
|
160
|
+
if (!this.isCancelled && this.config.onNotFound) {
|
|
161
|
+
this.config.onNotFound();
|
|
162
|
+
}
|
|
163
|
+
this.notFoundTimeout = null;
|
|
164
|
+
}, NOT_FOUND_TIMEOUT_MS);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const jsonText = await response.text();
|
|
171
|
+
|
|
172
|
+
// Check if aborted after text extraction
|
|
173
|
+
if (abortController.signal.aborted || this.isCancelled) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let parsed;
|
|
178
|
+
try {
|
|
179
|
+
parsed = JSON.parse(jsonText);
|
|
180
|
+
|
|
181
|
+
// Validate page data (logs warnings but doesn't fail - graceful degradation)
|
|
182
|
+
const validationResult = validatePageData(parsed);
|
|
183
|
+
if (!validationResult.valid) {
|
|
184
|
+
console.warn(`[RouteLoader] Page validation failed for ${path}:`,
|
|
185
|
+
validationResult.errors.map(e => `${e.path}: ${e.message}`).join('; '));
|
|
186
|
+
}
|
|
187
|
+
// Use validated data if available, otherwise use parsed (graceful degradation)
|
|
188
|
+
parsed = validationResult.valid ? validationResult.data : parsed;
|
|
189
|
+
} catch (parseError) {
|
|
190
|
+
// Set an error tree to trigger error boundary
|
|
191
|
+
const errorTree = this.createErrorTree(
|
|
192
|
+
'Invalid JSON format',
|
|
193
|
+
`Failed to parse JSON for page: ${path}`,
|
|
194
|
+
String(parseError)
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
if (this.config.onLoadComplete && !this.isCancelled) {
|
|
198
|
+
this.config.onLoadComplete(errorTree);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (this.config.onLoadError && !this.isCancelled) {
|
|
202
|
+
this.config.onLoadError(
|
|
203
|
+
parseError instanceof Error ? parseError : new Error(String(parseError)),
|
|
204
|
+
errorTree
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return errorTree;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Check if JSON has components section (page-specific components)
|
|
212
|
+
let tree: ComponentNode | ComponentNode[] | null = null;
|
|
213
|
+
|
|
214
|
+
if (parsed.components) {
|
|
215
|
+
// Merge page-specific components with global (page-specific override global)
|
|
216
|
+
try {
|
|
217
|
+
this.config.componentRegistry.merge(parsed.components);
|
|
218
|
+
// JavaScript will be executed via useEffect after componentTree renders
|
|
219
|
+
} catch (componentError) {
|
|
220
|
+
}
|
|
221
|
+
tree = parsed.root || parsed;
|
|
222
|
+
} else if (parsed.root) {
|
|
223
|
+
// Has root property but no components section - use only global components
|
|
224
|
+
tree = parsed.root;
|
|
225
|
+
// JavaScript will be executed via useEffect after componentTree renders
|
|
226
|
+
} else {
|
|
227
|
+
// Legacy format - direct component tree
|
|
228
|
+
tree = parsed;
|
|
229
|
+
// JavaScript will be executed via useEffect after componentTree renders
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Check one more time if aborted
|
|
233
|
+
if (abortController.signal.aborted || this.isCancelled) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (this.config.onLoadComplete && !this.isCancelled) {
|
|
238
|
+
this.config.onLoadComplete(tree);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
this.clearNotFoundTimeout();
|
|
242
|
+
this.abortController = null;
|
|
243
|
+
|
|
244
|
+
return tree;
|
|
245
|
+
} catch (error) {
|
|
246
|
+
// Ignore abort errors
|
|
247
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Check if aborted before handling error
|
|
252
|
+
if (abortController.signal.aborted || this.isCancelled) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Set a generic error tree
|
|
257
|
+
const errorTree = this.createErrorTree('Error loading page', String(error));
|
|
258
|
+
|
|
259
|
+
if (this.config.onLoadComplete && !this.isCancelled) {
|
|
260
|
+
this.config.onLoadComplete(errorTree);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (this.config.onLoadError && !this.isCancelled) {
|
|
264
|
+
this.config.onLoadError(
|
|
265
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
266
|
+
errorTree
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this.clearNotFoundTimeout();
|
|
271
|
+
this.abortController = null;
|
|
272
|
+
|
|
273
|
+
return errorTree;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Cancel current loading request
|
|
279
|
+
*/
|
|
280
|
+
cancel(): void {
|
|
281
|
+
this.isCancelled = true;
|
|
282
|
+
if (this.abortController) {
|
|
283
|
+
this.abortController.abort();
|
|
284
|
+
this.abortController = null;
|
|
285
|
+
}
|
|
286
|
+
this.clearNotFoundTimeout();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Create error tree ComponentNode structure
|
|
291
|
+
*/
|
|
292
|
+
private createErrorTree(
|
|
293
|
+
title: string,
|
|
294
|
+
message: string,
|
|
295
|
+
details?: string
|
|
296
|
+
): ComponentNode {
|
|
297
|
+
const children: ComponentNode[] = [
|
|
298
|
+
{
|
|
299
|
+
type: 'node',
|
|
300
|
+
tag: 'h2',
|
|
301
|
+
style: { color: '#c00' },
|
|
302
|
+
children: [title],
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
type: 'node',
|
|
306
|
+
tag: 'p',
|
|
307
|
+
style: { color: '#666', marginTop: '16px' },
|
|
308
|
+
children: [message],
|
|
309
|
+
},
|
|
310
|
+
];
|
|
311
|
+
|
|
312
|
+
if (details) {
|
|
313
|
+
children.push({
|
|
314
|
+
type: 'node',
|
|
315
|
+
tag: 'pre',
|
|
316
|
+
style: {
|
|
317
|
+
background: '#f5f5f5',
|
|
318
|
+
padding: '16px',
|
|
319
|
+
borderRadius: '4px',
|
|
320
|
+
textAlign: 'left',
|
|
321
|
+
maxWidth: '600px',
|
|
322
|
+
margin: '16px auto',
|
|
323
|
+
overflow: 'auto',
|
|
324
|
+
},
|
|
325
|
+
children: [details],
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
type: 'node',
|
|
331
|
+
tag: 'div',
|
|
332
|
+
style: { padding: '40px', textAlign: 'center' },
|
|
333
|
+
children,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Clear not found timeout
|
|
339
|
+
*/
|
|
340
|
+
private clearNotFoundTimeout(): void {
|
|
341
|
+
if (this.notFoundTimeout) {
|
|
342
|
+
clearTimeout(this.notFoundTimeout);
|
|
343
|
+
this.notFoundTimeout = null;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|