slicejs-web-framework 2.4.8 → 3.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/.worktrees/bundling-v2-precompiled-registrars/LICENSE +21 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/ContextManager/ContextManager.js +369 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/ContextManager/ContextManagerDebugger.js +297 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/Controller/Controller.js +972 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/Debugger/Debugger.css +620 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/Debugger/Debugger.html +73 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/Debugger/Debugger.js +1548 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/EventManager/EventManager.js +338 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/EventManager/EventManagerDebugger.js +361 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/Logger/Log.js +10 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/Logger/Logger.js +146 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/Router/Router.js +721 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/StylesManager/StylesManager.js +78 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Components/Structural/StylesManager/ThemeManager/ThemeManager.js +84 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/Slice.js +504 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/tests/bundle-v2-runtime-contract.test.js +268 -0
- package/.worktrees/bundling-v2-precompiled-registrars/Slice/tests/router-loading-finally.test.js +68 -0
- package/.worktrees/bundling-v2-precompiled-registrars/api/index.js +286 -0
- package/.worktrees/bundling-v2-precompiled-registrars/api/middleware/securityMiddleware.js +253 -0
- package/.worktrees/bundling-v2-precompiled-registrars/package.json +37 -0
- package/.worktrees/bundling-v2-precompiled-registrars/sliceConfig.schema.json +109 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/App/index.html +22 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/App/index.js +23 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/App/style.css +40 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/AppComponents/HomePage/HomePage.css +201 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/AppComponents/HomePage/HomePage.html +37 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/AppComponents/HomePage/HomePage.js +210 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/AppComponents/Playground/Playground.css +12 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/AppComponents/Playground/Playground.html +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/AppComponents/Playground/Playground.js +111 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Service/FetchManager/FetchManager.js +133 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Service/IndexedDbManager/IndexedDbManager.js +141 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Service/LocalStorageManager/LocalStorageManager.js +45 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Button/Button.css +47 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Button/Button.html +5 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Button/Button.js +93 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Card/Card.css +68 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Card/Card.html +7 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Card/Card.js +107 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Checkbox/Checkbox.css +87 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Checkbox/Checkbox.html +8 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Checkbox/Checkbox.js +86 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/CodeVisualizer/CodeVisualizer.css +130 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/CodeVisualizer/CodeVisualizer.html +4 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/CodeVisualizer/CodeVisualizer.js +262 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Details/Details.css +70 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Details/Details.html +9 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Details/Details.js +76 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/DropDown/DropDown.css +60 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/DropDown/DropDown.html +5 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/DropDown/DropDown.js +63 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Grid/Grid.css +7 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Grid/Grid.html +1 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Grid/Grid.js +57 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/Icon.css +510 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/Icon.html +1 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/Icon.js +89 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/slc.eot +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/slc.json +555 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/slc.styl +507 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/slc.svg +1485 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/slc.symbol.svg +1059 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/slc.ttf +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/slc.woff +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Icon/slc.woff2 +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Input/Input.css +91 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Input/Input.html +4 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Input/Input.js +215 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Layout/Layout.css +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Layout/Layout.html +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Layout/Layout.js +49 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Link/Link.css +8 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Link/Link.html +1 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Link/Link.js +63 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Loading/Loading.css +56 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Loading/Loading.html +83 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Loading/Loading.js +38 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/MultiRoute/MultiRoute.js +93 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Navbar/Navbar.css +115 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Navbar/Navbar.html +44 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Navbar/Navbar.js +141 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/NotFound/NotFound.css +117 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/NotFound/NotFound.html +24 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/NotFound/NotFound.js +16 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Route/Route.js +93 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Select/Select.css +84 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Select/Select.html +8 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Select/Select.js +195 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Switch/Switch.css +76 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Switch/Switch.html +8 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/Switch/Switch.js +102 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/TreeItem/TreeItem.css +36 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/TreeItem/TreeItem.html +1 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/TreeItem/TreeItem.js +126 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/TreeView/TreeView.css +8 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/TreeView/TreeView.html +1 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/Visual/TreeView/TreeView.js +48 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Components/components.js +27 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Styles/sliceStyles.css +34 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Themes/Dark.css +42 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Themes/Light.css +31 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/Themes/Slice.css +47 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/images/Slice.js-logo.png +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/images/favicon.ico +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/images/im2/Slice.js-logo.png +0 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/routes.js +16 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/sliceConfig.json +73 -0
- package/.worktrees/bundling-v2-precompiled-registrars/src/testing.js +888 -0
- package/Slice/Components/Structural/Controller/Controller.js +51 -29
- package/Slice/Components/Structural/Router/Router.js +25 -24
- package/Slice/Slice.js +50 -46
- package/Slice/tests/bundle-v2-runtime-contract.test.js +268 -0
- package/Slice/tests/router-loading-finally.test.js +68 -0
- package/package.json +1 -1
|
@@ -64,6 +64,26 @@ export default class Controller {
|
|
|
64
64
|
return importPromise;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Validate Bundling V2 module contract.
|
|
69
|
+
* Requires named exports: SLICE_BUNDLE_META and registerAll.
|
|
70
|
+
* @param {any} bundleModule
|
|
71
|
+
* @param {string} [bundleName]
|
|
72
|
+
* @returns {{metadata: object, registerAll: Function}}
|
|
73
|
+
*/
|
|
74
|
+
async validateBundleModule(bundleModule, bundleName = 'unknown') {
|
|
75
|
+
const metadata = bundleModule?.SLICE_BUNDLE_META;
|
|
76
|
+
const registerAll = bundleModule?.registerAll;
|
|
77
|
+
|
|
78
|
+
if (!metadata || typeof metadata !== 'object' || typeof registerAll !== 'function') {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Bundle "${bundleName}" missing Bundling V2 exports contract: requires SLICE_BUNDLE_META and registerAll`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { metadata, registerAll };
|
|
85
|
+
}
|
|
86
|
+
|
|
67
87
|
/**
|
|
68
88
|
* 📦 Loads a bundle by name or category
|
|
69
89
|
*/
|
|
@@ -72,44 +92,46 @@ export default class Controller {
|
|
|
72
92
|
return; // Already loaded
|
|
73
93
|
}
|
|
74
94
|
|
|
75
|
-
|
|
76
|
-
let bundleInfo = null;
|
|
95
|
+
let bundleInfo = null;
|
|
77
96
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
97
|
+
if (bundleName === 'critical') {
|
|
98
|
+
bundleInfo = this.bundleConfig?.bundles?.critical;
|
|
99
|
+
} else {
|
|
100
|
+
bundleInfo = this.bundleConfig?.bundles?.routes?.[bundleName];
|
|
101
|
+
}
|
|
83
102
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
103
|
+
if (!bundleInfo && this.bundleConfig?.bundles?.routes && bundleName !== 'critical') {
|
|
104
|
+
const normalizedName = bundleName?.toLowerCase();
|
|
105
|
+
const matchedKey = Object.keys(this.bundleConfig.bundles.routes).find(
|
|
106
|
+
(key) => key.toLowerCase() === normalizedName
|
|
107
|
+
);
|
|
108
|
+
if (matchedKey) {
|
|
109
|
+
bundleInfo = this.bundleConfig.bundles.routes[matchedKey];
|
|
92
110
|
}
|
|
111
|
+
}
|
|
93
112
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
if (!bundleInfo) {
|
|
114
|
+
console.warn(`Bundle ${bundleName} not found in configuration`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
98
117
|
|
|
99
|
-
|
|
118
|
+
const bundlePath = `/bundles/${bundleInfo.file}`;
|
|
100
119
|
|
|
101
|
-
|
|
120
|
+
const bundleModule = await this.importBundleOnce(bundlePath);
|
|
121
|
+
const { metadata, registerAll } = await this.validateBundleModule(bundleModule, bundleName);
|
|
102
122
|
|
|
103
|
-
|
|
104
|
-
await Promise.all(window.__slicePendingRegistrations);
|
|
105
|
-
window.__slicePendingRegistrations = [];
|
|
106
|
-
}
|
|
123
|
+
await registerAll(this, slice.stylesManager);
|
|
107
124
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
125
|
+
this.loadedBundles.add(bundleName);
|
|
126
|
+
const loadedBundleKey = metadata.bundleKey;
|
|
127
|
+
if (loadedBundleKey && loadedBundleKey !== bundleName) {
|
|
128
|
+
this.loadedBundles.add(loadedBundleKey);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (metadata.type === 'critical' || bundleName === 'critical') {
|
|
132
|
+
this.criticalBundleLoaded = true;
|
|
133
|
+
}
|
|
111
134
|
}
|
|
112
|
-
}
|
|
113
135
|
|
|
114
136
|
/**
|
|
115
137
|
* 📦 Registers a bundle's components (called automatically by bundle files)
|
|
@@ -419,34 +419,35 @@ export default class Router {
|
|
|
419
419
|
slice.loading.start();
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
existingComponent.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
targetElement.innerHTML = '';
|
|
437
|
-
targetElement.appendChild(component);
|
|
422
|
+
try {
|
|
423
|
+
if (existingComponent) {
|
|
424
|
+
targetElement.innerHTML = '';
|
|
425
|
+
if (existingComponent.update) {
|
|
426
|
+
existingComponent.props = { ...existingComponent.props, ...params };
|
|
427
|
+
await existingComponent.update();
|
|
428
|
+
}
|
|
429
|
+
targetElement.appendChild(existingComponent);
|
|
430
|
+
await this.renderRoutesInComponent(existingComponent);
|
|
431
|
+
} else {
|
|
432
|
+
const component = await slice.build(componentName, {
|
|
433
|
+
params,
|
|
434
|
+
sliceId: sliceId,
|
|
435
|
+
});
|
|
438
436
|
|
|
439
|
-
|
|
440
|
-
|
|
437
|
+
targetElement.innerHTML = '';
|
|
438
|
+
targetElement.appendChild(component);
|
|
441
439
|
|
|
442
|
-
|
|
443
|
-
|
|
440
|
+
await this.renderRoutesInComponent(component);
|
|
441
|
+
}
|
|
444
442
|
|
|
445
|
-
|
|
446
|
-
|
|
443
|
+
// Invalidar caché después de cambios importantes en el DOM
|
|
444
|
+
this.invalidateCache();
|
|
445
|
+
slice.router.activeRoute = route;
|
|
446
|
+
} finally {
|
|
447
|
+
if (slice.loading) {
|
|
448
|
+
slice.loading.stop();
|
|
449
|
+
}
|
|
447
450
|
}
|
|
448
|
-
|
|
449
|
-
slice.router.activeRoute = route;
|
|
450
451
|
}
|
|
451
452
|
|
|
452
453
|
/**
|
package/Slice/Slice.js
CHANGED
|
@@ -279,11 +279,7 @@ async function init() {
|
|
|
279
279
|
resolvedMode = 'development';
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
|
|
283
|
-
? `/bundles/${bundleConfigJson.bundles.critical.file}`
|
|
284
|
-
: null;
|
|
285
|
-
|
|
286
|
-
// 4. Load framework classes.
|
|
282
|
+
// 4. Load framework classes.
|
|
287
283
|
// In production the bundler generates slice-bundle.framework.js which
|
|
288
284
|
// sets window.SLICE_FRAMEWORK_CLASSES. In dev mode always use individual
|
|
289
285
|
// imports so the live /Slice/ source is served directly without bundles.
|
|
@@ -319,52 +315,60 @@ async function init() {
|
|
|
319
315
|
window.slice = new Slice(sliceConfig, frameworkClasses);
|
|
320
316
|
window.slice._mode = resolvedMode;
|
|
321
317
|
|
|
318
|
+
const createBundlingInitError = (step, error) => {
|
|
319
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
320
|
+
return new Error(`Bundling V2 initialization failed (${step}): ${detail}`, { cause: error });
|
|
321
|
+
};
|
|
322
|
+
|
|
322
323
|
// Initialize bundles before building components.
|
|
323
324
|
// Only in production — dev mode loads each component individually from source.
|
|
324
325
|
// bundleConfigJson was already fetched above (step 2); reuse it.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
326
|
+
if (resolvedMode === 'production' && bundleConfigJson) {
|
|
327
|
+
window.slice.controller.bundleConfig = bundleConfigJson;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (resolvedMode === 'production' && window.slice.controller.bundleConfig) {
|
|
331
|
+
const config = window.slice.controller.bundleConfig;
|
|
332
|
+
const criticalFile = config?.bundles?.critical?.file;
|
|
333
|
+
if (criticalFile) {
|
|
334
|
+
try {
|
|
335
|
+
await window.slice.controller.loadBundle('critical');
|
|
336
|
+
} catch (error) {
|
|
337
|
+
throw createBundlingInitError(`critical bundle "${criticalFile}"`, error);
|
|
338
|
+
}
|
|
328
339
|
}
|
|
329
340
|
|
|
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
|
-
} else {
|
|
362
|
-
setTimeout(() => loadRouteBundles(), 0);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
} catch (error) {
|
|
366
|
-
console.log('📄 Using individual component loading (no bundles found)');
|
|
367
|
-
}
|
|
341
|
+
const routeBundles = config?.routeBundles || {};
|
|
342
|
+
const initialPath = window.location.pathname || '/';
|
|
343
|
+
const bundlesForRoute = routeBundles[initialPath] || [];
|
|
344
|
+
|
|
345
|
+
const loadRouteBundles = async () => {
|
|
346
|
+
for (const bundleName of bundlesForRoute) {
|
|
347
|
+
if (bundleName === 'critical') continue;
|
|
348
|
+
const bundleInfo = config?.bundles?.routes?.[bundleName];
|
|
349
|
+
if (!bundleInfo?.file) continue;
|
|
350
|
+
await window.slice.controller.loadBundle(bundleName);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const preloadRouteBundles = () => {
|
|
355
|
+
loadRouteBundles().catch((error) => {
|
|
356
|
+
const bundlingError = createBundlingInitError(
|
|
357
|
+
`idle route preload "${initialPath}"`,
|
|
358
|
+
error
|
|
359
|
+
);
|
|
360
|
+
queueMicrotask(() => {
|
|
361
|
+
throw bundlingError;
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
if (typeof requestIdleCallback === 'function') {
|
|
367
|
+
requestIdleCallback(() => preloadRouteBundles());
|
|
368
|
+
} else {
|
|
369
|
+
setTimeout(() => preloadRouteBundles(), 0);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
368
372
|
|
|
369
373
|
slice.paths.structuralComponentFolderPath = '/Slice/Components/Structural';
|
|
370
374
|
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { register } from 'node:module';
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
|
|
9
|
+
test('validateBundleModule rejects module missing Bundling V2 exports contract', async () => {
|
|
10
|
+
const tempDir = await mkdtemp(path.join(tmpdir(), 'slice-controller-loader-'));
|
|
11
|
+
const loaderPath = path.join(tempDir, 'components-alias-loader.mjs');
|
|
12
|
+
await writeFile(
|
|
13
|
+
loaderPath,
|
|
14
|
+
`export async function resolve(specifier, context, nextResolve) {
|
|
15
|
+
if (specifier === '/Components/components.js') {
|
|
16
|
+
return {
|
|
17
|
+
shortCircuit: true,
|
|
18
|
+
url: 'data:text/javascript,export default {};',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return nextResolve(specifier, context);
|
|
22
|
+
}
|
|
23
|
+
`,
|
|
24
|
+
'utf8'
|
|
25
|
+
);
|
|
26
|
+
register(pathToFileURL(loaderPath).href);
|
|
27
|
+
|
|
28
|
+
const controllerModuleUrl = new URL('../Components/Structural/Controller/Controller.js', import.meta.url).href;
|
|
29
|
+
const { default: Controller } = await import(controllerModuleUrl);
|
|
30
|
+
const controller = new Controller();
|
|
31
|
+
const originalWindow = globalThis.window;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
globalThis.window = {
|
|
35
|
+
__slicePendingRegistrations: [],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
assert.equal(
|
|
39
|
+
typeof controller.validateBundleModule,
|
|
40
|
+
'function',
|
|
41
|
+
'Controller must expose validateBundleModule for Bundling V2 runtime contract checks'
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
if (typeof controller.validateBundleModule === 'function') {
|
|
45
|
+
await assert.rejects(
|
|
46
|
+
() => Promise.resolve(controller.validateBundleModule({ default: {} }, 'critical.js')),
|
|
47
|
+
/missing bundling v2 exports contract/i
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
} finally {
|
|
51
|
+
globalThis.window = originalWindow;
|
|
52
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('loadBundle marks requested bundle key and metadata bundleKey as loaded', async () => {
|
|
57
|
+
const tempDir = await mkdtemp(path.join(tmpdir(), 'slice-controller-loader-'));
|
|
58
|
+
const loaderPath = path.join(tempDir, 'components-alias-loader.mjs');
|
|
59
|
+
await writeFile(
|
|
60
|
+
loaderPath,
|
|
61
|
+
`export async function resolve(specifier, context, nextResolve) {
|
|
62
|
+
if (specifier === '/Components/components.js') {
|
|
63
|
+
return {
|
|
64
|
+
shortCircuit: true,
|
|
65
|
+
url: 'data:text/javascript,export default {};',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return nextResolve(specifier, context);
|
|
69
|
+
}
|
|
70
|
+
`,
|
|
71
|
+
'utf8'
|
|
72
|
+
);
|
|
73
|
+
register(pathToFileURL(loaderPath).href);
|
|
74
|
+
|
|
75
|
+
const controllerModuleUrl = new URL('../Components/Structural/Controller/Controller.js', import.meta.url).href;
|
|
76
|
+
const { default: Controller } = await import(controllerModuleUrl);
|
|
77
|
+
const controller = new Controller();
|
|
78
|
+
const originalSlice = globalThis.slice;
|
|
79
|
+
|
|
80
|
+
controller.bundleConfig = {
|
|
81
|
+
bundles: {
|
|
82
|
+
routes: {
|
|
83
|
+
dashboard: {
|
|
84
|
+
file: 'dashboard.js',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
controller.importBundleOnce = async () => ({});
|
|
91
|
+
controller.validateBundleModule = async () => ({
|
|
92
|
+
metadata: {
|
|
93
|
+
bundleKey: 'routes.dashboard.v2',
|
|
94
|
+
type: 'route',
|
|
95
|
+
},
|
|
96
|
+
registerAll: async () => {},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
globalThis.slice = {
|
|
101
|
+
stylesManager: {},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
await controller.loadBundle('dashboard');
|
|
105
|
+
|
|
106
|
+
assert.equal(controller.loadedBundles.has('dashboard'), true);
|
|
107
|
+
assert.equal(controller.loadedBundles.has('routes.dashboard.v2'), true);
|
|
108
|
+
assert.equal(controller.criticalBundleLoaded, false);
|
|
109
|
+
} finally {
|
|
110
|
+
globalThis.slice = originalSlice;
|
|
111
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('Slice init fails fast with contextual error on invalid Bundling V2 contract path', async () => {
|
|
116
|
+
const tempDir = await mkdtemp(path.join(tmpdir(), 'slice-init-loader-'));
|
|
117
|
+
const loaderPath = path.join(tempDir, 'components-alias-loader.mjs');
|
|
118
|
+
await writeFile(
|
|
119
|
+
loaderPath,
|
|
120
|
+
`export async function resolve(specifier, context, nextResolve) {
|
|
121
|
+
if (specifier === '/Components/components.js') {
|
|
122
|
+
return {
|
|
123
|
+
shortCircuit: true,
|
|
124
|
+
url: 'data:text/javascript,export default {};',
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return nextResolve(specifier, context);
|
|
128
|
+
}
|
|
129
|
+
`,
|
|
130
|
+
'utf8'
|
|
131
|
+
);
|
|
132
|
+
register(pathToFileURL(loaderPath).href);
|
|
133
|
+
|
|
134
|
+
const originalWindow = globalThis.window;
|
|
135
|
+
const originalDocument = globalThis.document;
|
|
136
|
+
const originalFetch = globalThis.fetch;
|
|
137
|
+
const originalAlert = globalThis.alert;
|
|
138
|
+
const originalSliceDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'slice');
|
|
139
|
+
const originalConsoleLog = console.log;
|
|
140
|
+
const originalConsoleWarn = console.warn;
|
|
141
|
+
const controllerModuleUrl = new URL('../Components/Structural/Controller/Controller.js', import.meta.url).href;
|
|
142
|
+
const { default: Controller } = await import(controllerModuleUrl);
|
|
143
|
+
const originalLoadBundle = Controller.prototype.loadBundle;
|
|
144
|
+
|
|
145
|
+
const loggedMessages = [];
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
globalThis.window = {
|
|
149
|
+
location: {
|
|
150
|
+
pathname: '/dashboard',
|
|
151
|
+
origin: 'http://localhost',
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
Object.defineProperty(globalThis, 'slice', {
|
|
156
|
+
configurable: true,
|
|
157
|
+
get() {
|
|
158
|
+
return globalThis.window?.slice;
|
|
159
|
+
},
|
|
160
|
+
set(value) {
|
|
161
|
+
if (!globalThis.window) {
|
|
162
|
+
globalThis.window = {};
|
|
163
|
+
}
|
|
164
|
+
globalThis.window.slice = value;
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
Controller.prototype.loadBundle = async () => {
|
|
169
|
+
throw new Error(
|
|
170
|
+
'Bundle "critical" missing Bundling V2 exports contract: requires SLICE_BUNDLE_META and registerAll'
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
globalThis.document = {
|
|
175
|
+
head: {
|
|
176
|
+
appendChild() {},
|
|
177
|
+
},
|
|
178
|
+
body: {
|
|
179
|
+
appendChild() {},
|
|
180
|
+
},
|
|
181
|
+
createElement() {
|
|
182
|
+
return {
|
|
183
|
+
appendChild() {},
|
|
184
|
+
innerHTML: '',
|
|
185
|
+
id: '',
|
|
186
|
+
};
|
|
187
|
+
},
|
|
188
|
+
createTextNode() {
|
|
189
|
+
return {};
|
|
190
|
+
},
|
|
191
|
+
addEventListener() {},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
globalThis.alert = () => {};
|
|
195
|
+
console.log = (...args) => {
|
|
196
|
+
loggedMessages.push(args.map(String).join(' '));
|
|
197
|
+
};
|
|
198
|
+
console.warn = () => {};
|
|
199
|
+
|
|
200
|
+
globalThis.fetch = async (url) => {
|
|
201
|
+
if (url === '/sliceConfig.json') {
|
|
202
|
+
return {
|
|
203
|
+
ok: true,
|
|
204
|
+
json: async () => ({
|
|
205
|
+
paths: {
|
|
206
|
+
routesFile: '/routes.js',
|
|
207
|
+
components: {},
|
|
208
|
+
},
|
|
209
|
+
themeManager: { enabled: false },
|
|
210
|
+
stylesManager: { requestedStyles: [] },
|
|
211
|
+
logger: { enabled: false },
|
|
212
|
+
debugger: { enabled: false },
|
|
213
|
+
loading: { enabled: false },
|
|
214
|
+
events: { enabled: false },
|
|
215
|
+
context: { enabled: false },
|
|
216
|
+
}),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (url === '/slice-env.json') {
|
|
221
|
+
return {
|
|
222
|
+
ok: true,
|
|
223
|
+
json: async () => ({ mode: 'production' }),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (url === '/bundles/bundle.config.json') {
|
|
228
|
+
return {
|
|
229
|
+
ok: true,
|
|
230
|
+
json: async () => ({
|
|
231
|
+
production: true,
|
|
232
|
+
bundles: {
|
|
233
|
+
critical: { file: 'critical.js' },
|
|
234
|
+
routes: {},
|
|
235
|
+
},
|
|
236
|
+
routeBundles: {},
|
|
237
|
+
}),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
throw new Error(`Unexpected fetch URL: ${url}`);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const sliceModuleUrl = new URL(`../Slice.js?fail-fast-test=${Date.now()}`, import.meta.url).href;
|
|
245
|
+
|
|
246
|
+
await assert.rejects(() => import(sliceModuleUrl), /Bundling V2 initialization failed/i);
|
|
247
|
+
|
|
248
|
+
assert.equal(
|
|
249
|
+
loggedMessages.some((message) => message.includes('Using individual component loading (no bundles found)')),
|
|
250
|
+
false,
|
|
251
|
+
'init must not log fallback bundle message on Bundling V2 contract failure'
|
|
252
|
+
);
|
|
253
|
+
} finally {
|
|
254
|
+
Controller.prototype.loadBundle = originalLoadBundle;
|
|
255
|
+
globalThis.window = originalWindow;
|
|
256
|
+
globalThis.document = originalDocument;
|
|
257
|
+
globalThis.fetch = originalFetch;
|
|
258
|
+
globalThis.alert = originalAlert;
|
|
259
|
+
if (originalSliceDescriptor) {
|
|
260
|
+
Object.defineProperty(globalThis, 'slice', originalSliceDescriptor);
|
|
261
|
+
} else {
|
|
262
|
+
delete globalThis.slice;
|
|
263
|
+
}
|
|
264
|
+
console.log = originalConsoleLog;
|
|
265
|
+
console.warn = originalConsoleWarn;
|
|
266
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
267
|
+
}
|
|
268
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
|
|
4
|
+
import Router from '../Components/Structural/Router/Router.js';
|
|
5
|
+
|
|
6
|
+
test('handleRoute stops loading when build throws on error path', async () => {
|
|
7
|
+
const originalSlice = globalThis.slice;
|
|
8
|
+
const originalDocument = globalThis.document;
|
|
9
|
+
|
|
10
|
+
const targetElement = {
|
|
11
|
+
innerHTML: 'existing',
|
|
12
|
+
appendChild() {},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
globalThis.document = {
|
|
17
|
+
querySelector(selector) {
|
|
18
|
+
if (selector === '#app') {
|
|
19
|
+
return targetElement;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const loadingCalls = [];
|
|
26
|
+
|
|
27
|
+
globalThis.slice = {
|
|
28
|
+
loading: {
|
|
29
|
+
start() {
|
|
30
|
+
loadingCalls.push('start');
|
|
31
|
+
},
|
|
32
|
+
stop() {
|
|
33
|
+
loadingCalls.push('stop');
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
controller: {
|
|
37
|
+
getComponent() {
|
|
38
|
+
return null;
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
build: async () => {
|
|
42
|
+
throw new Error('boom');
|
|
43
|
+
},
|
|
44
|
+
router: {
|
|
45
|
+
activeRoute: null,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const router = new Router([]);
|
|
50
|
+
|
|
51
|
+
await assert.rejects(
|
|
52
|
+
() =>
|
|
53
|
+
router.handleRoute(
|
|
54
|
+
{
|
|
55
|
+
component: 'Dashboard',
|
|
56
|
+
parentRoute: null,
|
|
57
|
+
},
|
|
58
|
+
{}
|
|
59
|
+
),
|
|
60
|
+
/boom/
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
assert.deepEqual(loadingCalls, ['start', 'stop']);
|
|
64
|
+
} finally {
|
|
65
|
+
globalThis.slice = originalSlice;
|
|
66
|
+
globalThis.document = originalDocument;
|
|
67
|
+
}
|
|
68
|
+
});
|