slicejs-web-framework 2.4.2 → 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;
@@ -327,6 +336,13 @@ export default class Controller {
327
336
  ? `Framework/Structural/${componentName}`
328
337
  : componentName;
329
338
  this.classes.set(registeredName, componentData.class);
339
+ if (componentName === 'Loading') {
340
+ console.log('🔎 Bundle class registered: Loading', {
341
+ registeredName,
342
+ type: typeof componentData.class,
343
+ isFunction: typeof componentData.class === 'function'
344
+ });
345
+ }
330
346
  if (componentName === 'InputSearchDocs' || componentName === 'MainMenu') {
331
347
  console.log(`🔎 Bundle class registered: ${componentName}`, {
332
348
  registeredName,
@@ -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
  /**
@@ -157,6 +162,13 @@ export default class Slice {
157
162
  delete props.sliceId;
158
163
 
159
164
  const ComponentClass = this.controller.classes.get(componentName);
165
+ if (componentName === 'Loading') {
166
+ console.log('🔎 Build component: Loading', {
167
+ classType: typeof ComponentClass,
168
+ isFunction: typeof ComponentClass === 'function',
169
+ classValue: ComponentClass
170
+ });
171
+ }
160
172
  if (componentName === 'InputSearchDocs' || componentName === 'MainMenu') {
161
173
  console.log(`🔎 Build component: ${componentName}`, {
162
174
  classType: typeof ComponentClass,
@@ -226,7 +238,6 @@ async function loadConfig() {
226
238
  const response = await fetch('/sliceConfig.json'); // 🔹 Express lo sirve desde `src/`
227
239
  if (!response.ok) throw new Error('Error loading sliceConfig.json');
228
240
  const json = await response.json();
229
- console.log(json);
230
241
  return json;
231
242
  } catch (error) {
232
243
  console.error(`Error loading config file: ${error.message}`);
@@ -243,6 +254,20 @@ async function init() {
243
254
  return;
244
255
  }
245
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)
246
271
  let frameworkClasses = null;
247
272
  let bundleConfigJson = null;
248
273
  try {
@@ -254,47 +279,68 @@ async function init() {
254
279
  // ignore
255
280
  }
256
281
 
257
- try {
258
- if (bundleConfigJson?.production) {
259
- await import('/bundles/slice-bundle.framework.js');
260
- frameworkClasses = window.SLICE_FRAMEWORK_CLASSES || null;
261
- }
262
- } catch (error) {
263
- console.warn('Framework bundle not available, falling back to dynamic imports');
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';
264
290
  }
265
291
 
266
- if (!frameworkClasses) {
267
- const imports = await Promise.all([
268
- import('./Components/Structural/Controller/Controller.js'),
269
- import('./Components/Structural/StylesManager/StylesManager.js')
270
- ]);
271
- frameworkClasses = {
272
- Controller: imports[0].default,
273
- StylesManager: imports[1].default
274
- };
275
- }
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
+ }
306
+ }
307
+
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
+ }
276
323
 
324
+ // 5. Create Slice instance and set resolved mode
277
325
  window.slice = new Slice(sliceConfig, frameworkClasses);
326
+ window.slice._mode = resolvedMode;
278
327
 
279
- // Initialize bundles before building components
280
- try {
281
- const configResponse = await fetch('/bundles/bundle.config.json', { cache: 'no-store' });
282
- if (configResponse.ok) {
283
- const config = await configResponse.json();
284
- window.slice.controller.bundleConfig = config;
285
- }
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
+ }
286
335
 
287
336
  if (window.slice.controller.bundleConfig) {
288
337
  const config = window.slice.controller.bundleConfig;
289
338
 
290
339
  const criticalFile = config?.bundles?.critical?.file;
291
340
  if (criticalFile) {
292
- const criticalModule = await import(`/bundles/${criticalFile}`);
293
- if (criticalModule.SLICE_BUNDLE) {
294
- window.slice.controller.registerBundle(criticalModule.SLICE_BUNDLE);
295
- window.slice.controller.loadedBundles.add('critical');
296
- window.slice.controller.criticalBundleLoaded = true;
297
- }
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}`);
298
344
  }
299
345
 
300
346
  const routeBundles = config?.routeBundles || {};
@@ -306,11 +352,8 @@ async function init() {
306
352
  if (bundleName === 'critical') continue;
307
353
  const bundleInfo = config?.bundles?.routes?.[bundleName];
308
354
  if (!bundleInfo?.file) continue;
309
- const routeModule = await import(`/bundles/${bundleInfo.file}`);
310
- if (routeModule.SLICE_BUNDLE) {
311
- window.slice.controller.registerBundle(routeModule.SLICE_BUNDLE);
312
- window.slice.controller.loadedBundles.add(bundleName);
313
- }
355
+ // Bundle auto-registers itself on import.
356
+ await import(`/bundles/${bundleInfo.file}`);
314
357
  }
315
358
  };
316
359
 
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.2",
3
+ "version": "2.4.4",
4
4
  "description": "",
5
5
  "engines": {
6
6
  "node": ">=20"