meno-core 1.0.46 → 1.0.48

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 (68) hide show
  1. package/build-astro.ts +13 -77
  2. package/dist/build-static.js +7 -7
  3. package/dist/chunks/{chunk-FED5MME6.js → chunk-3FHJUHAS.js} +5 -4
  4. package/dist/chunks/chunk-3FHJUHAS.js.map +7 -0
  5. package/dist/chunks/{chunk-XSWR3QLI.js → chunk-B2RTLDXY.js} +130 -130
  6. package/dist/chunks/chunk-B2RTLDXY.js.map +7 -0
  7. package/dist/chunks/{chunk-ZTKHJQ2Z.js → chunk-BJRKEPMP.js} +2 -2
  8. package/dist/chunks/{chunk-ORN7S4AP.js → chunk-D5E3OKSL.js} +5 -5
  9. package/dist/chunks/{chunk-KITQJYZV.js → chunk-EK4KESLU.js} +33 -3
  10. package/dist/chunks/chunk-EK4KESLU.js.map +7 -0
  11. package/dist/chunks/{chunk-C6U5T5S5.js → chunk-NKUV77SR.js} +43 -10
  12. package/dist/chunks/chunk-NKUV77SR.js.map +7 -0
  13. package/dist/chunks/{chunk-ZWYDT3QJ.js → chunk-NP76N4HQ.js} +4 -4
  14. package/dist/chunks/{chunk-ZWYDT3QJ.js.map → chunk-NP76N4HQ.js.map} +2 -2
  15. package/dist/chunks/{chunk-IF3RATBY.js → chunk-RQSTH2BS.js} +2 -2
  16. package/dist/chunks/{chunk-77ZB6353.js → chunk-TPQ7APVQ.js} +42 -86
  17. package/dist/chunks/chunk-TPQ7APVQ.js.map +7 -0
  18. package/dist/chunks/{configService-DYCUEURL.js → configService-IGJEC3MC.js} +3 -3
  19. package/dist/entries/server-router.js +8 -8
  20. package/dist/lib/client/index.js +16 -4
  21. package/dist/lib/client/index.js.map +2 -2
  22. package/dist/lib/server/index.js +1803 -383
  23. package/dist/lib/server/index.js.map +4 -4
  24. package/dist/lib/shared/index.js +8 -4
  25. package/dist/lib/shared/index.js.map +1 -1
  26. package/lib/client/core/ComponentBuilder.test.ts +38 -0
  27. package/lib/client/core/ComponentBuilder.ts +25 -1
  28. package/lib/server/astro/astroEmitHelpers.ts +5 -0
  29. package/lib/server/astro/cmsPageEmitter.ts +15 -2
  30. package/lib/server/astro/componentEmitter.ts +13 -5
  31. package/lib/server/astro/nodeToAstro.ts +23 -9
  32. package/lib/server/astro/pageEmitter.ts +15 -2
  33. package/lib/server/index.ts +1 -1
  34. package/lib/server/jsonLoader.test.ts +0 -17
  35. package/lib/server/jsonLoader.ts +0 -81
  36. package/lib/server/routes/api/variables.ts +4 -2
  37. package/lib/server/ssr/htmlGenerator.test.ts +4 -4
  38. package/lib/server/ssr/htmlGenerator.ts +7 -2
  39. package/lib/server/ssr/imageMetadata.ts +15 -9
  40. package/lib/server/ssr/ssrRenderer.test.ts +103 -1
  41. package/lib/server/ssr/ssrRenderer.ts +41 -35
  42. package/lib/server/utils/jsonLineMapper.test.ts +53 -1
  43. package/lib/server/utils/jsonLineMapper.ts +43 -3
  44. package/lib/server/webflow/buildWebflow.ts +343 -123
  45. package/lib/server/webflow/index.ts +1 -0
  46. package/lib/server/webflow/nodeToWebflow.test.ts +3170 -0
  47. package/lib/server/webflow/nodeToWebflow.ts +2141 -129
  48. package/lib/server/webflow/styleMapper.test.ts +389 -0
  49. package/lib/server/webflow/styleMapper.ts +517 -63
  50. package/lib/server/webflow/templateWrapper.ts +49 -0
  51. package/lib/server/webflow/types.ts +218 -18
  52. package/lib/shared/elementClassName.test.ts +15 -0
  53. package/lib/shared/elementClassName.ts +7 -3
  54. package/lib/shared/registry/nodeTypes/ListNodeType.ts +1 -1
  55. package/lib/shared/styleNodeUtils.test.ts +47 -1
  56. package/lib/shared/styleNodeUtils.ts +7 -7
  57. package/lib/shared/types/index.ts +1 -0
  58. package/lib/shared/types/variables.ts +37 -0
  59. package/package.json +1 -1
  60. package/dist/chunks/chunk-77ZB6353.js.map +0 -7
  61. package/dist/chunks/chunk-C6U5T5S5.js.map +0 -7
  62. package/dist/chunks/chunk-FED5MME6.js.map +0 -7
  63. package/dist/chunks/chunk-KITQJYZV.js.map +0 -7
  64. package/dist/chunks/chunk-XSWR3QLI.js.map +0 -7
  65. /package/dist/chunks/{chunk-ZTKHJQ2Z.js.map → chunk-BJRKEPMP.js.map} +0 -0
  66. /package/dist/chunks/{chunk-ORN7S4AP.js.map → chunk-D5E3OKSL.js.map} +0 -0
  67. /package/dist/chunks/{chunk-IF3RATBY.js.map → chunk-RQSTH2BS.js.map} +0 -0
  68. /package/dist/chunks/{configService-DYCUEURL.js.map → configService-IGJEC3MC.js.map} +0 -0
@@ -3,7 +3,7 @@ import {
3
3
  } from "../../chunks/chunk-4OFZP5NQ.js";
4
4
  import {
5
5
  buildStaticPages
6
- } from "../../chunks/chunk-ORN7S4AP.js";
6
+ } from "../../chunks/chunk-D5E3OKSL.js";
7
7
  import {
8
8
  ComponentService,
9
9
  EnumService,
@@ -32,7 +32,7 @@ import {
32
32
  logResponseTime,
33
33
  withErrorHandling,
34
34
  withLogging
35
- } from "../../chunks/chunk-C6U5T5S5.js";
35
+ } from "../../chunks/chunk-NKUV77SR.js";
36
36
  import {
37
37
  CMSService,
38
38
  ColorService,
@@ -62,13 +62,13 @@ import {
62
62
  generateThemeColorVariablesCSS,
63
63
  generateVariablesCSS,
64
64
  getJSValidationErrors,
65
+ getLocaleLinks,
65
66
  loadBreakpointConfig,
66
67
  loadComponentDirectory,
67
68
  loadI18nConfig,
68
69
  loadIconsConfig,
69
70
  loadJSONFile,
70
71
  loadProjectConfig,
71
- loadResponsiveScalesConfig,
72
72
  mapPageNameToPath,
73
73
  menoFilterScript,
74
74
  mergeLibraries,
@@ -83,11 +83,11 @@ import {
83
83
  styleToString,
84
84
  translatePath,
85
85
  variableService
86
- } from "../../chunks/chunk-77ZB6353.js";
86
+ } from "../../chunks/chunk-TPQ7APVQ.js";
87
87
  import {
88
88
  ConfigService,
89
89
  configService
90
- } from "../../chunks/chunk-ZTKHJQ2Z.js";
90
+ } from "../../chunks/chunk-BJRKEPMP.js";
91
91
  import {
92
92
  bundleFile,
93
93
  createRuntimeServer,
@@ -116,19 +116,35 @@ import {
116
116
  spawnProcess,
117
117
  writeFile
118
118
  } from "../../chunks/chunk-WQFG7PAH.js";
119
- import "../../chunks/chunk-IF3RATBY.js";
120
- import "../../chunks/chunk-KITQJYZV.js";
121
- import "../../chunks/chunk-ZWYDT3QJ.js";
119
+ import "../../chunks/chunk-RQSTH2BS.js";
122
120
  import {
121
+ resolvePaletteColor
122
+ } from "../../chunks/chunk-EK4KESLU.js";
123
+ import {
124
+ hasTemplates,
125
+ isHtmlMapping,
126
+ processCodeTemplates,
127
+ resolveHtmlMapping
128
+ } from "../../chunks/chunk-NP76N4HQ.js";
129
+ import {
130
+ addItemUrl,
131
+ buildTemplateContext,
123
132
  extractInteractiveStyleMappings,
124
133
  generateAllInteractiveCSS,
125
134
  generateElementClassName,
135
+ generateInteractiveCSS,
136
+ getNestedValue,
126
137
  hasIf,
127
138
  hasInteractiveStyleMappings,
139
+ isBooleanMapping,
140
+ isCssNamedColor,
128
141
  isItemDraftForLocale,
129
142
  isVoidElement,
143
+ resolveItemsTemplate,
144
+ resolvePropsFromDefinition,
145
+ shortHash,
130
146
  singularize
131
- } from "../../chunks/chunk-FED5MME6.js";
147
+ } from "../../chunks/chunk-3FHJUHAS.js";
132
148
  import {
133
149
  DEFAULT_BREAKPOINTS,
134
150
  DEFAULT_I18N_CONFIG,
@@ -136,8 +152,9 @@ import {
136
152
  getScaleMultiplier,
137
153
  isI18nValue,
138
154
  resolveI18nValue,
155
+ resolveVariableValueAtBreakpoint,
139
156
  scalePropertyValue
140
- } from "../../chunks/chunk-XSWR3QLI.js";
157
+ } from "../../chunks/chunk-B2RTLDXY.js";
141
158
  import "../../chunks/chunk-UB44F4Z2.js";
142
159
  import {
143
160
  HMR_ROUTE,
@@ -740,6 +757,9 @@ function stripRawHtmlPrefixDeep(value) {
740
757
  }
741
758
  return value;
742
759
  }
760
+ function astroComponentName(name) {
761
+ return name.charAt(0).toUpperCase() + name.slice(1);
762
+ }
743
763
 
744
764
  // lib/server/astro/nodeToAstro.ts
745
765
  function ind(ctx) {
@@ -795,7 +815,7 @@ function emitAttrValue(key, value, ctx) {
795
815
  }
796
816
  return `${key}="${escapeJSX(value)}"`;
797
817
  }
798
- function isHtmlMapping(value) {
818
+ function isHtmlMapping2(value) {
799
819
  return typeof value === "object" && value !== null && "_mapping" in value && value._mapping === true;
800
820
  }
801
821
  function isResponsiveStyle2(style) {
@@ -965,7 +985,7 @@ function resolveTemplate(text, ctx) {
965
985
  return `{${trimmed}}`;
966
986
  });
967
987
  }
