onelaraveljs 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.
Files changed (67) hide show
  1. package/README.md +87 -0
  2. package/docs/integration_analysis.md +116 -0
  3. package/docs/onejs_analysis.md +108 -0
  4. package/docs/optimization_implementation_group2.md +458 -0
  5. package/docs/optimization_plan.md +130 -0
  6. package/index.js +16 -0
  7. package/package.json +13 -0
  8. package/src/app.js +61 -0
  9. package/src/core/API.js +72 -0
  10. package/src/core/ChildrenRegistry.js +410 -0
  11. package/src/core/DOMBatcher.js +207 -0
  12. package/src/core/ErrorBoundary.js +226 -0
  13. package/src/core/EventDelegator.js +416 -0
  14. package/src/core/Helper.js +817 -0
  15. package/src/core/LoopContext.js +97 -0
  16. package/src/core/OneDOM.js +246 -0
  17. package/src/core/OneMarkup.js +444 -0
  18. package/src/core/Router.js +996 -0
  19. package/src/core/SEOConfig.js +321 -0
  20. package/src/core/SectionEngine.js +75 -0
  21. package/src/core/TemplateEngine.js +83 -0
  22. package/src/core/View.js +273 -0
  23. package/src/core/ViewConfig.js +229 -0
  24. package/src/core/ViewController.js +1410 -0
  25. package/src/core/ViewControllerOptimized.js +164 -0
  26. package/src/core/ViewIdentifier.js +361 -0
  27. package/src/core/ViewLoader.js +272 -0
  28. package/src/core/ViewManager.js +1962 -0
  29. package/src/core/ViewState.js +761 -0
  30. package/src/core/ViewSystem.js +301 -0
  31. package/src/core/ViewTemplate.js +4 -0
  32. package/src/core/helpers/BindingHelper.js +239 -0
  33. package/src/core/helpers/ConfigHelper.js +37 -0
  34. package/src/core/helpers/EventHelper.js +172 -0
  35. package/src/core/helpers/LifecycleHelper.js +17 -0
  36. package/src/core/helpers/ReactiveHelper.js +169 -0
  37. package/src/core/helpers/RenderHelper.js +15 -0
  38. package/src/core/helpers/ResourceHelper.js +89 -0
  39. package/src/core/helpers/TemplateHelper.js +11 -0
  40. package/src/core/managers/BindingManager.js +671 -0
  41. package/src/core/managers/ConfigurationManager.js +136 -0
  42. package/src/core/managers/EventManager.js +309 -0
  43. package/src/core/managers/LifecycleManager.js +356 -0
  44. package/src/core/managers/ReactiveManager.js +334 -0
  45. package/src/core/managers/RenderEngine.js +292 -0
  46. package/src/core/managers/ResourceManager.js +441 -0
  47. package/src/core/managers/ViewHierarchyManager.js +258 -0
  48. package/src/core/managers/ViewTemplateManager.js +127 -0
  49. package/src/core/reactive/ReactiveComponent.js +592 -0
  50. package/src/core/services/EventService.js +418 -0
  51. package/src/core/services/HttpService.js +106 -0
  52. package/src/core/services/LoggerService.js +57 -0
  53. package/src/core/services/StateService.js +512 -0
  54. package/src/core/services/StorageService.js +856 -0
  55. package/src/core/services/StoreService.js +258 -0
  56. package/src/core/services/TemplateDetectorService.js +361 -0
  57. package/src/core/services/Test.js +18 -0
  58. package/src/helpers/devWarnings.js +205 -0
  59. package/src/helpers/performance.js +226 -0
  60. package/src/helpers/utils.js +287 -0
  61. package/src/init.js +343 -0
  62. package/src/plugins/auto-plugin.js +34 -0
  63. package/src/services/Test.js +18 -0
  64. package/src/types/index.js +193 -0
  65. package/src/utils/date-helper.js +51 -0
  66. package/src/utils/helpers.js +39 -0
  67. package/src/utils/validation.js +32 -0
