slicejs-web-framework 2.4.3 → 2.4.4

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.
@@ -295,9 +295,18 @@ export default class Controller {
295
295
  return Promise.resolve(false);
296
296
  }
297
297
 
298
- const { components, metadata } = bundle;
298
+ // Set tracking flags synchronously before any async work, so callers that
299
+ // await import() see the flags set immediately when the Promise resolves.
300
+ const { components, metadata } = bundle;
301
+ const bundleKey = metadata?.bundleKey;
302
+ if (bundleKey) {
303
+ this.loadedBundles.add(bundleKey);
304
+ if (metadata?.type === 'critical') {
305
+ this.criticalBundleLoaded = true;
306
+ }
307
+ }
299
308
 
300
- console.log(`📦 Registering bundle: ${metadata.type} (${metadata.componentCount} components)`);
309
+ console.log(`📦 Registering bundle: ${metadata.type} (${metadata.componentCount} components)`);
301
310
 
302
311
  const entries = Object.entries(components);
303
312
  const chunkSize = 50;
@@ -19,7 +19,8 @@ export default class StylesManager {
19
19
  }
20
20
 
21
21
  if (slice.themeConfig.enabled) {
22
- const ThemeManagerClass = slice.frameworkClasses?.ThemeManager;
22
+ const ThemeManagerClass = slice.frameworkClasses?.ThemeManager
23
+ || await slice.getClass(`${slice.paths.structuralComponentFolderPath}/StylesManager/ThemeManager/ThemeManager.js`);
23
24
  if (!ThemeManagerClass) {
24
25
  throw new Error('ThemeManager not available');
25
26
  }
package/Slice/Slice.js CHANGED
@@ -21,6 +21,10 @@ export default class Slice {
21
21
  this.loadingConfig = sliceConfig.loading;
22
22
  this.eventsConfig = sliceConfig.events;
23
23
 
24
+ // Default to production until init() resolves the actual mode.
25
+ // Safe to call isProduction() before init() completes.
26
+ this._mode = 'production';
27
+
24
28
  // 📦 Bundle system is initialized automatically via import in index.js
25
29
  }
26
30
 
@@ -39,11 +43,12 @@ export default class Slice {
39
43
  }
40
44
 
41
45
  /**
42
- * Flag for production behavior (override in builds).
46
+ * Returns true when running in production mode.
47
+ * Reliable after init() has completed.
43
48
  * @returns {boolean}
44
49
  */
45
50
  isProduction() {
46
- return true;
51
+ return this._mode === 'production';
47
52
  }
48
53
 
49
54
  /**
@@ -233,7 +238,6 @@ async function loadConfig() {
233
238
  const response = await fetch('/sliceConfig.json'); // 🔹 Express lo sirve desde `src/`
234
239
  if (!response.ok) throw new Error('Error loading sliceConfig.json');
235
240
  const json = await response.json();
236
- console.log(json);
237
241
  return json;
238
242
  } catch (error) {
239
243
  console.error(`Error loading config file: ${error.message}`);
@@ -250,6 +254,20 @@ async function init() {
250
254
  return;
251
255
  }
252
256
 
257
+ // 1. Resolve runtime mode via dev server endpoint.
258
+ // In production the endpoint returns 404 (not registered), so catch/non-ok is expected.
259
+ let envMode = null;
260
+ try {
261
+ const envRes = await fetch('/slice-env.json', { cache: 'no-store' });
262
+ if (envRes.ok) {
263
+ const env = await envRes.json();
264
+ envMode = env.mode; // 'development' | 'production'
265
+ }
266
+ } catch (error) {
267
+ // Endpoint not available — normal in production
268
+ }
269
+
270
+ // 2. Fetch bundle config (existing logic, unchanged)
253
271
  let frameworkClasses = null;
254
272
  let bundleConfigJson = null;
255
273
  try {
@@ -261,47 +279,68 @@ async function init() {
261
279
  // ignore
262
280
  }
263
281
 
264
- try {
265
- if (bundleConfigJson?.production) {
266
- await import('/bundles/slice-bundle.framework.js');
267
- frameworkClasses = window.SLICE_FRAMEWORK_CLASSES || null;
268
- }
269
- } catch (error) {
270
- console.warn('Framework bundle not available, falling back to dynamic imports', error);
282
+ // 3. Determine canonical mode: env endpoint takes precedence, then bundle config
283
+ let resolvedMode;
284
+ if (envMode) {
285
+ resolvedMode = envMode;
286
+ } else if (bundleConfigJson?.production) {
287
+ resolvedMode = 'production';
288
+ } else {
289
+ resolvedMode = 'development';
290
+ }
291
+
292
+ // 4. Load framework classes.
293
+ // In production the bundler generates slice-bundle.framework.js which
294
+ // sets window.SLICE_FRAMEWORK_CLASSES. In dev mode always use individual
295
+ // imports so the live /Slice/ source is served directly without bundles.
296
+ if (resolvedMode === 'production' && bundleConfigJson?.bundles?.framework?.file) {
297
+ try {
298
+ await import(`/bundles/${bundleConfigJson.bundles.framework.file}`);
299
+ if (window.SLICE_FRAMEWORK_CLASSES) {
300
+ frameworkClasses = window.SLICE_FRAMEWORK_CLASSES;
301
+ }
302
+ } catch (e) {
303
+ // framework bundle failed — fall through to individual imports
304
+ console.error('[Slice.js] framework bundle import failed:', e?.message || e);
305
+ }
271
306
  }
272
307
 
273
- if (!frameworkClasses) {
274
- const imports = await Promise.all([
275
- import('./Components/Structural/Controller/Controller.js'),
276
- import('./Components/Structural/StylesManager/StylesManager.js')
277
- ]);
278
- frameworkClasses = {
279
- Controller: imports[0].default,
280
- StylesManager: imports[1].default
281
- };
282
- }
308
+ if (!frameworkClasses) {
309
+ try {
310
+ const imports = await Promise.all([
311
+ import('./Components/Structural/Controller/Controller.js'),
312
+ import('./Components/Structural/StylesManager/StylesManager.js')
313
+ ]);
314
+ frameworkClasses = {
315
+ Controller: imports[0].default,
316
+ StylesManager: imports[1].default
317
+ };
318
+ } catch (e) {
319
+ console.error('[Slice.js] individual imports fallback failed:', e?.message || e);
320
+ throw e;
321
+ }
322
+ }
283
323
 
324
+ // 5. Create Slice instance and set resolved mode
284
325
  window.slice = new Slice(sliceConfig, frameworkClasses);
326
+ window.slice._mode = resolvedMode;
285
327
 
286
- // Initialize bundles before building components
287
- try {
288
- const configResponse = await fetch('/bundles/bundle.config.json', { cache: 'no-store' });
289
- if (configResponse.ok) {
290
- const config = await configResponse.json();
291
- window.slice.controller.bundleConfig = config;
292
- }
328
+ // Initialize bundles before building components.
329
+ // Only in production — dev mode loads each component individually from source.
330
+ // bundleConfigJson was already fetched above (step 2); reuse it.
331
+ try {
332
+ if (resolvedMode === 'production' && bundleConfigJson) {
333
+ window.slice.controller.bundleConfig = bundleConfigJson;
334
+ }
293
335
 
294
336
  if (window.slice.controller.bundleConfig) {
295
337
  const config = window.slice.controller.bundleConfig;
296
338
 
297
339
  const criticalFile = config?.bundles?.critical?.file;
298
340
  if (criticalFile) {
299
- const criticalModule = await import(`/bundles/${criticalFile}`);
300
- if (criticalModule.SLICE_BUNDLE) {
301
- await window.slice.controller.registerBundle(criticalModule.SLICE_BUNDLE);
302
- window.slice.controller.loadedBundles.add('critical');
303
- window.slice.controller.criticalBundleLoaded = true;
304
- }
341
+ // Bundle auto-registers itself on import via its own registration block.
342
+ // No explicit registerBundle() call needed — flags are set inside registerBundle().
343
+ await import(`/bundles/${criticalFile}`);
305
344
  }
306
345
 
307
346
  const routeBundles = config?.routeBundles || {};
@@ -313,13 +352,10 @@ async function init() {
313
352
  if (bundleName === 'critical') continue;
314
353
  const bundleInfo = config?.bundles?.routes?.[bundleName];
315
354
  if (!bundleInfo?.file) continue;
316
- const routeModule = await import(`/bundles/${bundleInfo.file}`);
317
- if (routeModule.SLICE_BUNDLE) {
318
- await window.slice.controller.registerBundle(routeModule.SLICE_BUNDLE);
319
- window.slice.controller.loadedBundles.add(bundleName);
320
- }
321
- }
322
- };
355
+ // Bundle auto-registers itself on import.
356
+ await import(`/bundles/${bundleInfo.file}`);
357
+ }
358
+ };
323
359
 
324
360
  if (typeof requestIdleCallback === 'function') {
325
361
  requestIdleCallback(() => loadRouteBundles());
package/api/index.js CHANGED
@@ -86,6 +86,23 @@ app.use((req, res, next) => {
86
86
  }
87
87
  });
88
88
 
89
+ // ==============================================
90
+ // RUNTIME MODE ENDPOINT
91
+ // ==============================================
92
+
93
+ // Expone el modo actual al framework Slice.js en runtime.
94
+ // Solo se registra en development — 404 en production indica modo producción.
95
+ if (runMode === 'development') {
96
+ app.get('/slice-env.json', (req, res) => {
97
+ res.json({ mode: 'development' });
98
+ });
99
+ } else {
100
+ // Explicit 404 so the SPA fallback doesn't return 200 for this dev-only endpoint.
101
+ app.get('/slice-env.json', (req, res) => {
102
+ res.status(404).json({ error: 'Not found' });
103
+ });
104
+ }
105
+
89
106
  // ==============================================
90
107
  // ARCHIVOS ESTÁTICOS (DESPUÉS DE SEGURIDAD)
91
108
  // ==============================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slicejs-web-framework",
3
- "version": "2.4.3",
3
+ "version": "2.4.4",
4
4
  "description": "",
5
5
  "engines": {
6
6
  "node": ">=20"