968
- function hasTemplates(text) {
988
+ function hasTemplates2(text) {
969
989
  return /\{\{.+?\}\}/.test(text);
970
990
  }
971
991
  function buildElementClass(ctx, label) {
@@ -984,7 +1004,7 @@ function buildAttributesString(attributes, ctx) {
984
1004
  if (value) parts.push(key);
985
1005
  } else {
986
1006
  const strVal = String(value);
987
- if (hasTemplates(strVal) && ctx.isComponentDef) {
1007
+ if (hasTemplates2(strVal) && ctx.isComponentDef) {
988
1008
  const fullMatch = strVal.match(/^\{\{(.+)\}\}$/);
989
1009
  if (fullMatch) {
990
1010
  let expr = fullMatch[1].trim();
@@ -1006,7 +1026,7 @@ function buildAttributesString(attributes, ctx) {
1006
1026
  });
1007
1027
  parts.push(`${key}={\`${resolved}\`}`);
1008
1028
  }
1009
- } else if (ctx.listItemBinding && hasTemplates(strVal)) {
1029
+ } else if (ctx.listItemBinding && hasTemplates2(strVal)) {
1010
1030
  const fullMatch = strVal.match(/^\{\{(.+)\}\}$/);
1011
1031
  if (fullMatch) {
1012
1032
  let expr = fullMatch[1].trim();
@@ -1081,7 +1101,7 @@ function nodeToAstro(node, ctx) {
1081
1101
  return `${ind(ctx)}${transformed}
1082
1102
  `;
1083
1103
  }
1084
- if (hasTemplates(node) && ctx.isComponentDef) {
1104
+ if (hasTemplates2(node) && ctx.isComponentDef) {
1085
1105
  return `${ind(ctx)}${resolveTemplate(node, ctx)}
1086
1106
  `;
1087
1107
  }
@@ -1222,7 +1242,7 @@ function imageImportPath(srcPath, fileDepth) {
1222
1242
  function isStaticImageSrc(src, ctx) {
1223
1243
  if (!src) return false;
1224
1244
  if (typeof src !== "string") return false;
1225
- if (hasTemplates(src)) return false;
1245
+ if (hasTemplates2(src)) return false;
1226
1246
  if (!src.startsWith("/images/")) return false;
1227
1247
  if (!ctx.imageMetadataMap?.has(src)) return false;
1228
1248
  return true;
@@ -1303,6 +1323,9 @@ function emitImageNode(node, ctx) {
1303
1323
  let elementClass = null;
1304
1324
  if (node.interactiveStyles && node.interactiveStyles.length > 0 || node.generateElementClass) {
1305
1325
  elementClass = buildElementClass(ctx, node.label);
1326
+ if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1327
+ ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles);
1328
+ }
1306
1329
  }
1307
1330
  const { classExpr, styleAttr } = buildClassAndStyleExpression(
1308
1331
  style,
@@ -1414,7 +1437,7 @@ function emitHtmlNode(node, ctx) {
1414
1437
  const style = node.style;
1415
1438
  let isDynamic = false;
1416
1439
  let dynamicTagVar = "";
1417
- if (hasTemplates(tag) && ctx.isComponentDef) {
1440
+ if (hasTemplates2(tag) && ctx.isComponentDef) {
1418
1441
  isDynamic = true;
1419
1442
  dynamicTagVar = `Tag_${ctx.elementPath.join("_")}`;
1420
1443
  const resolved = tag.replace(/\{\{(.+?)\}\}/g, (_, expr) => `\${${expr.trim()}}`);
@@ -1426,6 +1449,9 @@ function emitHtmlNode(node, ctx) {
1426
1449
  let elementClass = null;
1427
1450
  if (node.interactiveStyles && node.interactiveStyles.length > 0 || node.generateElementClass) {
1428
1451
  elementClass = buildElementClass(ctx, label);
1452
+ if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1453
+ ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles);
1454
+ }
1429
1455
  }
1430
1456
  const { classExpr, styleAttr } = buildClassAndStyleExpression(style, node.interactiveStyles, elementClass, ctx);
1431
1457
  const attrs = buildAttributesString(node.attributes, ctx);
@@ -1444,15 +1470,15 @@ ${emitIfClose(node, ctx)}`;
1444
1470
  ${emitIfClose(node, ctx)}`;
1445
1471
  }
1446
1472
  function emitComponentInstance(node, ctx) {
1447
- const name = node.component;
1448
- ctx.imports.add(name);
1473
+ const name = astroComponentName(node.component);
1474
+ ctx.imports.add(node.component);
1449
1475
  const ifExpr = emitIfOpen(node, ctx);
1450
1476
  const propParts = [];
1451
1477
  if (node.props) {
1452
1478
  for (const [key, rawValue] of Object.entries(node.props)) {
1453
1479
  if (key === "children") continue;
1454
1480
  const value = resolveI18n(rawValue, ctx);
1455
- if (typeof value === "string" && hasTemplates(value) && ctx.isComponentDef) {
1481
+ if (typeof value === "string" && hasTemplates2(value) && ctx.isComponentDef) {
1456
1482
  const fullMatch = value.match(/^\{\{(.+)\}\}$/);
1457
1483
  if (fullMatch) {
1458
1484
  let expr = fullMatch[1].trim();
@@ -1529,10 +1555,13 @@ function emitEmbedNode(node, ctx) {
1529
1555
  let elementClass = null;
1530
1556
  if (node.interactiveStyles && node.interactiveStyles.length > 0 || node.generateElementClass) {
1531
1557
  elementClass = buildElementClass(ctx, node.label);
1558
+ if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1559
+ ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles);
1560
+ }
1532
1561
  }
1533
1562
  const { classExpr, styleAttr } = buildClassAndStyleExpression(style, node.interactiveStyles, elementClass, ctx);
1534
1563
  const attrs = buildAttributesString(node.attributes, ctx);
1535
- if (isHtmlMapping(node.html)) {
1564
+ if (isHtmlMapping2(node.html)) {
1536
1565
  if (ctx.isComponentDef) {
1537
1566
  const propRef = node.html.prop;
1538
1567
  return `${ifExpr}${ind(ctx)}<div${classExpr.replace('"', '"oem ') || ' class="oem"'}${attrs}>
@@ -1541,7 +1570,7 @@ ${ind(ctx)}</div>
1541
1570
  ${emitIfClose(node, ctx)}`;
1542
1571
  }
1543
1572
  }
1544
- if (typeof node.html === "string" && hasTemplates(node.html) && ctx.isComponentDef) {
1573
+ if (typeof node.html === "string" && hasTemplates2(node.html) && ctx.isComponentDef) {
1545
1574
  const fullMatch = node.html.match(/^\{\{(.+)\}\}$/);
1546
1575
  if (fullMatch) {
1547
1576
  let propRef = fullMatch[1].trim();
@@ -1582,6 +1611,9 @@ function emitLinkNode(node, ctx) {
1582
1611
  let elementClass = null;
1583
1612
  if (node.interactiveStyles && node.interactiveStyles.length > 0 || node.generateElementClass) {
1584
1613
  elementClass = buildElementClass(ctx, node.label);
1614
+ if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1615
+ ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles);
1616
+ }
1585
1617
  }
1586
1618
  const { classExpr, styleAttr } = buildClassAndStyleExpression(style, node.interactiveStyles, elementClass, ctx);
1587
1619
  let finalClassExpr = classExpr;
@@ -1604,7 +1636,7 @@ function emitLinkNode(node, ctx) {
1604
1636
  }
1605
1637
  } else {
1606
1638
  const href = typeof nodeHref === "string" ? nodeHref : "#";
1607
- if (hasTemplates(href) && ctx.isComponentDef) {
1639
+ if (hasTemplates2(href) && ctx.isComponentDef) {
1608
1640
  const fullMatch = href.match(/^\{\{(.+)\}\}$/);
1609
1641
  if (fullMatch) {
1610
1642
  let expr = fullMatch[1].trim();
@@ -1658,6 +1690,9 @@ function emitImageTypeNode(node, ctx) {
1658
1690
  let elementClass = null;
1659
1691
  if (node.interactiveStyles && node.interactiveStyles.length > 0 || node.generateElementClass) {
1660
1692
  elementClass = buildElementClass(ctx, node.label);
1693
+ if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1694
+ ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles);
1695
+ }
1661
1696
  }
1662
1697
  const { classExpr, styleAttr } = buildClassAndStyleExpression(
1663
1698
  style,
@@ -1838,6 +1873,9 @@ function emitLocaleListNode(node, ctx) {
1838
1873
  let elementClass = null;
1839
1874
  if (node.interactiveStyles && node.interactiveStyles.length > 0 || node.generateElementClass) {
1840
1875
  elementClass = buildElementClass(ctx, node.label);
1876
+ if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1877
+ ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles);
1878
+ }
1841
1879
  }
1842
1880
  const { classExpr: containerClassExpr, styleAttr: containerStyleAttr } = buildClassAndStyleExpression(
1843
1881
  style,
@@ -2089,7 +2127,7 @@ function mergeClassNameOntoRoot(template) {
2089
2127
  }
2090
2128
  return prefix + tagName + ` class={className}` + attrs + close + template.slice(fullMatch.length);
2091
2129
  }
2092
- function emitAstroComponent(name, def, allComponents, breakpoints = DEFAULT_BREAKPOINTS, defaultLocale = "en", responsiveScales) {
2130
+ function emitAstroComponent(name, def, allComponents, breakpoints = DEFAULT_BREAKPOINTS, defaultLocale = "en", responsiveScales, remConfig) {
2093
2131
  const comp = def.component;
2094
2132
  const propDefs = comp.interface || {};
2095
2133
  const structure = comp.structure;
@@ -2110,8 +2148,9 @@ function emitAstroComponent(name, def, allComponents, breakpoints = DEFAULT_BREA
2110
2148
  responsiveScales,
2111
2149
  defaultLocale,
2112
2150
  imageImports: /* @__PURE__ */ new Map(),
2113
- fileDepth: 0
2151
+ fileDepth: 0,
2114
2152
  // components live at src/components/
2153
+ collectedInteractiveStyles: /* @__PURE__ */ new Map()
2115
2154
  };
2116
2155
  let templateBody = nodeToAstro(structure, ctx);
2117
2156
  templateBody = mergeClassNameOntoRoot(templateBody);
@@ -2127,16 +2166,21 @@ function emitAstroComponent(name, def, allComponents, breakpoints = DEFAULT_BREA
2127
2166
  <style>
2128
2167
  ${comp.css}
2129
2168
  </style>
2169
+ ` : "";
2170
+ const interactiveStyleSection = ctx.collectedInteractiveStyles.size > 0 ? `
2171
+ <style is:global>
2172
+ ${generateAllInteractiveCSS(ctx.collectedInteractiveStyles, breakpoints, remConfig, responsiveScales)}
2173
+ </style>
2130
2174
  ` : "";
2131
2175
  const scriptSection = comp.javascript ? buildScriptSection(comp.javascript, comp, propDefs) : "";
2132
2176
  return `---
2133
2177
  ${frontmatter}---
2134
- ${templateBody}${styleSection}${scriptSection}`;
2178
+ ${templateBody}${styleSection}${interactiveStyleSection}${scriptSection}`;
2135
2179
  }
2136
2180
  function buildFrontmatter(componentName, propDefs, imports, dynamicTags, i18nDefaultLocale, imageImports) {
2137
2181
  const lines = [];
2138
2182
  for (const imp of Array.from(imports).sort()) {
2139
- lines.push(`import ${imp} from './${imp}.astro';`);
2183
+ lines.push(`import ${astroComponentName(imp)} from './${imp}.astro';`);
2140
2184
  }
2141
2185
  if (imageImports && imageImports.size > 0) {
2142
2186
  lines.push(`import { Picture } from 'astro:assets';`);
@@ -2237,7 +2281,7 @@ function transformDefineVarsJS(js, varNames) {
2237
2281
  return result;
2238
2282
  }
2239
2283
  function buildScriptSection(js, comp, propDefs) {
2240
- const cleanTextNodes = `(function _w(n){var c=n.firstChild,x;while(c){x=c.nextSibling;if(c.nodeType===3){if(!c.textContent.trim())n.removeChild(c)}else if(c.nodeType===1)_w(c);c=x}})(el);`;
2284
+ const cleanTextNodes = `(function _w(n){var c=n.firstChild,x;while(c){x=c.nextSibling;if(c.nodeType===3){if(!c.textContent.trim())n.removeChild(c)}else if(c.nodeType===1){if(c.tagName==='SCRIPT')n.removeChild(c);else _w(c)}c=x}})(el);`;
2241
2285
  const deferWrapper = (innerJS) => `var el = document.currentScript.previousElementSibling;
2242
2286
  function __init__() {
2243
2287
  ${cleanTextNodes}
@@ -2300,7 +2344,8 @@ function emitAstroPage(options) {
2300
2344
  currentPageSlugMap,
2301
2345
  slugMappings,
2302
2346
  imageFormat,
2303
- processedRawHtml
2347
+ processedRawHtml,
2348
+ remConfig
2304
2349
  } = options;
2305
2350
  const breakpoints = breakpointsOpt ?? DEFAULT_BREAKPOINTS;
2306
2351
  const root = pageData.root;
@@ -2331,7 +2376,8 @@ function emitAstroPage(options) {
2331
2376
  imageFormat,
2332
2377
  processedRawHtml,
2333
2378
  imageImports: /* @__PURE__ */ new Map(),
2334
- fileDepth
2379
+ fileDepth,
2380
+ collectedInteractiveStyles: /* @__PURE__ */ new Map()
2335
2381
  };
2336
2382
  const templateBody = nodeToAstro(root, ctx);
2337
2383
  const importLines = [];
@@ -2350,13 +2396,18 @@ function emitAstroPage(options) {
2350
2396
  const componentImports = Array.from(ctx.imports).sort();
2351
2397
  for (const comp of componentImports) {
2352
2398
  const path = componentImportPath(fileDepth, comp);
2353
- importLines.push(`import ${comp} from '${path}';`);
2399
+ importLines.push(`import ${astroComponentName(comp)} from '${path}';`);
2354
2400
  }
2355
2401
  const scriptsArrayLiteral = scriptPaths.length > 0 ? `[${scriptPaths.map((s) => `"${s}"`).join(", ")}]` : "[]";
2356
2402
  const libraryTagsLiteral = `{ headCSS: \`${escapeTemplateLiteral2(libraryTags.headCSS || "")}\`, headJS: \`${escapeTemplateLiteral2(libraryTags.headJS || "")}\`, bodyEndJS: \`${escapeTemplateLiteral2(libraryTags.bodyEndJS || "")}\` }`;
2357
2403
  const escapedMeta = escapeTemplateLiteral2(meta);
2358
2404
  const escapedFontPreloads = escapeTemplateLiteral2(fontPreloads);
2359
2405
  const extraFrontmatter = ctx.frontmatterLines && ctx.frontmatterLines.length > 0 ? "\n" + ctx.frontmatterLines.join("\n") : "";
2406
+ const interactiveStyleSection = ctx.collectedInteractiveStyles.size > 0 ? `
2407
+ <style is:global>
2408
+ ${generateAllInteractiveCSS(ctx.collectedInteractiveStyles, breakpoints, remConfig, responsiveScales)}
2409
+ </style>
2410
+ ` : "";
2360
2411
  return `---
2361
2412
  ${importLines.join("\n")}${extraFrontmatter}
2362
2413
  ---
@@ -2372,7 +2423,7 @@ ${importLines.join("\n")}${extraFrontmatter}
2372
2423
  <div id="root">
2373
2424
  ${templateBody} </div>
2374
2425
  </BaseLayout>
2375
- `;
2426
+ ${interactiveStyleSection}`;
2376
2427
  }
2377
2428
  function buildEmptyPage(layoutImport, title, meta, locale, theme, fontPreloads, libraryTags, scriptPaths) {
2378
2429
  const escapedMeta = escapeTemplateLiteral2(meta);
@@ -2521,7 +2572,8 @@ function emitCMSPage(options) {
2521
2572
  i18nConfig,
2522
2573
  isMultiLocale,
2523
2574
  slugMappings,
2524
- processedRawHtml
2575
+ processedRawHtml,
2576
+ remConfig
2525
2577
  } = options;
2526
2578
  const breakpoints = breakpointsOpt ?? DEFAULT_BREAKPOINTS;
2527
2579
  const binding = "entry";
@@ -2569,7 +2621,8 @@ function emitCMSPage(options) {
2569
2621
  imageFormat: options.imageFormat,
2570
2622
  processedRawHtml,
2571
2623
  imageImports: /* @__PURE__ */ new Map(),
2572
- fileDepth
2624
+ fileDepth,
2625
+ collectedInteractiveStyles: /* @__PURE__ */ new Map()
2573
2626
  };
2574
2627
  const templateBody = nodeToAstro(root, ctx);
2575
2628
  const importLines = [];
@@ -2585,7 +2638,7 @@ function emitCMSPage(options) {
2585
2638
  const componentImports = Array.from(ctx.imports).sort();
2586
2639
  for (const comp of componentImports) {
2587
2640
  const path = componentImportPath2(fileDepth, comp);
2588
- importLines.push(`import ${comp} from '${path}';`);
2641
+ importLines.push(`import ${astroComponentName(comp)} from '${path}';`);
2589
2642
  }
2590
2643
  const staticPaths = buildGetStaticPaths(cmsSchema, isMultiLocale, i18nConfig, locale);
2591
2644
  const scriptsArrayLiteral = scriptPaths.length > 0 ? `[${scriptPaths.map((s) => `"${s}"`).join(", ")}]` : "[]";
@@ -2600,6 +2653,11 @@ function emitCMSPage(options) {
2600
2653
  if (v && typeof v === 'object' && v._i18n) return v['${locale}'] ?? v['${i18nConfig.defaultLocale}'] ?? Object.values(v).find(x => x !== true && x !== undefined) ?? '';
2601
2654
  return v ?? '';
2602
2655
  }`;
2656
+ const interactiveStyleSection = ctx.collectedInteractiveStyles.size > 0 ? `
2657
+ <style is:global>
2658
+ ${generateAllInteractiveCSS(ctx.collectedInteractiveStyles, breakpoints, remConfig, responsiveScales)}
2659
+ </style>
2660
+ ` : "";
2603
2661
  return `---
2604
2662
  ${importLines.join("\n")}
2605
2663
 
@@ -2619,7 +2677,7 @@ ${resolverHelper}
2619
2677
  <div id="root">
2620
2678
  ${templateBody} </div>
2621
2679
  </BaseLayout>
2622
- `;
2680
+ ${interactiveStyleSection}`;
2623
2681
  }
2624
2682
  function buildEmptyCMSPage(layoutImport, title, meta, locale, theme, fontPreloads, libraryTags, scriptPaths, cmsSchema, isMultiLocale, i18nConfig, binding, richTextFields) {
2625
2683
  const escapedMeta = escapeTemplateLiteral3(meta);
@@ -2876,14 +2934,10 @@ import BaseLayout from '${importPath}';
2876
2934
  `;
2877
2935
  }
2878
2936
  async function buildAstroProject(projectRoot, outputDir) {
2879
- const startTime = Date.now();
2880
- console.log("\u{1F3D7}\uFE0F Building Astro export...\n");
2881
2937
  configService.reset();
2882
2938
  const projectConfig = await loadProjectConfig();
2883
2939
  const siteUrl = projectConfig.siteUrl?.replace(/\/$/, "") || "";
2884
2940
  const i18nConfig = await loadI18nConfig();
2885
- console.log(`\u{1F310} Locales: ${i18nConfig.locales.map((l) => l.code).join(", ")} (default: ${i18nConfig.defaultLocale})
2886
- `);
2887
2941
  await migrateTemplatesDirectory();
2888
2942
  const { components, warnings, errors: compErrors } = await loadComponentDirectory(projectPaths.components());
2889
2943
  const globalComponents = {};
@@ -2892,24 +2946,17 @@ async function buildAstroProject(projectRoot, outputDir) {
2892
2946
  });
2893
2947
  for (const w of warnings) console.warn(` Warning: ${w}`);
2894
2948
  for (const e of compErrors) console.error(` Error: ${e}`);
2895
- console.log(`Loaded ${components.size} global component(s)
2896
- `);
2897
2949
  const cmsProvider = new FileSystemCMSProvider(projectPaths.templates(), projectPaths.cms());
2898
2950
  const cmsService = new CMSService(cmsProvider);
2899
2951
  await cmsService.initialize();
2900
- console.log("CMS service initialized\n");
2901
2952
  const themeConfig = await colorService.loadThemeConfig();
2902
2953
  const variablesConfig = await variableService.loadConfig();
2903
2954
  const breakpoints = await loadBreakpointConfig();
2904
- const responsiveScales = await loadResponsiveScalesConfig();
2905
2955
  await configService.load();
2956
+ const responsiveScales = configService.getResponsiveScales();
2906
2957
  const globalLibraries = configService.getLibraries();
2907
2958
  const componentLibraries = collectComponentLibraries(globalComponents);
2908
2959
  const imageMetadataMap = await buildImageMetadataMap();
2909
- if (imageMetadataMap.size > 0) {
2910
- console.log(`Loaded image metadata for ${imageMetadataMap.size} image(s)
2911
- `);
2912
- }
2913
2960
  const outDir = outputDir || join(projectPaths.project, "astro-export");
2914
2961
  if (existsSync(outDir)) {
2915
2962
  rmSync(outDir, { recursive: true, force: true });
@@ -2935,8 +2982,6 @@ async function buildAstroProject(projectRoot, outputDir) {
2935
2982
  console.warn("No pages found in ./pages directory");
2936
2983
  return { pages: 0, cmsPages: 0, collections: 0, errors: 0 };
2937
2984
  }
2938
- console.log(`Found ${pageFiles.length} page(s) to process
2939
- `);
2940
2985
  const slugMappings = [];
2941
2986
  for (const file of pageFiles) {
2942
2987
  const pageName = file.replace(".json", "");
@@ -3010,7 +3055,6 @@ async function buildAstroProject(projectRoot, outputDir) {
3010
3055
  const pageData = parseJSON(pageContent);
3011
3056
  const isDevBuild = process.env.MENO_DEV_BUILD === "true";
3012
3057
  if (pageData.meta?.draft === true && !isDevBuild) {
3013
- console.log(` Skipping draft: ${basePath}`);
3014
3058
  continue;
3015
3059
  }
3016
3060
  const slugs = pageData.meta?.slugs;
@@ -3044,7 +3088,6 @@ async function buildAstroProject(projectRoot, outputDir) {
3044
3088
  // isProductionBuild
3045
3089
  );
3046
3090
  processRenderResult(result, urlPath, astroFilePath, fileDepth, pageData, pageName, false);
3047
- console.log(` Rendered: ${urlPath}`);
3048
3091
  }
3049
3092
  } catch (error) {
3050
3093
  console.error(` Error rendering ${basePath}:`, error?.message || error);
@@ -3087,16 +3130,12 @@ async function buildAstroProject(projectRoot, outputDir) {
3087
3130
  const faviconTag = iconsConfig.favicon ? `<link rel="icon" href="${iconsConfig.favicon.replace(/"/g, "&quot;")}" />` : "";
3088
3131
  const appleTouchIconTag = iconsConfig.appleTouchIcon ? `<link rel="apple-touch-icon" href="${iconsConfig.appleTouchIcon.replace(/"/g, "&quot;")}" />` : "";
3089
3132
  const iconTagsHtml = [faviconTag, appleTouchIconTag].filter(Boolean).join("\n ");
3133
+ const remConversionConfig = configService.getRemConversion();
3090
3134
  const templatesDir = projectPaths.templates();
3091
3135
  const templateSchemas = [];
3092
3136
  let cmsPageCount = 0;
3093
3137
  if (existsSync(templatesDir)) {
3094
3138
  const templateFiles = readdirSync(templatesDir).filter((f) => f.endsWith(".json"));
3095
- if (templateFiles.length > 0) {
3096
- console.log(`
3097
- Processing ${templateFiles.length} CMS template(s)...
3098
- `);
3099
- }
3100
3139
  for (const file of templateFiles) {
3101
3140
  const templateContent = await loadJSONFile(join(templatesDir, file));
3102
3141
  if (!templateContent) continue;
@@ -3104,7 +3143,6 @@ Processing ${templateFiles.length} CMS template(s)...
3104
3143
  const pageData = parseJSON(templateContent);
3105
3144
  const isDevBuild = process.env.MENO_DEV_BUILD === "true";
3106
3145
  if (pageData.meta?.draft === true && !isDevBuild) {
3107
- console.log(` Skipping draft template: ${file}`);
3108
3146
  continue;
3109
3147
  }
3110
3148
  if (!isCMSPage(pageData)) {
@@ -3113,14 +3151,8 @@ Processing ${templateFiles.length} CMS template(s)...
3113
3151
  }
3114
3152
  const cmsSchema = pageData.meta.cms;
3115
3153
  templateSchemas.push(cmsSchema);
3116
- console.log(` CMS Collection: ${cmsSchema.id}`);
3117
3154
  const items = await cmsService.queryItems({ collection: cmsSchema.id });
3118
3155
  const itemCount = items.length;
3119
- if (itemCount === 0) {
3120
- console.log(` No items found in cms/${cmsSchema.id}/`);
3121
- } else {
3122
- console.log(` Found ${itemCount} item(s)`);
3123
- }
3124
3156
  const defaultLocale = i18nConfig.defaultLocale;
3125
3157
  const dummyPath = cmsSchema.urlPattern.replace("{{slug}}", "__placeholder__");
3126
3158
  const metaResult = await renderPageSSR(
@@ -3188,7 +3220,8 @@ Processing ${templateFiles.length} CMS template(s)...
3188
3220
  // Each file handles one locale
3189
3221
  slugMappings,
3190
3222
  imageFormat: configService.getImageFormat(),
3191
- processedRawHtml: metaResult.processedRawHtmlCollector
3223
+ processedRawHtml: metaResult.processedRawHtmlCollector,
3224
+ remConfig: remConversionConfig
3192
3225
  });
3193
3226
  const astroFileFull = join(pagesOutDir, astroFilePath);
3194
3227
  const astroFileDir = astroFileFull.substring(0, astroFileFull.lastIndexOf("/"));
@@ -3197,7 +3230,6 @@ Processing ${templateFiles.length} CMS template(s)...
3197
3230
  }
3198
3231
  await writeFile2(astroFileFull, astroContent, "utf-8");
3199
3232
  }
3200
- console.log(` Generated: ${pathPrefix}[slug].astro (${itemCount} items \xD7 ${localesToEmit.length} locale(s))`);
3201
3233
  cmsPageCount += itemCount * i18nConfig.locales.length;
3202
3234
  } catch (error) {
3203
3235
  console.error(` Error processing template ${file}:`, error?.message || error);
@@ -3209,16 +3241,14 @@ Processing ${templateFiles.length} CMS template(s)...
3209
3241
  const fontCSS = generateFontCSS();
3210
3242
  const themeColorCSS = generateThemeColorVariablesCSS(themeConfig);
3211
3243
  const variablesCSS = generateVariablesCSS(variablesConfig, breakpoints, responsiveScales);
3212
- const remConversionConfig = configService.getRemConversion();
3213
- const interactiveCSS = generateAllInteractiveCSS(allInteractiveStyles, breakpoints, remConversionConfig, responsiveScales);
3214
3244
  const componentCSSCombined = Array.from(allComponentCSS).join("\n");
3215
3245
  const baseCSS = `@layer base {
3216
3246
  * { margin: 0; padding: 0; box-sizing: border-box; }
3217
3247
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif; }
3218
3248
  button { background: none; border: none; padding: 0; font: inherit; cursor: pointer; outline: inherit; }
3219
- img { display: block; width: 100%; height: 100%; }
3249
+ img { max-width: 100%; height: auto; }
3220
3250
  picture { display: block; }
3221
- .olink { text-decoration: none; display: block; }
3251
+ .olink { text-decoration: none; display: block; color: inherit; }
3222
3252
  .oem { display: inline-block; }
3223
3253
  }`;
3224
3254
  const safelistClasses = Array.from(mappingClasses);
@@ -3226,10 +3256,8 @@ Processing ${templateFiles.length} CMS template(s)...
3226
3256
  const tailwindDirectives = safelistDirectives ? `@import "tailwindcss";
3227
3257
 
3228
3258
  ${safelistDirectives}` : `@import "tailwindcss";`;
3229
- const globalCSS = [tailwindDirectives, fontCSS, themeColorCSS, variablesCSS, baseCSS, componentCSSCombined, interactiveCSS].filter(Boolean).join("\n\n");
3259
+ const globalCSS = [tailwindDirectives, fontCSS, themeColorCSS, variablesCSS, baseCSS, componentCSSCombined].filter(Boolean).join("\n\n");
3230
3260
  await writeFile2(join(stylesDir, "global.css"), globalCSS, "utf-8");
3231
- console.log(`
3232
- Generated global.css (${(globalCSS.length / 1024).toFixed(1)} KB)`);
3233
3261
  const escForTemplateLiteral = (s) => s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
3234
3262
  const customHeadLiteral = escForTemplateLiteral(customCode.head || "");
3235
3263
  const customBodyStartLiteral = escForTemplateLiteral(customCode.bodyStart || "");
@@ -3277,18 +3305,16 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
3277
3305
  </html>
3278
3306
  `;
3279
3307
  await writeFile2(join(layoutsDir, "BaseLayout.astro"), baseLayoutContent, "utf-8");
3280
- console.log("Generated BaseLayout.astro");
3281
3308
  let componentFileCount = 0;
3282
3309
  for (const [compName, compDef] of Object.entries(globalComponents)) {
3283
3310
  try {
3284
- const astroContent = emitAstroComponent(compName, compDef, globalComponents, breakpoints, i18nConfig.defaultLocale, responsiveScales);
3311
+ const astroContent = emitAstroComponent(compName, compDef, globalComponents, breakpoints, i18nConfig.defaultLocale, responsiveScales, remConversionConfig);
3285
3312
  await writeFile2(join(componentsOutDir, `${compName}.astro`), astroContent, "utf-8");
3286
3313
  componentFileCount++;
3287
3314
  } catch (error) {
3288
3315
  console.warn(` Warning: could not generate component ${compName}: ${error?.message}`);
3289
3316
  }
3290
3317
  }
3291
- console.log(`Generated ${componentFileCount} component .astro file(s)`);
3292
3318
  for (const result of allResults) {
3293
3319
  const importPath = layoutImportPath(result.fileDepth);
3294
3320
  let scriptPaths = [];
@@ -3318,7 +3344,8 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
3318
3344
  currentPageSlugMap: pageSlugMap,
3319
3345
  slugMappings: i18nConfig.locales.length > 1 ? slugMappings : void 0,
3320
3346
  imageFormat: configService.getImageFormat(),
3321
- processedRawHtml: result.processedRawHtmlCollector
3347
+ processedRawHtml: result.processedRawHtmlCollector,
3348
+ remConfig: remConversionConfig
3322
3349
  });
3323
3350
  } catch (error) {
3324
3351
  console.warn(` Warning: component emission failed for ${result.urlPath}, using SSR fallback: ${error?.message}`);
@@ -3336,7 +3363,6 @@ const { title, meta = '', scripts = [], locale = 'en', theme = '${themeConfig.de
3336
3363
  }
3337
3364
  await writeFile2(astroFileFull, astroContent, "utf-8");
3338
3365
  }
3339
- console.log(`Generated ${allResults.length} .astro page file(s)`);
3340
3366
  const robotsTsContent = `import type { APIRoute } from 'astro';
3341
3367
 
3342
3368
  export const GET: APIRoute = () => {
@@ -3354,7 +3380,6 @@ export const GET: APIRoute = () => {
3354
3380
  };
3355
3381
  `;
3356
3382
  await writeFile2(join(pagesOutDir, "robots.txt.ts"), robotsTsContent, "utf-8");
3357
- console.log("Generated robots.txt.ts endpoint");
3358
3383
  let collectionCount = 0;
3359
3384
  if (templateSchemas.length > 0) {
3360
3385
  const contentDir = join(srcDir, "content");
@@ -3407,27 +3432,22 @@ ${collectionDefs.join(",\n")}
3407
3432
  export { collections };
3408
3433
  `;
3409
3434
  await writeFile2(join(srcDir, "content.config.ts"), configContent, "utf-8");
3410
- console.log(`Generated ${collectionCount} content collection(s) with content.config.ts`);
3411
3435
  }
3412
- let copiedAssets = 0;
3413
3436
  const imagesSrcDir = join(projectPaths.project, "images");
3414
3437
  if (existsSync(imagesSrcDir)) {
3415
3438
  copyDirectory(imagesSrcDir, join(srcDir, "assets", "images"), shouldCopyImageForAstro);
3416
3439
  copyDirectory(imagesSrcDir, join(publicDir, "images"));
3417
- copiedAssets++;
3418
3440
  }
3419
3441
  const publicAssetDirs = ["fonts", "icons", "videos", "assets"];
3420
3442
  for (const dir of publicAssetDirs) {
3421
3443
  const srcAssetDir = join(projectPaths.project, dir);
3422
3444
  if (existsSync(srcAssetDir)) {
3423
3445
  copyDirectory(srcAssetDir, join(publicDir, dir));
3424
- copiedAssets++;
3425
3446
  }
3426
3447
  }
3427
3448
  const librariesDir = join(projectPaths.project, "libraries");
3428
3449
  if (existsSync(librariesDir)) {
3429
3450
  copyDirectory(librariesDir, join(publicDir, "libraries"));
3430
- copiedAssets++;
3431
3451
  }
3432
3452
  for (const relPath of localLibsToCopy) {
3433
3453
  const srcPath = join(projectPaths.project, relPath);
@@ -3435,10 +3455,6 @@ export { collections };
3435
3455
  const destDir = destPath.substring(0, destPath.lastIndexOf("/"));
3436
3456
  if (destDir && !existsSync(destDir)) mkdirSync(destDir, { recursive: true });
3437
3457
  copyFileSync(srcPath, destPath);
3438
- copiedAssets++;
3439
- }
3440
- if (copiedAssets > 0) {
3441
- console.log(`Copied ${copiedAssets} asset director${copiedAssets === 1 ? "y" : "ies"} to public/`);
3442
3458
  }
3443
3459
  const packageJson = {
3444
3460
  name: "astro-export",
@@ -3487,28 +3503,8 @@ export default defineConfig({${siteUrl ? `
3487
3503
  extends: "astro/tsconfigs/strict"
3488
3504
  };
3489
3505
  await writeFile2(join(outDir, "tsconfig.json"), JSON.stringify(tsConfig, null, 2), "utf-8");
3490
- console.log("Generated package.json, astro.config.mjs, tsconfig.json");
3491
- const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
3506
+ await writeFile2(join(outDir, "src", "env.d.ts"), '/// <reference path="../.astro/types.d.ts" />\n', "utf-8");
3492
3507
  const totalPages = allResults.length;
3493
- console.log("\n" + "=".repeat(50));
3494
- console.log("Astro export complete!");
3495
- console.log(` Pages: ${totalPages - cmsPageCount}`);
3496
- if (cmsPageCount > 0) {
3497
- console.log(` CMS pages: ${cmsPageCount}`);
3498
- }
3499
- if (collectionCount > 0) {
3500
- console.log(` Content collections: ${collectionCount}`);
3501
- }
3502
- if (errorCount > 0) {
3503
- console.log(` Errors: ${errorCount}`);
3504
- }
3505
- console.log(` SEO: sitemap (@astrojs/sitemap), robots.txt`);
3506
- if (projectNeedsFormHandler) {
3507
- console.log(` Forms: fetch handler injected`);
3508
- }
3509
- console.log(` Time: ${elapsed}s`);
3510
- console.log(` Output: ${outDir}`);
3511
- console.log("");
3512
3508
  return {
3513
3509
  pages: totalPages - cmsPageCount,
3514
3510
  cmsPages: cmsPageCount,
@@ -3519,38 +3515,19 @@ export default defineConfig({${siteUrl ? `
3519
3515
 
3520
3516
  // lib/server/webflow/buildWebflow.ts
3521
3517
  import { existsSync as existsSync2, readdirSync as readdirSync2 } from "fs";
3522
- import { join as join2 } from "path";
3523
-
3524
- // lib/server/webflow/types.ts
3525
- function mapCMSFieldType(menoType) {
3526
- switch (menoType) {
3527
- case "string":
3528
- return "PlainText";
3529
- case "text":
3530
- case "rich-text":
3531
- return "RichText";
3532
- case "number":
3533
- return "Number";
3534
- case "boolean":
3535
- return "Switch";
3536
- case "image":
3537
- return "Image";
3538
- case "date":
3539
- return "Date";
3540
- case "select":
3541
- return "Option";
3542
- case "file":
3543
- return "File";
3544
- case "reference":
3545
- return "Reference";
3546
- default:
3547
- return "PlainText";
3548
- }
3549
- }
3518
+ import { join as join3 } from "path";
3519
+ init_constants();
3550
3520
 
3551
3521
  // lib/server/webflow/nodeToWebflow.ts
3552
3522
  init_constants();
3553
3523
 
3524
+ // lib/server/webflow/types.ts
3525
+ var MENO_BIND_SENTINEL_PREFIX = "__MENO_BIND__:";
3526
+ var MENO_BIND_SENTINEL_SUFFIX = ":__";
3527
+ var MENO_BIND_SENTINEL_RE = /__MENO_BIND__:([^:]+):__/g;
3528
+ var MENO_BIND_SENTINEL_EXACT_RE = /^__MENO_BIND__:([^:]+):__$/;
3529
+ var COLLECTION_LIST_TAG = "__collection_list__";
3530
+
3554
3531
  // lib/server/webflow/styleMapper.ts
3555
3532
  var UNITLESS_PROPERTIES = /* @__PURE__ */ new Set([
3556
3533
  "opacity",
@@ -3565,6 +3542,28 @@ var UNITLESS_PROPERTIES = /* @__PURE__ */ new Set([
3565
3542
  "font-weight",
3566
3543
  "tab-size"
3567
3544
  ]);
3545
+ var TIME_PROPERTIES = /* @__PURE__ */ new Set([
3546
+ "transition-duration",
3547
+ "transition-delay",
3548
+ "animation-duration",
3549
+ "animation-delay"
3550
+ ]);
3551
+ function normalizeZero(cssProp, cssValue) {
3552
+ if (cssValue !== "0") return cssValue;
3553
+ if (UNITLESS_PROPERTIES.has(cssProp)) return cssValue;
3554
+ if (TIME_PROPERTIES.has(cssProp)) return cssValue;
3555
+ return "0px";
3556
+ }
3557
+ var COLOR_PROPS_CAMEL = /* @__PURE__ */ new Set(["color", "backgroundColor", "borderColor"]);
3558
+ function maybeWrapColorVar(camelProp, value) {
3559
+ if (!COLOR_PROPS_CAMEL.has(camelProp)) return value;
3560
+ if (!value) return value;
3561
+ if (value.startsWith("#")) return value;
3562
+ if (value.startsWith("var(")) return value;
3563
+ if (value.includes("(")) return value;
3564
+ if (isCssNamedColor(value)) return value;
3565
+ return `var(--${value})`;
3566
+ }
3568
3567
  function isStyleMapping4(value) {
3569
3568
  return typeof value === "object" && value !== null && "_mapping" in value && value._mapping === true;
3570
3569
  }
@@ -3574,6 +3573,65 @@ function isResponsiveStyle4(style) {
3574
3573
  function toKebabCase(prop) {
3575
3574
  return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
3576
3575
  }
3576
+ function splitTopLevel(value) {
3577
+ const out = [];
3578
+ let depth = 0;
3579
+ let buf = "";
3580
+ for (const ch of value.trim()) {
3581
+ if (ch === "(") depth++;
3582
+ else if (ch === ")") depth--;
3583
+ if (depth === 0 && /\s/.test(ch)) {
3584
+ if (buf) {
3585
+ out.push(buf);
3586
+ buf = "";
3587
+ }
3588
+ continue;
3589
+ }
3590
+ buf += ch;
3591
+ }
3592
+ if (buf) out.push(buf);
3593
+ return out;
3594
+ }
3595
+ function expandShorthand(cssProp, cssValue) {
3596
+ if (cssProp !== "margin" && cssProp !== "padding" && cssProp !== "gap") {
3597
+ return null;
3598
+ }
3599
+ const parts = splitTopLevel(cssValue);
3600
+ if (cssProp === "gap") {
3601
+ if (parts.length === 1) {
3602
+ const v = normalizeZero("row-gap", parts[0]);
3603
+ return { "row-gap": v, "column-gap": v };
3604
+ }
3605
+ if (parts.length === 2) {
3606
+ return {
3607
+ "row-gap": normalizeZero("row-gap", parts[0]),
3608
+ "column-gap": normalizeZero("column-gap", parts[1])
3609
+ };
3610
+ }
3611
+ return null;
3612
+ }
3613
+ let top, right, bottom, left;
3614
+ if (parts.length === 1) {
3615
+ top = right = bottom = left = parts[0];
3616
+ } else if (parts.length === 2) {
3617
+ top = bottom = parts[0];
3618
+ right = left = parts[1];
3619
+ } else if (parts.length === 3) {
3620
+ top = parts[0];
3621
+ right = left = parts[1];
3622
+ bottom = parts[2];
3623
+ } else if (parts.length === 4) {
3624
+ [top, right, bottom, left] = parts;
3625
+ } else {
3626
+ return null;
3627
+ }
3628
+ return {
3629
+ [`${cssProp}-top`]: normalizeZero(`${cssProp}-top`, top),
3630
+ [`${cssProp}-right`]: normalizeZero(`${cssProp}-right`, right),
3631
+ [`${cssProp}-bottom`]: normalizeZero(`${cssProp}-bottom`, bottom),
3632
+ [`${cssProp}-left`]: normalizeZero(`${cssProp}-left`, left)
3633
+ };
3634
+ }
3577
3635
  function styleObjectToCSS(style) {
3578
3636
  const css = {};
3579
3637
  for (const [prop, value] of Object.entries(style)) {
@@ -3581,11 +3639,18 @@ function styleObjectToCSS(style) {
3581
3639
  if (value === "" || value === void 0 || value === null) continue;
3582
3640
  if (typeof value === "boolean" || typeof value === "object") continue;
3583
3641
  const cssProp = toKebabCase(prop);
3642
+ let cssValue;
3584
3643
  if (typeof value === "number") {
3585
3644
  if (isNaN(value)) continue;
3586
- css[cssProp] = UNITLESS_PROPERTIES.has(cssProp) ? String(value) : `${value}px`;
3645
+ cssValue = UNITLESS_PROPERTIES.has(cssProp) ? String(value) : `${value}px`;
3646
+ } else {
3647
+ cssValue = maybeWrapColorVar(prop, String(value));
3648
+ }
3649
+ const expanded = expandShorthand(cssProp, cssValue);
3650
+ if (expanded) {
3651
+ Object.assign(css, expanded);
3587
3652
  } else {
3588
- css[cssProp] = String(value);
3653
+ css[cssProp] = normalizeZero(cssProp, cssValue);
3589
3654
  }
3590
3655
  }
3591
3656
  return css;
@@ -3612,14 +3677,85 @@ function collectStyleMappings2(style) {
3612
3677
  return result;
3613
3678
  }
3614
3679
  function postfixToPseudoState(postfix) {
3615
- if (postfix.includes(":hover")) return "hover";
3616
3680
  if (postfix.includes(":focus-visible")) return "focus-visible";
3681
+ if (postfix.includes(":focus-within")) return "focus-within";
3682
+ if (postfix.includes(":nth-child(odd)")) return "nth-child(odd)";
3683
+ if (postfix.includes(":nth-child(even)")) return "nth-child(even)";
3684
+ if (postfix.includes(":first-child")) return "first-child";
3685
+ if (postfix.includes(":last-child")) return "last-child";
3686
+ if (postfix.includes(":placeholder")) return "placeholder";
3687
+ if (postfix.includes(":empty")) return "empty";
3688
+ if (postfix.includes(":before")) return "before";
3689
+ if (postfix.includes(":after")) return "after";
3690
+ if (postfix.includes(":hover")) return "hover";
3617
3691
  if (postfix.includes(":focus")) return "focus";
3618
3692
  if (postfix.includes(":active")) return "active";
3619
3693
  if (postfix.includes(":visited")) return "visited";
3694
+ if (postfix.includes(":pressed")) return "pressed";
3695
+ if (postfix.includes("::before")) return "before";
3696
+ if (postfix.includes("::after")) return "after";
3697
+ if (postfix.includes("::placeholder")) return "placeholder";
3620
3698
  return null;
3621
3699
  }
3622
- function mapStylesToWebflow(className, style, interactiveStyles, breakpoints) {
3700
+ function isWebflowHandledRule(rule) {
3701
+ if (rule.prefix && rule.prefix.trim().length > 0) return false;
3702
+ if (!rule.postfix) return false;
3703
+ if (postfixToPseudoState(rule.postfix) === null) return false;
3704
+ const s = rule.style;
3705
+ if (!s || typeof s !== "object") return false;
3706
+ const responsiveKeys = Object.keys(s).filter((k) => k !== "base");
3707
+ return responsiveKeys.length === 0;
3708
+ }
3709
+ function widthToWebflowBreakpoint(maxWidthPx) {
3710
+ if (maxWidthPx < 480) return "tiny";
3711
+ if (maxWidthPx < 768) return "small";
3712
+ if (maxWidthPx < 992) return "medium";
3713
+ if (maxWidthPx < 1280) return "main";
3714
+ if (maxWidthPx < 1440) return "large";
3715
+ if (maxWidthPx < 1920) return "xl";
3716
+ return "xxl";
3717
+ }
3718
+ function menoBreakpointToWebflow(bpName, breakpoints) {
3719
+ if (bpName === "tablet") return "medium";
3720
+ if (bpName === "mobile") return "small";
3721
+ const entry = breakpoints[bpName];
3722
+ if (entry && typeof entry.breakpoint === "number") {
3723
+ return widthToWebflowBreakpoint(entry.breakpoint);
3724
+ }
3725
+ return "main";
3726
+ }
3727
+ function mergeIntoBreakpoint(cls, tier, css) {
3728
+ if (!cls.breakpoints) cls.breakpoints = {};
3729
+ cls.breakpoints[tier] = { ...cls.breakpoints[tier], ...css };
3730
+ }
3731
+ function kebabToCamel(s) {
3732
+ return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
3733
+ }
3734
+ function applyAutoScaling(cls, breakpoints, responsiveScales) {
3735
+ if (!responsiveScales?.enabled) return;
3736
+ const baseRef = responsiveScales.baseReference || 16;
3737
+ for (const [prop, baseValue] of Object.entries(cls.base)) {
3738
+ if (!baseValue || baseValue.includes("var(--")) continue;
3739
+ const camelProp = kebabToCamel(prop);
3740
+ for (const [bpName, bpEntry] of Object.entries(breakpoints)) {
3741
+ if (!bpEntry) continue;
3742
+ const scale = getScaleMultiplier(responsiveScales, camelProp, bpName);
3743
+ if (scale === null) continue;
3744
+ const scaled = scalePropertyValue(baseValue, baseRef, scale);
3745
+ if (scaled === null || scaled === baseValue) continue;
3746
+ const tier = menoBreakpointToWebflow(bpName, breakpoints);
3747
+ if (!cls.breakpoints) cls.breakpoints = {};
3748
+ const bucket = cls.breakpoints[tier] || {};
3749
+ if (bucket[prop] !== void 0) continue;
3750
+ bucket[prop] = scaled;
3751
+ cls.breakpoints[tier] = bucket;
3752
+ }
3753
+ }
3754
+ }
3755
+ function mapStylesToWebflow(className, style, interactiveStyles, breakpoints, responsiveScales, options) {
3756
+ const instanceProps = options?.instanceProps;
3757
+ const componentDefaults = options?.componentDefaults;
3758
+ const themeSuffix = options?.themeSuffix ?? "";
3623
3759
  const webflowClassName = className.replace(/_/g, "-");
3624
3760
  const primaryClass = {
3625
3761
  name: webflowClassName,
@@ -3631,21 +3767,12 @@ function mapStylesToWebflow(className, style, interactiveStyles, breakpoints) {
3631
3767
  if (responsive.base) {
3632
3768
  primaryClass.base = styleObjectToCSS(responsive.base);
3633
3769
  }
3634
- if (responsive.tablet) {
3635
- if (!primaryClass.breakpoints) primaryClass.breakpoints = {};
3636
- primaryClass.breakpoints.Tablet = styleObjectToCSS(responsive.tablet);
3637
- }
3638
- if (responsive.mobile) {
3639
- if (!primaryClass.breakpoints) primaryClass.breakpoints = {};
3640
- primaryClass.breakpoints.MobilePortrait = styleObjectToCSS(responsive.mobile);
3641
- }
3642
3770
  for (const [bpName, bpStyle] of Object.entries(responsive)) {
3643
- if (!bpStyle || bpName === "base" || bpName === "tablet" || bpName === "mobile") continue;
3644
- if (!primaryClass.breakpoints) primaryClass.breakpoints = {};
3645
- primaryClass.breakpoints.Tablet = {
3646
- ...primaryClass.breakpoints.Tablet,
3647
- ...styleObjectToCSS(bpStyle)
3648
- };
3771
+ if (!bpStyle || bpName === "base") continue;
3772
+ const css = styleObjectToCSS(bpStyle);
3773
+ if (Object.keys(css).length === 0) continue;
3774
+ const tier = menoBreakpointToWebflow(bpName, breakpoints);
3775
+ mergeIntoBreakpoint(primaryClass, tier, css);
3649
3776
  }
3650
3777
  } else {
3651
3778
  primaryClass.base = styleObjectToCSS(style);
@@ -3653,92 +3780,546 @@ function mapStylesToWebflow(className, style, interactiveStyles, breakpoints) {
3653
3780
  }
3654
3781
  if (interactiveStyles && interactiveStyles.length > 0) {
3655
3782
  for (const rule of interactiveStyles) {
3656
- if (!rule.postfix) continue;
3783
+ if (!isWebflowHandledRule(rule)) continue;
3784
+ const baseProps = isResponsiveStyle4(rule.style) ? styleObjectToCSS(rule.style.base || {}) : styleObjectToCSS(rule.style);
3785
+ if (Object.keys(baseProps).length === 0) continue;
3657
3786
  const pseudoState = postfixToPseudoState(rule.postfix);
3658
3787
  if (!pseudoState) continue;
3659
- const ruleStyle = rule.style;
3660
3788
  if (!primaryClass.pseudoStates) primaryClass.pseudoStates = {};
3661
- if (isResponsiveStyle4(ruleStyle)) {
3662
- const responsive = ruleStyle;
3663
- if (responsive.base) {
3664
- primaryClass.pseudoStates[pseudoState] = {
3665
- ...primaryClass.pseudoStates[pseudoState],
3666
- ...styleObjectToCSS(responsive.base)
3667
- };
3668
- }
3669
- } else {
3670
- primaryClass.pseudoStates[pseudoState] = {
3671
- ...primaryClass.pseudoStates[pseudoState],
3672
- ...styleObjectToCSS(ruleStyle)
3673
- };
3674
- }
3789
+ primaryClass.pseudoStates[pseudoState] = {
3790
+ ...primaryClass.pseudoStates[pseudoState],
3791
+ ...baseProps
3792
+ };
3675
3793
  }
3676
3794
  }
3677
3795
  const comboClasses = [];
3678
3796
  const mappings = collectStyleMappings2(style);
3797
+ const comboCss = {};
3798
+ const comboNameParts = [];
3679
3799
  for (const { property, mapping } of mappings) {
3680
- for (const [value, cssValue] of Object.entries(mapping.values)) {
3681
- if (cssValue === "" || cssValue === void 0) continue;
3682
- const comboName = `is-${sanitizeClassName(mapping.prop)}-${sanitizeClassName(String(value))}`;
3683
- const comboClass = {
3684
- name: comboName,
3685
- base: {
3686
- [toKebabCase(property)]: typeof cssValue === "number" ? UNITLESS_PROPERTIES.has(toKebabCase(property)) ? String(cssValue) : `${cssValue}px` : String(cssValue)
3687
- },
3688
- comboParent: webflowClassName
3689
- };
3690
- comboClasses.push(comboClass);
3800
+ const defaultValue = componentDefaults?.[mapping.prop];
3801
+ const defaultKey = defaultValue != null ? String(defaultValue) : void 0;
3802
+ if (defaultKey !== void 0 && defaultKey in mapping.values) {
3803
+ const defaultCss = mappingValueToCSS(property, mapping.values[defaultKey]);
3804
+ if (defaultCss) {
3805
+ primaryClass.base = { ...primaryClass.base, ...defaultCss };
3806
+ }
3691
3807
  }
3808
+ if (!instanceProps) continue;
3809
+ const instanceValue = instanceProps[mapping.prop];
3810
+ if (instanceValue == null) continue;
3811
+ const instanceKey = String(instanceValue);
3812
+ if (instanceKey === defaultKey) continue;
3813
+ if (!(instanceKey in mapping.values)) continue;
3814
+ const css = mappingValueToCSS(property, mapping.values[instanceKey]);
3815
+ if (!css) continue;
3816
+ Object.assign(comboCss, css);
3817
+ const part = `${sanitizeClassName(mapping.prop)}-${sanitizeClassName(instanceKey)}`;
3818
+ if (!comboNameParts.includes(part)) comboNameParts.push(part);
3819
+ }
3820
+ if (comboNameParts.length > 0 && Object.keys(comboCss).length > 0) {
3821
+ comboNameParts.sort();
3822
+ comboClasses.push({
3823
+ name: `is-${comboNameParts.join("-")}${themeSuffix}`,
3824
+ base: comboCss,
3825
+ comboParent: webflowClassName
3826
+ });
3827
+ }
3828
+ applyAutoScaling(primaryClass, breakpoints, responsiveScales);
3829
+ for (const combo of comboClasses) {
3830
+ applyAutoScaling(combo, breakpoints, responsiveScales);
3692
3831
  }
3693
3832
  return { primaryClass, comboClasses };
3694
3833
  }
3834
+ function mappingValueToCSS(property, rawValue) {
3835
+ if (rawValue === "" || rawValue === void 0 || rawValue === null) return null;
3836
+ const cssProp = toKebabCase(property);
3837
+ const cssValueStr = typeof rawValue === "number" ? UNITLESS_PROPERTIES.has(cssProp) ? String(rawValue) : `${rawValue}px` : maybeWrapColorVar(property, String(rawValue));
3838
+ return expandShorthand(cssProp, cssValueStr) ?? { [cssProp]: normalizeZero(cssProp, cssValueStr) };
3839
+ }
3695
3840
  function sanitizeClassName(name) {
3696
3841
  return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
3697
3842
  }
3843
+ function buildInstanceStyleCombo(comboName, rootClassName, style, interactiveStyles, breakpoints, responsiveScales) {
3844
+ const base = {};
3845
+ const bps = {};
3846
+ const pseudos = {};
3847
+ if (style) {
3848
+ if (isResponsiveStyle4(style)) {
3849
+ const responsive = style;
3850
+ if (responsive.base) Object.assign(base, styleObjectToCSS(responsive.base));
3851
+ for (const [bpName, bpStyle] of Object.entries(responsive)) {
3852
+ if (!bpStyle || bpName === "base") continue;
3853
+ const css = styleObjectToCSS(bpStyle);
3854
+ if (Object.keys(css).length === 0) continue;
3855
+ const tier = menoBreakpointToWebflow(bpName, breakpoints);
3856
+ bps[tier] = { ...bps[tier], ...css };
3857
+ }
3858
+ } else {
3859
+ Object.assign(base, styleObjectToCSS(style));
3860
+ }
3861
+ }
3862
+ if (interactiveStyles && interactiveStyles.length > 0) {
3863
+ for (const rule of interactiveStyles) {
3864
+ if (!isWebflowHandledRule(rule)) continue;
3865
+ const ruleStyle = rule.style;
3866
+ const flat = isResponsiveStyle4(ruleStyle) ? ruleStyle.base : ruleStyle;
3867
+ if (!flat) continue;
3868
+ const css = styleObjectToCSS(flat);
3869
+ if (Object.keys(css).length === 0) continue;
3870
+ const pseudo = postfixToPseudoState(rule.postfix);
3871
+ if (!pseudo) continue;
3872
+ pseudos[pseudo] = { ...pseudos[pseudo], ...css };
3873
+ }
3874
+ }
3875
+ if (Object.keys(base).length === 0 && Object.keys(bps).length === 0 && Object.keys(pseudos).length === 0) return null;
3876
+ const cls = {
3877
+ name: comboName,
3878
+ base,
3879
+ comboParent: rootClassName
3880
+ };
3881
+ if (Object.keys(bps).length > 0) cls.breakpoints = bps;
3882
+ if (Object.keys(pseudos).length > 0) cls.pseudoStates = pseudos;
3883
+ applyAutoScaling(cls, breakpoints, responsiveScales);
3884
+ return cls;
3885
+ }
3698
3886
 
3699
3887
  // lib/server/webflow/nodeToWebflow.ts
3888
+ import { readFile as readFile2 } from "fs/promises";
3889
+ import { join as join2, basename, extname } from "path";
3890
+ var PROMOTED_TO_WEBFLOW_COMPONENT = /* @__PURE__ */ new Set(["Navigation", "Footer"]);
3700
3891
  function buildElementClass2(ctx, label) {
3701
- return generateElementClassName({
3892
+ const generated = generateElementClassName({
3702
3893
  fileType: ctx.fileType,
3703
3894
  fileName: ctx.fileName,
3704
3895
  label,
3705
3896
  path: ctx.elementPath
3706
3897
  });
3898
+ if (ctx.fileType !== "component") return generated;
3899
+ const stripped = generated.replace(/^c_/, "");
3900
+ return stripped.replace(/^([a-z0-9-]+)_\1$/, "$1");
3901
+ }
3902
+ function themedClassSuffix(ctx) {
3903
+ const t = ctx.currentTheme;
3904
+ if (!t) return "";
3905
+ if (ctx.defaultTheme && t === ctx.defaultTheme) return "";
3906
+ const safe = t.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
3907
+ if (!safe) return "";
3908
+ return `-theme-${safe}`;
3909
+ }
3910
+ function withThemeSuffix(name, ctx) {
3911
+ return name + themedClassSuffix(ctx);
3912
+ }
3913
+ function mintInstanceComboName(ctx, identity) {
3914
+ let attempt = 0;
3915
+ let name = `is-${shortHash(identity)}`;
3916
+ while (true) {
3917
+ const claimedBy = ctx.comboIdentityByName.get(name);
3918
+ if (!claimedBy || claimedBy === identity) {
3919
+ ctx.comboIdentityByName.set(name, identity);
3920
+ return name;
3921
+ }
3922
+ attempt++;
3923
+ name = `is-${shortHash(`${identity}#${attempt}`)}`;
3924
+ }
3925
+ }
3926
+ function extractInterfaceDefaults(iface) {
3927
+ if (!iface) return void 0;
3928
+ const out = {};
3929
+ let any = false;
3930
+ for (const [key, def] of Object.entries(iface)) {
3931
+ if (def && "default" in def) {
3932
+ out[key] = def.default;
3933
+ any = true;
3934
+ }
3935
+ }
3936
+ return any ? out : void 0;
3707
3937
  }
3708
- function resolveTemplate2(text, props) {
3709
- if (!props) return text;
3710
- return text.replace(/\{\{(.+?)\}\}/g, (_, expr) => {
3711
- const trimmed = expr.trim();
3712
- const orMatch = trimmed.match(/^(.+?)\s*\|\|\s*['"](.+?)['"]$/);
3713
- if (orMatch) {
3714
- const value2 = resolveNestedProp(props, orMatch[1].trim());
3715
- return value2 !== void 0 && value2 !== "" && value2 !== null ? String(value2) : orMatch[2];
3716
- }
3717
- const ternaryMatch = trimmed.match(/^(.+?)\s*\?\s*['"](.+?)['"]\s*:\s*['"](.+?)['"]$/);
3718
- if (ternaryMatch) {
3719
- const value2 = resolveNestedProp(props, ternaryMatch[1].trim());
3720
- return value2 ? ternaryMatch[2] : ternaryMatch[3];
3721
- }
3722
- const value = resolveNestedProp(props, trimmed);
3723
- return value !== void 0 ? String(value) : "";
3724
- });
3938
+ function buildTemplateProps(ctx, instanceProps) {
3939
+ return {
3940
+ ...ctx.templateContext ?? {},
3941
+ ...ctx.cmsContext ?? {},
3942
+ ...ctx.componentDefaults ?? {},
3943
+ ...ctx.slotInstanceProps ?? {},
3944
+ ...instanceProps ?? {}
3945
+ };
3946
+ }
3947
+ function resolveStringTemplate(text, ctx, instanceProps) {
3948
+ if (!hasTemplates(text)) return text;
3949
+ const out = processCodeTemplates(text, buildTemplateProps(ctx, instanceProps));
3950
+ return out.startsWith(RAW_HTML_PREFIX) ? flattenInlineHtmlToText(out.slice(RAW_HTML_PREFIX.length)) : out;
3951
+ }
3952
+ var HEADING_TAGS = /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
3953
+ var LINK_LIKE_TAGS = /* @__PURE__ */ new Set(["button"]);
3954
+ var LIST_TAGS = /* @__PURE__ */ new Set(["ul", "ol"]);
3955
+ function applyHeadingMarginDefaults(cls) {
3956
+ const base = cls.base;
3957
+ if (!("margin-top" in base)) base["margin-top"] = "0px";
3958
+ if (!("margin-bottom" in base)) base["margin-bottom"] = "0px";
3959
+ }
3960
+ function applyParagraphMarginDefaults(cls) {
3961
+ const base = cls.base;
3962
+ if (!("margin-bottom" in base)) base["margin-bottom"] = "0px";
3963
+ }
3964
+ function applyListMarginDefaults(cls) {
3965
+ const base = cls.base;
3966
+ if (!("margin-bottom" in base)) base["margin-bottom"] = "0px";
3967
+ }
3968
+ function applyGridRowsDefault(cls) {
3969
+ const base = cls.base;
3970
+ const display = base["display"];
3971
+ if (display !== "grid" && display !== "inline-grid") return;
3972
+ if ("grid" in base) return;
3973
+ if ("grid-template" in base) return;
3974
+ if ("grid-template-rows" in base) return;
3975
+ base["grid-template-rows"] = "1fr";
3976
+ }
3977
+ function applyLinkTextDecorationDefault(cls) {
3978
+ const base = cls.base;
3979
+ if ("text-decoration" in base || "text-decoration-line" in base) return;
3980
+ base["text-decoration"] = "none";
3981
+ }
3982
+ function mergeComboClasses(inner, outer, parentName) {
3983
+ const innerSuffix = inner.name.startsWith("is-") ? inner.name.slice(3) : inner.name;
3984
+ const merged = {
3985
+ name: `${outer.name}_${innerSuffix}`,
3986
+ base: { ...inner.base, ...outer.base },
3987
+ comboParent: parentName
3988
+ };
3989
+ if (inner.breakpoints || outer.breakpoints) {
3990
+ const bps = {};
3991
+ const tiers = /* @__PURE__ */ new Set([
3992
+ ...Object.keys(inner.breakpoints || {}),
3993
+ ...Object.keys(outer.breakpoints || {})
3994
+ ]);
3995
+ for (const tier of tiers) {
3996
+ bps[tier] = { ...inner.breakpoints?.[tier] || {}, ...outer.breakpoints?.[tier] || {} };
3997
+ }
3998
+ merged.breakpoints = bps;
3999
+ }
4000
+ if (inner.pseudoStates || outer.pseudoStates) {
4001
+ const ps = {};
4002
+ const states = /* @__PURE__ */ new Set([
4003
+ ...Object.keys(inner.pseudoStates || {}),
4004
+ ...Object.keys(outer.pseudoStates || {})
4005
+ ]);
4006
+ for (const state of states) {
4007
+ ps[state] = { ...inner.pseudoStates?.[state] || {}, ...outer.pseudoStates?.[state] || {} };
4008
+ }
4009
+ merged.pseudoStates = ps;
4010
+ }
4011
+ return merged;
4012
+ }
4013
+ var IMAGE_EXT_MIME = {
4014
+ ".png": "image/png",
4015
+ ".jpg": "image/jpeg",
4016
+ ".jpeg": "image/jpeg",
4017
+ ".gif": "image/gif",
4018
+ ".webp": "image/webp",
4019
+ ".avif": "image/avif",
4020
+ ".svg": "image/svg+xml",
4021
+ ".bmp": "image/bmp",
4022
+ ".ico": "image/x-icon"
4023
+ };
4024
+ function isAbsoluteUrl(s) {
4025
+ return /^[a-z][a-z0-9+.-]*:\/\//i.test(s) || s.startsWith("data:");
4026
+ }
4027
+ async function maybeInlineLocalImage(element, src) {
4028
+ if (!src || isAbsoluteUrl(src)) return;
4029
+ const projectRoot = getProjectRoot();
4030
+ const rel = src.replace(/^\/+/, "");
4031
+ const abs = join2(projectRoot, rel);
4032
+ if (!abs.startsWith(projectRoot)) return;
4033
+ try {
4034
+ const buf = await readFile2(abs);
4035
+ const ext = extname(abs).toLowerCase();
4036
+ const mime = IMAGE_EXT_MIME[ext] || "application/octet-stream";
4037
+ element.imageDataBase64 = buf.toString("base64");
4038
+ element.imageDataMime = mime;
4039
+ element.imageDataFileName = basename(abs);
4040
+ } catch {
4041
+ }
4042
+ }
4043
+ function flattenInlineHtmlToText(raw) {
4044
+ const decode = (s) => s.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, " ");
4045
+ let out = "";
4046
+ const tagRe = /<!--[\s\S]*?-->|<\/?([a-zA-Z][a-zA-Z0-9]*)[^>]*>/g;
4047
+ let lastIndex = 0;
4048
+ let match;
4049
+ while ((match = tagRe.exec(raw)) !== null) {
4050
+ if (match.index > lastIndex) {
4051
+ out += decode(raw.slice(lastIndex, match.index));
4052
+ }
4053
+ lastIndex = match.index + match[0].length;
4054
+ if (match[0].startsWith("<!--")) continue;
4055
+ if ((match[1] || "").toLowerCase() === "br") out += "\n";
4056
+ }
4057
+ if (lastIndex < raw.length) out += decode(raw.slice(lastIndex));
4058
+ return out;
3725
4059
  }
3726
- function resolveNestedProp(obj, path) {
3727
- const parts = path.split(".");
3728
- let current = obj;
3729
- for (const part of parts) {
3730
- if (current === null || current === void 0 || typeof current !== "object") return void 0;
3731
- current = current[part];
4060
+ function resolveVarsInValue(value, ctx, breakpoint = "base") {
4061
+ if (!value || !ctx.themeVars && !ctx.projectVars) return value;
4062
+ const themeMap = ctx.themeVars ? (ctx.currentTheme && ctx.themeVars[ctx.currentTheme]) ?? (ctx.defaultTheme ? ctx.themeVars[ctx.defaultTheme] : void 0) : void 0;
4063
+ const projectMap = ctx.projectVars ? ctx.projectVars[breakpoint] || ctx.projectVars.base : void 0;
4064
+ const pattern = /var\(\s*(--[\w-]+)\s*(?:,\s*([^)]+))?\)/g;
4065
+ let current = value;
4066
+ for (let pass = 0; pass < 3; pass++) {
4067
+ let changed = false;
4068
+ current = current.replace(pattern, (match, name, fallback) => {
4069
+ const resolved = (themeMap && themeMap[name]) ?? (projectMap && projectMap[name]);
4070
+ if (resolved !== void 0) {
4071
+ changed = true;
4072
+ return resolved;
4073
+ }
4074
+ if (fallback !== void 0) {
4075
+ changed = true;
4076
+ return fallback.trim();
4077
+ }
4078
+ return match;
4079
+ });
4080
+ if (!changed) break;
3732
4081
  }
3733
4082
  return current;
3734
4083
  }
3735
- function hasTemplates2(text) {
3736
- return /\{\{.+?\}\}/.test(text);
4084
+ function substituteVarsInStyleObject(style, ctx, breakpoint = "base") {
4085
+ const out = {};
4086
+ for (const [k, v] of Object.entries(style)) {
4087
+ out[k] = typeof v === "string" ? resolveVarsInValue(v, ctx, breakpoint) : v;
4088
+ }
4089
+ return out;
4090
+ }
4091
+ function projectBreakpointNames(ctx) {
4092
+ return Object.keys(ctx.breakpoints);
4093
+ }
4094
+ function expandResponsiveVarsInto(baseStyle, responsive, ctx) {
4095
+ if (!ctx.projectVars) return;
4096
+ const bps = projectBreakpointNames(ctx);
4097
+ if (bps.length === 0) return;
4098
+ for (const [prop, rawVal] of Object.entries(baseStyle)) {
4099
+ if (typeof rawVal !== "string" || !rawVal.includes("var(--")) continue;
4100
+ const baseResolved = resolveVarsInValue(rawVal, ctx, "base");
4101
+ for (const bp of bps) {
4102
+ const bpResolved = resolveVarsInValue(rawVal, ctx, bp);
4103
+ if (bpResolved === baseResolved) continue;
4104
+ const bucket = responsive[bp] || {};
4105
+ if (bucket[prop] !== void 0) continue;
4106
+ bucket[prop] = bpResolved;
4107
+ responsive[bp] = bucket;
4108
+ }
4109
+ }
4110
+ }
4111
+ function substituteVarsInStyle(style, ctx) {
4112
+ if (!style) return style;
4113
+ if (isResponsiveStyleObject(style)) {
4114
+ const out = {};
4115
+ for (const [bp, obj] of Object.entries(style)) {
4116
+ if (!obj || typeof obj !== "object") continue;
4117
+ out[bp] = substituteVarsInStyleObject(obj, ctx, bp);
4118
+ }
4119
+ const baseObj = style.base;
4120
+ if (baseObj) expandResponsiveVarsInto(baseObj, out, ctx);
4121
+ return out;
4122
+ }
4123
+ const flatStyle = style;
4124
+ const baseSubstituted = substituteVarsInStyleObject(flatStyle, ctx, "base");
4125
+ const responsive = { base: baseSubstituted };
4126
+ expandResponsiveVarsInto(flatStyle, responsive, ctx);
4127
+ if (Object.keys(responsive).length === 1) return baseSubstituted;
4128
+ return responsive;
4129
+ }
4130
+ function substituteVarsInInteractive(rules, ctx) {
4131
+ if (!rules) return rules;
4132
+ return rules.map((rule) => ({
4133
+ ...rule,
4134
+ style: substituteVarsInStyle(rule.style, ctx) ?? rule.style
4135
+ }));
4136
+ }
4137
+ function substituteVarsInStyleClass(cls, ctx) {
4138
+ const varBaseProps = [];
4139
+ for (const [k, v] of Object.entries(cls.base)) {
4140
+ if (typeof v === "string" && v.includes("var(--")) {
4141
+ varBaseProps.push({ prop: k, raw: v });
4142
+ }
4143
+ }
4144
+ for (const k of Object.keys(cls.base)) {
4145
+ cls.base[k] = resolveVarsInValue(cls.base[k], ctx, "base");
4146
+ }
4147
+ if (cls.breakpoints) {
4148
+ for (const [tier, bp] of Object.entries(cls.breakpoints)) {
4149
+ if (!bp) continue;
4150
+ for (const k of Object.keys(bp)) {
4151
+ bp[k] = resolveVarsInValue(bp[k], ctx, tier);
4152
+ }
4153
+ }
4154
+ }
4155
+ if (cls.pseudoStates) {
4156
+ for (const ps of Object.values(cls.pseudoStates)) {
4157
+ if (!ps) continue;
4158
+ for (const k of Object.keys(ps)) ps[k] = resolveVarsInValue(ps[k], ctx, "base");
4159
+ }
4160
+ }
4161
+ if (varBaseProps.length === 0 || !ctx.projectVars) return;
4162
+ for (const bpName of Object.keys(ctx.breakpoints)) {
4163
+ const tier = menoBreakpointToWebflowTier(bpName, ctx.breakpoints);
4164
+ for (const { prop, raw } of varBaseProps) {
4165
+ const baseResolved = cls.base[prop];
4166
+ const bpResolved = resolveVarsInValue(raw, ctx, bpName);
4167
+ if (bpResolved === baseResolved) continue;
4168
+ if (!cls.breakpoints) cls.breakpoints = {};
4169
+ const bucket = cls.breakpoints[tier] || {};
4170
+ if (bucket[prop] !== void 0) continue;
4171
+ bucket[prop] = bpResolved;
4172
+ cls.breakpoints[tier] = bucket;
4173
+ }
4174
+ }
4175
+ }
4176
+ function menoBreakpointToWebflowTier(bpName, breakpoints) {
4177
+ if (bpName === "tablet") return "medium";
4178
+ if (bpName === "mobile") return "small";
4179
+ const entry = breakpoints[bpName];
4180
+ const w = entry && typeof entry.breakpoint === "number" ? entry.breakpoint : 992;
4181
+ if (w < 480) return "tiny";
4182
+ if (w < 768) return "small";
4183
+ if (w < 992) return "medium";
4184
+ if (w < 1280) return "main";
4185
+ if (w < 1440) return "large";
4186
+ if (w < 1920) return "xl";
4187
+ return "xxl";
4188
+ }
4189
+ function isResponsiveStyleObject(style) {
4190
+ return "base" in style || "tablet" in style || "mobile" in style;
4191
+ }
4192
+ function extractBaseColor(style, ctx, instanceProps) {
4193
+ if (!style) return void 0;
4194
+ const flat = isResponsiveStyleObject(style) ? style.base : style;
4195
+ const c = flat?.color;
4196
+ if (typeof c === "string") return c;
4197
+ if (c && typeof c === "object" && c._mapping === true) {
4198
+ const mapping = c;
4199
+ const propValue = instanceProps?.[mapping.prop] ?? ctx?.componentDefaults?.[mapping.prop];
4200
+ if (propValue == null) return void 0;
4201
+ const key = String(propValue);
4202
+ const raw = mapping.values[key];
4203
+ if (typeof raw !== "string" || raw === "") return void 0;
4204
+ return ctx ? resolveVarsInValue(raw, ctx) : raw;
4205
+ }
4206
+ return void 0;
4207
+ }
4208
+ function concreteColor(c) {
4209
+ if (!c) return void 0;
4210
+ const lower = c.trim().toLowerCase();
4211
+ if (lower === "currentcolor" || lower === "inherit" || lower === "unset" || lower === "initial") {
4212
+ return void 0;
4213
+ }
4214
+ return c;
4215
+ }
4216
+ function inlineCurrentColorInSvg(svg, color) {
4217
+ return svg.replace(/currentColor/gi, color);
4218
+ }
4219
+ function resolveTemplatesInStyleObject(style, props) {
4220
+ const result = {};
4221
+ for (const [key, value] of Object.entries(style)) {
4222
+ if (typeof value === "string" && hasTemplates(value)) {
4223
+ result[key] = processCodeTemplates(value, props);
4224
+ } else {
4225
+ result[key] = value;
4226
+ }
4227
+ }
4228
+ return result;
4229
+ }
4230
+ function resolveStyleTemplates(style, props) {
4231
+ if (!style || !props) return style;
4232
+ if (isResponsiveStyleObject(style)) {
4233
+ const result = {};
4234
+ for (const [bp, styleObj] of Object.entries(style)) {
4235
+ if (styleObj && typeof styleObj === "object") {
4236
+ result[bp] = resolveTemplatesInStyleObject(styleObj, props);
4237
+ }
4238
+ }
4239
+ return result;
4240
+ }
4241
+ return resolveTemplatesInStyleObject(style, props);
4242
+ }
4243
+ var SINGLE_TEMPLATE_RE = /^([^{]*)\{\{\s*([A-Za-z_$][\w$]*)\s*\}\}([^{]*)$/;
4244
+ function extractSingleTemplateProp(value) {
4245
+ const m = SINGLE_TEMPLATE_RE.exec(value);
4246
+ if (!m) return null;
4247
+ return { prefix: m[1] ?? "", prop: m[2], suffix: m[3] ?? "" };
4248
+ }
4249
+ function templatesToSyntheticMappings(style, componentDefaults, instanceProps) {
4250
+ const out = {};
4251
+ for (const [key, value] of Object.entries(style)) {
4252
+ if (typeof value !== "string" || !hasTemplates(value)) {
4253
+ out[key] = value;
4254
+ continue;
4255
+ }
4256
+ const parsed = extractSingleTemplateProp(value);
4257
+ if (!parsed) {
4258
+ out[key] = value;
4259
+ continue;
4260
+ }
4261
+ const { prop } = parsed;
4262
+ const defaultPropValue = componentDefaults[prop];
4263
+ if (defaultPropValue == null) {
4264
+ out[key] = value;
4265
+ continue;
4266
+ }
4267
+ const instancePropValue = instanceProps[prop] ?? defaultPropValue;
4268
+ const defaultResolved = processCodeTemplates(value, { ...instanceProps, [prop]: defaultPropValue });
4269
+ const instanceResolved = processCodeTemplates(value, { ...instanceProps, [prop]: instancePropValue });
4270
+ out[key] = {
4271
+ _mapping: true,
4272
+ prop,
4273
+ values: {
4274
+ [String(defaultPropValue)]: defaultResolved,
4275
+ [String(instancePropValue)]: instanceResolved
4276
+ }
4277
+ };
4278
+ }
4279
+ return out;
4280
+ }
4281
+ function convertStyleTemplatesToMappings(style, componentDefaults, instanceProps) {
4282
+ if (!style || !componentDefaults || !instanceProps) return style;
4283
+ if (isResponsiveStyleObject(style)) {
4284
+ const result = {};
4285
+ for (const [bp, styleObj] of Object.entries(style)) {
4286
+ if (styleObj && typeof styleObj === "object") {
4287
+ result[bp] = templatesToSyntheticMappings(styleObj, componentDefaults, instanceProps);
4288
+ }
4289
+ }
4290
+ return result;
4291
+ }
4292
+ return templatesToSyntheticMappings(style, componentDefaults, instanceProps);
4293
+ }
4294
+ function shouldRenderNode(node, instanceProps) {
4295
+ if (!hasIf(node)) return true;
4296
+ const ifValue = node.if;
4297
+ if (typeof ifValue === "boolean") return ifValue;
4298
+ if (isBooleanMapping(ifValue)) {
4299
+ if (!instanceProps) return true;
4300
+ const propValue = instanceProps[ifValue.prop];
4301
+ const mapped = ifValue.values[String(propValue)];
4302
+ return mapped !== void 0 ? Boolean(mapped) : true;
4303
+ }
4304
+ if (typeof ifValue === "string") {
4305
+ if (!instanceProps) return true;
4306
+ const resolved = processCodeTemplates(ifValue, instanceProps);
4307
+ return Boolean(resolved) && resolved !== "false" && resolved !== "0" && resolved !== "";
4308
+ }
4309
+ return true;
4310
+ }
4311
+ function resolveInteractiveStyleTemplates(rules, props) {
4312
+ if (!rules || !props) return rules;
4313
+ return rules.map((rule) => ({
4314
+ ...rule,
4315
+ style: resolveStyleTemplates(rule.style, props) ?? rule.style
4316
+ }));
3737
4317
  }
3738
- function nodeToWebflow(node, ctx, instanceProps) {
4318
+ async function nodeToWebflow(node, ctx, instanceProps) {
3739
4319
  if (node === null || node === void 0) return [];
3740
4320
  if (typeof node === "string") {
3741
- const text = instanceProps ? resolveTemplate2(node, instanceProps) : node;
4321
+ const text = resolveStringTemplate(node, ctx, instanceProps);
4322
+ if (text === "" || text.trim() === "") return [];
3742
4323
  return [{ tag: "span", textContent: text }];
3743
4324
  }
3744
4325
  if (typeof node === "number") {
@@ -3750,88 +4331,114 @@ function nodeToWebflow(node, ctx, instanceProps) {
3750
4331
  const child = node[i];
3751
4332
  const savedPath = [...ctx.elementPath];
3752
4333
  ctx.elementPath = [...ctx.elementPath, i];
3753
- results.push(...nodeToWebflow(child, ctx, instanceProps));
4334
+ results.push(...await nodeToWebflow(child, ctx, instanceProps));
3754
4335
  ctx.elementPath = savedPath;
3755
4336
  }
3756
4337
  return results;
3757
4338
  }
4339
+ if (!shouldRenderNode(node, instanceProps)) return [];
3758
4340
  switch (node.type) {
3759
4341
  case NODE_TYPE.NODE:
3760
- return [emitHtmlNode2(node, ctx, instanceProps)];
4342
+ return [await emitHtmlNode2(node, ctx, instanceProps)];
3761
4343
  case NODE_TYPE.COMPONENT:
3762
4344
  return emitComponentInstance2(node, ctx, instanceProps);
3763
4345
  case NODE_TYPE.SLOT:
3764
4346
  return emitSlotMarker2(node, ctx, instanceProps);
3765
4347
  case NODE_TYPE.EMBED:
3766
- return [emitEmbedNode2(node, ctx, instanceProps)];
4348
+ return [await emitEmbedNode2(node, ctx, instanceProps)];
3767
4349
  case NODE_TYPE.LINK:
3768
- return [emitLinkNode2(node, ctx, instanceProps)];
4350
+ return [await emitLinkNode2(node, ctx, instanceProps)];
3769
4351
  case NODE_TYPE.LIST:
3770
4352
  case "cms-list":
4353
+ return emitListNode2(node, ctx, instanceProps);
3771
4354
  case NODE_TYPE.LOCALE_LIST:
3772
- return [{ tag: "div", attributes: { "data-meno-type": node.type } }];
4355
+ return emitLocaleListNode2(node, ctx, instanceProps);
3773
4356
  default:
3774
4357
  return [];
3775
4358
  }
3776
4359
  }
3777
- function emitHtmlNode2(node, ctx, instanceProps) {
3778
- const tag = hasTemplates2(node.tag) && instanceProps ? resolveTemplate2(node.tag, instanceProps) : node.tag;
3779
- const style = node.style;
3780
- const interactiveStyles = node.interactiveStyles;
3781
- const needsClass = style || interactiveStyles && interactiveStyles.length > 0 || node.generateElementClass;
4360
+ async function emitHtmlNode2(node, ctx, instanceProps) {
4361
+ const tag = resolveStringTemplate(node.tag, ctx, instanceProps);
4362
+ const attributes = {};
4363
+ if (node.attributes) {
4364
+ for (const [key, value] of Object.entries(node.attributes)) {
4365
+ attributes[key] = typeof value === "string" ? resolveStringTemplate(value, ctx, instanceProps) : value;
4366
+ }
4367
+ }
4368
+ const themedCtx = typeof attributes.theme === "string" && attributes.theme ? { ...ctx, currentTheme: attributes.theme } : ctx;
4369
+ const rawStyle = node.style;
4370
+ const rawInteractiveStyles = node.interactiveStyles;
4371
+ const styleWithMappings = convertStyleTemplatesToMappings(
4372
+ rawStyle,
4373
+ ctx.componentDefaults,
4374
+ instanceProps
4375
+ );
4376
+ const style = substituteVarsInStyle(
4377
+ resolveStyleTemplates(styleWithMappings, instanceProps),
4378
+ themedCtx
4379
+ );
4380
+ const interactiveStyles = substituteVarsInInteractive(
4381
+ resolveInteractiveStyleTemplates(rawInteractiveStyles, instanceProps),
4382
+ themedCtx
4383
+ );
4384
+ const tagLower = tag.toLowerCase();
4385
+ const isHeading = HEADING_TAGS.has(tagLower);
4386
+ const isLinkLike = LINK_LIKE_TAGS.has(tagLower);
4387
+ const isParagraph = tagLower === "p";
4388
+ const isList = LIST_TAGS.has(tagLower);
4389
+ const needsClass = style || interactiveStyles && interactiveStyles.length > 0 || node.generateElementClass || isHeading || isLinkLike || isParagraph || isList;
3782
4390
  let className;
3783
4391
  let comboClassNames;
3784
4392
  if (needsClass) {
3785
- const elementClass = buildElementClass2(ctx, node.label);
4393
+ const elementClass = withThemeSuffix(buildElementClass2(ctx, node.label), themedCtx);
3786
4394
  const { primaryClass, comboClasses } = mapStylesToWebflow(
3787
4395
  elementClass,
3788
4396
  style,
3789
4397
  interactiveStyles,
3790
- ctx.breakpoints
4398
+ ctx.breakpoints,
4399
+ ctx.responsiveScales,
4400
+ { instanceProps, componentDefaults: ctx.componentDefaults, themeSuffix: themedClassSuffix(themedCtx) }
3791
4401
  );
4402
+ substituteVarsInStyleClass(primaryClass, themedCtx);
4403
+ if (isHeading) applyHeadingMarginDefaults(primaryClass);
4404
+ if (isParagraph) applyParagraphMarginDefaults(primaryClass);
4405
+ if (isList) applyListMarginDefaults(primaryClass);
4406
+ if (isLinkLike) applyLinkTextDecorationDefault(primaryClass);
4407
+ applyGridRowsDefault(primaryClass);
3792
4408
  className = primaryClass.name;
3793
4409
  ctx.styleClasses.set(primaryClass.name, primaryClass);
3794
4410
  if (comboClasses.length > 0) {
3795
4411
  comboClassNames = [];
3796
4412
  for (const combo of comboClasses) {
4413
+ substituteVarsInStyleClass(combo, themedCtx);
3797
4414
  ctx.styleClasses.set(combo.name, combo);
3798
4415
  comboClassNames.push(combo.name);
3799
4416
  }
3800
4417
  }
3801
- }
3802
- const attributes = {};
3803
- if (node.attributes) {
3804
- for (const [key, value] of Object.entries(node.attributes)) {
3805
- if (instanceProps && typeof value === "string" && hasTemplates2(value)) {
3806
- attributes[key] = resolveTemplate2(value, instanceProps);
3807
- } else {
3808
- attributes[key] = value;
3809
- }
4418
+ if (ctx.interactiveStylesMap && Array.isArray(interactiveStyles) && interactiveStyles.length > 0) {
4419
+ ctx.interactiveStylesMap.set(primaryClass.name, interactiveStyles);
3810
4420
  }
3811
4421
  }
3812
4422
  let children;
3813
4423
  let textContent;
3814
4424
  if (!isVoidElement(tag) && node.children) {
3815
4425
  if (typeof node.children === "string") {
3816
- textContent = instanceProps ? resolveTemplate2(node.children, instanceProps) : node.children;
4426
+ textContent = resolveStringTemplate(node.children, ctx, instanceProps);
3817
4427
  } else if (Array.isArray(node.children) && node.children.length === 1 && typeof node.children[0] === "string") {
3818
4428
  const text = node.children[0];
3819
- textContent = instanceProps ? resolveTemplate2(text, instanceProps) : text;
4429
+ textContent = resolveStringTemplate(text, ctx, instanceProps);
3820
4430
  } else {
3821
- const innerCtx = { ...ctx, elementPath: [...ctx.elementPath] };
3822
- children = convertChildren(node.children, innerCtx, instanceProps);
4431
+ const innerCtx = { ...themedCtx, elementPath: [...themedCtx.elementPath] };
4432
+ const ownColor = concreteColor(extractBaseColor(style, themedCtx, instanceProps));
4433
+ if (ownColor) innerCtx.inheritedColor = ownColor;
4434
+ children = await convertChildren(node.children, innerCtx, instanceProps);
3823
4435
  if (children.length === 0) children = void 0;
3824
4436
  }
3825
4437
  }
3826
- let conditional;
3827
- const ifValue = node.if;
3828
- if (ifValue !== void 0 && ifValue !== true) {
3829
- if (typeof ifValue === "object" && ifValue._mapping) {
3830
- conditional = { prop: ifValue.prop, condition: "truthy" };
3831
- } else if (typeof ifValue === "string") {
3832
- const match = ifValue.match(/^\{\{(.+)\}\}$/);
3833
- conditional = { prop: match ? match[1].trim() : ifValue, condition: "truthy" };
3834
- }
4438
+ if (textContent && textContent.startsWith(RAW_HTML_PREFIX)) {
4439
+ textContent = flattenInlineHtmlToText(textContent.slice(RAW_HTML_PREFIX.length));
4440
+ } else if (textContent && (isHeading || isParagraph) && /<[a-zA-Z!]/.test(textContent)) {
4441
+ textContent = flattenInlineHtmlToText(textContent);
3835
4442
  }
3836
4443
  const element = { tag };
3837
4444
  if (className) element.className = className;
@@ -3839,115 +4446,786 @@ function emitHtmlNode2(node, ctx, instanceProps) {
3839
4446
  if (textContent) element.textContent = textContent;
3840
4447
  if (children) element.children = children;
3841
4448
  if (Object.keys(attributes).length > 0) element.attributes = attributes;
3842
- if (conditional) element.conditional = conditional;
4449
+ if (tag.toLowerCase() === "img" && typeof attributes.src === "string") {
4450
+ await maybeInlineLocalImage(element, attributes.src);
4451
+ }
3843
4452
  return element;
3844
4453
  }
3845
- function emitComponentInstance2(node, ctx, parentProps) {
4454
+ async function emitComponentInstance2(node, ctx, parentProps) {
3846
4455
  const compDef = ctx.globalComponents[node.component];
3847
4456
  if (!compDef) {
3848
4457
  return [{ tag: "div", attributes: { "data-component": node.component } }];
3849
4458
  }
3850
- const resolvedProps = {};
3851
4459
  const structured = compDef.component;
3852
- if (structured?.interface) {
3853
- for (const [key, propDef] of Object.entries(structured.interface)) {
3854
- resolvedProps[key] = propDef.default;
3855
- }
3856
- }
4460
+ const passedProps = {};
3857
4461
  if (node.props) {
4462
+ const parentTemplateProps = parentProps ?? buildTemplateProps(ctx);
3858
4463
  for (const [key, value] of Object.entries(node.props)) {
3859
4464
  if (key === "children") continue;
3860
- if (typeof value === "string" && hasTemplates2(value) && parentProps) {
3861
- resolvedProps[key] = resolveTemplate2(value, parentProps);
3862
- } else {
3863
- resolvedProps[key] = value;
3864
- }
4465
+ passedProps[key] = typeof value === "string" && hasTemplates(value) ? processCodeTemplates(value, parentTemplateProps) : value;
3865
4466
  }
3866
4467
  }
4468
+ const resolvedProps = structured ? resolvePropsFromDefinition(
4469
+ structured,
4470
+ passedProps,
4471
+ node.children,
4472
+ ctx.locale,
4473
+ ctx.i18nConfig
4474
+ ) : { ...passedProps };
4475
+ delete resolvedProps.children;
3867
4476
  const body = structured?.structure || compDef.node;
3868
4477
  if (!body) return [];
4478
+ const hasInstanceStyleOverrides = Boolean(structured?.acceptsStyles) && (node.style && Object.keys(node.style).length > 0 || Array.isArray(node.interactiveStyles) && node.interactiveStyles.length > 0);
4479
+ const promotedNames = ctx.promotedComponentNames || PROMOTED_TO_WEBFLOW_COMPONENT;
4480
+ if (promotedNames.has(node.component) && ctx.promotedComponents && !hasInstanceStyleOverrides) {
4481
+ const promoted = ctx.promotedComponents;
4482
+ if (!promoted.has(node.component)) {
4483
+ const defaultProps = {};
4484
+ if (structured?.interface) {
4485
+ for (const [k, p] of Object.entries(structured.interface)) {
4486
+ defaultProps[k] = p.default;
4487
+ }
4488
+ }
4489
+ const compBodyCtx = {
4490
+ ...ctx,
4491
+ fileType: "component",
4492
+ fileName: node.component,
4493
+ elementPath: [0],
4494
+ slotChildren: void 0,
4495
+ slotEmitContext: void 0,
4496
+ // Disable further promotion inside the component body — a Navigation
4497
+ // that nests Footer (or itself) would loop. Nested promoted names
4498
+ // inline within the registered Component's body.
4499
+ promotedComponents: null,
4500
+ componentDefaults: defaultProps
4501
+ };
4502
+ const elements = await nodeToWebflow(body, compBodyCtx, defaultProps);
4503
+ promoted.set(node.component, { name: node.component, elements });
4504
+ }
4505
+ const fallback = await emitInlineComponentBody(
4506
+ node,
4507
+ compDef,
4508
+ resolvedProps,
4509
+ body,
4510
+ { ...ctx, promotedComponents: null },
4511
+ parentProps
4512
+ );
4513
+ return [{ tag: "div", componentRef: node.component, inlineFallback: fallback }];
4514
+ }
4515
+ return emitInlineComponentBody(node, compDef, resolvedProps, body, ctx, parentProps);
4516
+ }
4517
+ async function emitInlineComponentBody(node, compDef, resolvedProps, body, ctx, parentProps) {
4518
+ const structured = compDef.component;
4519
+ let effectiveChildren = node.children;
4520
+ let inheritedSlotEmitContext = ctx.slotEmitContext;
4521
+ let inheritedSlotInstanceProps = void 0;
4522
+ let didForwardSlot = false;
4523
+ if (node.children != null && typeof node.children !== "string") {
4524
+ const childArr = Array.isArray(node.children) ? node.children : [node.children];
4525
+ if (childArr.some((c) => c && typeof c === "object" && c.type === NODE_TYPE.SLOT)) {
4526
+ const out = [];
4527
+ for (const c of childArr) {
4528
+ if (c && typeof c === "object" && c.type === NODE_TYPE.SLOT) {
4529
+ didForwardSlot = true;
4530
+ if (ctx.slotChildren !== void 0) {
4531
+ const sc = ctx.slotChildren;
4532
+ const subArr = Array.isArray(sc) ? sc : [sc];
4533
+ out.push(...subArr);
4534
+ } else {
4535
+ const def = c.default;
4536
+ if (def !== void 0) {
4537
+ const defArr = Array.isArray(def) ? def : [def];
4538
+ out.push(...defArr);
4539
+ }
4540
+ }
4541
+ } else {
4542
+ out.push(c);
4543
+ }
4544
+ }
4545
+ if (didForwardSlot) {
4546
+ effectiveChildren = out;
4547
+ inheritedSlotInstanceProps = ctx.slotInstanceProps;
4548
+ }
4549
+ }
4550
+ }
3869
4551
  const compCtx = {
3870
4552
  ...ctx,
3871
4553
  fileType: "component",
3872
4554
  fileName: node.component,
3873
4555
  elementPath: [0],
3874
- slotChildren: node.children
4556
+ slotChildren: effectiveChildren,
4557
+ slotEmitContext: didForwardSlot && inheritedSlotEmitContext ? inheritedSlotEmitContext : {
4558
+ fileType: ctx.fileType,
4559
+ fileName: ctx.fileName,
4560
+ elementPath: [...ctx.elementPath]
4561
+ },
4562
+ // Slot children authored alongside this instance refer to the OUTER
4563
+ // component's interface (the one whose body contains `<Component …>{slot
4564
+ // children}</Component>`). When the body hits its `<slot/>`, those
4565
+ // children must resolve `{{title}}` etc. against `parentProps` — not
4566
+ // against this component's resolved props. For forwarded slot children,
4567
+ // use the original outer slot's `slotInstanceProps` instead so they keep
4568
+ // their original authoring scope through the wrapper.
4569
+ slotInstanceProps: didForwardSlot ? inheritedSlotInstanceProps : parentProps,
4570
+ componentDefaults: extractInterfaceDefaults(structured?.interface)
3875
4571
  };
3876
- return nodeToWebflow(body, compCtx, resolvedProps);
4572
+ const ctxTemplate = ctx.templateContext;
4573
+ const bodyProps = ctxTemplate ? { ...ctxTemplate, ...resolvedProps } : resolvedProps;
4574
+ const emitted = await nodeToWebflow(body, compCtx, bodyProps);
4575
+ if (structured?.acceptsStyles && emitted.length > 0 && (node.style && Object.keys(node.style).length > 0 || Array.isArray(node.interactiveStyles) && node.interactiveStyles.length > 0)) {
4576
+ const root = emitted[0];
4577
+ if (root && typeof root === "object") {
4578
+ if (!root.className) {
4579
+ const minted = withThemeSuffix(buildElementClass2(compCtx, void 0), compCtx);
4580
+ ctx.styleClasses.set(minted, { name: minted, base: {} });
4581
+ root.className = minted;
4582
+ }
4583
+ const rawStyle = node.style;
4584
+ const rawInteractive = node.interactiveStyles;
4585
+ const resolvedStyle = substituteVarsInStyle(
4586
+ resolveStyleTemplates(rawStyle, parentProps),
4587
+ ctx
4588
+ );
4589
+ const resolvedInteractive = substituteVarsInInteractive(
4590
+ resolveInteractiveStyleTemplates(rawInteractive, parentProps),
4591
+ ctx
4592
+ );
4593
+ const instanceLocClass = withThemeSuffix(buildElementClass2(ctx, node.label), ctx);
4594
+ const comboName = mintInstanceComboName(ctx, instanceLocClass);
4595
+ const combo = buildInstanceStyleCombo(
4596
+ comboName,
4597
+ root.className,
4598
+ resolvedStyle,
4599
+ resolvedInteractive,
4600
+ ctx.breakpoints,
4601
+ ctx.responsiveScales
4602
+ );
4603
+ if (combo) {
4604
+ substituteVarsInStyleClass(combo, ctx);
4605
+ const existingComboName = Array.isArray(root.comboClasses) && root.comboClasses.length > 0 ? root.comboClasses[0] : void 0;
4606
+ const inner = existingComboName ? ctx.styleClasses.get(existingComboName) : void 0;
4607
+ if (inner) {
4608
+ const merged = mergeComboClasses(inner, combo, root.className);
4609
+ ctx.styleClasses.set(merged.name, merged);
4610
+ root.comboClasses = [merged.name];
4611
+ } else {
4612
+ ctx.styleClasses.set(combo.name, combo);
4613
+ root.comboClasses = [combo.name];
4614
+ }
4615
+ }
4616
+ }
4617
+ }
4618
+ if (emitted.length > 0 && structured?.javascript) {
4619
+ const root = emitted[0];
4620
+ if (root && typeof root === "object") {
4621
+ root.attributes = root.attributes || {};
4622
+ const existing = root.attributes["data-component"];
4623
+ root.attributes["data-component"] = existing ? `${existing} ${node.component}` : node.component;
4624
+ const defineVars = structured.defineVars;
4625
+ if (defineVars) {
4626
+ const varsToExpose = defineVars === true ? Object.keys(structured.interface || {}) : defineVars;
4627
+ const propsForJS = {};
4628
+ for (const varName of varsToExpose) {
4629
+ if (resolvedProps[varName] !== void 0) {
4630
+ propsForJS[varName] = resolvedProps[varName];
4631
+ }
4632
+ }
4633
+ let existingPropsMap = {};
4634
+ const existingPropsStr = root.attributes["data-props"];
4635
+ if (typeof existingPropsStr === "string" && existingPropsStr) {
4636
+ try {
4637
+ const parsed = JSON.parse(decodeURIComponent(existingPropsStr));
4638
+ if (parsed && typeof parsed === "object") {
4639
+ existingPropsMap = parsed;
4640
+ }
4641
+ } catch {
4642
+ }
4643
+ }
4644
+ existingPropsMap[node.component] = propsForJS;
4645
+ root.attributes["data-props"] = encodeURIComponent(JSON.stringify(existingPropsMap));
4646
+ }
4647
+ }
4648
+ }
4649
+ return emitted;
3877
4650
  }
3878
- function emitSlotMarker2(node, ctx, instanceProps) {
4651
+ async function emitSlotMarker2(node, ctx, instanceProps) {
3879
4652
  if (ctx.slotChildren) {
4653
+ const restored = ctx.slotEmitContext;
3880
4654
  const parentCtx = {
3881
4655
  ...ctx,
3882
- slotChildren: void 0
4656
+ ...restored ? {
4657
+ fileType: restored.fileType,
4658
+ fileName: restored.fileName,
4659
+ elementPath: [...restored.elementPath]
4660
+ } : {},
4661
+ slotChildren: void 0,
3883
4662
  // prevent infinite slot nesting
4663
+ slotEmitContext: void 0,
4664
+ // consumed
4665
+ slotInstanceProps: void 0
4666
+ // consumed
3884
4667
  };
3885
- return convertChildren(ctx.slotChildren, parentCtx, instanceProps);
4668
+ const slotProps = ctx.slotInstanceProps ?? instanceProps;
4669
+ return convertChildren(ctx.slotChildren, parentCtx, slotProps);
3886
4670
  }
3887
4671
  if (node.default) {
3888
4672
  return convertChildren(node.default, ctx, instanceProps);
3889
4673
  }
3890
4674
  return [];
3891
4675
  }
3892
- function emitEmbedNode2(node, ctx, instanceProps) {
3893
- const style = node.style;
3894
- const interactiveStyles = node.interactiveStyles;
4676
+ async function emitEmbedNode2(node, ctx, instanceProps) {
4677
+ const style = substituteVarsInStyle(
4678
+ resolveStyleTemplates(
4679
+ node.style,
4680
+ instanceProps
4681
+ ),
4682
+ ctx
4683
+ );
4684
+ const interactiveStyles = substituteVarsInInteractive(
4685
+ resolveInteractiveStyleTemplates(
4686
+ node.interactiveStyles,
4687
+ instanceProps
4688
+ ),
4689
+ ctx
4690
+ );
3895
4691
  let className;
3896
4692
  if (style || interactiveStyles && interactiveStyles.length > 0) {
3897
- const elementClass = buildElementClass2(ctx, node.label);
4693
+ const elementClass = withThemeSuffix(buildElementClass2(ctx, node.label), ctx);
3898
4694
  const { primaryClass } = mapStylesToWebflow(
3899
4695
  elementClass,
3900
4696
  style,
3901
4697
  interactiveStyles,
3902
- ctx.breakpoints
4698
+ ctx.breakpoints,
4699
+ ctx.responsiveScales
3903
4700
  );
4701
+ substituteVarsInStyleClass(primaryClass, ctx);
3904
4702
  className = primaryClass.name;
3905
4703
  ctx.styleClasses.set(primaryClass.name, primaryClass);
3906
4704
  }
3907
- const htmlStr = typeof node.html === "string" ? node.html : "";
3908
- const element = {
3909
- tag: "div",
3910
- rawHtml: htmlStr
3911
- };
4705
+ const props = buildTemplateProps(ctx, instanceProps);
4706
+ let htmlStr;
4707
+ if (isHtmlMapping(node.html)) {
4708
+ htmlStr = resolveHtmlMapping(node.html, props) ?? "";
4709
+ } else if (typeof node.html === "string") {
4710
+ htmlStr = hasTemplates(node.html) ? processCodeTemplates(node.html, props) : node.html;
4711
+ } else {
4712
+ htmlStr = "";
4713
+ }
4714
+ const element = { tag: "div" };
3912
4715
  if (className) element.className = className;
4716
+ const trimmed = htmlStr.trim();
4717
+ if (/^<svg\b/i.test(trimmed) && /<\/svg\s*>\s*$/i.test(trimmed)) {
4718
+ let svg = trimmed;
4719
+ if (/currentColor/i.test(svg)) {
4720
+ const resolved = concreteColor(extractBaseColor(style, ctx, instanceProps)) ?? ctx.inheritedColor;
4721
+ if (resolved) svg = inlineCurrentColorInSvg(svg, resolved);
4722
+ }
4723
+ element.svgSource = svg;
4724
+ if (node.label) element.imageAlt = node.label;
4725
+ return element;
4726
+ }
4727
+ const imgMatch = trimmed.match(/^<img\b([^>]*)\/?>\s*$/i);
4728
+ if (imgMatch) {
4729
+ const attrs = imgMatch[1];
4730
+ const src = /\bsrc\s*=\s*"([^"]*)"|\bsrc\s*=\s*'([^']*)'/i.exec(attrs);
4731
+ const alt = /\balt\s*=\s*"([^"]*)"|\balt\s*=\s*'([^']*)'/i.exec(attrs);
4732
+ if (src) {
4733
+ element.imageSrc = src[1] ?? src[2] ?? "";
4734
+ element.imageAlt = (alt?.[1] ?? alt?.[2] ?? node.label) || "";
4735
+ await maybeInlineLocalImage(element, element.imageSrc);
4736
+ return element;
4737
+ }
4738
+ }
4739
+ if (trimmed.length) {
4740
+ element.unsupportedEmbed = {
4741
+ reason: "embed-not-svg-or-image",
4742
+ preview: trimmed.slice(0, 120),
4743
+ label: node.label
4744
+ };
4745
+ }
3913
4746
  return element;
3914
4747
  }
3915
- function emitLinkNode2(node, ctx, instanceProps) {
3916
- const style = node.style;
3917
- const interactiveStyles = node.interactiveStyles;
4748
+ function resolveLinkHref(rawHref, ctx) {
4749
+ if (!rawHref) return rawHref;
4750
+ if (!rawHref.startsWith("/") || rawHref.startsWith("//")) return rawHref;
4751
+ if (!ctx.i18nConfig || !ctx.locale || !ctx.slugMappings) return rawHref;
4752
+ const hashIdx = rawHref.indexOf("#");
4753
+ const queryIdx = rawHref.indexOf("?");
4754
+ let cut = -1;
4755
+ if (hashIdx >= 0 && queryIdx >= 0) cut = Math.min(hashIdx, queryIdx);
4756
+ else if (hashIdx >= 0) cut = hashIdx;
4757
+ else if (queryIdx >= 0) cut = queryIdx;
4758
+ const path = cut >= 0 ? rawHref.slice(0, cut) : rawHref;
4759
+ const suffix = cut >= 0 ? rawHref.slice(cut) : "";
4760
+ if (!ctx.slugIndex) {
4761
+ ctx.slugIndex = buildSlugIndex(ctx.slugMappings);
4762
+ }
4763
+ const translated = translatePath(
4764
+ path,
4765
+ ctx.locale,
4766
+ ctx.i18nConfig.defaultLocale,
4767
+ ctx.i18nConfig.defaultLocale,
4768
+ ctx.slugIndex
4769
+ );
4770
+ return translated + suffix;
4771
+ }
4772
+ async function emitLinkNode2(node, ctx, instanceProps) {
4773
+ const style = substituteVarsInStyle(
4774
+ resolveStyleTemplates(
4775
+ node.style,
4776
+ instanceProps
4777
+ ),
4778
+ ctx
4779
+ );
4780
+ const interactiveStyles = substituteVarsInInteractive(
4781
+ resolveInteractiveStyleTemplates(
4782
+ node.interactiveStyles,
4783
+ instanceProps
4784
+ ),
4785
+ ctx
4786
+ );
3918
4787
  let className;
3919
- if (style || interactiveStyles && interactiveStyles.length > 0) {
3920
- const elementClass = buildElementClass2(ctx, node.label);
3921
- const { primaryClass } = mapStylesToWebflow(
4788
+ let comboClassNames;
4789
+ {
4790
+ const elementClass = withThemeSuffix(buildElementClass2(ctx, node.label), ctx);
4791
+ const { primaryClass, comboClasses } = mapStylesToWebflow(
3922
4792
  elementClass,
3923
4793
  style,
3924
4794
  interactiveStyles,
3925
- ctx.breakpoints
4795
+ ctx.breakpoints,
4796
+ ctx.responsiveScales,
4797
+ { instanceProps, componentDefaults: ctx.componentDefaults, themeSuffix: themedClassSuffix(ctx) }
3926
4798
  );
4799
+ substituteVarsInStyleClass(primaryClass, ctx);
4800
+ applyLinkTextDecorationDefault(primaryClass);
4801
+ applyGridRowsDefault(primaryClass);
3927
4802
  className = primaryClass.name;
3928
4803
  ctx.styleClasses.set(primaryClass.name, primaryClass);
4804
+ if (comboClasses.length > 0) {
4805
+ comboClassNames = [];
4806
+ for (const combo of comboClasses) {
4807
+ substituteVarsInStyleClass(combo, ctx);
4808
+ ctx.styleClasses.set(combo.name, combo);
4809
+ comboClassNames.push(combo.name);
4810
+ }
4811
+ }
4812
+ if (ctx.interactiveStylesMap && Array.isArray(interactiveStyles) && interactiveStyles.length > 0) {
4813
+ ctx.interactiveStylesMap.set(primaryClass.name, interactiveStyles);
4814
+ }
3929
4815
  }
3930
4816
  let href = "#";
3931
4817
  if (typeof node.href === "string") {
3932
- href = instanceProps && hasTemplates2(node.href) ? resolveTemplate2(node.href, instanceProps) : node.href;
4818
+ href = resolveStringTemplate(node.href, ctx, instanceProps);
3933
4819
  }
4820
+ href = resolveLinkHref(href, ctx);
3934
4821
  const attributes = { href };
3935
4822
  if (node.attributes) {
3936
4823
  for (const [key, value] of Object.entries(node.attributes)) {
3937
- attributes[key] = value;
4824
+ attributes[key] = typeof value === "string" ? resolveStringTemplate(value, ctx, instanceProps) : value;
3938
4825
  }
3939
4826
  }
3940
4827
  let children;
3941
4828
  if (node.children) {
3942
- children = convertChildren(node.children, ctx, instanceProps);
4829
+ const ownColor = concreteColor(extractBaseColor(style, ctx, instanceProps));
4830
+ const childCtx = ownColor ? { ...ctx, inheritedColor: ownColor } : ctx;
4831
+ children = await convertChildren(node.children, childCtx, instanceProps);
3943
4832
  if (children.length === 0) children = void 0;
3944
4833
  }
3945
4834
  const element = { tag: "a", attributes };
3946
4835
  if (className) element.className = className;
4836
+ if (comboClassNames) element.comboClasses = comboClassNames;
3947
4837
  if (children) element.children = children;
3948
4838
  return element;
3949
4839
  }
3950
- function convertChildren(children, ctx, instanceProps) {
4840
+ function singularize2(name) {
4841
+ if (name.endsWith("ies") && name.length > 3) return name.slice(0, -3) + "y";
4842
+ if (name.endsWith("s") && name.length > 1) return name.slice(0, -1);
4843
+ return name;
4844
+ }
4845
+ function resolveItemI18n(item, ctx) {
4846
+ if (!ctx.locale || !ctx.i18nConfig) return item;
4847
+ const out = {};
4848
+ for (const [k, v] of Object.entries(item)) {
4849
+ out[k] = isI18nValue(v) ? resolveI18nValue(v, ctx.locale, ctx.i18nConfig) : v;
4850
+ }
4851
+ return out;
4852
+ }
4853
+ async function getCollectionItemsForExport(node, source, ctx) {
4854
+ if (!ctx.cmsService) return [];
4855
+ let items = [];
4856
+ if (node.items) {
4857
+ const itemsField = node.items;
4858
+ let resolvedIds;
4859
+ if (typeof itemsField === "string" && itemsField.startsWith("{{")) {
4860
+ if (itemsField.startsWith("{{cms.") && ctx.cmsContext?.cms) {
4861
+ const fieldPath = itemsField.slice(6, -2);
4862
+ let value = ctx.cmsContext.cms;
4863
+ for (const part of fieldPath.split(".")) {
4864
+ if (value && typeof value === "object" && part in value) {
4865
+ value = value[part];
4866
+ } else {
4867
+ value = void 0;
4868
+ break;
4869
+ }
4870
+ }
4871
+ if (value !== null && value !== void 0) {
4872
+ resolvedIds = Array.isArray(value) ? value.map((v) => String(v)) : String(value);
4873
+ }
4874
+ } else {
4875
+ resolvedIds = resolveItemsTemplate(
4876
+ itemsField,
4877
+ ctx.templateContext || { _type: "template" }
4878
+ );
4879
+ }
4880
+ if (!resolvedIds) return [];
4881
+ const ids = Array.isArray(resolvedIds) ? resolvedIds : [resolvedIds];
4882
+ items = await ctx.cmsService.getItemsByIds(source, ids);
4883
+ } else {
4884
+ const ids = Array.isArray(itemsField) ? itemsField : [itemsField];
4885
+ items = await ctx.cmsService.getItemsByIds(source, ids);
4886
+ }
4887
+ if (ctx.locale) {
4888
+ items = items.filter((it) => !isItemDraftForLocale(it, ctx.locale));
4889
+ }
4890
+ } else {
4891
+ items = await ctx.cmsService.queryItems({
4892
+ collection: source,
4893
+ filter: node.filter,
4894
+ sort: node.sort,
4895
+ limit: node.limit,
4896
+ offset: node.offset,
4897
+ excludeDraftLocale: ctx.locale
4898
+ });
4899
+ }
4900
+ if (node.excludeCurrentItem && ctx.cmsContext?.cms?._id) {
4901
+ const currentId = ctx.cmsContext.cms._id;
4902
+ items = items.filter((it) => it._id !== currentId);
4903
+ }
4904
+ return items;
4905
+ }
4906
+ function getPropItemsForExport(source, ctx, instanceProps) {
4907
+ if (source.startsWith("{{") && source.endsWith("}}")) {
4908
+ const path = source.slice(2, -2).trim();
4909
+ const ctxObj = ctx.templateContext;
4910
+ if (ctxObj) {
4911
+ const resolved = getNestedValue(ctxObj, path);
4912
+ if (Array.isArray(resolved)) return resolved;
4913
+ }
4914
+ if (instanceProps) {
4915
+ const resolved = getNestedValue(instanceProps, path);
4916
+ if (Array.isArray(resolved)) return resolved;
4917
+ }
4918
+ return [];
4919
+ }
4920
+ if (instanceProps && Array.isArray(instanceProps[source])) {
4921
+ return instanceProps[source];
4922
+ }
4923
+ if (ctx.cmsContext?.cms) {
4924
+ const cmsValue = ctx.cmsContext.cms[source];
4925
+ if (Array.isArray(cmsValue)) return cmsValue;
4926
+ }
4927
+ return [];
4928
+ }
4929
+ function buildBindingPlaceholderItem(schema) {
4930
+ const item = {};
4931
+ if (schema?.fields) {
4932
+ for (const fieldSlug of Object.keys(schema.fields)) {
4933
+ item[fieldSlug] = `${MENO_BIND_SENTINEL_PREFIX}${fieldSlug}${MENO_BIND_SENTINEL_SUFFIX}`;
4934
+ }
4935
+ }
4936
+ item._id = `${MENO_BIND_SENTINEL_PREFIX}_id${MENO_BIND_SENTINEL_SUFFIX}`;
4937
+ item._slug = `${MENO_BIND_SENTINEL_PREFIX}_slug${MENO_BIND_SENTINEL_SUFFIX}`;
4938
+ item._url = `${MENO_BIND_SENTINEL_PREFIX}_url${MENO_BIND_SENTINEL_SUFFIX}`;
4939
+ return item;
4940
+ }
4941
+ function applyBindingMarkers(el) {
4942
+ if (typeof el.textContent === "string") {
4943
+ const exact = el.textContent.match(MENO_BIND_SENTINEL_EXACT_RE);
4944
+ if (exact) {
4945
+ const field = exact[1];
4946
+ el.menoBind = { ...el.menoBind || {}, textField: field };
4947
+ el.textContent = `{${field}}`;
4948
+ } else if (MENO_BIND_SENTINEL_RE.test(el.textContent)) {
4949
+ MENO_BIND_SENTINEL_RE.lastIndex = 0;
4950
+ const fields = [];
4951
+ el.textContent = el.textContent.replace(MENO_BIND_SENTINEL_RE, (_m, f) => {
4952
+ fields.push(f);
4953
+ return `{${f}}`;
4954
+ });
4955
+ if (fields.length) {
4956
+ el.menoBind = { ...el.menoBind || {}, textField: fields[0] };
4957
+ }
4958
+ }
4959
+ MENO_BIND_SENTINEL_RE.lastIndex = 0;
4960
+ }
4961
+ if (el.attributes) {
4962
+ const attrFields = el.menoBind?.attrFields || {};
4963
+ for (const [key, rawValue] of Object.entries(el.attributes)) {
4964
+ if (typeof rawValue !== "string") continue;
4965
+ const exact = rawValue.match(MENO_BIND_SENTINEL_EXACT_RE);
4966
+ if (exact) {
4967
+ attrFields[key] = exact[1];
4968
+ el.attributes[key] = `{${exact[1]}}`;
4969
+ continue;
4970
+ }
4971
+ MENO_BIND_SENTINEL_RE.lastIndex = 0;
4972
+ if (MENO_BIND_SENTINEL_RE.test(rawValue)) {
4973
+ MENO_BIND_SENTINEL_RE.lastIndex = 0;
4974
+ let firstField;
4975
+ const replaced = rawValue.replace(MENO_BIND_SENTINEL_RE, (_m, f) => {
4976
+ if (!firstField) firstField = f;
4977
+ return `{${f}}`;
4978
+ });
4979
+ if (firstField) attrFields[key] = firstField;
4980
+ el.attributes[key] = replaced;
4981
+ }
4982
+ MENO_BIND_SENTINEL_RE.lastIndex = 0;
4983
+ }
4984
+ if (Object.keys(attrFields).length > 0) {
4985
+ el.menoBind = { ...el.menoBind || {}, attrFields };
4986
+ }
4987
+ }
4988
+ if (el.menoBind?.attrFields?.src && (el.imageDataBase64 || el.imageSrc)) {
4989
+ delete el.imageDataBase64;
4990
+ delete el.imageDataMime;
4991
+ delete el.imageDataFileName;
4992
+ delete el.imageSrc;
4993
+ }
4994
+ if (Array.isArray(el.children)) {
4995
+ for (const child of el.children) {
4996
+ if (typeof child !== "string") applyBindingMarkers(child);
4997
+ }
4998
+ }
4999
+ if (Array.isArray(el.inlineFallback)) {
5000
+ for (const child of el.inlineFallback) applyBindingMarkers(child);
5001
+ }
5002
+ }
5003
+ function normalizeListChildren(elements) {
5004
+ if (!Array.isArray(elements)) return;
5005
+ for (const el of elements) {
5006
+ if (typeof el === "string") continue;
5007
+ const tag = (el.tag || "").toLowerCase();
5008
+ if ((tag === "ul" || tag === "ol") && Array.isArray(el.children)) {
5009
+ el.children = el.children.map((child) => {
5010
+ if (typeof child === "string") return { tag: "li", children: [child] };
5011
+ if ((child.tag || "").toLowerCase() === "li") return child;
5012
+ return { tag: "li", children: [child] };
5013
+ });
5014
+ }
5015
+ if (Array.isArray(el.children)) normalizeListChildren(el.children);
5016
+ if (Array.isArray(el.inlineFallback)) normalizeListChildren(el.inlineFallback);
5017
+ }
5018
+ }
5019
+ async function emitListNode2(node, ctx, instanceProps) {
5020
+ const nodeType = node.type;
5021
+ const isLegacyCMSList = nodeType === "cms-list";
5022
+ const sourceType = isLegacyCMSList ? "collection" : node.sourceType || "prop";
5023
+ if (sourceType === "collection" && !ctx.cmsService) return [];
5024
+ const rawSource = node.source || node.collection;
5025
+ const source = typeof rawSource === "string" ? rawSource : "";
5026
+ const sourceIsResolved = Array.isArray(rawSource);
5027
+ let variableName;
5028
+ if (node.itemAs) variableName = node.itemAs;
5029
+ else if (sourceType === "collection") variableName = singularize2(source);
5030
+ else variableName = "item";
5031
+ if (sourceType === "collection" && ctx.bindCollectionLists && ctx.cmsService && source) {
5032
+ const schema = ctx.cmsService.getSchema(source) || void 0;
5033
+ const placeholder = buildBindingPlaceholderItem(schema);
5034
+ const templateContext = buildTemplateContext(
5035
+ variableName,
5036
+ placeholder,
5037
+ 0,
5038
+ 1,
5039
+ ctx.templateContext
5040
+ );
5041
+ const childCtx = {
5042
+ ...ctx,
5043
+ templateContext
5044
+ };
5045
+ const mergedProps = {
5046
+ ...instanceProps || {},
5047
+ ...templateContext
5048
+ };
5049
+ const renderedChildren = node.children ? await convertChildren(node.children, childCtx, mergedProps) : [];
5050
+ for (const child of renderedChildren) {
5051
+ if (typeof child !== "string") applyBindingMarkers(child);
5052
+ }
5053
+ return [{
5054
+ tag: COLLECTION_LIST_TAG,
5055
+ menoCollectionRef: source,
5056
+ children: renderedChildren
5057
+ }];
5058
+ }
5059
+ let items;
5060
+ if (sourceType === "collection") {
5061
+ items = await getCollectionItemsForExport(node, source, ctx);
5062
+ } else if (sourceIsResolved) {
5063
+ items = rawSource;
5064
+ } else if (source) {
5065
+ items = getPropItemsForExport(source, ctx, instanceProps);
5066
+ if (node.offset) items = items.slice(node.offset);
5067
+ if (node.limit) items = items.slice(0, node.limit);
5068
+ } else {
5069
+ items = [];
5070
+ }
5071
+ return expandListItems(node, ctx, instanceProps, items, variableName, source);
5072
+ }
5073
+ async function expandListItems(node, ctx, instanceProps, items, variableName, source) {
5074
+ if (items.length === 0) return [];
5075
+ const schema = ctx.cmsService ? ctx.cmsService.getSchema(source) || void 0 : void 0;
5076
+ const out = [];
5077
+ for (let i = 0; i < items.length; i++) {
5078
+ const rawItem = items[i];
5079
+ const enriched = schema && ctx.locale && ctx.i18nConfig ? addItemUrl(rawItem, schema, ctx.locale, ctx.i18nConfig) : rawItem;
5080
+ const item = resolveItemI18n(enriched, ctx);
5081
+ const templateContext = buildTemplateContext(
5082
+ variableName,
5083
+ item,
5084
+ i,
5085
+ items.length,
5086
+ ctx.templateContext
5087
+ );
5088
+ const childCtx = {
5089
+ ...ctx,
5090
+ templateContext
5091
+ };
5092
+ const mergedProps = {
5093
+ ...instanceProps || {},
5094
+ ...templateContext
5095
+ };
5096
+ if (node.children) {
5097
+ const savedPath = [...childCtx.elementPath];
5098
+ childCtx.elementPath = [...savedPath, i];
5099
+ out.push(...await convertChildren(node.children, childCtx, mergedProps));
5100
+ childCtx.elementPath = savedPath;
5101
+ }
5102
+ }
5103
+ return out;
5104
+ }
5105
+ async function emitLocaleListNode2(node, ctx, instanceProps) {
5106
+ if (!ctx.slugMappings || !ctx.pagePath || !ctx.i18nConfig || !ctx.locale) {
5107
+ return [{ tag: "div", attributes: { "data-locale-list": "true" } }];
5108
+ }
5109
+ if (!ctx.slugIndex) {
5110
+ ctx.slugIndex = buildSlugIndex(ctx.slugMappings);
5111
+ }
5112
+ const localeLinks = getLocaleLinks(ctx.pagePath, ctx.locale, ctx.i18nConfig, ctx.slugIndex);
5113
+ const showCurrent = node.showCurrent !== false;
5114
+ const showSeparator = node.showSeparator !== false;
5115
+ const showFlag = node.showFlag !== false;
5116
+ const displayType = node.displayType || "nativeName";
5117
+ const localeIconMap = /* @__PURE__ */ new Map();
5118
+ for (const lc of ctx.i18nConfig.locales) {
5119
+ if (lc.icon) localeIconMap.set(lc.code, lc.icon);
5120
+ }
5121
+ const containerStyle = substituteVarsInStyle(
5122
+ resolveStyleTemplates(
5123
+ node.style,
5124
+ instanceProps
5125
+ ),
5126
+ ctx
5127
+ );
5128
+ const containerInteractive = substituteVarsInInteractive(
5129
+ resolveInteractiveStyleTemplates(
5130
+ node.interactiveStyles,
5131
+ instanceProps
5132
+ ),
5133
+ ctx
5134
+ );
5135
+ let containerClass;
5136
+ if (containerStyle || containerInteractive && containerInteractive.length > 0) {
5137
+ const elementClass = withThemeSuffix(buildElementClass2(ctx, node.label), ctx);
5138
+ const { primaryClass } = mapStylesToWebflow(
5139
+ elementClass,
5140
+ containerStyle,
5141
+ containerInteractive,
5142
+ ctx.breakpoints,
5143
+ ctx.responsiveScales
5144
+ );
5145
+ substituteVarsInStyleClass(primaryClass, ctx);
5146
+ containerClass = primaryClass.name;
5147
+ ctx.styleClasses.set(primaryClass.name, primaryClass);
5148
+ }
5149
+ const buildSubClass = (style, suffix) => {
5150
+ const resolved = substituteVarsInStyle(
5151
+ resolveStyleTemplates(
5152
+ style,
5153
+ instanceProps
5154
+ ),
5155
+ ctx
5156
+ );
5157
+ if (!resolved) return void 0;
5158
+ const className = withThemeSuffix(`${buildElementClass2(ctx, node.label)}-${suffix}`, ctx);
5159
+ const { primaryClass } = mapStylesToWebflow(className, resolved, void 0, ctx.breakpoints, ctx.responsiveScales);
5160
+ substituteVarsInStyleClass(primaryClass, ctx);
5161
+ if (Object.keys(primaryClass.base).length === 0 && !primaryClass.breakpoints && !primaryClass.pseudoStates) return void 0;
5162
+ ctx.styleClasses.set(primaryClass.name, primaryClass);
5163
+ return primaryClass.name;
5164
+ };
5165
+ const itemClassName = buildSubClass(node.itemStyle, "item");
5166
+ const activeItemClassName = buildSubClass(node.activeItemStyle, "item-active");
5167
+ const separatorClassName = buildSubClass(node.separatorStyle, "separator");
5168
+ const flagClassName = buildSubClass(node.flagStyle, "flag");
5169
+ const linkChildren = [];
5170
+ for (let i = 0; i < localeLinks.length; i++) {
5171
+ const link = localeLinks[i];
5172
+ if (!showCurrent && link.isCurrent) continue;
5173
+ if (i > 0 && showSeparator) {
5174
+ const sep = { tag: "span" };
5175
+ if (separatorClassName) sep.className = separatorClassName;
5176
+ linkChildren.push(sep);
5177
+ }
5178
+ const anchor = {
5179
+ tag: "a",
5180
+ attributes: {
5181
+ href: link.path,
5182
+ hreflang: link.langTag,
5183
+ "data-current": link.isCurrent ? "true" : "false",
5184
+ "data-locale": link.locale
5185
+ }
5186
+ };
5187
+ if (link.isCurrent && (activeItemClassName || itemClassName)) {
5188
+ anchor.className = itemClassName;
5189
+ if (activeItemClassName) anchor.comboClasses = [activeItemClassName];
5190
+ } else if (itemClassName) {
5191
+ anchor.className = itemClassName;
5192
+ }
5193
+ const innerChildren = [];
5194
+ const icon = localeIconMap.get(link.locale);
5195
+ if (showFlag && icon) {
5196
+ const img = {
5197
+ tag: "img",
5198
+ attributes: { src: icon, alt: `${link.nativeName} flag` }
5199
+ };
5200
+ if (flagClassName) img.className = flagClassName;
5201
+ innerChildren.push(img);
5202
+ }
5203
+ let displayText;
5204
+ switch (displayType) {
5205
+ case "code":
5206
+ displayText = link.locale.toUpperCase();
5207
+ break;
5208
+ case "name":
5209
+ displayText = link.name;
5210
+ break;
5211
+ case "nativeName":
5212
+ default:
5213
+ displayText = link.nativeName;
5214
+ break;
5215
+ }
5216
+ innerChildren.push({ tag: "div", textContent: displayText });
5217
+ anchor.children = innerChildren;
5218
+ linkChildren.push(anchor);
5219
+ }
5220
+ const wrapper = {
5221
+ tag: "div",
5222
+ attributes: { "data-locale-list": "true" },
5223
+ children: linkChildren
5224
+ };
5225
+ if (containerClass) wrapper.className = containerClass;
5226
+ return [wrapper];
5227
+ }
5228
+ async function convertChildren(children, ctx, instanceProps) {
3951
5229
  if (!children) return [];
3952
5230
  if (typeof children === "string") {
3953
5231
  return nodeToWebflow(children, ctx, instanceProps);
@@ -3967,7 +5245,7 @@ function scanJSONFiles2(dir, prefix = "") {
3967
5245
  if (entry.isFile() && entry.name.endsWith(".json")) {
3968
5246
  results.push(prefix ? `${prefix}/${entry.name}` : entry.name);
3969
5247
  } else if (entry.isDirectory()) {
3970
- results.push(...scanJSONFiles2(join2(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name));
5248
+ results.push(...scanJSONFiles2(join3(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name));
3971
5249
  }
3972
5250
  }
3973
5251
  return results;
@@ -3975,12 +5253,38 @@ function scanJSONFiles2(dir, prefix = "") {
3975
5253
  function isCMSPage2(pageData) {
3976
5254
  return pageData.meta?.source === "cms" && !!pageData.meta?.cms;
3977
5255
  }
3978
- function buildCMSItemPath(urlPattern, item, slugField, locale, i18nConfig) {
3979
- let slug = item[slugField] ?? item._slug ?? item._id;
3980
- if (isI18nValue(slug)) {
3981
- slug = resolveI18nValue(slug, locale, i18nConfig);
5256
+ function flattenCMSItemForLocale(item, locale, i18nConfig) {
5257
+ const flatten = (value) => {
5258
+ if (value === null || value === void 0) return value;
5259
+ if (isI18nValue(value)) {
5260
+ return flatten(resolveI18nValue(value, locale, i18nConfig));
5261
+ }
5262
+ if (typeof value === "object" && "__richtext__" in value) {
5263
+ const html = value.html;
5264
+ if (typeof html === "string") return RAW_HTML_PREFIX + html;
5265
+ }
5266
+ return value;
5267
+ };
5268
+ const out = {};
5269
+ for (const [key, value] of Object.entries(item)) {
5270
+ out[key] = flatten(value);
3982
5271
  }
3983
- return urlPattern.replace("{{slug}}", String(slug));
5272
+ return out;
5273
+ }
5274
+ function resolveMetaString(value, locale, i18nConfig) {
5275
+ if (value === void 0 || value === null || value === "") return void 0;
5276
+ const resolved = isI18nValue(value) ? resolveI18nValue(value, locale, i18nConfig) : value;
5277
+ if (resolved === void 0 || resolved === null || resolved === "") return void 0;
5278
+ return String(resolved);
5279
+ }
5280
+ function buildPageMetaForLocale(meta, locale, i18nConfig) {
5281
+ return {
5282
+ description: resolveMetaString(meta.description, locale, i18nConfig),
5283
+ keywords: resolveMetaString(meta.keywords, locale, i18nConfig),
5284
+ ogTitle: resolveMetaString(meta.ogTitle, locale, i18nConfig),
5285
+ ogDescription: resolveMetaString(meta.ogDescription, locale, i18nConfig),
5286
+ ogImage: resolveMetaString(meta.ogImage, locale, i18nConfig)
5287
+ };
3984
5288
  }
3985
5289
  function scanAssets(projectRoot) {
3986
5290
  const assets = [];
@@ -3991,7 +5295,7 @@ function scanAssets(projectRoot) {
3991
5295
  { dir: "assets", type: "file" }
3992
5296
  ];
3993
5297
  for (const { dir, type } of assetDirs) {
3994
- const fullDir = join2(projectRoot, dir);
5298
+ const fullDir = join3(projectRoot, dir);
3995
5299
  if (!existsSync2(fullDir)) continue;
3996
5300
  const files = scanJSONFiles2(fullDir).map((f) => f.replace(".json", ""));
3997
5301
  const allFiles = scanAllFiles(fullDir);
@@ -4014,27 +5318,50 @@ function scanAllFiles(dir, prefix = "") {
4014
5318
  if (entry.isFile()) {
4015
5319
  results.push(relativePath);
4016
5320
  } else if (entry.isDirectory()) {
4017
- results.push(...scanAllFiles(join2(dir, entry.name), relativePath));
5321
+ results.push(...scanAllFiles(join3(dir, entry.name), relativePath));
4018
5322
  }
4019
5323
  }
4020
5324
  return results;
4021
5325
  }
4022
- function extractCSSVariables(themeColorCSS, variablesCSS) {
4023
- const vars = {};
4024
- const regex = /--([\w-]+)\s*:\s*([^;]+)/g;
4025
- for (const css of [themeColorCSS, variablesCSS]) {
4026
- let match;
4027
- while ((match = regex.exec(css)) !== null) {
4028
- vars[`--${match[1]}`] = match[2].trim();
5326
+ function buildThemeVarMaps(themeConfig) {
5327
+ const palette = themeConfig.palette;
5328
+ const byTheme = {};
5329
+ for (const [themeName, theme] of Object.entries(themeConfig.themes)) {
5330
+ const vars = {};
5331
+ for (const [name, value] of Object.entries(theme.colors)) {
5332
+ vars[`--${name}`] = resolvePaletteColor(value, palette);
5333
+ }
5334
+ byTheme[themeName] = vars;
5335
+ }
5336
+ return { byTheme, defaultTheme: themeConfig.default };
5337
+ }
5338
+ function buildProjectVarMaps(variablesConfig, breakpoints, responsiveScales) {
5339
+ const out = { base: {} };
5340
+ for (const bpName of Object.keys(breakpoints)) out[bpName] = {};
5341
+ for (const variable of variablesConfig.variables) {
5342
+ if (!variable.cssVar) continue;
5343
+ out.base[variable.cssVar] = variable.value;
5344
+ for (const bpName of Object.keys(breakpoints)) {
5345
+ const resolved = resolveVariableValueAtBreakpoint(
5346
+ { value: variable.value, type: variable.type, scales: variable.scales },
5347
+ bpName,
5348
+ responsiveScales
5349
+ );
5350
+ out[bpName][variable.cssVar] = resolved;
4029
5351
  }
4030
5352
  }
4031
- return vars;
5353
+ return out;
4032
5354
  }
4033
- async function buildWebflowPayload(projectRoot) {
5355
+ async function buildWebflowPayload(options) {
5356
+ const bindCollectionLists = options?.bindCollectionLists === true;
5357
+ const promotedComponentNames = Array.isArray(options?.promotedComponentNames) ? new Set(options.promotedComponentNames.filter((n) => typeof n === "string" && n.length > 0)) : void 0;
4034
5358
  configService.reset();
4035
5359
  const projectConfig = await loadProjectConfig();
4036
5360
  const siteUrl = projectConfig.siteUrl?.replace(/\/$/, "") || "";
4037
5361
  const i18nConfig = await loadI18nConfig();
5362
+ const requestedLocale = options?.locale;
5363
+ const selectedLocale = requestedLocale && i18nConfig.locales.some((l) => l.code === requestedLocale) ? requestedLocale : i18nConfig.defaultLocale;
5364
+ const localesToBuild = i18nConfig.locales.filter((l) => l.code === selectedLocale);
4038
5365
  await migrateTemplatesDirectory();
4039
5366
  const { components } = await loadComponentDirectory(projectPaths.components());
4040
5367
  const globalComponents = {};
@@ -4047,8 +5374,8 @@ async function buildWebflowPayload(projectRoot) {
4047
5374
  const themeConfig = await colorService.loadThemeConfig();
4048
5375
  const variablesConfig = await variableService.loadConfig();
4049
5376
  const breakpoints = await loadBreakpointConfig();
4050
- const responsiveScales = await loadResponsiveScalesConfig();
4051
5377
  await configService.load();
5378
+ const responsiveScales = configService.getResponsiveScales();
4052
5379
  const pagesDir = projectPaths.pages();
4053
5380
  if (!existsSync2(pagesDir)) {
4054
5381
  return emptyPayload();
@@ -4061,7 +5388,7 @@ async function buildWebflowPayload(projectRoot) {
4061
5388
  for (const file of pageFiles) {
4062
5389
  const pageName = file.replace(".json", "");
4063
5390
  const basePath = mapPageNameToPath(pageName);
4064
- const pageContent = await loadJSONFile(join2(pagesDir, file));
5391
+ const pageContent = await loadJSONFile(join3(pagesDir, file));
4065
5392
  if (!pageContent) continue;
4066
5393
  try {
4067
5394
  const pageData = parseJSON(pageContent);
@@ -4074,16 +5401,22 @@ async function buildWebflowPayload(projectRoot) {
4074
5401
  }
4075
5402
  const allPages = [];
4076
5403
  const allStyleClasses = /* @__PURE__ */ new Map();
5404
+ const allInteractiveStylesMap = /* @__PURE__ */ new Map();
5405
+ const allComboIdentityByName = /* @__PURE__ */ new Map();
5406
+ const promotedComponents = /* @__PURE__ */ new Map();
5407
+ const variablesCSS = generateVariablesCSS(variablesConfig, breakpoints, responsiveScales);
5408
+ const projectVars = buildProjectVarMaps(variablesConfig, breakpoints, responsiveScales);
5409
+ const { byTheme: themeVars, defaultTheme } = buildThemeVarMaps(themeConfig);
4077
5410
  for (const file of pageFiles) {
4078
5411
  const pageName = file.replace(".json", "");
4079
5412
  const basePath = mapPageNameToPath(pageName);
4080
- const pageContent = await loadJSONFile(join2(pagesDir, file));
5413
+ const pageContent = await loadJSONFile(join3(pagesDir, file));
4081
5414
  if (!pageContent) continue;
4082
5415
  try {
4083
5416
  const pageData = parseJSON(pageContent);
4084
5417
  if (pageData.meta?.draft === true) continue;
4085
5418
  const slugs = pageData.meta?.slugs;
4086
- for (const localeConfig of i18nConfig.locales) {
5419
+ for (const localeConfig of localesToBuild) {
4087
5420
  const locale = localeConfig.code;
4088
5421
  const isDefault = locale === i18nConfig.defaultLocale;
4089
5422
  let slug;
@@ -4113,16 +5446,32 @@ async function buildWebflowPayload(projectRoot) {
4113
5446
  fileType: "page",
4114
5447
  fileName: pageName,
4115
5448
  breakpoints,
4116
- styleClasses: allStyleClasses
5449
+ styleClasses: allStyleClasses,
5450
+ comboIdentityByName: allComboIdentityByName,
5451
+ interactiveStylesMap: allInteractiveStylesMap,
5452
+ cmsService,
5453
+ i18nConfig,
5454
+ locale,
5455
+ slugMappings,
5456
+ pagePath: urlPath,
5457
+ themeVars,
5458
+ projectVars,
5459
+ defaultTheme,
5460
+ responsiveScales,
5461
+ promotedComponents,
5462
+ promotedComponentNames,
5463
+ bindCollectionLists
4117
5464
  };
4118
5465
  const body = pageData.root || pageData.node;
4119
- const elements = body ? nodeToWebflow(body, ctx) : [];
5466
+ const elements = body ? await nodeToWebflow(body, ctx) : [];
5467
+ normalizeListChildren(elements);
5468
+ const pageMeta = extractPageMeta(pageData);
4120
5469
  allPages.push({
4121
5470
  title: result.title,
4122
5471
  slug: slug || "index",
4123
- metaDescription: typeof pageData.meta?.description === "string" ? pageData.meta.description : void 0,
4124
5472
  elements,
4125
- locale
5473
+ locale,
5474
+ ...buildPageMetaForLocale(pageMeta, locale, i18nConfig)
4126
5475
  });
4127
5476
  }
4128
5477
  } catch (error) {
@@ -4130,11 +5479,10 @@ async function buildWebflowPayload(projectRoot) {
4130
5479
  }
4131
5480
  }
4132
5481
  const templatesDir = projectPaths.templates();
4133
- const cmsCollections = [];
4134
5482
  if (existsSync2(templatesDir)) {
4135
5483
  const templateFiles = readdirSync2(templatesDir).filter((f) => f.endsWith(".json"));
4136
5484
  for (const file of templateFiles) {
4137
- const templateContent = await loadJSONFile(join2(templatesDir, file));
5485
+ const templateContent = await loadJSONFile(join3(templatesDir, file));
4138
5486
  if (!templateContent) continue;
4139
5487
  try {
4140
5488
  const pageData = parseJSON(templateContent);
@@ -4142,92 +5490,137 @@ async function buildWebflowPayload(projectRoot) {
4142
5490
  if (!isCMSPage2(pageData)) continue;
4143
5491
  const cmsSchema = pageData.meta.cms;
4144
5492
  const items = await cmsService.queryItems({ collection: cmsSchema.id });
4145
- const fields = [];
4146
- if (cmsSchema.fields) {
4147
- for (const [fieldName, fieldDef] of Object.entries(cmsSchema.fields)) {
4148
- fields.push({
4149
- name: fieldDef.label || fieldName,
4150
- slug: fieldName,
4151
- type: mapCMSFieldType(fieldDef.type),
4152
- required: fieldDef.required,
4153
- options: fieldDef.options
4154
- });
4155
- }
4156
- }
4157
- const resolvedItems = [];
4158
- for (const item of items) {
4159
- const resolved = {};
4160
- for (const [key, value] of Object.entries(item)) {
4161
- if (isI18nValue(value)) {
4162
- resolved[key] = resolveI18nValue(value, i18nConfig.defaultLocale, i18nConfig);
4163
- } else {
4164
- resolved[key] = value;
4165
- }
4166
- }
4167
- resolvedItems.push(resolved);
4168
- }
4169
- cmsCollections.push({
4170
- name: cmsSchema.id,
4171
- slug: cmsSchema.id,
4172
- urlPattern: cmsSchema.urlPattern,
4173
- fields,
4174
- items: resolvedItems
4175
- });
4176
- for (const item of items) {
4177
- for (const localeConfig of i18nConfig.locales) {
4178
- const locale = localeConfig.code;
4179
- if (isItemDraftForLocale(item, locale)) continue;
4180
- const itemPath = buildCMSItemPath(cmsSchema.urlPattern, item, cmsSchema.slugField, locale, i18nConfig);
4181
- const itemWithUrl = { ...item, _url: itemPath };
4182
- const result = await renderPageSSR(
4183
- pageData,
4184
- globalComponents,
4185
- itemPath,
4186
- siteUrl,
4187
- locale,
4188
- i18nConfig,
4189
- slugMappings,
4190
- { cms: itemWithUrl },
4191
- cmsService,
4192
- true
4193
- );
4194
- const ctx = {
4195
- globalComponents,
4196
- elementPath: [0],
4197
- fileType: "page",
4198
- fileName: file.replace(".json", ""),
4199
- breakpoints,
4200
- styleClasses: allStyleClasses
4201
- };
4202
- const body = pageData.root || pageData.node;
4203
- const cmsProps = { cms: itemWithUrl };
4204
- const elements = body ? nodeToWebflow(body, ctx, cmsProps) : [];
4205
- const slug = itemPath.startsWith("/") ? itemPath.substring(1) : itemPath;
4206
- allPages.push({
4207
- title: result.title,
4208
- slug,
4209
- elements,
4210
- locale
4211
- });
5493
+ if (items.length === 0) continue;
5494
+ const item = items[0];
5495
+ const pageName = file.replace(".json", "");
5496
+ for (const localeConfig of localesToBuild) {
5497
+ const locale = localeConfig.code;
5498
+ let itemSlug = item[cmsSchema.slugField] ?? item._slug ?? item._id;
5499
+ if (isI18nValue(itemSlug)) {
5500
+ itemSlug = resolveI18nValue(itemSlug, locale, i18nConfig);
4212
5501
  }
5502
+ const resolvedItemPath = cmsSchema.urlPattern.replace("{{slug}}", String(itemSlug));
5503
+ const isDefault = locale === i18nConfig.defaultLocale;
5504
+ const localizedItemPath = isDefault ? resolvedItemPath : `/${locale}${resolvedItemPath.startsWith("/") ? "" : "/"}${resolvedItemPath}`;
5505
+ const slug = localizedItemPath.startsWith("/") ? localizedItemPath.substring(1) : localizedItemPath;
5506
+ const urlPath = localizedItemPath;
5507
+ const flatItem = flattenCMSItemForLocale(item, locale, i18nConfig);
5508
+ const itemWithUrl = { ...flatItem, _url: urlPath };
5509
+ const ssrItem = { ...item, _url: urlPath };
5510
+ const result = await renderPageSSR(
5511
+ pageData,
5512
+ globalComponents,
5513
+ urlPath,
5514
+ siteUrl,
5515
+ locale,
5516
+ i18nConfig,
5517
+ slugMappings,
5518
+ { cms: ssrItem },
5519
+ cmsService,
5520
+ true
5521
+ );
5522
+ const ctx = {
5523
+ globalComponents,
5524
+ elementPath: [0],
5525
+ fileType: "page",
5526
+ fileName: pageName,
5527
+ breakpoints,
5528
+ styleClasses: allStyleClasses,
5529
+ comboIdentityByName: allComboIdentityByName,
5530
+ interactiveStylesMap: allInteractiveStylesMap,
5531
+ cmsService,
5532
+ i18nConfig,
5533
+ locale,
5534
+ slugMappings,
5535
+ pagePath: urlPath,
5536
+ cmsContext: { cms: itemWithUrl },
5537
+ // Seed `templateContext` with the bound item so descendants inside
5538
+ // a component slot still see `cms`. `emitInlineComponentBody`
5539
+ // merges `ctx.templateContext` into the body's `instanceProps`
5540
+ // (nodeToWebflow.ts:1140-1143), and `<slot>` rendering forwards
5541
+ // those merged props to slot children — without this, anything
5542
+ // wrapped in (e.g.) Section loses the page-level CMS scope and
5543
+ // `{{cms.title}}` resolves to empty.
5544
+ templateContext: { cms: itemWithUrl },
5545
+ themeVars,
5546
+ projectVars,
5547
+ defaultTheme,
5548
+ responsiveScales,
5549
+ promotedComponents,
5550
+ promotedComponentNames,
5551
+ bindCollectionLists
5552
+ };
5553
+ const body = pageData.root || pageData.node;
5554
+ const elements = body ? await nodeToWebflow(body, ctx, { cms: itemWithUrl }) : [];
5555
+ normalizeListChildren(elements);
5556
+ const cmsPageMeta = extractPageMeta(pageData);
5557
+ allPages.push({
5558
+ title: result.title,
5559
+ slug,
5560
+ elements,
5561
+ locale,
5562
+ ...buildPageMetaForLocale(cmsPageMeta, locale, i18nConfig)
5563
+ });
4213
5564
  }
4214
5565
  } catch (error) {
4215
5566
  console.error(`Error processing template ${file}:`, error?.message);
4216
5567
  }
4217
5568
  }
4218
5569
  }
4219
- const themeColorCSS = generateThemeColorVariablesCSS(themeConfig);
4220
- const variablesCSS = generateVariablesCSS(variablesConfig, breakpoints, responsiveScales);
4221
- const cssVariables = extractCSSVariables(themeColorCSS, variablesCSS);
5570
+ for (const def of promotedComponents.values()) {
5571
+ normalizeListChildren(def.elements);
5572
+ }
5573
+ const styleClasses = Array.from(allStyleClasses.values());
4222
5574
  const assets = scanAssets(projectPaths.project);
5575
+ const scripts = [];
5576
+ const cssBlocks = [];
5577
+ for (const [name, def] of Object.entries(globalComponents)) {
5578
+ const code = def.component?.javascript;
5579
+ if (typeof code === "string" && code.trim().length > 0) {
5580
+ scripts.push({
5581
+ componentName: name,
5582
+ code,
5583
+ defineVars: def.component?.defineVars
5584
+ });
5585
+ }
5586
+ const css = def.component?.css;
5587
+ if (typeof css === "string" && css.trim().length > 0) {
5588
+ cssBlocks.push(`/* ${name}.css */
5589
+ ${css.trim()}`);
5590
+ }
5591
+ }
5592
+ const componentCss = cssBlocks.length > 0 ? cssBlocks.join("\n\n") : void 0;
5593
+ const interactiveCssBlocks = [];
5594
+ for (const [elementClass, rules] of allInteractiveStylesMap) {
5595
+ const customRules = rules.filter((r) => !isWebflowHandledRule(r));
5596
+ if (customRules.length === 0) continue;
5597
+ const css = generateInteractiveCSS(elementClass, customRules, breakpoints, void 0, responsiveScales);
5598
+ if (css && css.trim().length > 0) {
5599
+ interactiveCssBlocks.push(css);
5600
+ }
5601
+ }
5602
+ const interactiveCss = interactiveCssBlocks.length > 0 ? interactiveCssBlocks.join("\n\n") : void 0;
4223
5603
  return {
4224
5604
  version: 1,
4225
5605
  exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
4226
5606
  pages: allPages,
4227
- styles: Array.from(allStyleClasses.values()),
4228
- cms: cmsCollections,
5607
+ styles: styleClasses,
5608
+ cms: [],
4229
5609
  assets,
4230
- cssVariables: Object.keys(cssVariables).length > 0 ? cssVariables : void 0
5610
+ slugMappings: slugMappings.length > 0 ? slugMappings : void 0,
5611
+ scripts: scripts.length > 0 ? scripts : void 0,
5612
+ components: promotedComponents.size > 0 ? Array.from(promotedComponents.values()) : void 0,
5613
+ componentCss,
5614
+ interactiveCss,
5615
+ i18n: {
5616
+ defaultLocale: i18nConfig.defaultLocale,
5617
+ locales: i18nConfig.locales.map((l) => ({
5618
+ code: l.code,
5619
+ name: l.name,
5620
+ nativeName: l.nativeName
5621
+ })),
5622
+ selectedLocale
5623
+ }
4231
5624
  };
4232
5625
  }
4233
5626
  function emptyPayload() {
@@ -4241,6 +5634,32 @@ function emptyPayload() {
4241
5634
  };
4242
5635
  }
4243
5636
 
5637
+ // lib/server/webflow/templateWrapper.ts
5638
+ import { readFile as readFile3 } from "fs/promises";
5639
+ var cachedTemplate = null;
5640
+ async function getWebflowTemplate(appName) {
5641
+ if (cachedTemplate) return cachedTemplate;
5642
+ const url = `https://webflow-ext.com/template/v2?name=${encodeURIComponent(appName)}`;
5643
+ const res = await fetch(url);
5644
+ if (!res.ok) throw new Error(`Failed to fetch Webflow template: ${res.status}`);
5645
+ cachedTemplate = await res.text();
5646
+ return cachedTemplate;
5647
+ }
5648
+ async function wrapInWebflowTemplate(html, manifestPath) {
5649
+ try {
5650
+ const manifest = JSON.parse(await readFile3(manifestPath, "utf-8"));
5651
+ const template = await getWebflowTemplate(manifest.name || "Meno Import");
5652
+ const headMatch = html.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
5653
+ const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
5654
+ const headContent = headMatch ? headMatch[1] : "";
5655
+ const bodyContent = bodyMatch ? bodyMatch[1] : "";
5656
+ return template.replace("{{ui}}", headContent + bodyContent);
5657
+ } catch (err) {
5658
+ console.warn("Could not fetch Webflow wrapper template:", err.message);
5659
+ return html;
5660
+ }
5661
+ }
5662
+
4244
5663
  // lib/server/index.ts
4245
5664
  init_constants();
4246
5665
  export {
@@ -4336,6 +5755,7 @@ export {
4336
5755
  variableService,
4337
5756
  withErrorHandling,
4338
5757
  withLogging,
5758
+ wrapInWebflowTemplate,
4339
5759
  writeFile
4340
5760
  };
4341
5761
  //# sourceMappingURL=index.js.map