meno-core 1.0.39 → 1.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/build-astro.ts +195 -68
  2. package/dist/bin/cli.js +1 -1
  3. package/dist/build-static.js +6 -6
  4. package/dist/chunks/{chunk-WK5XLASY.js → chunk-3NOZVNM4.js} +3 -3
  5. package/dist/chunks/{chunk-W6HDII4T.js → chunk-GKICS7CF.js} +27 -14
  6. package/dist/chunks/chunk-GKICS7CF.js.map +7 -0
  7. package/dist/chunks/{chunk-P3FX5HJM.js → chunk-LOJLO2EY.js} +1 -1
  8. package/dist/chunks/chunk-LOJLO2EY.js.map +7 -0
  9. package/dist/chunks/{chunk-HNAS6BSS.js → chunk-MOCRENNU.js} +55 -5
  10. package/dist/chunks/{chunk-HNAS6BSS.js.map → chunk-MOCRENNU.js.map} +3 -3
  11. package/dist/chunks/{chunk-NV25WXCA.js → chunk-OJ5SROQN.js} +5 -3
  12. package/dist/chunks/chunk-OJ5SROQN.js.map +7 -0
  13. package/dist/chunks/{chunk-AIXKUVNG.js → chunk-V4SVSX3X.js} +3 -3
  14. package/dist/chunks/{chunk-KULPBDC7.js → chunk-Z7SAOCDG.js} +5 -2
  15. package/dist/chunks/{chunk-KULPBDC7.js.map → chunk-Z7SAOCDG.js.map} +2 -2
  16. package/dist/chunks/{constants-5CRJRQNR.js → constants-L75FR445.js} +2 -2
  17. package/dist/entries/server-router.js +6 -6
  18. package/dist/lib/client/index.js +5 -5
  19. package/dist/lib/client/index.js.map +2 -2
  20. package/dist/lib/server/index.js +2007 -197
  21. package/dist/lib/server/index.js.map +4 -4
  22. package/dist/lib/shared/index.js +3 -3
  23. package/dist/lib/test-utils/index.js +1 -1
  24. package/lib/client/core/builders/embedBuilder.ts +2 -2
  25. package/lib/server/astro/cmsPageEmitter.ts +417 -0
  26. package/lib/server/astro/componentEmitter.ts +90 -5
  27. package/lib/server/astro/nodeToAstro.ts +830 -37
  28. package/lib/server/astro/pageEmitter.ts +39 -3
  29. package/lib/server/astro/tailwindMapper.ts +69 -8
  30. package/lib/server/astro/templateTransformer.ts +107 -0
  31. package/lib/server/index.ts +9 -0
  32. package/lib/server/routes/api/components.ts +62 -0
  33. package/lib/server/routes/api/core-routes.ts +8 -0
  34. package/lib/server/ssr/ssrRenderer.ts +30 -10
  35. package/lib/server/webflow/buildWebflow.ts +415 -0
  36. package/lib/server/webflow/index.ts +22 -0
  37. package/lib/server/webflow/nodeToWebflow.ts +423 -0
  38. package/lib/server/webflow/styleMapper.ts +241 -0
  39. package/lib/server/webflow/types.ts +196 -0
  40. package/lib/shared/constants.ts +2 -0
  41. package/lib/shared/types/components.ts +1 -0
  42. package/lib/shared/validation/schemas.ts +1 -0
  43. package/package.json +1 -1
  44. package/dist/chunks/chunk-NV25WXCA.js.map +0 -7
  45. package/dist/chunks/chunk-P3FX5HJM.js.map +0 -7
  46. package/dist/chunks/chunk-W6HDII4T.js.map +0 -7
  47. /package/dist/chunks/{chunk-WK5XLASY.js.map → chunk-3NOZVNM4.js.map} +0 -0
  48. /package/dist/chunks/{chunk-AIXKUVNG.js.map → chunk-V4SVSX3X.js.map} +0 -0
  49. /package/dist/chunks/{constants-5CRJRQNR.js.map → constants-L75FR445.js.map} +0 -0