@@ -0,0 +1,441 @@
1
+ /**
2
+ * ResourceManager - Manages view resources (styles and scripts)
3
+ * Handles insertion, removal, and reference counting
4
+ *
5
+ * Extracted from ViewController.js to improve maintainability
6
+ * @author GitHub Copilot
7
+ * @date 2025-12-29
8
+ */
9
+
10
+ import logger from '../services/LoggerService.js';
11
+
12
+ export class ResourceManager {
13
+ /**
14
+ * @param {ViewController} controller - Parent controller instance
15
+ */
16
+ constructor(controller) {
17
+ this.controller = controller;
18
+ this.path = controller.path;
19
+ }
20
+
21
+ updateController(newController) {
22
+ this.controller = newController;
23
+ this.path = newController.path;
24
+ }
25
+
26
+ /**
27
+ * Insert all resources (styles + scripts)
28
+ */
29
+ insertResources() {
30
+ this.insertStyles();
31
+ this.insertScripts();
32
+ }
33
+
34
+ /**
35
+ * Remove all resources
36
+ */
37
+ removeResources() {
38
+ this.removeStyles();
39
+ this.removeScripts();
40
+ }
41
+
42
+ /**
43
+ * Insert styles into DOM with reference counting
44
+ * Extracted from ViewController.js line 428
45
+ */
46
+ insertStyles() {
47
+ if (!this.controller.styles || this.controller.styles.length === 0) {
48
+ return;
49
+ }
50
+
51
+ this.controller.styles.forEach(style => {
52
+ const resourceKey = this.controller.App.View.Engine.getResourceKey({
53
+ ...style,
54
+ viewPath: this.path,
55
+ resourceType: 'style'
56
+ });
57
+
58
+ // Check if already in registry
59
+ let registryEntry = this.controller.App.View.Engine.resourceRegistry.get(resourceKey);
60
+
61
+ if (registryEntry) {
62
+ // Resource already exists - just add this view path to registry
63
+ registryEntry.viewPaths.add(this.path);
64
+ registryEntry.referenceCount++;
65
+ this.controller.insertedResourceKeys.add(resourceKey);
66
+ return; // Don't insert again
67
+ }
68
+
69
+ // Create and insert new style element
70
+ let element;
71
+
72
+ if (style.type === 'href') {
73
+ // External stylesheet
74
+ element = document.createElement('link');
75
+ element.rel = 'stylesheet';
76
+ element.href = style.href;
77
+
78
+ // Add attributes
79
+ if (style.attributes) {
80
+ Object.entries(style.attributes).forEach(([key, value]) => {
81
+ if (value === true) {
82
+ element.setAttribute(key, '');
83
+ } else {
84
+ element.setAttribute(key, value);
85
+ }
86
+ });
87
+ }
88
+
89
+ if (style.id) {
90
+ element.id = style.id;
91
+ }
92
+
93
+ element.setAttribute('data-view-path', this.path);
94
+ element.setAttribute('data-resource-key', resourceKey);
95
+
96
+ document.head.appendChild(element);
97
+
98
+ } else if (style.type === 'code') {
99
+ // Inline CSS
100
+ element = document.createElement('style');
101
+ element.textContent = style.content;
102
+
103
+ if (style.id) {
104
+ element.id = style.id;
105
+ }
106
+
107
+ if (style.attributes) {
108
+ Object.entries(style.attributes).forEach(([key, value]) => {
109
+ if (key !== 'id' && key !== 'type') {
110
+ if (value === true) {
111
+ element.setAttribute(key, '');
112
+ } else {
113
+ element.setAttribute(key, value);
114
+ }
115
+ }
116
+ });
117
+ }
118
+
119
+ element.setAttribute('data-view-path', this.path);
120
+ element.setAttribute('data-resource-key', resourceKey);
121
+
122
+ document.head.appendChild(element);
123
+ }
124
+
125
+ // Register in global registry
126
+ this.controller.App.View.Engine.resourceRegistry.set(resourceKey, {
127
+ element: element,
128
+ viewPaths: new Set([this.path]),
129
+ referenceCount: 1,
130
+ resourceType: 'style'
131
+ });
132
+
133
+ this.controller.insertedResourceKeys.add(resourceKey);
134
+ });
135
+ }
136
+
137
+ /**
138
+ * Insert scripts into DOM with reference counting
139
+ * Extracted from ViewController.js line 523
140
+ */
141
+ insertScripts() {
142
+ if (!this.controller.scripts || this.controller.scripts.length === 0) {
143
+ return;
144
+ }
145
+
146
+ this.controller.scripts.forEach(script => {
147
+ const resourceKey = this.controller.App.View.Engine.getResourceKey({
148
+ ...script,
149
+ viewPath: this.path,
150
+ resourceType: 'script'
151
+ });
152
+
153
+ // Check if already in registry
154
+ let registryEntry = this.controller.App.View.Engine.resourceRegistry.get(resourceKey);
155
+
156
+ if (registryEntry) {
157
+ // Resource already exists - just add this view path to registry
158
+ registryEntry.viewPaths.add(this.path);
159
+ registryEntry.referenceCount++;
160
+ this.controller.insertedResourceKeys.add(resourceKey);
161
+
162
+ // If script already loaded, call onload callback (only for external scripts with element)
163
+ if (script.onload && registryEntry.element && registryEntry.element.readyState === 'complete') {
164
+ try {
165
+ script.onload();
166
+ } catch (error) {
167
+ logger.warn(`ResourceManager.insertScripts: Error calling onload for ${resourceKey}:`, error);
168
+ }
169
+ }
170
+
171
+ // For function wrappers, don't execute again (already executed)
172
+ if (script.function && registryEntry.executed) {
173
+ return; // Function already executed, just tracking reference
174
+ }
175
+
176
+ return; // Don't insert again
177
+ }
178
+
179
+ // Create and insert new script element
180
+ let element;
181
+
182
+ if (script.type === 'src') {
183
+ // External script
184
+ element = document.createElement('script');
185
+ element.src = script.src;
186
+ element.type = script.attributes?.type || 'text/javascript';
187
+
188
+ // Add attributes
189
+ if (script.attributes) {
190
+ Object.entries(script.attributes).forEach(([key, value]) => {
191
+ if (key === 'async' || key === 'defer') {
192
+ element[key] = value === true || value === 'true';
193
+ } else if (value === true) {
194
+ element.setAttribute(key, '');
195
+ } else {
196
+ element.setAttribute(key, value);
197
+ }
198
+ });
199
+ }
200
+
201
+ if (script.id) {
202
+ element.id = script.id;
203
+ }
204
+
205
+ element.setAttribute('data-view-path', this.path);
206
+ element.setAttribute('data-resource-key', resourceKey);
207
+
208
+ // Handle onload/onerror
209
+ if (script.onload) {
210
+ element.onload = script.onload;
211
+ }
212
+ if (script.onerror) {
213
+ element.onerror = script.onerror;
214
+ }
215
+
216
+ document.body.appendChild(element);
217
+
218
+ } else if (script.type === 'code') {
219
+ // Inline script - Use registered script function if available
220
+ if (script.function) {
221
+ // Get script from ViewEngine.scripts registry
222
+ const scriptCallback = this.controller.App.View.Engine.getScript(this.path, script.function);
223
+
224
+ if (scriptCallback && typeof scriptCallback === 'function') {
225
+ // Function wrapper: Execute only once, track in registry
226
+ try {
227
+ scriptCallback.call(this.controller);
228
+
229
+ // Register in global registry with executed flag
230
+ this.controller.App.View.Engine.resourceRegistry.set(resourceKey, {
231
+ element: null, // No DOM element for function wrapper
232
+ viewPaths: new Set([this.path]),
233
+ referenceCount: 1,
234
+ resourceType: 'script',
235
+ executed: true, // Mark as executed - ensures single execution
236
+ functionName: script.function // Store function name for reference
237
+ });
238
+
239
+ this.controller.insertedResourceKeys.add(resourceKey);
240
+ } catch (error) {
241
+ logger.error(`ResourceManager.insertScripts: Error executing script function ${script.function}:`, error);
242
+ }
243
+ return; // Don't continue to element creation
244
+ } else {
245
+ logger.warn(`ResourceManager.insertScripts: Script function ${script.function} not found in registry for view ${this.path}`);
246
+ }
247
+
248
+ } else if (script.content) {
249
+ // Fallback: Insert raw content (legacy support)
250
+ element = document.createElement('script');
251
+ element.textContent = script.content;
252
+ element.type = script.attributes?.type || 'text/javascript';
253
+
254
+ if (script.id) {
255
+ element.id = script.id;
256
+ }
257
+
258
+ if (script.attributes) {
259
+ Object.entries(script.attributes).forEach(([key, value]) => {
260
+ if (key !== 'id' && key !== 'type') {
261
+ if (value === true) {
262
+ element.setAttribute(key, '');
263
+ } else {
264
+ element.setAttribute(key, value);
265
+ }
266
+ }
267
+ });
268
+ }
269
+
270
+ element.setAttribute('data-view-path', this.path);
271
+ element.setAttribute('data-resource-key', resourceKey);
272
+ try {
273
+ document.body.appendChild(element);
274
+ } catch (error) {
275
+ logger.warn(`ResourceManager.insertScripts: Error appending script ${resourceKey}:`, error);
276
+ logger.log(element);
277
+ }
278
+ } else {
279
+ logger.warn(`ResourceManager.insertScripts: Script ${resourceKey} has no function or content`);
280
+ return; // Don't register if no content/function
281
+ }
282
+ }
283
+
284
+ // Register in global registry (for non-function scripts)
285
+ if (element) {
286
+ this.controller.App.View.Engine.resourceRegistry.set(resourceKey, {
287
+ element: element,
288
+ viewPaths: new Set([this.path]),
289
+ referenceCount: 1,
290
+ resourceType: 'script'
291
+ });
292
+
293
+ this.controller.insertedResourceKeys.add(resourceKey);
294
+ }
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Remove styles from registry with reference counting
300
+ * Extracted from ViewController.js line 683
301
+ */
302
+ removeStyles() {
303
+ if (!this.controller.styles || this.controller.styles.length === 0) {
304
+ // Fallback: Remove all styles with this view path from DOM
305
+ this.removeStylesByViewPath();
306
+ return;
307
+ }
308
+
309
+ this.controller.styles.forEach(style => {
310
+ const resourceKey = this.controller.App.View.Engine.getResourceKey({
311
+ ...style,
312
+ viewPath: this.path,
313
+ resourceType: 'style'
314
+ });
315
+
316
+ const registryEntry = this.controller.App.View.Engine.resourceRegistry.get(resourceKey);
317
+
318
+ if (registryEntry) {
319
+ // Remove this view path from registry
320
+ registryEntry.viewPaths.delete(this.path);
321
+ registryEntry.referenceCount--;
322
+
323
+ // Only remove from DOM if no other views are using it
324
+ if (registryEntry.referenceCount <= 0) {
325
+ if (registryEntry.element && registryEntry.element.parentNode) {
326
+ registryEntry.element.remove();
327
+ }
328
+
329
+ // Remove from registry
330
+ this.controller.App.View.Engine.resourceRegistry.delete(resourceKey);
331
+ }
332
+
333
+ this.controller.insertedResourceKeys.delete(resourceKey);
334
+ } else {
335
+ // Fallback: If not found in registry, try to remove from DOM directly
336
+ const elements = document.querySelectorAll(`[data-view-path="${this.path}"][data-resource-key="${resourceKey}"]`);
337
+ elements.forEach(element => {
338
+ if (element.parentNode) {
339
+ element.remove();
340
+ }
341
+ });
342
+ }
343
+ });
344
+
345
+ // Final fallback: Remove any remaining styles with this view path
346
+ this.removeStylesByViewPath();
347
+ }
348
+
349
+ /**
350
+ * Remove all styles from DOM that belong to a view path
351
+ * Extracted from ViewController.js line 738
352
+ */
353
+ removeStylesByViewPath(viewPath = null) {
354
+ const targetPath = viewPath || this.path;
355
+ if (!targetPath) {
356
+ return;
357
+ }
358
+
359
+ // Find all style/link elements with this view path
360
+ const styleElements = document.querySelectorAll(`style[data-view-path="${targetPath}"], link[data-view-path="${targetPath}"]`);
361
+
362
+ styleElements.forEach(element => {
363
+ const resourceKey = element.getAttribute('data-resource-key');
364
+
365
+ if (resourceKey) {
366
+ // Check registry to see if other views are using this resource
367
+ const registryEntry = this.controller.App.View.Engine.resourceRegistry.get(resourceKey);
368
+
369
+ if (registryEntry) {
370
+ // Remove this view path from registry
371
+ registryEntry.viewPaths.delete(targetPath);
372
+ registryEntry.referenceCount--;
373
+
374
+ // Only remove from DOM if no other views are using it
375
+ if (registryEntry.referenceCount <= 0) {
376
+ if (element.parentNode) {
377
+ element.remove();
378
+ }
379
+ // Remove from registry
380
+ this.controller.App.View.Engine.resourceRegistry.delete(resourceKey);
381
+ }
382
+ } else {
383
+ // Not in registry, safe to remove directly
384
+ if (element.parentNode) {
385
+ element.remove();
386
+ }
387
+ }
388
+
389
+ // Remove from insertedResourceKeys (if this is the same instance)
390
+ if (this.path === targetPath) {
391
+ this.controller.insertedResourceKeys.delete(resourceKey);
392
+ }
393
+ } else {
394
+ // No resource key, but has view path - safe to remove
395
+ if (element.parentNode) {
396
+ element.remove();
397
+ }
398
+ }
399
+ });
400
+ }
401
+
402
+ /**
403
+ * Remove scripts from registry with reference counting
404
+ * Extracted from ViewController.js line 790
405
+ */
406
+ removeScripts() {
407
+ if (!this.controller.scripts || this.controller.scripts.length === 0) {
408
+ return;
409
+ }
410
+
411
+ this.controller.scripts.forEach(script => {
412
+ const resourceKey = this.controller.App.View.Engine.getResourceKey({
413
+ ...script,
414
+ viewPath: this.path,
415
+ resourceType: 'script'
416
+ });
417
+
418
+ const registryEntry = this.controller.App.View.Engine.resourceRegistry.get(resourceKey);
419
+
420
+ if (registryEntry) {
421
+ // Remove this view path from registry
422
+ registryEntry.viewPaths.delete(this.path);
423
+ registryEntry.referenceCount--;
424
+
425
+ // Only remove from DOM if no other views are using it
426
+ if (registryEntry.referenceCount <= 0) {
427
+ // For function wrappers, don't remove from DOM (no element)
428
+ // Just remove from registry
429
+ if (registryEntry.element && registryEntry.element.parentNode) {
430
+ registryEntry.element.remove();
431
+ }
432
+
433
+ // Remove from registry
434
+ this.controller.App.View.Engine.resourceRegistry.delete(resourceKey);
435
+ }
436
+
437
+ this.controller.insertedResourceKeys.delete(resourceKey);
438
+ }
439
+ });
440
+ }
441
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * ViewHierarchyManager - Manages view relationships and hierarchy
3
+ *
4
+ * Responsibilities:
5
+ * - Manage parent-child relationships
6
+ * - Handle super view (layout inheritance)
7
+ * - Manage original view references
8
+ * - Create and configure child/super views
9
+ *
10
+ * Extracted from ViewController.js (Phase 9) to reduce complexity
11
+ * @author GitHub Copilot
12
+ * @date 2025-12-29
13
+ */
14
+
15
+ export class ViewHierarchyManager {
16
+ /**
17
+ * @param {ViewController} controller - Parent controller instance
18
+ */
19
+ constructor(controller) {
20
+ this.controller = controller;
21
+ this.scanChildrenIDs = [];
22
+
23
+ this.renewChildrenIDs = [];
24
+
25
+ this.rcChildrenIDs = [];
26
+
27
+ }
28
+
29
+ /**
30
+ * Set super view (layout)
31
+ * @param {View} superView - Super view instance
32
+ * @returns {ViewController} Controller instance for chaining
33
+ */
34
+ setSuperView(superView) {
35
+ this.controller.superView = superView.__;
36
+ return this.controller;
37
+ }
38
+
39
+ /**
40
+ * Set parent view
41
+ * @param {View} parent - Parent view instance
42
+ * @returns {ViewController} Controller instance for chaining
43
+ */
44
+ setParent(parent) {
45
+ this.controller.parent = parent.__;
46
+ return this.controller;
47
+ }
48
+
49
+ /**
50
+ * Set original view (for extended views)
51
+ * @param {View} originalView - Original view instance
52
+ * @returns {ViewController} Controller instance for chaining
53
+ */
54
+ setOriginalView(originalView) {
55
+ this.controller.originalView = originalView.__;
56
+ this.controller.originalViewPath = originalView.path;
57
+ this.controller.originalViewId = originalView.id;
58
+ return this.controller;
59
+ }
60
+
61
+ /**
62
+ * Add child view
63
+ * Creates scope object and registers child in ChildrenRegistry
64
+ *
65
+ * @param {View} child - Child view to add
66
+ * @param {Object} data - Data to add to the child view
67
+ * @returns {ViewController} Controller instance for chaining
68
+ */
69
+ addChild(child, data = {}) {
70
+ // Get parent reactive component if in render context
71
+ const parentRC = this.controller._reactiveManager?.currentRenderingComponent;
72
+
73
+ // Register in ChildrenRegistry (handles controller storage and scope)
74
+ const registry = this.controller._childrenRegistry;
75
+
76
+ // Get correct index BEFORE register() pushes to array
77
+ // Add defensive check for children array
78
+ if (!this.controller.children) {
79
+ console.error('ViewHierarchyManager.addChild: this.controller.children is undefined/null', {
80
+ controller: this.controller,
81
+ child: child,
82
+ viewPath: this.controller?.path
83
+ });
84
+ this.controller.children = [];
85
+ }
86
+ const currentIndex = this.controller.children.length;
87
+
88
+ registry.register(child, {
89
+ data,
90
+ parentReactiveComponent: parentRC,
91
+ index: currentIndex // ✅ Fixed: calculate index before push
92
+ });
93
+
94
+ return this.controller;
95
+ }
96
+
97
+ /**
98
+ * Remove child view
99
+ * Removes from registry and children array
100
+ *
101
+ * @param {View} child - Child view to remove
102
+ * @returns {ViewController} Controller instance for chaining
103
+ */
104
+ removeChild(child) {
105
+ const childController = child.__;
106
+ const childId = child.id;
107
+
108
+ // Remove from registry (handles cleanup of registry, mounted state, RC tracking)
109
+ const registry = this.controller._childrenRegistry;
110
+ if (registry) {
111
+ registry.destroy(childId);
112
+ }
113
+
114
+ // Remove from children array (ViewHierarchyManager's responsibility)
115
+ if (!this.controller.children) {
116
+ this.controller.children = [];
117
+ }
118
+ this.controller.children = this.controller.children.filter(c => c !== childController);
119
+
120
+ return this.controller;
121
+ }
122
+
123
+ /**
124
+ * Reset original view (destroy it)
125
+ */
126
+ resetOriginalView() {
127
+ if (this.controller.originalView && this.controller.originalView instanceof ViewController) {
128
+ this.controller.originalView.destroy();
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Eject original view (clear references)
134
+ */
135
+ ejectOriginalView() {
136
+ if (this.controller.originalView && this.controller.originalView instanceof ViewController) {
137
+ this.controller.originalView = null;
138
+ this.controller.originalViewId = null;
139
+ this.controller.originalViewPath = null;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Remove this view and all children
145
+ * @returns {ViewController} Controller instance for chaining
146
+ */
147
+ remove() {
148
+ this.controller.master?.__?.removeChild(this.controller.view);
149
+
150
+ // Add defensive check before forEach
151
+ if (this.controller.children && Array.isArray(this.controller.children)) {
152
+ this.controller.children.forEach(childCtrl => childCtrl.remove());
153
+ }
154
+
155
+ this.controller.master = null;
156
+ this.controller.children = [];
157
+ return this.controller;
158
+ }
159
+
160
+ /**
161
+ * Create super view (layout inheritance)
162
+ * Called by __extends template helper
163
+ *
164
+ * @param {string} path - Super view path
165
+ * @param {Object} data - Data to pass to super view
166
+ * @returns {View|null} Super view instance
167
+ */
168
+ createSuperView(path, data = {}) {
169
+ const originData = this.controller.data ? this.controller.data : {};
170
+ if (originData.__SSR_VIEW_ID__) {
171
+ originData.__SSR_VIEW_ID__ = null;
172
+ delete originData.__SSR_VIEW_ID__;
173
+ }
174
+
175
+ const viewInstance = this.controller.App.View.extendView(path, { ...originData, ...data });
176
+ if (!viewInstance) {
177
+ return null;
178
+ }
179
+
180
+ this.controller.superViewPath = path;
181
+ this.setSuperView(viewInstance);
182
+ viewInstance.__.setOriginalView(this.controller.view);
183
+ viewInstance.__.viewType = 'layout';
184
+ return viewInstance;
185
+ }
186
+
187
+ /**
188
+ * Create child view (include)
189
+ * Called by $include helper
190
+ * Uses ChildrenRegistry for intelligent reuse and cleanup
191
+ *
192
+ * @param {string} path - Child view path
193
+ * @param {Object} data - Data to pass to child view
194
+ * @returns {View|null} Child view instance
195
+ */
196
+ createChildView(path, data = {}) {
197
+ const registry = this.controller._childrenRegistry;
198
+
199
+ // 1. Try to find reusable child from renewChildrenIDs
200
+ if (this.renewChildrenIDs.length) {
201
+ let childCtrl = this.controller.children.find(c =>
202
+ c.view.name == path && this.renewChildrenIDs.includes(c.view.id)
203
+ );
204
+
205
+ if (childCtrl) {
206
+ const view = childCtrl.view;
207
+
208
+ // Update child in registry (refresh data and parentReactiveComponent)
209
+ const childNode = registry.get(view.id);
210
+ if (childNode) {
211
+ Object.assign(childNode.scope.data, data);
212
+
213
+ // Update parentReactiveComponent for reused child
214
+ const currentRC = this.controller._reactiveManager?.currentRenderingComponent;
215
+ if (currentRC && childNode.parentReactiveComponent !== currentRC) {
216
+ registry.updateParentReactiveComponent(view.id, currentRC);
217
+ }
218
+ }
219
+
220
+ // Mark as needing refresh
221
+ view.__._templateManager.wrapperConfig.enable = true;
222
+ view.__.isScanned = false;
223
+ view.__.isFirstClientRendering = true;
224
+ view.__.updateVariableData(data);
225
+
226
+ this.rcChildrenIDs.push(childCtrl.view.id);
227
+ return view;
228
+ }
229
+ }
230
+
231
+ // 2. Create new child
232
+ const parentData = this.controller.data ? this.controller.data : {};
233
+ if (parentData.__SSR_VIEW_ID__) {
234
+ parentData.__SSR_VIEW_ID__ = null;
235
+ delete parentData.__SSR_VIEW_ID__;
236
+ }
237
+
238
+ const viewInstance = this.controller.App.View.include(path, { ...parentData, ...data });
239
+ if (!viewInstance) {
240
+ return null;
241
+ }
242
+
243
+ // 3. Setup for rendering
244
+ viewInstance.__._templateManager.wrapperConfig.enable = true;
245
+ viewInstance.__.isScanned = false;
246
+ viewInstance.__.isFirstClientRendering = true;
247
+ viewInstance.__.setParent(this.controller.view);
248
+
249
+ // 4. Register child (uses ChildrenRegistry)
250
+ this.addChild(viewInstance, data);
251
+
252
+ viewInstance.__.viewType = 'template';
253
+ this.scanChildrenIDs.push(viewInstance.id);
254
+ this.rcChildrenIDs.push(viewInstance.id);
255
+
256
+ return viewInstance;
257
+ }
258
+ }