slicejs-web-framework 2.2.13 → 2.3.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/Slice/Components/Structural/ContextManager/ContextManager.js +361 -0
- package/Slice/Components/Structural/Controller/Controller.js +925 -890
- package/Slice/Components/Structural/EventManager/EventManager.js +329 -0
- package/Slice/Components/Structural/Router/Router.js +598 -589
- package/Slice/Slice.js +297 -249
- package/opencode.json +11 -0
- package/package.json +1 -1
- package/src/sliceConfig.json +60 -54
|
@@ -1,890 +1,925 @@
|
|
|
1
|
-
import components from '/Components/components.js';
|
|
2
|
-
|
|
3
|
-
export default class Controller {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.componentCategories = new Map(Object.entries(components));
|
|
6
|
-
this.templates = new Map();
|
|
7
|
-
this.classes = new Map();
|
|
8
|
-
this.requestedStyles = new Set(); // ✅ CRÍTICO: Para tracking de CSS cargados
|
|
9
|
-
this.activeComponents = new Map();
|
|
10
|
-
|
|
11
|
-
// 🚀 OPTIMIZACIÓN: Índice inverso para búsqueda rápida de hijos
|
|
12
|
-
// parentSliceId → Set<childSliceId>
|
|
13
|
-
this.childrenIndex = new Map();
|
|
14
|
-
|
|
15
|
-
// 📦 Bundle system
|
|
16
|
-
this.loadedBundles = new Set();
|
|
17
|
-
this.bundleConfig = null;
|
|
18
|
-
this.criticalBundleLoaded = false;
|
|
19
|
-
|
|
20
|
-
this.idCounter = 0;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 📦 Initializes bundle system (called automatically when config is loaded)
|
|
25
|
-
*/
|
|
26
|
-
initializeBundles(config = null) {
|
|
27
|
-
if (config) {
|
|
28
|
-
this.bundleConfig = config;
|
|
29
|
-
|
|
30
|
-
// Register critical bundle components if available
|
|
31
|
-
if (config.bundles?.critical) {
|
|
32
|
-
// The critical bundle should already be loaded, register its components
|
|
33
|
-
this.loadedBundles.add('critical');
|
|
34
|
-
// Note: Critical bundle registration is handled by the auto-import
|
|
35
|
-
}
|
|
36
|
-
this.criticalBundleLoaded = true;
|
|
37
|
-
} else {
|
|
38
|
-
// No bundles available, will use individual component loading
|
|
39
|
-
this.bundleConfig = null;
|
|
40
|
-
this.criticalBundleLoaded = false;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* 📦 Loads a bundle by name or category
|
|
46
|
-
*/
|
|
47
|
-
async loadBundle(bundleName) {
|
|
48
|
-
if (this.loadedBundles.has(bundleName)) {
|
|
49
|
-
return; // Already loaded
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
let bundleInfo = this.bundleConfig?.bundles?.routes?.[bundleName];
|
|
54
|
-
|
|
55
|
-
if (!bundleInfo && this.bundleConfig?.bundles?.routes) {
|
|
56
|
-
const normalizedName = bundleName?.toLowerCase();
|
|
57
|
-
const matchedKey = Object.keys(this.bundleConfig.bundles.routes)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
} catch (error) {
|
|
82
|
-
console.warn(`Failed to load bundle ${bundleName}:`, error);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 📦 Registers a bundle's components (called automatically by bundle files)
|
|
88
|
-
*/
|
|
89
|
-
registerBundleLegacy(bundle) {
|
|
90
|
-
const { components, metadata } = bundle;
|
|
91
|
-
|
|
92
|
-
console.log(`📦 Registering bundle: ${metadata.type} (${metadata.componentCount} components)`);
|
|
93
|
-
|
|
94
|
-
// Phase 1: Register templates and CSS for all components first
|
|
95
|
-
for (const [componentName, componentData] of Object.entries(components)) {
|
|
96
|
-
try {
|
|
97
|
-
// Register HTML template
|
|
98
|
-
if (componentData.html !== undefined && !this.templates.has(componentName)) {
|
|
99
|
-
const template = document.createElement('template');
|
|
100
|
-
template.innerHTML = componentData.html || '';
|
|
101
|
-
this.templates.set(componentName, template);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Register CSS styles
|
|
105
|
-
if (componentData.css !== undefined && !this.requestedStyles.has(componentName)) {
|
|
106
|
-
// Use the existing stylesManager to register component styles
|
|
107
|
-
if (window.slice && window.slice.stylesManager) {
|
|
108
|
-
window.slice.stylesManager.registerComponentStyles(componentName, componentData.css || '');
|
|
109
|
-
this.requestedStyles.add(componentName);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.warn(`❌ Failed to register assets for ${componentName}:`, error);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Phase 2: Evaluate all external file dependencies
|
|
118
|
-
const processedDeps = new Set();
|
|
119
|
-
for (const [componentName, componentData] of Object.entries(components)) {
|
|
120
|
-
if (componentData.dependencies) {
|
|
121
|
-
for (const [depName, depContent] of Object.entries(componentData.dependencies)) {
|
|
122
|
-
if (!processedDeps.has(depName)) {
|
|
123
|
-
try {
|
|
124
|
-
// Convert ES6 exports to global assignments
|
|
125
|
-
let processedContent = depContent
|
|
126
|
-
.replace(/export\s+const\s+(\w+)\s*=/g, 'window.$1 =')
|
|
127
|
-
.replace(/export\s+let\s+(\w+)\s*=/g, 'window.$1 =')
|
|
128
|
-
.replace(/export\s+var\s+(\w+)\s*=/g, 'window.$1 =')
|
|
129
|
-
.replace(/export\s+function\s+(\w+)/g, 'window.$1 = function')
|
|
130
|
-
.replace(/export\s+default\s+/g, 'window.defaultExport =')
|
|
131
|
-
.replace(/export\s*{\s*([^}]+)\s*}/g, (match, exports) => {
|
|
132
|
-
return exports
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
//
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
component
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if (
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
)
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
return
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
*
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
this.
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
if (
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
//
|
|
819
|
-
if (component.
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
*
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1
|
+
import components from '/Components/components.js';
|
|
2
|
+
|
|
3
|
+
export default class Controller {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.componentCategories = new Map(Object.entries(components));
|
|
6
|
+
this.templates = new Map();
|
|
7
|
+
this.classes = new Map();
|
|
8
|
+
this.requestedStyles = new Set(); // ✅ CRÍTICO: Para tracking de CSS cargados
|
|
9
|
+
this.activeComponents = new Map();
|
|
10
|
+
|
|
11
|
+
// 🚀 OPTIMIZACIÓN: Índice inverso para búsqueda rápida de hijos
|
|
12
|
+
// parentSliceId → Set<childSliceId>
|
|
13
|
+
this.childrenIndex = new Map();
|
|
14
|
+
|
|
15
|
+
// 📦 Bundle system
|
|
16
|
+
this.loadedBundles = new Set();
|
|
17
|
+
this.bundleConfig = null;
|
|
18
|
+
this.criticalBundleLoaded = false;
|
|
19
|
+
|
|
20
|
+
this.idCounter = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 📦 Initializes bundle system (called automatically when config is loaded)
|
|
25
|
+
*/
|
|
26
|
+
initializeBundles(config = null) {
|
|
27
|
+
if (config) {
|
|
28
|
+
this.bundleConfig = config;
|
|
29
|
+
|
|
30
|
+
// Register critical bundle components if available
|
|
31
|
+
if (config.bundles?.critical) {
|
|
32
|
+
// The critical bundle should already be loaded, register its components
|
|
33
|
+
this.loadedBundles.add('critical');
|
|
34
|
+
// Note: Critical bundle registration is handled by the auto-import
|
|
35
|
+
}
|
|
36
|
+
this.criticalBundleLoaded = true;
|
|
37
|
+
} else {
|
|
38
|
+
// No bundles available, will use individual component loading
|
|
39
|
+
this.bundleConfig = null;
|
|
40
|
+
this.criticalBundleLoaded = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 📦 Loads a bundle by name or category
|
|
46
|
+
*/
|
|
47
|
+
async loadBundle(bundleName) {
|
|
48
|
+
if (this.loadedBundles.has(bundleName)) {
|
|
49
|
+
return; // Already loaded
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
let bundleInfo = this.bundleConfig?.bundles?.routes?.[bundleName];
|
|
54
|
+
|
|
55
|
+
if (!bundleInfo && this.bundleConfig?.bundles?.routes) {
|
|
56
|
+
const normalizedName = bundleName?.toLowerCase();
|
|
57
|
+
const matchedKey = Object.keys(this.bundleConfig.bundles.routes).find(
|
|
58
|
+
(key) => key.toLowerCase() === normalizedName
|
|
59
|
+
);
|
|
60
|
+
if (matchedKey) {
|
|
61
|
+
bundleInfo = this.bundleConfig.bundles.routes[matchedKey];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!bundleInfo) {
|
|
66
|
+
console.warn(`Bundle ${bundleName} not found in configuration`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const bundlePath = `/bundles/${bundleInfo.file}`;
|
|
71
|
+
|
|
72
|
+
// Dynamic import of the bundle
|
|
73
|
+
const bundleModule = await import(bundlePath);
|
|
74
|
+
|
|
75
|
+
// Manually register components from the imported bundle
|
|
76
|
+
if (bundleModule.SLICE_BUNDLE) {
|
|
77
|
+
this.registerBundle(bundleModule.SLICE_BUNDLE);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.loadedBundles.add(bundleName);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.warn(`Failed to load bundle ${bundleName}:`, error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 📦 Registers a bundle's components (called automatically by bundle files)
|
|
88
|
+
*/
|
|
89
|
+
registerBundleLegacy(bundle) {
|
|
90
|
+
const { components, metadata } = bundle;
|
|
91
|
+
|
|
92
|
+
console.log(`📦 Registering bundle: ${metadata.type} (${metadata.componentCount} components)`);
|
|
93
|
+
|
|
94
|
+
// Phase 1: Register templates and CSS for all components first
|
|
95
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
96
|
+
try {
|
|
97
|
+
// Register HTML template
|
|
98
|
+
if (componentData.html !== undefined && !this.templates.has(componentName)) {
|
|
99
|
+
const template = document.createElement('template');
|
|
100
|
+
template.innerHTML = componentData.html || '';
|
|
101
|
+
this.templates.set(componentName, template);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Register CSS styles
|
|
105
|
+
if (componentData.css !== undefined && !this.requestedStyles.has(componentName)) {
|
|
106
|
+
// Use the existing stylesManager to register component styles
|
|
107
|
+
if (window.slice && window.slice.stylesManager) {
|
|
108
|
+
window.slice.stylesManager.registerComponentStyles(componentName, componentData.css || '');
|
|
109
|
+
this.requestedStyles.add(componentName);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.warn(`❌ Failed to register assets for ${componentName}:`, error);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Phase 2: Evaluate all external file dependencies
|
|
118
|
+
const processedDeps = new Set();
|
|
119
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
120
|
+
if (componentData.dependencies) {
|
|
121
|
+
for (const [depName, depContent] of Object.entries(componentData.dependencies)) {
|
|
122
|
+
if (!processedDeps.has(depName)) {
|
|
123
|
+
try {
|
|
124
|
+
// Convert ES6 exports to global assignments
|
|
125
|
+
let processedContent = depContent
|
|
126
|
+
.replace(/export\s+const\s+(\w+)\s*=/g, 'window.$1 =')
|
|
127
|
+
.replace(/export\s+let\s+(\w+)\s*=/g, 'window.$1 =')
|
|
128
|
+
.replace(/export\s+var\s+(\w+)\s*=/g, 'window.$1 =')
|
|
129
|
+
.replace(/export\s+function\s+(\w+)/g, 'window.$1 = function')
|
|
130
|
+
.replace(/export\s+default\s+/g, 'window.defaultExport =')
|
|
131
|
+
.replace(/export\s*{\s*([^}]+)\s*}/g, (match, exports) => {
|
|
132
|
+
return exports
|
|
133
|
+
.split(',')
|
|
134
|
+
.map((exp) => {
|
|
135
|
+
const cleanExp = exp.trim();
|
|
136
|
+
const varName = cleanExp.split(' as ')[0].trim();
|
|
137
|
+
return `window.${varName} = ${varName};`;
|
|
138
|
+
})
|
|
139
|
+
.join('\n');
|
|
140
|
+
})
|
|
141
|
+
// Remove any remaining export keywords
|
|
142
|
+
.replace(/^\s*export\s+/gm, '');
|
|
143
|
+
|
|
144
|
+
// Evaluate the dependency
|
|
145
|
+
try {
|
|
146
|
+
new Function('slice', 'customElements', 'window', 'document', processedContent)(
|
|
147
|
+
window.slice,
|
|
148
|
+
window.customElements,
|
|
149
|
+
window,
|
|
150
|
+
window.document
|
|
151
|
+
);
|
|
152
|
+
} catch (evalError) {
|
|
153
|
+
console.warn(`❌ Failed to evaluate processed dependency ${depName}:`, evalError);
|
|
154
|
+
console.warn('Processed content preview:', processedContent.substring(0, 200));
|
|
155
|
+
// Try evaluating the original content as fallback
|
|
156
|
+
try {
|
|
157
|
+
new Function('slice', 'customElements', 'window', 'document', depContent)(
|
|
158
|
+
window.slice,
|
|
159
|
+
window.customElements,
|
|
160
|
+
window,
|
|
161
|
+
window.document
|
|
162
|
+
);
|
|
163
|
+
console.log(`✅ Fallback evaluation succeeded for ${depName}`);
|
|
164
|
+
} catch (fallbackError) {
|
|
165
|
+
console.warn(`❌ Fallback evaluation also failed for ${depName}:`, fallbackError);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
processedDeps.add(depName);
|
|
170
|
+
console.log(`📄 Dependency loaded: ${depName}`);
|
|
171
|
+
} catch (depError) {
|
|
172
|
+
console.warn(`⚠️ Failed to load dependency ${depName} for ${componentName}:`, depError);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Phase 3: Evaluate all component classes (now that dependencies are available)
|
|
180
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
181
|
+
// For JavaScript classes, we need to evaluate the code
|
|
182
|
+
if (componentData.js && !this.classes.has(componentName)) {
|
|
183
|
+
try {
|
|
184
|
+
// Create evaluation context with dependencies
|
|
185
|
+
let evalCode = componentData.js;
|
|
186
|
+
|
|
187
|
+
// Prepend dependencies to make them available
|
|
188
|
+
if (componentData.dependencies) {
|
|
189
|
+
const depCode = Object.entries(componentData.dependencies)
|
|
190
|
+
.map(([depName, depContent]) => {
|
|
191
|
+
// Convert ES6 exports to global assignments
|
|
192
|
+
return depContent
|
|
193
|
+
.replace(/export\s+const\s+(\w+)\s*=/g, 'window.$1 =')
|
|
194
|
+
.replace(/export\s+let\s+(\w+)\s*=/g, 'window.$1 =')
|
|
195
|
+
.replace(/export\s+function\s+(\w+)/g, 'window.$1 = function')
|
|
196
|
+
.replace(/export\s+default\s+/g, 'window.defaultExport =')
|
|
197
|
+
.replace(/export\s*{\s*([^}]+)\s*}/g, (match, exports) => {
|
|
198
|
+
return exports
|
|
199
|
+
.split(',')
|
|
200
|
+
.map((exp) => {
|
|
201
|
+
const cleanExp = exp.trim();
|
|
202
|
+
return `window.${cleanExp} = ${cleanExp};`;
|
|
203
|
+
})
|
|
204
|
+
.join('\n');
|
|
205
|
+
});
|
|
206
|
+
})
|
|
207
|
+
.join('\n\n');
|
|
208
|
+
|
|
209
|
+
evalCode = depCode + '\n\n' + evalCode;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Evaluate the complete code
|
|
213
|
+
const componentClass = new Function(
|
|
214
|
+
'slice',
|
|
215
|
+
'customElements',
|
|
216
|
+
'window',
|
|
217
|
+
'document',
|
|
218
|
+
`
|
|
219
|
+
"use strict";
|
|
220
|
+
${evalCode}
|
|
221
|
+
return ${componentName};
|
|
222
|
+
`
|
|
223
|
+
)(window.slice, window.customElements, window, window.document);
|
|
224
|
+
|
|
225
|
+
if (componentClass) {
|
|
226
|
+
this.classes.set(componentName, componentClass);
|
|
227
|
+
console.log(`📝 Class registered for: ${componentName}`);
|
|
228
|
+
}
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.warn(`❌ Failed to evaluate class for ${componentName}:`, error);
|
|
231
|
+
console.warn('Code that failed:', componentData.js.substring(0, 200) + '...');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* 📦 New bundle registration method (simplified and robust)
|
|
239
|
+
*/
|
|
240
|
+
registerBundle(bundle) {
|
|
241
|
+
const { components, metadata } = bundle;
|
|
242
|
+
|
|
243
|
+
console.log(`📦 Registering bundle: ${metadata.type} (${metadata.componentCount} components)`);
|
|
244
|
+
|
|
245
|
+
// Phase 1: Register all templates and CSS first
|
|
246
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
247
|
+
try {
|
|
248
|
+
if (componentData.html !== undefined && !this.templates.has(componentName)) {
|
|
249
|
+
const template = document.createElement('template');
|
|
250
|
+
template.innerHTML = componentData.html || '';
|
|
251
|
+
this.templates.set(componentName, template);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (componentData.css !== undefined && !this.requestedStyles.has(componentName)) {
|
|
255
|
+
if (window.slice && window.slice.stylesManager) {
|
|
256
|
+
window.slice.stylesManager.registerComponentStyles(componentName, componentData.css || '');
|
|
257
|
+
this.requestedStyles.add(componentName);
|
|
258
|
+
console.log(`🎨 CSS registered for: ${componentName}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.warn(`❌ Failed to register assets for ${componentName}:`, error);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Phase 2: Evaluate all external file dependencies first
|
|
267
|
+
const processedDeps = new Set();
|
|
268
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
269
|
+
if (componentData.externalDependencies) {
|
|
270
|
+
for (const [depName, depEntry] of Object.entries(componentData.externalDependencies)) {
|
|
271
|
+
const depKey = depName || '';
|
|
272
|
+
if (!processedDeps.has(depKey)) {
|
|
273
|
+
try {
|
|
274
|
+
const depContent = typeof depEntry === 'string' ? depEntry : depEntry.content;
|
|
275
|
+
const bindings = typeof depEntry === 'string' ? [] : depEntry.bindings || [];
|
|
276
|
+
|
|
277
|
+
const fileBaseName = depKey
|
|
278
|
+
? depKey
|
|
279
|
+
.split('/')
|
|
280
|
+
.pop()
|
|
281
|
+
.replace(/\.[^.]+$/, '')
|
|
282
|
+
: '';
|
|
283
|
+
const dataName = fileBaseName ? `${fileBaseName}Data` : '';
|
|
284
|
+
const exportPrefix = dataName ? `window.${dataName} = ` : '';
|
|
285
|
+
|
|
286
|
+
// Process ES6 exports to make the code evaluable
|
|
287
|
+
let processedContent = depContent
|
|
288
|
+
// Convert named exports: export const varName = ... → window.varName = ...
|
|
289
|
+
.replace(/export\s+const\s+(\w+)\s*=\s*/g, 'window.$1 = ')
|
|
290
|
+
.replace(/export\s+let\s+(\w+)\s*=\s*/g, 'window.$1 = ')
|
|
291
|
+
.replace(/export\s+function\s+(\w+)/g, 'window.$1 = function')
|
|
292
|
+
.replace(/export\s+default\s+/g, 'window.defaultExport = ')
|
|
293
|
+
// Promote default export to <file>Data for data modules
|
|
294
|
+
.replace(/window\.defaultExport\s*=\s*/g, exportPrefix || 'window.defaultExport = ')
|
|
295
|
+
// Handle export { var1, var2 } statements
|
|
296
|
+
.replace(/export\s*{\s*([^}]+)\s*}/g, (match, exportsStr) => {
|
|
297
|
+
const exports = exportsStr.split(',').map((exp) => exp.trim().split(' as ')[0].trim());
|
|
298
|
+
return exports.map((varName) => `window.${varName} = ${varName};`).join('\n');
|
|
299
|
+
})
|
|
300
|
+
// Remove any remaining export keywords
|
|
301
|
+
.replace(/^\s*export\s+/gm, '');
|
|
302
|
+
|
|
303
|
+
// Evaluate the processed content
|
|
304
|
+
new Function('slice', 'customElements', 'window', 'document', processedContent)(
|
|
305
|
+
window.slice,
|
|
306
|
+
window.customElements,
|
|
307
|
+
window,
|
|
308
|
+
window.document
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Apply import bindings to map local identifiers to globals
|
|
312
|
+
for (const binding of bindings) {
|
|
313
|
+
if (!binding?.localName) continue;
|
|
314
|
+
|
|
315
|
+
if (binding.type === 'default') {
|
|
316
|
+
if (!window[binding.localName]) {
|
|
317
|
+
const fallbackValue =
|
|
318
|
+
dataName && window[dataName] !== undefined ? window[dataName] : window.defaultExport;
|
|
319
|
+
if (fallbackValue !== undefined) {
|
|
320
|
+
window[binding.localName] = fallbackValue;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (binding.type === 'named') {
|
|
326
|
+
if (!window[binding.localName] && window[binding.importedName] !== undefined) {
|
|
327
|
+
window[binding.localName] = window[binding.importedName];
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (binding.type === 'namespace' && !window[binding.localName]) {
|
|
332
|
+
const namespace = {};
|
|
333
|
+
Object.keys(window).forEach((key) => {
|
|
334
|
+
namespace[key] = window[key];
|
|
335
|
+
});
|
|
336
|
+
window[binding.localName] = namespace;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
processedDeps.add(depKey);
|
|
341
|
+
console.log(`📄 External dependency loaded: ${depName}`);
|
|
342
|
+
} catch (depError) {
|
|
343
|
+
console.warn(`⚠️ Failed to load external dependency ${depName}:`, depError);
|
|
344
|
+
const preview = typeof depEntry === 'string' ? depEntry : depEntry.content;
|
|
345
|
+
console.warn('Original content preview:', preview.substring(0, 200));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Phase 3: Evaluate all component classes (external dependencies are now available)
|
|
353
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
354
|
+
if (componentData.js && !this.classes.has(componentName)) {
|
|
355
|
+
try {
|
|
356
|
+
// Simple evaluation
|
|
357
|
+
const componentClass = new Function(
|
|
358
|
+
'slice',
|
|
359
|
+
'customElements',
|
|
360
|
+
'window',
|
|
361
|
+
'document',
|
|
362
|
+
`
|
|
363
|
+
${componentData.js}
|
|
364
|
+
return ${componentName};
|
|
365
|
+
`
|
|
366
|
+
)(window.slice, window.customElements, window, window.document);
|
|
367
|
+
|
|
368
|
+
if (componentClass) {
|
|
369
|
+
this.classes.set(componentName, componentClass);
|
|
370
|
+
console.log(`📝 Class registered for: ${componentName}`);
|
|
371
|
+
}
|
|
372
|
+
} catch (error) {
|
|
373
|
+
console.warn(`❌ Failed to evaluate class for ${componentName}:`, error);
|
|
374
|
+
// Continue with other components instead of failing completely
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
console.log(`✅ Bundle registration completed: ${metadata.componentCount} components processed`);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* 📦 Determines which bundle to load for a component
|
|
384
|
+
*/
|
|
385
|
+
getBundleForComponent(componentName) {
|
|
386
|
+
if (!this.bundleConfig?.bundles) return null;
|
|
387
|
+
|
|
388
|
+
// Check if component is in critical bundle
|
|
389
|
+
if (this.bundleConfig.bundles.critical?.components?.includes(componentName)) {
|
|
390
|
+
return 'critical';
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Find component in route bundles
|
|
394
|
+
if (this.bundleConfig.bundles.routes) {
|
|
395
|
+
for (const [bundleName, bundleInfo] of Object.entries(this.bundleConfig.bundles.routes)) {
|
|
396
|
+
if (bundleInfo.components?.includes(componentName)) {
|
|
397
|
+
return bundleName;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 📦 Checks if a component is available from loaded bundles
|
|
407
|
+
*/
|
|
408
|
+
isComponentFromBundle(componentName) {
|
|
409
|
+
if (!this.bundleConfig?.bundles) return false;
|
|
410
|
+
|
|
411
|
+
// Check critical bundle
|
|
412
|
+
if (this.bundleConfig.bundles.critical?.components?.includes(componentName)) {
|
|
413
|
+
return this.criticalBundleLoaded;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Check route bundles
|
|
417
|
+
if (this.bundleConfig.bundles.routes) {
|
|
418
|
+
for (const [bundleName, bundleInfo] of Object.entries(this.bundleConfig.bundles.routes)) {
|
|
419
|
+
if (bundleInfo.components?.includes(componentName)) {
|
|
420
|
+
return this.loadedBundles.has(bundleName);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* 📦 Gets component data from loaded bundles
|
|
430
|
+
*/
|
|
431
|
+
getComponentFromBundle(componentName) {
|
|
432
|
+
if (!this.bundleConfig?.bundles) return null;
|
|
433
|
+
|
|
434
|
+
// Find component in any loaded bundle
|
|
435
|
+
const allBundles = [
|
|
436
|
+
{ name: 'critical', data: this.bundleConfig.bundles.critical },
|
|
437
|
+
...Object.entries(this.bundleConfig.bundles.routes || {}).map(([name, data]) => ({ name, data })),
|
|
438
|
+
];
|
|
439
|
+
|
|
440
|
+
for (const { name: bundleName, data: bundleData } of allBundles) {
|
|
441
|
+
if (bundleData?.components?.includes(componentName) && this.loadedBundles.has(bundleName)) {
|
|
442
|
+
// Find the bundle file and extract component data
|
|
443
|
+
// This is a simplified version - in practice you'd need to access the loaded bundle data
|
|
444
|
+
return { bundleName, componentName };
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
logActiveComponents() {
|
|
452
|
+
this.activeComponents.forEach((component) => {
|
|
453
|
+
let parent = component.parentComponent;
|
|
454
|
+
let parentName = parent ? parent.constructor.name : null;
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
getTopParentsLinkedToActiveComponents() {
|
|
459
|
+
let topParentsLinkedToActiveComponents = new Map();
|
|
460
|
+
this.activeComponents.forEach((component) => {
|
|
461
|
+
let parent = component.parentComponent;
|
|
462
|
+
while (parent && parent.parentComponent) {
|
|
463
|
+
parent = parent.parentComponent;
|
|
464
|
+
}
|
|
465
|
+
if (!topParentsLinkedToActiveComponents.has(parent)) {
|
|
466
|
+
topParentsLinkedToActiveComponents.set(parent, []);
|
|
467
|
+
}
|
|
468
|
+
topParentsLinkedToActiveComponents.get(parent).push(component);
|
|
469
|
+
});
|
|
470
|
+
return topParentsLinkedToActiveComponents;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
verifyComponentIds(component) {
|
|
474
|
+
const htmlId = component.id;
|
|
475
|
+
|
|
476
|
+
if (htmlId && htmlId.trim() !== '') {
|
|
477
|
+
if (this.activeComponents.has(htmlId)) {
|
|
478
|
+
slice.logger.logError(
|
|
479
|
+
'Controller',
|
|
480
|
+
`A component with the same html id attribute is already registered: ${htmlId}`
|
|
481
|
+
);
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
let sliceId = component.sliceId;
|
|
487
|
+
|
|
488
|
+
if (sliceId && sliceId.trim() !== '') {
|
|
489
|
+
if (this.activeComponents.has(sliceId)) {
|
|
490
|
+
slice.logger.logError(
|
|
491
|
+
'Controller',
|
|
492
|
+
`A component with the same slice id attribute is already registered: ${sliceId}`
|
|
493
|
+
);
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
} else {
|
|
497
|
+
sliceId = `${component.constructor.name[0].toLowerCase()}${component.constructor.name.slice(1)}-${this.idCounter}`;
|
|
498
|
+
component.sliceId = sliceId;
|
|
499
|
+
this.idCounter++;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
component.sliceId = sliceId;
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Registra un componente y actualiza el índice de relaciones padre-hijo
|
|
508
|
+
* 🚀 OPTIMIZADO: Ahora mantiene childrenIndex y precalcula profundidad
|
|
509
|
+
*/
|
|
510
|
+
registerComponent(component, parent = null) {
|
|
511
|
+
component.parentComponent = parent;
|
|
512
|
+
|
|
513
|
+
// 🚀 OPTIMIZACIÓN: Precalcular y guardar profundidad
|
|
514
|
+
component._depth = parent ? (parent._depth || 0) + 1 : 0;
|
|
515
|
+
|
|
516
|
+
// Registrar en activeComponents
|
|
517
|
+
this.activeComponents.set(component.sliceId, component);
|
|
518
|
+
|
|
519
|
+
// 🚀 OPTIMIZACIÓN: Actualizar índice inverso de hijos
|
|
520
|
+
if (parent) {
|
|
521
|
+
if (!this.childrenIndex.has(parent.sliceId)) {
|
|
522
|
+
this.childrenIndex.set(parent.sliceId, new Set());
|
|
523
|
+
}
|
|
524
|
+
this.childrenIndex.get(parent.sliceId).add(component.sliceId);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return true;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
registerComponentsRecursively(component, parent = null) {
|
|
531
|
+
// Assign parent if not already set
|
|
532
|
+
if (!component.parentComponent) {
|
|
533
|
+
component.parentComponent = parent;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Recursively assign parent to children
|
|
537
|
+
component.querySelectorAll('*').forEach((child) => {
|
|
538
|
+
if (child.tagName.startsWith('SLICE-')) {
|
|
539
|
+
if (!child.parentComponent) {
|
|
540
|
+
child.parentComponent = component;
|
|
541
|
+
}
|
|
542
|
+
this.registerComponentsRecursively(child, component);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
getComponent(sliceId) {
|
|
548
|
+
return this.activeComponents.get(sliceId);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
loadTemplateToComponent(component) {
|
|
552
|
+
const className = component.constructor.name;
|
|
553
|
+
const template = this.templates.get(className);
|
|
554
|
+
|
|
555
|
+
if (!template) {
|
|
556
|
+
slice.logger.logError(`Template not found for component: ${className}`);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
component.innerHTML = template.innerHTML;
|
|
561
|
+
return component;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
getComponentCategory(componentSliceId) {
|
|
565
|
+
return this.componentCategories.get(componentSliceId);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async fetchText(componentName, resourceType, componentCategory, customPath) {
|
|
569
|
+
try {
|
|
570
|
+
const baseUrl = window.location.origin;
|
|
571
|
+
let path;
|
|
572
|
+
|
|
573
|
+
if (!componentCategory) {
|
|
574
|
+
componentCategory = this.componentCategories.get(componentName);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
let isVisual = resourceType === 'html' || resourceType === 'css';
|
|
578
|
+
|
|
579
|
+
if (isVisual) {
|
|
580
|
+
if (slice.paths.components[componentCategory]) {
|
|
581
|
+
path = `${baseUrl}${slice.paths.components[componentCategory].path}/${componentName}`;
|
|
582
|
+
resourceType === 'html' ? (path += `/${componentName}.html`) : (path += `/${componentName}.css`);
|
|
583
|
+
} else {
|
|
584
|
+
if (componentCategory === 'Structural') {
|
|
585
|
+
path = `${baseUrl}/Slice/Components/Structural/${componentName}`;
|
|
586
|
+
resourceType === 'html' ? (path += `/${componentName}.html`) : (path += `/${componentName}.css`);
|
|
587
|
+
} else {
|
|
588
|
+
throw new Error(`Component category '${componentCategory}' not found in paths configuration`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (resourceType === 'theme') {
|
|
594
|
+
path = `${baseUrl}${slice.paths.themes}/${componentName}.css`;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (resourceType === 'styles') {
|
|
598
|
+
path = `${baseUrl}${slice.paths.styles}/${componentName}.css`;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (customPath) {
|
|
602
|
+
path = customPath;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
slice.logger.logInfo('Controller', `Fetching ${resourceType} from: ${path}`);
|
|
606
|
+
|
|
607
|
+
const response = await fetch(path);
|
|
608
|
+
|
|
609
|
+
if (!response.ok) {
|
|
610
|
+
throw new Error(`Failed to fetch ${path}: ${response.statusText}`);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const content = await response.text();
|
|
614
|
+
slice.logger.logInfo('Controller', `Successfully fetched ${resourceType} for ${componentName}`);
|
|
615
|
+
return content;
|
|
616
|
+
} catch (error) {
|
|
617
|
+
slice.logger.logError('Controller', `Error fetching ${resourceType} for component ${componentName}:`, error);
|
|
618
|
+
throw error;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
setComponentProps(component, props) {
|
|
623
|
+
const ComponentClass = component.constructor;
|
|
624
|
+
const componentName = ComponentClass.name;
|
|
625
|
+
|
|
626
|
+
// Aplicar defaults si tiene static props
|
|
627
|
+
if (ComponentClass.props) {
|
|
628
|
+
this.applyDefaultProps(component, ComponentClass.props, props);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Validar solo en desarrollo
|
|
632
|
+
if (ComponentClass.props && !slice.isProduction()) {
|
|
633
|
+
this.validatePropsInDevelopment(ComponentClass, props, componentName);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Aplicar props
|
|
637
|
+
for (const prop in props) {
|
|
638
|
+
component[`_${prop}`] = null;
|
|
639
|
+
component[prop] = props[prop];
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
getComponentPropsForDebugger(component) {
|
|
644
|
+
const ComponentClass = component.constructor;
|
|
645
|
+
|
|
646
|
+
if (ComponentClass.props) {
|
|
647
|
+
return {
|
|
648
|
+
availableProps: Object.keys(ComponentClass.props),
|
|
649
|
+
propsConfig: ComponentClass.props,
|
|
650
|
+
usedProps: this.extractUsedProps(component, ComponentClass.props),
|
|
651
|
+
};
|
|
652
|
+
} else {
|
|
653
|
+
return {
|
|
654
|
+
availableProps: this.extractUsedProps(component),
|
|
655
|
+
propsConfig: null,
|
|
656
|
+
usedProps: this.extractUsedProps(component),
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
applyDefaultProps(component, staticProps, providedProps) {
|
|
662
|
+
Object.entries(staticProps).forEach(([prop, config]) => {
|
|
663
|
+
if (config.default !== undefined && !(prop in (providedProps || {}))) {
|
|
664
|
+
component[`_${prop}`] = null;
|
|
665
|
+
component[prop] = config.default;
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
validatePropsInDevelopment(ComponentClass, providedProps, componentName) {
|
|
671
|
+
const staticProps = ComponentClass.props;
|
|
672
|
+
const usedProps = Object.keys(providedProps || {});
|
|
673
|
+
|
|
674
|
+
const availableProps = Object.keys(staticProps);
|
|
675
|
+
const unknownProps = usedProps.filter((prop) => !availableProps.includes(prop));
|
|
676
|
+
|
|
677
|
+
if (unknownProps.length > 0) {
|
|
678
|
+
slice.logger.logWarning(
|
|
679
|
+
'PropsValidator',
|
|
680
|
+
`${componentName}: Unknown props [${unknownProps.join(', ')}]. Available: [${availableProps.join(', ')}]`
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const requiredProps = Object.entries(staticProps)
|
|
685
|
+
.filter(([_, config]) => config.required)
|
|
686
|
+
.map(([prop, _]) => prop);
|
|
687
|
+
|
|
688
|
+
const missingRequired = requiredProps.filter((prop) => !(prop in (providedProps || {})));
|
|
689
|
+
if (missingRequired.length > 0) {
|
|
690
|
+
slice.logger.logError(componentName, `Missing required props: [${missingRequired.join(', ')}]`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
extractUsedProps(component, staticProps = null) {
|
|
695
|
+
const usedProps = {};
|
|
696
|
+
|
|
697
|
+
if (staticProps) {
|
|
698
|
+
Object.keys(staticProps).forEach((prop) => {
|
|
699
|
+
if (component[prop] !== undefined) {
|
|
700
|
+
usedProps[prop] = component[prop];
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
} else {
|
|
704
|
+
Object.getOwnPropertyNames(component).forEach((key) => {
|
|
705
|
+
if (key.startsWith('_') && key !== '_isActive') {
|
|
706
|
+
const propName = key.substring(1);
|
|
707
|
+
usedProps[propName] = component[propName];
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return usedProps;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// ============================================================================
|
|
716
|
+
// 🚀 MÉTODOS DE DESTRUCCIÓN OPTIMIZADOS
|
|
717
|
+
// ============================================================================
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Encuentra recursivamente todos los hijos de un componente
|
|
721
|
+
* 🚀 OPTIMIZADO: O(m) en lugar de O(n*d) - usa childrenIndex
|
|
722
|
+
* @param {string} parentSliceId - sliceId del componente padre
|
|
723
|
+
* @param {Set<string>} collected - Set de sliceIds ya recolectados
|
|
724
|
+
* @returns {Set<string>} Set de todos los sliceIds de componentes hijos
|
|
725
|
+
*/
|
|
726
|
+
findAllChildComponents(parentSliceId, collected = new Set()) {
|
|
727
|
+
// 🚀 Buscar directamente en el índice: O(1)
|
|
728
|
+
const children = this.childrenIndex.get(parentSliceId);
|
|
729
|
+
|
|
730
|
+
if (!children) return collected;
|
|
731
|
+
|
|
732
|
+
// 🚀 Iterar solo los hijos directos: O(k) donde k = número de hijos
|
|
733
|
+
for (const childSliceId of children) {
|
|
734
|
+
collected.add(childSliceId);
|
|
735
|
+
// Recursión solo sobre hijos, no todos los componentes
|
|
736
|
+
this.findAllChildComponents(childSliceId, collected);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return collected;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Encuentra recursivamente todos los componentes dentro de un contenedor DOM
|
|
744
|
+
* Útil para destroyByContainer cuando no tenemos el sliceId del padre
|
|
745
|
+
* @param {HTMLElement} container - Elemento contenedor
|
|
746
|
+
* @param {Set<string>} collected - Set de sliceIds ya recolectados
|
|
747
|
+
* @returns {Set<string>} Set de todos los sliceIds encontrados
|
|
748
|
+
*/
|
|
749
|
+
findAllNestedComponentsInContainer(container, collected = new Set()) {
|
|
750
|
+
// Buscar todos los elementos con slice-id en el contenedor
|
|
751
|
+
const sliceComponents = container.querySelectorAll('[slice-id]');
|
|
752
|
+
|
|
753
|
+
sliceComponents.forEach((element) => {
|
|
754
|
+
const sliceId = element.getAttribute('slice-id') || element.sliceId;
|
|
755
|
+
if (sliceId && this.activeComponents.has(sliceId)) {
|
|
756
|
+
collected.add(sliceId);
|
|
757
|
+
// 🚀 Usar índice para buscar hijos recursivamente
|
|
758
|
+
this.findAllChildComponents(sliceId, collected);
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
return collected;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Destruye uno o múltiples componentes DE FORMA RECURSIVA
|
|
767
|
+
* 🚀 OPTIMIZADO: O(m log m) en lugar de O(n*d + m log m)
|
|
768
|
+
* @param {HTMLElement|Array<HTMLElement>|string|Array<string>} components
|
|
769
|
+
* @returns {number} Cantidad de componentes destruidos (incluyendo hijos)
|
|
770
|
+
*/
|
|
771
|
+
destroyComponent(components) {
|
|
772
|
+
const toDestroy = Array.isArray(components) ? components : [components];
|
|
773
|
+
const allSliceIdsToDestroy = new Set();
|
|
774
|
+
|
|
775
|
+
// PASO 1: Recolectar todos los componentes padres y sus hijos recursivamente
|
|
776
|
+
for (const item of toDestroy) {
|
|
777
|
+
let sliceId = null;
|
|
778
|
+
|
|
779
|
+
if (typeof item === 'string') {
|
|
780
|
+
if (!this.activeComponents.has(item)) {
|
|
781
|
+
slice.logger.logWarning('Controller', `Component with sliceId "${item}" not found`);
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
sliceId = item;
|
|
785
|
+
} else if (item && item.sliceId) {
|
|
786
|
+
sliceId = item.sliceId;
|
|
787
|
+
} else {
|
|
788
|
+
slice.logger.logWarning('Controller', `Invalid component or sliceId provided to destroyComponent`);
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
allSliceIdsToDestroy.add(sliceId);
|
|
793
|
+
|
|
794
|
+
// 🚀 OPTIMIZADO: Usa childrenIndex en lugar de recorrer todos los componentes
|
|
795
|
+
this.findAllChildComponents(sliceId, allSliceIdsToDestroy);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// PASO 2: Ordenar por profundidad (más profundos primero)
|
|
799
|
+
// 🚀 OPTIMIZADO: Usa _depth precalculada en lugar de calcularla cada vez
|
|
800
|
+
const sortedSliceIds = Array.from(allSliceIdsToDestroy).sort((a, b) => {
|
|
801
|
+
const compA = this.activeComponents.get(a);
|
|
802
|
+
const compB = this.activeComponents.get(b);
|
|
803
|
+
|
|
804
|
+
if (!compA || !compB) return 0;
|
|
805
|
+
|
|
806
|
+
// 🚀 O(1) en lugar de O(d) - usa profundidad precalculada
|
|
807
|
+
return (compB._depth || 0) - (compA._depth || 0);
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
let destroyedCount = 0;
|
|
811
|
+
|
|
812
|
+
// PASO 3: Destruir en orden correcto (hijos antes que padres)
|
|
813
|
+
for (const sliceId of sortedSliceIds) {
|
|
814
|
+
const component = this.activeComponents.get(sliceId);
|
|
815
|
+
|
|
816
|
+
if (!component) continue;
|
|
817
|
+
|
|
818
|
+
// Ejecutar hook beforeDestroy si existe
|
|
819
|
+
if (typeof component.beforeDestroy === 'function') {
|
|
820
|
+
try {
|
|
821
|
+
component.beforeDestroy();
|
|
822
|
+
} catch (error) {
|
|
823
|
+
slice.logger.logError('Controller', `Error in beforeDestroy for ${sliceId}`, error);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// Limpiar suscripciones de eventos del componente
|
|
828
|
+
if (slice.events) {
|
|
829
|
+
slice.events.cleanupComponent(sliceId);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// 🚀 Limpiar del índice de hijos
|
|
833
|
+
this.childrenIndex.delete(sliceId);
|
|
834
|
+
|
|
835
|
+
// Si tiene padre, remover de la lista de hijos del padre
|
|
836
|
+
if (component.parentComponent) {
|
|
837
|
+
const parentChildren = this.childrenIndex.get(component.parentComponent.sliceId);
|
|
838
|
+
if (parentChildren) {
|
|
839
|
+
parentChildren.delete(sliceId);
|
|
840
|
+
// Si el padre no tiene más hijos, eliminar entrada vacía
|
|
841
|
+
if (parentChildren.size === 0) {
|
|
842
|
+
this.childrenIndex.delete(component.parentComponent.sliceId);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// Eliminar del mapa de componentes activos
|
|
848
|
+
this.activeComponents.delete(sliceId);
|
|
849
|
+
|
|
850
|
+
// Remover del DOM si está conectado
|
|
851
|
+
if (component.isConnected) {
|
|
852
|
+
component.remove();
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
destroyedCount++;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (destroyedCount > 0) {
|
|
859
|
+
slice.logger.logInfo('Controller', `Destroyed ${destroyedCount} component(s) recursively`);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
return destroyedCount;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Destruye todos los componentes Slice dentro de un contenedor (RECURSIVO)
|
|
867
|
+
* 🚀 OPTIMIZADO: Usa el índice inverso para búsqueda rápida
|
|
868
|
+
* @param {HTMLElement} container - Elemento contenedor
|
|
869
|
+
* @returns {number} Cantidad de componentes destruidos
|
|
870
|
+
*/
|
|
871
|
+
destroyByContainer(container) {
|
|
872
|
+
if (!container) {
|
|
873
|
+
slice.logger.logWarning('Controller', 'No container provided to destroyByContainer');
|
|
874
|
+
return 0;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// 🚀 Recolectar componentes usando índice optimizado
|
|
878
|
+
const allSliceIds = this.findAllNestedComponentsInContainer(container);
|
|
879
|
+
|
|
880
|
+
if (allSliceIds.size === 0) {
|
|
881
|
+
return 0;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Destruir usando el método principal optimizado
|
|
885
|
+
const count = this.destroyComponent(Array.from(allSliceIds));
|
|
886
|
+
|
|
887
|
+
if (count > 0) {
|
|
888
|
+
slice.logger.logInfo('Controller', `Destroyed ${count} component(s) from container (including nested)`);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
return count;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Destruye componentes cuyos sliceId coincidan con un patrón (RECURSIVO)
|
|
896
|
+
* 🚀 OPTIMIZADO: Usa destrucción optimizada
|
|
897
|
+
* @param {string|RegExp} pattern - Patrón a buscar
|
|
898
|
+
* @returns {number} Cantidad de componentes destruidos
|
|
899
|
+
*/
|
|
900
|
+
destroyByPattern(pattern) {
|
|
901
|
+
const componentsToDestroy = [];
|
|
902
|
+
const regex = pattern instanceof RegExp ? pattern : new RegExp(pattern);
|
|
903
|
+
|
|
904
|
+
for (const [sliceId, component] of this.activeComponents) {
|
|
905
|
+
if (regex.test(sliceId)) {
|
|
906
|
+
componentsToDestroy.push(component);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if (componentsToDestroy.length === 0) {
|
|
911
|
+
return 0;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const count = this.destroyComponent(componentsToDestroy);
|
|
915
|
+
|
|
916
|
+
if (count > 0) {
|
|
917
|
+
slice.logger.logInfo(
|
|
918
|
+
'Controller',
|
|
919
|
+
`Destroyed ${count} component(s) matching pattern: ${pattern} (including nested)`
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
return count;
|
|
924
|
+
}
|
|
925
|
+
}
|