package/build-astro.ts CHANGED
@@ -39,7 +39,10 @@ import { collectComponentLibraries, filterLibrariesByContext, mergeLibraries, ge
39
39
  import { migrateTemplatesDirectory } from "./lib/server/migrateTemplates";
40
40
  import { emitAstroComponent } from "./lib/server/astro/componentEmitter";
41
41
  import { emitAstroPage } from "./lib/server/astro/pageEmitter";
42
+ import { emitCMSPage } from './lib/server/astro/cmsPageEmitter';
42
43
  import { collectAllMappingClasses } from "./lib/server/astro/cssCollector";
44
+ import { buildImageMetadataMap } from "./lib/server/ssr/imageMetadata";
45
+ import { extractUtilityClassesFromHTML, generateUtilityCSS } from "./lib/shared/cssGeneration";
43
46
 
44
47
  // ---------------------------------------------------------------------------
45
48
  // Helpers
@@ -118,6 +121,28 @@ function escapeTemplateLiteral(s: string): string {
118
121
  return s.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
119
122
  }
120
123
 
124
+ /**
125
+ * Compute locale → URL path map for a page's slug translations.
126
+ * Used by locale list rendering to generate correct links.
127
+ */
128
+ function computePageSlugMap(
129
+ slugs: Record<string, string>,
130
+ i18nConfig: I18nConfig
131
+ ): Record<string, string> {
132
+ const map: Record<string, string> = {};
133
+ for (const localeConfig of i18nConfig.locales) {
134
+ const code = localeConfig.code;
135
+ const isDefault = code === i18nConfig.defaultLocale;
136
+ const slug = slugs[code] || '';
137
+ if (isDefault) {
138
+ map[code] = slug === '' ? '/' : `/${slug}`;
139
+ } else {
140
+ map[code] = slug === '' ? `/${code}` : `/${code}/${slug}`;
141
+ }
142
+ }
143
+ return map;
144
+ }
145
+
121
146
  /**
122
147
  * Map a CMS field type to a Zod schema string for the Astro content config.
123
148
  */
@@ -126,7 +151,8 @@ function cmsFieldToZod(field: CMSFieldDefinition): string {
126
151
  case 'string':
127
152
  case 'text':
128
153
  case 'rich-text':
129
- return 'z.string()';
154
+ // Support both plain strings and i18n objects { _i18n: true, en: "...", pl: "..." }
155
+ return 'z.union([z.string(), z.object({ _i18n: z.literal(true) }).passthrough()])';
130
156
  case 'number':
131
157
  return 'z.number()';
132
158
  case 'boolean':
@@ -180,6 +206,8 @@ interface PageRenderResult {
180
206
  pageName?: string;
181
207
  /** Whether this is a CMS template page */
182
208
  isCMSPage?: boolean;
209
+ /** SSR fallback HTML for complex nodes (list, locale-list) keyed by element path */
210
+ ssrFallbackCollector?: Map<string, string>;
183
211
  }
184
212
 
185
213
  interface AstroBuildStats {
@@ -275,6 +303,12 @@ export async function buildAstroProject(
275
303
  const globalLibraries = configService.getLibraries();
276
304
  const componentLibraries = collectComponentLibraries(globalComponents);
277
305
 
306
+ // Build image metadata map for responsive image generation
307
+ const imageMetadataMap = await buildImageMetadataMap();
308
+ if (imageMetadataMap.size > 0) {
309
+ console.log(`Loaded image metadata for ${imageMetadataMap.size} image(s)\n`);
310
+ }
311
+
278
312
  // ----------------------------------------------------------
279
313
  // 2. Clean and create output directory
280
314
  // ----------------------------------------------------------
@@ -350,7 +384,7 @@ export async function buildAstroProject(
350
384
 
351
385
  // Helper to process a render result
352
386
  function processRenderResult(
353
- result: { html: string; meta: string; title: string; javascript: string; componentCSS?: string; locale: string; interactiveStylesMap: Map<string, InteractiveStyles>; preloadImages: any[]; neededCollections: Set<string> },
387
+ result: { html: string; meta: string; title: string; javascript: string; componentCSS?: string; locale: string; interactiveStylesMap: Map<string, InteractiveStyles>; preloadImages: any[]; neededCollections: Set<string>; ssrFallbackCollector?: Map<string, string> },
354
388
  urlPath: string,
355
389
  astroFilePath: string,
356
390
  fileDepth: number,
@@ -388,6 +422,7 @@ export async function buildAstroProject(
388
422
  pageData,
389
423
  pageName,
390
424
  isCMSPage,
425
+ ssrFallbackCollector: result.ssrFallbackCollector,
391
426
  });
392
427
  }
393
428
 
@@ -460,6 +495,13 @@ export async function buildAstroProject(
460
495
  }
461
496
  }
462
497
 
498
+ // Pre-compute layout dependencies needed by both CMS and regular page emission
499
+ const fontPreloads = generateFontPreloadTags();
500
+ const mergedLibraries = mergeLibraries(globalLibraries, componentLibraries);
501
+ const buildLibraries = filterLibrariesByContext(mergedLibraries, 'build');
502
+ const libraryTags = generateLibraryTags(buildLibraries);
503
+ const defaultTheme = themeConfig.default || 'light';
504
+
463
505
  // ---------- CMS template pages ----------
464
506
  const templatesDir = projectPaths.templates();
465
507
  const templateSchemas: CMSSchema[] = [];
@@ -494,54 +536,114 @@ export async function buildAstroProject(
494
536
  templateSchemas.push(cmsSchema);
495
537
  console.log(` CMS Collection: ${cmsSchema.id}`);
496
538
 
539
+ // Count items for stats
497
540
  const items = await cmsService.queryItems({ collection: cmsSchema.id });
498
- if (items.length === 0) {
541
+ const itemCount = items.length;
542
+
543
+ if (itemCount === 0) {
499
544
  console.log(` No items found in cms/${cmsSchema.id}/`);
500
- continue;
545
+ } else {
546
+ console.log(` Found ${itemCount} item(s)`);
501
547
  }
502
548
 
503
- console.log(` Found ${items.length} item(s)`);
504
-
505
- for (const item of items) {
506
- for (const localeConfig of i18nConfig.locales) {
507
- const locale = localeConfig.code;
508
- const isDefault = locale === i18nConfig.defaultLocale;
509
-
510
- const isDevBuild = process.env.MENO_DEV_BUILD === 'true';
511
- if (!isDevBuild && isItemDraftForLocale(item, locale)) {
512
- continue;
513
- }
514
-
515
- const itemPath = buildCMSItemPath(cmsSchema.urlPattern, item, cmsSchema.slugField, locale, i18nConfig);
516
- const itemWithUrl: CMSItem = { ...item, _url: itemPath };
517
-
518
- const result = await renderPageSSR(
519
- pageData,
520
- globalComponents,
521
- itemPath,
522
- siteUrl,
523
- locale,
524
- i18nConfig,
525
- slugMappings,
526
- { cms: itemWithUrl },
527
- cmsService,
528
- true
529
- );
549
+ // Render SSR once for metadata collection (interactive styles, component CSS, JS)
550
+ const defaultLocale = i18nConfig.defaultLocale;
551
+ const dummyPath = cmsSchema.urlPattern.replace('{{slug}}', '__placeholder__');
530
552
 
531
- // Determine .astro file path: strip leading slash, add .astro
532
- const pathWithoutSlash = itemPath.startsWith('/') ? itemPath.substring(1) : itemPath;
533
- const astroFilePath = isDefault
534
- ? `${pathWithoutSlash}.astro`
535
- : `${locale}/${pathWithoutSlash}.astro`;
536
- const fileDepth = astroFilePath.split('/').length - 1;
553
+ const metaResult = await renderPageSSR(
554
+ pageData,
555
+ globalComponents,
556
+ dummyPath,
557
+ siteUrl,
558
+ defaultLocale,
559
+ i18nConfig,
560
+ slugMappings,
561
+ undefined, // no CMS context - just collecting metadata
562
+ cmsService,
563
+ true
564
+ );
537
565
 
538
- const urlPath = isDefault ? itemPath : `/${locale}${itemPath}`;
566
+ // Collect interactive styles and component CSS
567
+ mergeInteractiveStyles(metaResult.interactiveStylesMap);
568
+ if (metaResult.componentCSS) {
569
+ allComponentCSS.add(metaResult.componentCSS);
570
+ }
539
571
 
540
- processRenderResult(result, urlPath, astroFilePath, fileDepth, pageData, file.replace('.json', ''), true);
541
- console.log(` Rendered: ${urlPath}`);
542
- cmsPageCount++;
572
+ // Deduplicate JavaScript by content hash
573
+ const scriptPaths: string[] = [];
574
+ if (metaResult.javascript) {
575
+ const hash = hashContent(metaResult.javascript);
576
+ if (!jsContents.has(hash)) {
577
+ jsContents.set(hash, metaResult.javascript);
543
578
  }
579
+ scriptPaths.push(`/_scripts/${hash}.js`);
544
580
  }
581
+
582
+ // Determine route file path from urlPattern
583
+ const isMultiLocale = i18nConfig.locales.length > 1;
584
+ const urlPatternWithoutSlash = cmsSchema.urlPattern.replace(/^\//, '');
585
+ const slugPlaceholderIdx = urlPatternWithoutSlash.indexOf('{{');
586
+ const pathPrefix = slugPlaceholderIdx > 0 ? urlPatternWithoutSlash.substring(0, slugPlaceholderIdx) : '';
587
+
588
+ // Generate CMS page with getStaticPaths()
589
+ const ssrFallbacks = metaResult.ssrFallbackCollector ?? new Map<string, string>();
590
+
591
+ // For each locale (or just default), generate a route file
592
+ const localesToEmit = isMultiLocale ? i18nConfig.locales : [{ code: i18nConfig.defaultLocale }];
593
+
594
+ for (const localeEntry of localesToEmit) {
595
+ const localeCode = localeEntry.code;
596
+ const isDefault = localeCode === i18nConfig.defaultLocale;
597
+
598
+ // Route file path: blog/[slug].astro for default, pl/blog/[slug].astro for non-default
599
+ let astroFilePath: string;
600
+ if (pathPrefix) {
601
+ astroFilePath = isDefault
602
+ ? `${pathPrefix}[slug].astro`
603
+ : `${localeCode}/${pathPrefix}[slug].astro`;
604
+ } else {
605
+ astroFilePath = isDefault
606
+ ? '[slug].astro'
607
+ : `${localeCode}/[slug].astro`;
608
+ }
609
+
610
+ const fileDepth = astroFilePath.split('/').length - 1;
611
+ const importPath = layoutImportPath(fileDepth);
612
+
613
+ const astroContent = emitCMSPage({
614
+ pageData,
615
+ globalComponents,
616
+ cmsSchema,
617
+ title: String(pageData.meta?.title || cmsSchema.name),
618
+ meta: metaResult.meta,
619
+ locale: localeCode,
620
+ theme: defaultTheme,
621
+ fontPreloads,
622
+ libraryTags,
623
+ scriptPaths,
624
+ layoutImportPath: importPath,
625
+ fileDepth,
626
+ ssrFallbacks,
627
+ pageName: file.replace('.json', ''),
628
+ breakpoints,
629
+ imageMetadataMap,
630
+ i18nConfig,
631
+ isMultiLocale: false, // Each file handles one locale
632
+ slugMappings,
633
+ });
634
+
635
+ const astroFileFull = join(pagesOutDir, astroFilePath);
636
+ const astroFileDir = astroFileFull.substring(0, astroFileFull.lastIndexOf('/'));
637
+ if (!existsSync(astroFileDir)) {
638
+ mkdirSync(astroFileDir, { recursive: true });
639
+ }
640
+
641
+ await writeFile(astroFileFull, astroContent, 'utf-8');
642
+ }
643
+
644
+ console.log(` Generated: ${pathPrefix}[slug].astro (${itemCount} items × ${localesToEmit.length} locale(s))`);
645
+
646
+ cmsPageCount += itemCount * i18nConfig.locales.length;
545
647
  } catch (error: any) {
546
648
  console.error(` Error processing template ${file}:`, error?.message || error);
547
649
  errorCount++;
@@ -585,13 +687,6 @@ export async function buildAstroProject(
585
687
  // ----------------------------------------------------------
586
688
  // 7. Generate BaseLayout.astro
587
689
  // ----------------------------------------------------------
588
- const fontPreloads = generateFontPreloadTags();
589
-
590
- // Merge global + component libraries for the layout
591
- const mergedLibraries = mergeLibraries(globalLibraries, componentLibraries);
592
- const buildLibraries = filterLibrariesByContext(mergedLibraries, 'build');
593
- const libraryTags = generateLibraryTags(buildLibraries);
594
-
595
690
  const baseLayoutContent = `---
596
691
  import '../styles/global.css';
597
692
 
@@ -635,7 +730,7 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
635
730
  let componentFileCount = 0;
636
731
  for (const [compName, compDef] of Object.entries(globalComponents)) {
637
732
  try {
638
- const astroContent = emitAstroComponent(compName, compDef, globalComponents, breakpoints);
733
+ const astroContent = emitAstroComponent(compName, compDef, globalComponents, breakpoints, i18nConfig.defaultLocale);
639
734
  await writeFile(join(componentsOutDir, `${compName}.astro`), astroContent, 'utf-8');
640
735
  componentFileCount++;
641
736
  } catch (error: any) {
@@ -647,7 +742,7 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
647
742
  // ----------------------------------------------------------
648
743
  // 8. Generate .astro page files (component-structured)
649
744
  // ----------------------------------------------------------
650
- const defaultTheme = themeConfig.default || 'light';
745
+ const allFallbackHtml: string[] = []; // Collect SSR fallback HTML for utility CSS generation
651
746
 
652
747
  for (const result of allResults) {
653
748
  const importPath = layoutImportPath(result.fileDepth);
@@ -672,10 +767,21 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
672
767
 
673
768
  let astroContent: string;
674
769
 
675
- // Use component-structured emission for regular pages with page data
676
- // CMS template pages keep the SSR fallback approach
677
- if (result.pageData && !result.isCMSPage) {
770
+ // Use component-structured emission for pages with page data
771
+ if (result.pageData) {
678
772
  try {
773
+ // Use SSR fallback collector from the initial render (paths already match nodeToAstro convention)
774
+ const ssrFallbacks = result.ssrFallbackCollector ?? new Map<string, string>();
775
+ ssrFallbacks.forEach((html) => {
776
+ allFallbackHtml.push(html);
777
+ });
778
+
779
+ // Compute slug map for locale list rendering
780
+ const pageSlugMap: Record<string, string> | undefined =
781
+ result.pageData.meta?.slugs
782
+ ? computePageSlugMap(result.pageData.meta.slugs, i18nConfig)
783
+ : undefined;
784
+
679
785
  astroContent = emitAstroPage({
680
786
  pageData: result.pageData,
681
787
  globalComponents,
@@ -688,9 +794,13 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
688
794
  scriptPaths,
689
795
  layoutImportPath: importPath,
690
796
  fileDepth: result.fileDepth,
691
- ssrFallbacks: new Map(), // SSR fallbacks for complex nodes
797
+ ssrFallbacks,
692
798
  pageName: result.pageName || 'index',
693
799
  breakpoints,
800
+ imageMetadataMap,
801
+ i18nConfig: i18nConfig.locales.length > 1 ? i18nConfig : undefined,
802
+ currentPageSlugMap: pageSlugMap,
803
+ slugMappings: i18nConfig.locales.length > 1 ? slugMappings : undefined,
694
804
  });
695
805
  } catch (error: any) {
696
806
  // Fallback to SSR HTML if component emission fails
@@ -698,8 +808,9 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
698
808
  astroContent = buildSSRFallbackPage(result, importPath, fontPreloads, libraryTags, defaultTheme, scriptPaths);
699
809
  }
700
810
  } else {
701
- // CMS pages and pages without pageData: use SSR fallback
811
+ // Pages without pageData: use SSR fallback
702
812
  astroContent = buildSSRFallbackPage(result, importPath, fontPreloads, libraryTags, defaultTheme, scriptPaths);
813
+ allFallbackHtml.push(result.html);
703
814
  }
704
815
 
705
816
  const astroFileFull = join(pagesOutDir, result.astroFilePath);
@@ -713,6 +824,26 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
713
824
 
714
825
  console.log(`Generated ${allResults.length} .astro page file(s)`);
715
826
 
827
+ // ----------------------------------------------------------
828
+ // 8.5. Generate utility CSS for SSR fallback HTML
829
+ // ----------------------------------------------------------
830
+ if (allFallbackHtml.length > 0) {
831
+ const allClasses = new Set<string>();
832
+ for (const html of allFallbackHtml) {
833
+ for (const cls of extractUtilityClassesFromHTML(html)) {
834
+ allClasses.add(cls);
835
+ }
836
+ }
837
+ if (allClasses.size > 0) {
838
+ const utilityCSS = generateUtilityCSS(allClasses, breakpoints, responsiveScales);
839
+ if (utilityCSS) {
840
+ const existingCSS = await readFile(join(stylesDir, 'global.css'), 'utf-8');
841
+ await writeFile(join(stylesDir, 'global.css'), existingCSS + '\n\n/* SSR fallback utility classes */\n' + utilityCSS, 'utf-8');
842
+ console.log(`Added ${allClasses.size} utility classes for SSR fallback content`);
843
+ }
844
+ }
845
+ }
846
+
716
847
  // ----------------------------------------------------------
717
848
  // 9. Generate CMS content collections (if templates exist)
718
849
  // ----------------------------------------------------------
@@ -738,15 +869,8 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
738
869
  const rawContent = await readFile(join(cmsItemsDir, itemFile), 'utf-8');
739
870
  const item = JSON.parse(rawContent) as CMSItem;
740
871
 
741
- // Resolve i18n values to the default locale
742
- const resolved: Record<string, unknown> = {};
743
- for (const [key, value] of Object.entries(item)) {
744
- if (isI18nValue(value)) {
745
- resolved[key] = resolveI18nValue(value, i18nConfig.defaultLocale, i18nConfig);
746
- } else {
747
- resolved[key] = value;
748
- }
749
- }
872
+ // Keep i18n values as-is so getStaticPaths() can resolve per-locale
873
+ const resolved: Record<string, unknown> = { ...item };
750
874
 
751
875
  await writeFile(
752
876
  join(collectionDir, itemFile),
@@ -836,7 +960,6 @@ export { collections };
836
960
  },
837
961
  dependencies: {
838
962
  'astro': '^4.0.0',
839
- '@astrojs/sitemap': '^3.0.0',
840
963
  '@astrojs/tailwind': '^5.0.0',
841
964
  'tailwindcss': '^3.4.0',
842
965
  },
@@ -845,12 +968,16 @@ export { collections };
845
968
  await writeFile(join(outDir, 'package.json'), JSON.stringify(packageJson, null, 2), 'utf-8');
846
969
 
847
970
  // astro.config.mjs
971
+ const localeCodes = i18nConfig.locales.map(l => l.code);
972
+ const i18nBlock = i18nConfig.locales.length > 1
973
+ ? `\n i18n: {\n defaultLocale: '${i18nConfig.defaultLocale}',\n locales: [${localeCodes.map(c => `'${c}'`).join(', ')}],\n routing: { prefixDefaultLocale: false },\n },`
974
+ : '';
975
+
848
976
  const astroConfig = `import { defineConfig } from 'astro/config';
849
- import sitemap from '@astrojs/sitemap';
850
977
  import tailwind from '@astrojs/tailwind';
851
978
 
852
- export default defineConfig({${siteUrl ? `\n site: '${siteUrl}',` : ''}
853
- integrations: [sitemap(), tailwind({ applyBaseStyles: false })],
979
+ export default defineConfig({${siteUrl ? `\n site: '${siteUrl}',` : ''}${i18nBlock}
980
+ integrations: [tailwind({ applyBaseStyles: false })],
854
981
  });
855
982
  `;
856
983
 
package/dist/bin/cli.js CHANGED
@@ -82,7 +82,7 @@ function getMatchingHeaders(pathname, headersMap) {
82
82
  return result;
83
83
  }
84
84
  async function startStaticServer(distPath) {
85
- const { SERVE_PORT } = await import("../chunks/constants-5CRJRQNR.js");
85
+ const { SERVE_PORT } = await import("../chunks/constants-L75FR445.js");
86
86
  const headersMap = parseHeadersFile(distPath);
87
87
  const server = await createRuntimeServer({
88
88
  port: SERVE_PORT,
@@ -9,17 +9,17 @@ import {
9
9
  hashContent,
10
10
  injectTrackingScript,
11
11
  isCMSPage
12
- } from "./chunks/chunk-WK5XLASY.js";
13
- import "./chunks/chunk-W6HDII4T.js";
12
+ } from "./chunks/chunk-3NOZVNM4.js";
13
+ import "./chunks/chunk-GKICS7CF.js";
14
14
  import "./chunks/chunk-A6KWUEA6.js";
15
15
  import "./chunks/chunk-YSZ5IUFM.js";
16
16
  import "./chunks/chunk-WQFG7PAH.js";
17
- import "./chunks/chunk-P3FX5HJM.js";
18
- import "./chunks/chunk-AIXKUVNG.js";
19
- import "./chunks/chunk-NV25WXCA.js";
17
+ import "./chunks/chunk-LOJLO2EY.js";
18
+ import "./chunks/chunk-V4SVSX3X.js";
19
+ import "./chunks/chunk-OJ5SROQN.js";
20
20
  import "./chunks/chunk-PGH3ATYI.js";
21
21
  import "./chunks/chunk-UB44F4Z2.js";
22
- import "./chunks/chunk-KULPBDC7.js";
22
+ import "./chunks/chunk-Z7SAOCDG.js";
23
23
  import "./chunks/chunk-KSBZ2L7C.js";
24
24
  export {
25
25
  buildCMSItemPath,
@@ -15,7 +15,7 @@ import {
15
15
  migrateTemplatesDirectory,
16
16
  parseJSON,
17
17
  prepareClientData
18
- } from "./chunk-W6HDII4T.js";
18
+ } from "./chunk-GKICS7CF.js";
19
19
  import {
20
20
  minifyJS,
21
21
  projectPaths
@@ -26,7 +26,7 @@ import {
26
26
  import {
27
27
  isItemDraftForLocale,
28
28
  isItemFullyPublished
29
- } from "./chunk-NV25WXCA.js";
29
+ } from "./chunk-OJ5SROQN.js";
30
30
  import {
31
31
  isI18nValue,
32
32
  resolveI18nValue
@@ -1130,4 +1130,4 @@ export {
1130
1130
  injectTrackingScript,
1131
1131
  buildStaticPages
1132
1132
  };
1133
- //# sourceMappingURL=chunk-WK5XLASY.js.map
1133
+ //# sourceMappingURL=chunk-3NOZVNM4.js.map
@@ -16,14 +16,14 @@ import {
16
16
  isSafePathSegment,
17
17
  isValidIdentifier,
18
18
  resolvePaletteColor
19
- } from "./chunk-P3FX5HJM.js";
19
+ } from "./chunk-LOJLO2EY.js";
20
20
  import {
21
21
  extractAttributesFromNode,
22
22
  isHtmlMapping,
23
23
  processStructure,
24
24
  resolveHtmlMapping,
25
25
  skipEmptyTemplateAttributes
26
- } from "./chunk-AIXKUVNG.js";
26
+ } from "./chunk-V4SVSX3X.js";
27
27
  import {
28
28
  DEFAULT_PREFETCH_CONFIG,
29
29
  SSRRegistry,
@@ -60,7 +60,7 @@ import {
60
60
  singularize,
61
61
  validateCMSItem,
62
62
  validateComponentDefinition
63
- } from "./chunk-NV25WXCA.js";
63
+ } from "./chunk-OJ5SROQN.js";
64
64
  import {
65
65
  DEFAULT_BREAKPOINTS,
66
66
  DEFAULT_I18N_CONFIG,
@@ -81,7 +81,7 @@ import {
81
81
  NODE_TYPE,
82
82
  RAW_HTML_PREFIX,
83
83
  init_constants
84
- } from "./chunk-KULPBDC7.js";
84
+ } from "./chunk-Z7SAOCDG.js";
85
85
  import {
86
86
  __require
87
87
  } from "./chunk-KSBZ2L7C.js";
@@ -1992,7 +1992,8 @@ async function buildComponentHTML(node, globalComponents = {}, pageComponents =
1992
1992
  const interactiveStylesMap = /* @__PURE__ */ new Map();
1993
1993
  const preloadImages = [];
1994
1994
  const neededCollections = /* @__PURE__ */ new Set();
1995
- if (!node) return { html: "", interactiveStylesMap, preloadImages, neededCollections };
1995
+ const ssrFallbackCollector = /* @__PURE__ */ new Map();
1996
+ if (!node) return { html: "", interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector };
1996
1997
  ssrComponentRegistry.merge(globalComponents);
1997
1998
  ssrComponentRegistry.merge(pageComponents);
1998
1999
  const breakpoints = await loadBreakpointConfig();
@@ -2016,10 +2017,12 @@ async function buildComponentHTML(node, globalComponents = {}, pageComponents =
2016
2017
  // Collect high-priority images for preloading
2017
2018
  neededCollections,
2018
2019
  // Track collections that need client-side data
2019
- isProductionBuild
2020
+ isProductionBuild,
2021
+ ssrFallbackCollector
2022
+ // Collect SSR fallback HTML for complex nodes
2020
2023
  };
2021
2024
  const html = await renderNode(node, ctx);
2022
- return { html, interactiveStylesMap, preloadImages, neededCollections };
2025
+ return { html, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector };
2023
2026
  }
2024
2027
  async function renderNestedListPlaceholder(node, ctx) {
2025
2028
  const sourceValue = node.source || node.collection;
@@ -2123,7 +2126,11 @@ async function processList(node, ctx) {
2123
2126
  const templateContent = await renderChildrenAsync(node.children, templateCtx);
2124
2127
  templateHtml = `<template data-meno-item>${templateContent}</template>`;
2125
2128
  }
2126
- return childrenHTML + templateHtml;
2129
+ const listResult = childrenHTML + templateHtml;
2130
+ if (ctx.ssrFallbackCollector && ctx.elementPath) {
2131
+ ctx.ssrFallbackCollector.set(ctx.elementPath.join("."), listResult);
2132
+ }
2133
+ return listResult;
2127
2134
  }
2128
2135
  async function getCollectionItems(node, source, ctx) {
2129
2136
  if (!ctx.cmsService) return [];
@@ -2318,8 +2325,8 @@ async function renderNode(node, ctx) {
2318
2325
  }
2319
2326
  const purify = getDOMPurify();
2320
2327
  const sanitizedHtml = purify ? purify.sanitize(htmlContent, {
2321
- ALLOWED_TAGS: ["svg", "path", "circle", "rect", "line", "polyline", "polygon", "g", "text", "tspan", "image", "defs", "use", "linearGradient", "radialGradient", "stop", "clipPath", "mask", "pattern", "marker", "symbol", "a", "div", "span", "p", "br", "button", "img", "iframe", "video", "audio", "source", "canvas", "b", "i", "u", "strong", "em", "sub", "sup", "mark", "s", "small", "del", "ins", "q", "abbr", "code", "pre", "blockquote", "ul", "ol", "li", "h1", "h2", "h3", "h4", "h5", "h6"],
2322
- ALLOWED_ATTR: ["class", "id", "style", "width", "height", "viewBox", "xmlns", "fill", "stroke", "stroke-width", "stroke-linecap", "stroke-linejoin", "stroke-dasharray", "stroke-dashoffset", "d", "cx", "cy", "r", "x", "y", "x1", "y1", "x2", "y2", "points", "href", "src", "alt", "target", "rel", "data-*", "aria-*", "transform", "opacity", "fill-opacity", "stroke-opacity", "font-size", "font-family", "text-anchor", "dominant-baseline", "offset", "stop-color", "stop-opacity", "frameborder", "allowfullscreen", "allow", "title"],
2328
+ ALLOWED_TAGS: ["svg", "path", "circle", "rect", "line", "polyline", "polygon", "g", "text", "tspan", "image", "defs", "use", "linearGradient", "radialGradient", "stop", "clipPath", "mask", "pattern", "marker", "symbol", "a", "div", "span", "p", "br", "button", "img", "iframe", "video", "audio", "source", "canvas", "b", "i", "u", "strong", "em", "sub", "sup", "mark", "s", "small", "del", "ins", "q", "abbr", "code", "pre", "blockquote", "ul", "ol", "li", "h1", "h2", "h3", "h4", "h5", "h6", "style", "animate", "animateTransform", "animateMotion", "set"],
2329
+ ALLOWED_ATTR: ["class", "id", "style", "width", "height", "viewBox", "xmlns", "fill", "stroke", "stroke-width", "stroke-linecap", "stroke-linejoin", "stroke-dasharray", "stroke-dashoffset", "d", "cx", "cy", "r", "x", "y", "x1", "y1", "x2", "y2", "points", "href", "src", "alt", "target", "rel", "data-*", "aria-*", "transform", "opacity", "fill-opacity", "stroke-opacity", "font-size", "font-family", "text-anchor", "dominant-baseline", "offset", "stop-color", "stop-opacity", "frameborder", "allowfullscreen", "allow", "title", "attributeName", "values", "dur", "begin", "end", "repeatCount", "repeatDur", "keyTimes", "keySplines", "calcMode", "from", "to", "by", "additive", "accumulate", "type", "rotate", "keyPoints", "path"],
2323
2330
  KEEP_CONTENT: true
2324
2331
  }) : htmlContent;
2325
2332
  const optimizedHtml = ctx.imageMetadataMap ? rewriteRichTextImages(sanitizedHtml, ctx.imageMetadataMap) : sanitizedHtml;
@@ -2803,7 +2810,11 @@ function renderLocaleList(node, ctx) {
2803
2810
  const linksHTML = showSeparator ? links.join(`<span${separatorClassAttr}></span>`) : links.join("");
2804
2811
  const nodeAttributes = extractAttributesFromNode(node);
2805
2812
  const attrsStr = buildAttributes(nodeAttributes);
2806
- return `<div data-locale-list="true"${containerClassAttr}${localeListStyleAttr}${attrsStr}>${linksHTML}</div>`;
2813
+ const localeListResult = `<div data-locale-list="true"${containerClassAttr}${localeListStyleAttr}${attrsStr}>${linksHTML}</div>`;
2814
+ if (ctx.ssrFallbackCollector && ctx.elementPath) {
2815
+ ctx.ssrFallbackCollector.set(ctx.elementPath.join("."), localeListResult);
2816
+ }
2817
+ return localeListResult;
2807
2818
  }
2808
2819
  return '<div data-locale-list="true"></div>';
2809
2820
  }
@@ -2831,7 +2842,7 @@ async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", ba
2831
2842
  }
2832
2843
  }
2833
2844
  const pageComponents = pageData?.components || {};
2834
- const { html: contentHTML, interactiveStylesMap, preloadImages, neededCollections } = rootNode ? await buildComponentHTML(rootNode, globalComponents, pageComponents, effectiveLocale, config, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild) : { html: "", interactiveStylesMap: /* @__PURE__ */ new Map(), preloadImages: [], neededCollections: /* @__PURE__ */ new Set() };
2845
+ const { html: contentHTML, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector } = rootNode ? await buildComponentHTML(rootNode, globalComponents, pageComponents, effectiveLocale, config, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild) : { html: "", interactiveStylesMap: /* @__PURE__ */ new Map(), preloadImages: [], neededCollections: /* @__PURE__ */ new Set(), ssrFallbackCollector: /* @__PURE__ */ new Map() };
2835
2846
  const javascript = await collectComponentJavaScript(globalComponents, pageComponents);
2836
2847
  const componentCSS = collectComponentCSS(globalComponents, pageComponents);
2837
2848
  const fullUrl = baseUrl ? `${baseUrl}${pagePath}` : pagePath;
@@ -2851,7 +2862,8 @@ async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", ba
2851
2862
  locale: effectiveLocale,
2852
2863
  interactiveStylesMap,
2853
2864
  preloadImages,
2854
- neededCollections
2865
+ neededCollections,
2866
+ ssrFallbackCollector
2855
2867
  };
2856
2868
  }
2857
2869
 
@@ -3749,6 +3761,7 @@ export {
3749
3761
  generateThemeColorVariablesCSS,
3750
3762
  generateVariablesCSS,
3751
3763
  buildSlugIndex,
3764
+ translatePath,
3752
3765
  resolveSlugToPageId,
3753
3766
  escapeHtml,
3754
3767
  buildAttributes,
@@ -3777,4 +3790,4 @@ export {
3777
3790
  FileSystemCMSProvider,
3778
3791
  migrateTemplatesDirectory
3779
3792
  };
3780
- //# sourceMappingURL=chunk-W6HDII4T.js.map
3793
+ //# sourceMappingURL=chunk-GKICS7CF.js.map